PE文件结构
dos头:共64字节,前两个字节固定是4D5A,最后4个字节指向PE头。
dos存根: 无用。
PE头:4字节PE标识符,20字节的PE文件头,224字节的PE可选头。
节表:40字节,在PE文件头里已经定义好了节表数目(WORD NumberOfSections;)。
DOS头
为了兼容dos系统而遗留。
MZ头部
这也是真正的dos头,mz头部只是dos头的别名。
(PE内称的dos头往往包括dos存根,实际的dos程序的dos头只是当前这个头)
dos头的最后给出了PE头的位置(e_lfanew)
1 | IMAGE_DOS_HEADER = { |
DOS存根
仅用于在exe执行与dos中时给出友好提示信息。(没用)
PE头
真正的windows程序的头部。
1 | IMAGE_NT_HEADERS = { |
可选头的最后一项是数据目录。
数据目录是一个数组,每一项包括两个字段,共20字节,分别是数据目录项的VitualAddress(实际上是RVA)和size
该头部分为32位和64位两个版本,其定义取决于是否定义了_WIN64宏。
如IMAGE_NT_HEADERS32的定义:
1 | typedef struct _IMAGE_NT_HEADERS{ |
节表
节表也算作头部的一部分。
需要多少个节表项,就构造多少节表头(IMAGE_SECTION_HEADER)
节表项(节)指 .text .data .idata
这样。
在之前的IMAGE_OPTIONAL_HEADER中已经指明了SizeOfHeader,根据其字段值在所有节表项(整个头部)的后面填充0补足。
节数据(程序体)
指具体的 .text .data .idata
的内容,其所占空间大小在节表中定义。
关于PE
当文件对齐(FileAlignment)与节对齐(SegmentAlignment)相同时:
VA - ImageBase = FOA(文件偏移地址) = RVA(相对虚拟地址)
当二者不同时:
首先计算RVA=VA-ImageBase
判断RVA或FOA所在的节
RVA-VOffset == FOA - ROffset
总之就是无论什么情况下,节中的地址相对于节起始的偏移是不变的。
导入表
1 | // 导入表描述符 |