PE结构学习笔记
PE文件结构学习笔记
结构图片
基本概念
名称 | 描述 |
---|---|
地址 | 是“虚拟地址” 而不是“物理地址”。 |
镜像文件 | 包含以 EXE 文件为代表的 “可执行文件”、以DLL文件为代表的“动态链接库”。 |
RVA | 英文全称 Relatively Virtual Address。偏移(又称“相对虚拟地址”)。相对镜像基址的偏移。 |
节 | 节是PE文件中代码或数据的基本单元。原则上讲,节只分为“代码节”和“数据节”。 |
VA | 英文全称 Virtual Address。基址 |
PE( Portable Execute)文件是Windows下可执行文件的总称,常见的有 DLL,EXE,OCX,SYS 等。它是微软在 UNIX 平台的 COFF(通用对象文件格式)基础上制作而成。最初设计用来提高程序在不同操作系统上的移植性,但实际上这种文件格式仅用在 Windows 系列操作系统下。
PE文件是指 32 位可执行文件,也称为PE32。64位的可执行文件称为 PE+ 或 PE32+,是PE(PE32)的一种扩展形式(不是PE64)。
PE文件的结构一般来说如下图所示:从起始位置开始依次是 DOS头,NT头,节表 以及 具体的节。
PE 文件的执行顺序
当一个PE文件被执行时, PE装载器首先检查DOS header里的PE header的偏移量。如果找到,则直接跳转到PE header的位置。
当PE装载器跳转到PE header后,第二步要做的就是检查PE header是否有效。如果该PE header有效,就跳转到PE header的尾部。
紧跟PE header尾部的是节表。PE装载器执行完第二步后开始读取节表中的节段信息,并采用文件映射的方法将这些节段映射到内存,同时附上节表里指定节段的读写属性。
在执行一个PE文件的时候,Windows并不在一开始就将整个文件读入内存,而是采用与内存映射的机制,也就是说,Windows装载器在装载的时候仅仅建立好虚拟地址和PE文件之间的映射关系,只有真正执行到某个内存页中的指令或者访问某一页中的数据时,这个页面才会被从磁盘提交到物理内存,这种机制使文件装入的速度和文件大小没有太大的关系。
PE文件映射入内存后,PE装载器将继续处理PE文件中类似import table(输入表)的逻辑部分。
PE 文件结构说明
1. DOS头
用来兼容 MS-DOS 操作系统的,目的是当这个文件在 MS-DOS 上运行时提示一段文字,大部分情况下是:This program cannot be run in DOS mode. 还有一个目的,就是指明 NT 头在文件中的位置。
2. NT头
包含windows PE文件的主要信息,其中包括一个 'PE' 字样的签名,PE文件头(IMAGE_FILE_HEADER)和 PE可选头(IMAGE_OPTIONAL_HEADER32)。
3. 节表
是 PE 文件后续节的描述,windows 根据节表的描述加载每个节。
4. 节
每个节实际上是一个容器,可以包含代码、数据等等,每个节可以有独立的内存权限,比如代码节默认有读/执行权限,节的名字和数量可以自己定义,未必是上图中的三个。
相对虚拟地址RVA和虚拟地址VA
当一个 PE 文件被加载到内存中以后,我们称之为 " 映象 "(image)。
一般来说,PE文件在硬盘上和在内存里是不完全一样的,被加载到内存以后其占用的虚拟地址空间要比在硬盘上占用的空间大一些,这是因为各个节在硬盘上是连续的,而在内存中是按页对齐的,所以加载到内存以后节之间会出现一些 “空洞” 。
因为存在这种对齐,所以在 PE 结构内部,表示某个位置的地址采用了两种方式:
- 针对在硬盘上存储文件中的地址,称为 原始存储地址 或 物理地址,表示距离文件头的偏移。
- 针对加载到内存以后映象中的地址,称为 相对虚拟地址(RVA),表示相对内存映象头的偏移。
堆栈:堆栈里面的数据都是临时存储的数据,作为参数、局部变量、计算的中间值等临时的存储位置。使用数据时,用pop出栈使用数据,并清除堆栈上对应数据。
然而 CPU 的某些指令是需要使用绝对地址的,比如取全局变量的地址,传递函数的地址,以及编译后的汇编指令中肯定需要用到绝对地址而不是相对映象头的偏移,因此 PE 文件会建议操作系统将其加载到某个内存地址,这个叫基地址。段地址其实就是一种基地址,但基地址并不等于就是段地址。
所谓基地址(可以理解为汇编中全局变量),顾名思义就可以理解为基本地址,他是相对偏移量的计算基准,即参考位置。
基地址可以理解为内存中整个PE文件的头地址文件最开始的位置,编译器便根据这个地址求出代码中一些全局变量和函数的地址,并将这些地址用到对应的指令中。
这种表示方式叫做 虚拟地址(VA)。
未完待续