linux proc maps文件分析
Posted aspirs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux proc maps文件分析相关的知识,希望对你有一定的参考价值。
Proc/pid/maps显示进程映射了的内存区域和访问权限。对应内核中的操作集为proc_pid_maps_op,具体的导出函数为show_map。内核中进程的一段地址空间用一个vm_area_struct结构体表示,所有地址空间存储在task->mm->mmap链表中。
一个文件可以映射到进程的一段内存区域中,映射的文件描述符保存在vm_area_struct->vm_file域中,这种内存区域叫做有名内存区域,相反,属于匿名映射内存区域。Vm_area_struct每项对应解析如下表所示:
内核每进程的vm_area_struct项 |
/proc/pid/maps中的项 |
含义 |
vm_start |
“-”前一列,如00377000 |
此段虚拟地址空间起始地址 |
vm_end |
“-”后一列,如00390000 |
此段虚拟地址空间结束地址 |
vm_flags |
第三列,如r-xp |
此段虚拟地址空间的属性。每种属性用一个字段表示,r表示可读,w表示可写,x表示可执行,p和s共用一个字段,互斥关系,p表示私有段,s表示共享段,如果没有相应权限,则用’-’代替 |
vm_pgoff |
第四列,如00000000 |
对有名映射,表示此段虚拟内存起始地址在文件中以页为单位的偏移。对匿名映射,它等于0或者vm_start/PAGE_SIZE |
vm_file->f_dentry->d_inode->i_sb->s_dev |
第五列,如fd:00 |
映射文件所属设备号。对匿名映射来说,因为没有文件在磁盘上,所以没有设备号,始终为00:00。对有名映射来说,是映射的文件所在设备的设备号 |
vm_file->f_dentry->d_inode->i_ino |
第六列,如9176473 |
映射文件所属节点号。对匿名映射来说,因为没有文件在磁盘上,所以没有节点号,始终为00:00。对有名映射来说,是映射的文件的节点号 |
|
第七列,如/lib/ld-2.5.so |
对有名来说,是映射的文件名。对匿名映射来说,是此段虚拟内存在进程中的角色。[stack]表示在进程中作为栈使用,[heap]表示堆。其余情况则无显示 |
下面一起看下一个proc maps的例子。
cat /proc/19970/task/19970/maps
001f7000-00212000 r-xp 00000000 fd:00 2719760 /lib/ld-2.5.so
00212000-00213000 r-xp 0001a000 fd:00 2719760 /lib/ld-2.5.so
00213000-00214000 rwxp 0001b000 fd:00 2719760 /lib/ld-2.5.so
00214000-0036b000 r-xp 00000000 fd:00 2719767 /lib/libc-2.5.so
0036b000-0036d000 r-xp 00157000 fd:00 2719767 /lib/libc-2.5.so
0036d000-0036e000 rwxp 00159000 fd:00 2719767 /lib/libc-2.5.so
0036e000-00371000 rwxp 0036e000 00:00 0
0054f000-00565000 r-xp 00000000 fd:00 2719791 /lib/libpthread-2.5.so
00565000-00566000 r-xp 00015000 fd:00 2719791 /lib/libpthread-2.5.so
00566000-00567000 rwxp 00016000 fd:00 2719791 /lib/libpthread-2.5.so
00567000-00569000 rwxp 00567000 00:00 0
006f5000-006f6000 r-xp 006f5000 00:00 0 [vdso]
08048000-08049000 r-xp 00000000 fd:00 3145810 /home/lijz/code/pthread
08049000-0804a000 rw-p 00000000 fd:00 3145810 /home/lijz/code/pthread
08c50000-08c71000 rw-p 08c50000 00:00 0 [heap]
b75d7000-b75d8000 ---p b75d7000 00:00 0
b75d8000-b7fda000 rw-p b75d8000 00:00 0
b7fe4000-b7fe5000 rw-p b7fe4000 00:00 0
bf987000-bf99c000 rw-p bffea000 00:00 0 [stack]
进程的每段地址空间由struct vm_area_struct 描述。如上所示的每一行对应一个vm_area_struct结构体。一个文件可以映射到内存中,vm_area_struct的vm_file保存了文件描述符,这种映射称为有名映射,反之则为匿名映射。下面以第十四行为例,解释各例的内容。
第一列:08049000-0804a000-----本段内存映射的虚拟地址空间范围,对应vm_area_struct中的vm_start和vm_end。
第二列:rw-p----权限 r-读,w-写 x-可执行 p-私有,对应vm_flags。
第三列:00000000----针对有名映射,指本段映射地址在文件中的偏移,对应vm_pgoff。对匿名映射而言,为vm_area_struct->vm_start。
第四列:fd:00----所映射的文件所属设备的设备号,对应vm_file->f_dentry->d_inode->i_sb->s_dev。匿名映射为0。其中fd为主设备号,00为次设备号。
第五列:3145810----文件的索引节点号,对应vm_file->f_dentry->d_inode->i_ino,与ls –i显示的内容相符。匿名映射为0。
第六列:/home/lijz/code/pthread---所映射的文件名。对有名映射而言,是映射的文件名,对匿名映射来说,是此段内存在进程中的作用。[stack]表示本段内存作为栈来使用,[heap]作为堆来使用,其他情况则为无。
经过上面的分析,proc maps中的每一列代表的意思已经非常清晰了。接下来看下proc每maps中每一行的解析。各共享库的代码段,存放着二进制可执行的机器指令,由kernel把该库ELF文件的代码段map到虚存空间;各共享库的数据段,存放着程序执行所需的全局变量,由kernel把ELF文件的数据段map到虚存空间;用户代码段,存放着二进制形式的可执行的机器指令,由kernel把ELF文件的代码段map到虚存空间;用户数据段,存放着程序执行所需的全局变量,由kernel把ELF文件的数据段map到虚存空间;堆(heap),当且仅当malloc调用时存在,由kernel把匿名内存map到虚存空间,堆则在程序中没有调用malloc的情况下不存在;栈(stack),作为进程的临时数据区,由kernel把匿名内存map到虚存空间,栈空间的增长方向是从高地址到低地址。
pthread这个应用程序在maps中占用了两行,内容如下:
08048000-08049000 r-xp 00000000 fd:00 3145810 /home/lijz/code/pthread
08049000-0804a000 rw-p 00000000 fd:00 3145810 /home/lijz/code/pthread
其中第一行的权限是只读,并且可执行,说明第一行是应用程序的代码段,而第二行的权限是可读可写,但是没有执行权限,说明该段是pthread的数据段。
00c56000-00dad000 r-xp 00000000 fd:00 2719767 /lib/libc-2.5.so
00dad000-00daf000 r-xp 00157000 fd:00 2719767 /lib/libc-2.5.so
00daf000-00db0000 rwxp 00159000 fd:00 2719767 /lib/libc-2.5.so
以上是libc-2.5共享库在maps文件中的记录,每个共享库在maps文件中对应着三行,分别是数据段与代码段。
堆[heap]段。
08c64000-08c85000 rw-p 08c64000 00:00 0 [heap]
有些maps文件并不会出现该记录,这主要跟程序中有无使用malloc相关,如果主线程使用了malloc就会有该记录,否则就没有。在子线程中调用malloc,会产生另外的堆映射,但是并不会标记[heap]。例如,在子线程中动态分配1MB的内存空间,pthread2应用程序的执行结果如下所示:
tid addr 0xbfd818f0
child thread run
stackbase 0xb7f4f3c0
stackaddr =0x7754e008----malloc分配的地址
guardsize 4096
对应的maps文件:
08048000-08049000 r-xp 00000000 fd:00 3145811 /home/lijz/code/pthread2
08049000-0804a000 rw-p 00000000 fd:00 3145811 /home/lijz/code/pthread2
0945a000-0947b000 rw-p 0945a000 00:00 0 [heap]
7754e000-b754f000 rw-p 7754e000 00:00 0 -----------区间大小正是1MB
b754f000-b7550000 ---p b754f000 00:00 0
b7550000-b7f52000 rw-p b7550000 00:00 0
b7f5c000-b7f5d000 rw-p b7f5c000 00:00 0
bfd6e000-bfd83000 rw-p bffea000 00:00 0 [stack]
maps文件中红色标注的行,从内容上看,本段内存大小是1MB,权限为读写私有,偏移为本段内存的开始地址,设备号和文件索引节点为0。可以看出本段内存是进程通过mmap映射的一段空间,是匿名映射。在pthread2程序中,正好用malloc分配了一个1MB的内存,能够与这段内存对应。同时,malloc分配的地址0x7754e008正落在该区间,并且偏向区间低地址部分,说明该区间是个堆地址空间。说明了这段1M的内存确实是进程调用malloc分配的,其中malloc又调用mmap系统调用匿名映射。
栈段[stack],下面用几个例子来说明栈段。
bfd50000-bfd65000 rw-p bffea000 00:00 0 [stack]
对于单线程应用程序而言,只有一个[stack]段,对应多线程应用程序,[stack]段是主线程的栈空间,子线程的栈空间则用pthread库自动分配。
例1,将一个单线程的应用的局部变量的地址打印出来,执行的结果如下所示:
./pthread2
tid addr 0xbfc73600
对应的maps文件:
08048000-08049000 r-xp 00000000 fd:00 3145811 /home/lijz/code/pthread2
08049000-0804a000 rw-p 00000000 fd:00 3145811 /home/lijz/code/pthread2
b7f7e000-b7f80000 rw-p b7f7e000 00:00 0
b7f8a000-b7f8b000 rw-p b7f8a000 00:00 0
bfc5f000-bfc74000 rw-p bffea000 00:00 0 [stack]
局部变量的地址0xbfc73600在[stack]区间。
例2:将一个拥有一个子线程的应用局部变量打印出来,执行的结果如下所示:
tid addr 0xbfd64740---------主线程中打印的局部变量地址
child thread run
stackaddr 0xb7fc93c4--------子线程中打印的局部变量地址
guardsize 4096---------栈保护页大小
对应的maps文件如下:
08048000-08049000 r-xp 00000000 fd:00 3145811 /home/lijz/code/pthread2
08049000-0804a000 rw-p 00000000 fd:00 3145811 /home/lijz/code/pthread2
08c64000-08c85000 rw-p 08c64000 00:00 0 [heap]
b75c9000-b75ca000 ---p b75c9000 00:00 0---------pthread_create默认的栈溢出保护区
b75ca000-b7fcc000 rw-p b75ca000 00:000------------pthread_create创建的子线程的栈空间
b7fd6000-b7fd7000 rw-p b7fd6000 00:00 0------------------4KB应该也是通过mmap产生的匿名映射
bfd50000-bfd65000 rw-p bffea000 00:00 0 [stack]---------主进程的栈空间
由上执行结果显示,主线程中局部变量地址0xbfd64740落在[stack]区间,而子线程局部变量地址0xb7fc93c4则落在b75ca000-b7fcc000 rw-p b75ca00区间,并且局部变量的地址从高地址开始分配,说明该VMA正是子线程的栈地址空间。另外,对栈空间,pthread默认设置了一个4KB的栈保护页,对应的区间为:b75c9000-b75ca000---p b75c9000,该区间不可读,不可写,也不能执行,通过这些属性信息的设置,可以达到栈溢出保护的作用。
例3:在例2的基础上,多创建一个线程,pthread2程序的执行结果如下所示:
./pthread2
tid addr 0xbfc81610----------主线程局部变量地址
child thread run
stackaddr = 0xb7f183c0-------子线程1局部变量地址
guardsize 4096
child thread2 run
stackaddr =0xb75173c4 ----------子线程局部变量地址
guardsize 4096
对应的maps文件:
08048000-08049000 r-xp 00000000 fd:00 3145811 /home/lijz/code/pthread2
08049000-0804a000 rw-p 00000000 fd:00 3145811 /home/lijz/code/pthread2
092d6000-092f7000 rw-p 092d6000 00:00 0 [heap]
76b16000-b6b17000 rw-p 76b16000 00:00 0 ----------mallocmmap
b6b17000-b6b18000 ---p b6b17000 00:00 0
b6b18000-b7518000 rw-p b6b18000 00:000---------pthread thread2 stack space
b7518000-b7519000 ---p b7518000 00:00 0
b7519000-b7f1b000 rw-p b7519000 00:000----------pthread thread1 stack space
b7f25000-b7f26000 rw-p b7f25000 00:00 0
bfc6e000-bfc83000 rw-p bffea000 00:00 0 [stack]---main thread stack space
从maps文件记录上看,增加一个子线程,在maps文件中就增加了两条记录,分别是子线程的栈空间和栈保护页的记录。默认情况下,pthread为子线程预留的栈空间大小为1MB,栈保护页为4KB(这主要跟页大小相关)。
总之,proc maps文件可以查看进程的内存映射,每一段内存的权限属性等信息。
以上是关于linux proc maps文件分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向Android 系统文件分析 ( /proc/pid 进程号对应进程目录 | oom_adj | maps | smaps | mem | task | environ )(代码片
Android 逆向Android 系统文件分析 ( /proc/pid 进程号对应进程目录 | oom_adj | maps | smaps | mem | task | environ )(代码片