博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
binary hacks读数笔记(装载)
阅读量:5234 次
发布时间:2019-06-14

本文共 10296 字,大约阅读时间需要 34 分钟。

1、地址空间

    在linux系统中,每个进程拥有自己独立的虚拟地址空间,这个虚拟地址空间的大小是由计算机硬件决定的,具体地说,是由CPU的位数决定的。比如,32位硬件平台决定的虚拟地址空间大小:0——232-1。即0x00000000——0xFFFFFFFF。

  在linux系统中,整个4GB内存空间被分成两部分。其中操作系统本身用去一部分,从地址0xC0000000到0xFFFFFFFF。剩下的低地址的0x00000000到0xBFFFFFFF的3GB虚拟空间由进程使用。

  装载有两种方式——覆盖装入与页映射。现在操作系统主要是页映射方式。装载过程在创建进程时主要做以下三件事:

1、创建一个独立的虚拟地址空间;

2、读取可执行文件头,并且建立虚拟地址空间与可执行文件的映射;

3、将CPU指令寄存器设置成可执行文件的入口地址,启动执行。

创建虚拟地址空间并不是创建空间,而是创建映射函数所需要的数据结构,实际上就是分配一个页目录,甚至不用设置页映射关系,这些映射关系可以等到程序发生页错误时在进行设置。

读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系:当程序发生页错误的时候,操作系统将从物理内存中分配一个物理页,然后将该”缺页”从磁盘中读取到内存中,再设置缺页的虚拟页和物理页的映射关系。当操作系统捕获到缺页错误时,它应知道程序当前所需要的页在可执行文件中的哪一个位置。这就是虚拟空间与可执行文件之间的映射关系。Linux中将进程虚拟空间中的一个段叫做虚拟内存区域(VMA),在Windows中将这个叫做”虚拟段”。

将CPU指令寄存器设置成可执行文件入口,启动运行:操作系统通过设置CPU的指令寄存器将控制权交给进程,由此进程开始执行。

2、页错误

当CPU开始打算执行一个地址指令时,发现页面是空页面,于是它认为这是一个页错误。CPU将控制权交给操作系统,操作系统将查询第二步建立的数据结构,然后找到VMA,计算出相应的页面在可执行文件中的偏移,然后在物理内存中分配一个物理页面,将进程中该虚拟页与分配的物理页之间建立映射关系,然后把控制权交回给进程,进程从刚才错误页位置重新开始执行。

3、 进程虚存空间分布

3.1、ELF文件链接视图和执行视图

如果可执行文件只有一个代码段,所有它被操作系统转载至进程地址空间之后,相对应的只有一个VMA。

当段的数量增多时,就会产生空间浪费问题。为了避免这种问题,可以把相同的段合并。

看一个例子:

1 #include
2 3 int main() 4 { 5 while(1) 6 { 7 sleep(1000); 8 } 9 return 0; 10 }

gcc -static SectionMapping.c -o SectionMapping.elf

下面看一下,上述elf文件的section分布。

[root@tlinux /]# readelf -S SectionMapping.elf There are 34 section headers, starting at offset 0xd0aa0:Section Headers:  [Nr] Name              Type             Address           Offset       Size              EntSize          Flags  Link  Info  Align  [ 0]                   NULL             0000000000000000  00000000       0000000000000000  0000000000000000           0     0     0  [ 1] .note.ABI-tag     NOTE             0000000000400190  00000190       0000000000000020  0000000000000000   A       0     0     4  [ 2] .note.gnu.build-i NOTE             00000000004001b0  000001b0       0000000000000024  0000000000000000   A       0     0     4  [ 3] .rela.plt         RELA             00000000004001d8  000001d8       0000000000000108  0000000000000018  AI       0    25     8  [ 4] .init             PROGBITS         00000000004002e0  000002e0       000000000000001a  0000000000000000  AX       0     0     4  [ 5] .plt              PROGBITS         0000000000400300  00000300       00000000000000b0  0000000000000000  AX       0     0     16  [ 6] .text             PROGBITS         00000000004003b0  000003b0       0000000000091916  0000000000000000  AX       0     0     16  [ 7] __libc_thread_fre PROGBITS         0000000000491cd0  00091cd0       00000000000000b2  0000000000000000  AX       0     0     16  [ 8] __libc_freeres_fn PROGBITS         0000000000491d90  00091d90       0000000000001aef  0000000000000000  AX       0     0     16  [ 9] .fini             PROGBITS         0000000000493880  00093880       0000000000000009  0000000000000000  AX       0     0     4  [10] .rodata           PROGBITS         00000000004938a0  000938a0       00000000000196f8  0000000000000000   A       0     0     32  [11] .stapsdt.base     PROGBITS         00000000004acf98  000acf98       0000000000000001  0000000000000000   A       0     0     1  [12] __libc_thread_sub PROGBITS         00000000004acfa0  000acfa0       0000000000000008  0000000000000000   A       0     0     8  [13] __libc_subfreeres PROGBITS         00000000004acfa8  000acfa8       0000000000000050  0000000000000000   A       0     0     8  [14] __libc_IO_vtables PROGBITS         00000000004ad000  000ad000       00000000000006a8  0000000000000000   A       0     0     32  [15] __libc_atexit     PROGBITS         00000000004ad6a8  000ad6a8       0000000000000008  0000000000000000   A       0     0     8  [16] .eh_frame         PROGBITS         00000000004ad6b0  000ad6b0       000000000000e234  0000000000000000   A       0     0     8  [17] .gcc_except_table PROGBITS         00000000004bb8e4  000bb8e4       0000000000000105  0000000000000000   A       0     0     1  [18] .tdata            PROGBITS         00000000006bbeb0  000bbeb0       0000000000000020  0000000000000000 WAT       0     0     16  [19] .tbss             NOBITS           00000000006bbed0  000bbed0       0000000000000038  0000000000000000 WAT       0     0     16  [20] .init_array       INIT_ARRAY       00000000006bbed0  000bbed0       0000000000000010  0000000000000008  WA       0     0     8  [21] .fini_array       FINI_ARRAY       00000000006bbee0  000bbee0       0000000000000010  0000000000000008  WA       0     0     8  [22] .jcr              PROGBITS         00000000006bbef0  000bbef0       0000000000000008  0000000000000000  WA       0     0     8  [23] .data.rel.ro      PROGBITS         00000000006bbf00  000bbf00       00000000000000e4  0000000000000000  WA       0     0     32  [24] .got              PROGBITS         00000000006bbfe8  000bbfe8       0000000000000008  0000000000000008  WA       0     0     8  [25] .got.plt          PROGBITS         00000000006bc000  000bc000       0000000000000070  0000000000000008  WA       0     0     8  [26] .data             PROGBITS         00000000006bc080  000bc080       0000000000001690  0000000000000000  WA       0     0     32  [27] .bss              NOBITS           00000000006bd720  000bd710       0000000000002158  0000000000000000  WA       0     0     32  [28] __libc_freeres_pt NOBITS           00000000006bf878  000bd710       0000000000000030  0000000000000000  WA       0     0     8  [29] .comment          PROGBITS         0000000000000000  000bd710       000000000000002d  0000000000000001  MS       0     0     1  [30] .note.stapsdt     NOTE             0000000000000000  000bd740       0000000000000f88  0000000000000000           0     0     4  [31] .symtab           SYMTAB           0000000000000000  000be6c8       000000000000b9e8  0000000000000018          32   815     8  [32] .strtab           STRTAB           0000000000000000  000ca0b0       0000000000006870  0000000000000000           0     0     1  [33] .shstrtab         STRTAB           0000000000000000  000d0920       000000000000017b  0000000000000000           0     0     1Key to Flags:  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),  L (link order), O (extra OS processing required), G (group), T (TLS),  C (compressed), x (unknown), o (OS specific), E (exclude),  l (large), p (processor specific)

操作系统至关心段的权限(可读,可写,可执行)。

ELF文件中,段的权限基本是三种:

  • 以代码段为代表的权限可读可执行的段
  • 以数据段和BSS段为代码的权限为可读可写的段
  • 以只读数据为代表的权限为只读的段

一个简单的方案:对于相同权限的段,把它们合并到一起当作一个段进行映射。

一个”Segment”包含一个或多个属性类似的”Section”。
从链接的角度看,ELF文件是按”Section”存储的,从装载的角度看ELF文件又可以按照”Segment”划分。
“Section”和”Segment”是从不同角度来划分同一个ELF文件的。这个在ELF种被称为视图,从”Section”的角度来看ELF文件就是链接视图,从”Segment”的角度来看就是执行视图

 

 ELF可执行文件中有一个专门的数据结构叫做程序头表用来保存”Segment”信息。

数据段和BSS段唯一区别就是:数据段从文件中初始化内容,而BSS段的内容全都初始化为0。

程序的程序头可由readelf -l SectionMapping.elf 获取

Elf file type is EXEC (Executable file)Entry point 0x400ecdThere are 6 program headers, starting at offset 64Program Headers:  Type           Offset             VirtAddr           PhysAddr                 FileSiz            MemSiz              Flags  Align  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000                 0x00000000000bb9e9 0x00000000000bb9e9  R E    200000  LOAD           0x00000000000bbeb0 0x00000000006bbeb0 0x00000000006bbeb0                 0x0000000000001860 0x00000000000039f8  RW     200000  NOTE           0x0000000000000190 0x0000000000400190 0x0000000000400190                 0x0000000000000044 0x0000000000000044  R      4  TLS            0x00000000000bbeb0 0x00000000006bbeb0 0x00000000006bbeb0                 0x0000000000000020 0x0000000000000058  R      10  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000                 0x0000000000000000 0x0000000000000000  RW     10  GNU_RELRO      0x00000000000bbeb0 0x00000000006bbeb0 0x00000000006bbeb0                 0x0000000000000150 0x0000000000000150  R      1 Section to Segment mapping:  Segment Sections...   00     .note.ABI-tag .note.gnu.build-id .rela.plt .init .plt .text __libc_thread_freeres_fn __libc_freeres_fn .fini .rodata .stapsdt.base __libc_thread_subfreeres __libc_subfreeres __libc_IO_vtables __libc_atexit .eh_frame .gcc_except_table    01     .tdata .init_array .fini_array .jcr .data.rel.ro .got .got.plt .data .bss __libc_freeres_ptrs    02     .note.ABI-tag .note.gnu.build-id    03     .tdata .tbss    04        05     .tdata .init_array .fini_array .jcr .data.rel.ro .got

 

6.4.2 堆和栈

操作系统通过使用VMA来对进程的地址空间进行管理。进程在执行的时候它还需要用到等空间。一个进程中的栈和堆分别对应一个VMA。

进程虚拟地址空间的概念:操作系统通过给进程空间划分一个个VMA来管理进程的虚拟空间;基本原则就是将相同权限属性的、有相同映像文件的映射成一个VMA;一个进程基本上可以分为几种VMA区域:

    • 代码VMA,权限只读、可执行;有映像文件。
    • 数据VMA,权限可读写、可执行;有映像文件。
    • 堆VMA,权限可读写、可执行;无映像文件,匿名,可向上扩展。
    • 栈VMA,权限可读写、不可执行;无映像文件,匿名,可向下扩展。

6.4.3 堆的最大申请数量

理论上Linum下虚拟地址空间分给进程本身的是3GB,win是2GB,但实际上,Linux分配的程序的最大内存是2.9GB,win分配的为1.5GB。

有些操作系统使用了一种叫做随机地址空间分布技术,使得进程的堆空间变小。

下面的代码可以简单查看操作系统可申请堆大小:

1 #include
2 #include
3 4 unsigned maxinum=0; 5 int main() 6 { 7 8 unsigned blocked[]={
1024*1024,1024,1}; 9 int i,count; 10 for(i=0;i<3;i++){ 11 for(count=1;;count++){ 12 void* block=malloc(maxinum+blocked[i]*count); 13 if(block){ 14 maxinum=maxinum+blocked[i]*count; 15 }else{ 16 17 break; 18 } 19 } 20 } 21 printf("maxinum malloc size= %u bytes\n",maxinum); 22 }

 

6.4.4 段地址对齐

可执行文件最终是要被操作系统装载运行的,这个装载的过程一般是通过虚拟内存页映射机制完成。在映射过程中,页是映射的最小单位。

6.4.5 进程栈初始化

进程刚开始启动的时候,须知道一些进程运行的环境,最基本的就是系统环境变量和进程的运行参数,很常见的一种做法是操作系统在进程启动前将这些信息提前保存到进程的虚拟空间的栈中。可以看一下,进程地址空间中堆、栈等的分布:

./SectionMapping.elf &

[1] 59117
[root@tlinux /]# cat /proc/59117/maps
00400000-004bc000 r-xp 00000000 08:05 4456                               /SectionMapping.elf
006bb000-006be000 rw-p 000bb000 08:05 4456                               /SectionMapping.elf
006be000-006c0000 rw-p 00000000 00:00 0 
017e1000-01804000 rw-p 00000000 00:00 0                                  [heap]
7ffc12ce6000-7ffc12d07000 rw-p 00000000 00:00 0                          [stack]
7ffc12db7000-7ffc12db9000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

6.5 Linux内核装载ELF过程简介

首先在用户层面,bash进程会调用fork()系统调用创建一个新的进程,然后新的进程调用execve()系统调用执行指定的ELF文件,原先的bash进程继续返回等待刚才启动的新进程结束,然后继续等待用户输入命令。

转载于:https://www.cnblogs.com/wsw-seu/p/10634908.html

你可能感兴趣的文章
[转载]oracle xml操作
查看>>
Java并发--Java中的CAS操作和实现原理
查看>>
理解serialVersionUID是什么?有什么用?如何生成?
查看>>
java1.8新特性整理(全)
查看>>
java.util.ConcurrentModificationException 异常问题详解
查看>>
快速安装python3
查看>>
elementUI之通过指定 Table 组件的 row-class-name 属性来为 Table 中的某一行添加 class改变该行的颜色等样式。...
查看>>
小技巧
查看>>
深度学习图像配准 Image Registration: From SIFT to Deep Learning
查看>>
检测算法简介及其原理——fast R-CNN,faster R-CNN,YOLO,SSD,YOLOv2,YOLOv3
查看>>
随机采样方法整理与讲解(Acceptance-Rejection、MCMC、Gibbs Sampling等)
查看>>
高斯混合模型(GMM)及MATLAB代码
查看>>
MATLAB 可以画的各种类型的图总结
查看>>
全面解读Group Normalization,对比BN,LN,IN
查看>>
VLAD算法浅析, BOF、FV比较
查看>>
RAdam VS Adam
查看>>
可分离卷积详解及计算量 Basic Introduction to Separable Convolutions
查看>>
CNN中各类卷积总结:残差、shuffle、空洞卷积、变形卷积核、可分离卷积等
查看>>
Mean Average Precision(mAP),Precision,Recall,Accuracy,F1_score,PR曲线、ROC曲线,AUC值,决定系数R^2 的含义与计算...
查看>>
一声祝贺,几句致歉和我的话
查看>>