进程的内存映像

配套视频已上线:55 节视频,80+ 份实验素材,独立代码仓库

进程的内存映像

前言

在阅读《UNIX 环境高级编程》的第 14 章时,看到一个“打印不同类型的数据所存放的位置”的例子,它非常清晰地从程序内部反应了“进程的内存映像”,通过结合它与《Gcc 编译的背后》《缓冲区溢出与注入分析》的相关内容,可以更好地辅助理解相关的内容。

进程内存映像表

首先回顾一下《缓冲区溢出与注入》中提到的"进程内存映像表",并把共享内存的大概位置加入该表:

地址

内核空间

描述

0xC0000000

(program flie) 程序名

execve 的第一个参数

(environment) 环境变量

execve 的第三个参数,main 的第三个参数

(arguments) 参数

execve 的第二个参数,main 的形参

(stack) 栈

自动变量以及每次函数调用时所需保存的信息都

存放在此,包括函数返回地址、调用者的

环境信息等,函数的参数,局部变量都存放在此

(shared memory) 共享内存

共享内存的大概位置

...

...

(heap) 堆

主要在这里进行动态存储分配,比如 malloc,new 等。

...

.bss (uninitilized data)

没有初始化的数据(全局变量哦)

.data (initilized global data)

已经初始化的全局数据(全局变量)

.text (Executable Instructions)

通常是可执行指令

0x08048000

0x00000000

...

在程序内部打印内存分布信息

为了能够反应上述内存分布情况,这里在《UNIX 环境高级编程》的程序 14-11 的基础上,添加了一个已经初始化的全局变量(存放在已经初始化的数据段内),并打印了它以及 main 函数(处在代码正文部分)的位置。

该程序的运行结果如下:

上述运行结果反应了几个重要部分数据的大概分布情况,比如 data 段(那个初始化过的全局变量就位于这里)、bss 段、stack、heap,以及 shared memory 和main(代码段)的内存分布情况。

在程序内部获取完整内存分布信息

不过,这些结果还是没有精确和完整地反应所有相关信息,如果要想在程序内完整反应这些信息,结合《Gcc编译的背后》,就不难想到,我们还可以通过扩展一些已经链接到可执行文件中的外部符号来获取它们。这些外部符号全部定义在可执行文件的符号表中,可以通过 nm/readelf -s/objdump -t 等查看到,例如:

第三列的符号在我们的程序中被扩展以后就可以直接引用,这些符号基本上就已经完整地覆盖了相关的信息了,这样就可以得到一个更完整的程序,从而完全反应上面提到的内存分布表的信息。

运行结果:

后记

上述程序完整地勾勒出了进程的内存分布的各个重要部分,这样就可以从程序内部获取跟程序相关的所有数据了,一个非常典型的例子是,在程序运行的过程中检查代码正文部分是否被恶意篡改。

如果想更深地理解相关内容,那么可以试着利用 readelfobjdump 等来分析 ELF 可执行文件格式的结构,并利用 gdb 来了解程序运行过程中的内存变化情况。

参考资料

关注作者公众号

Last updated

Was this helpful?