Windows PE 权威指南

发布于 2021-12-11  258 次阅读


DOS头

DOS头部分的存在见证了PE的强大兼容性。为了保持与16位系统的兼容,在PE里依旧保留了16位系统下的标准可执行程序执行时所必需的文件头部(DOS MZ头)和指令代码(DOS Stub)

DOS头分为两部分,DOS MZ头和DOS Stub(即指令字节码)。大部分情况下,这些指令实现的功能都非常简单,根本不会涉及重定位信息。再往后的PE头和PE数据区可以看做是16位系统下的可执行文件的冗余数据。

DOS MZ头 (IMAGE_DOS_HEADER)

在Windows的PE格式中,DOS MZ头的定义如下:

用记事本打开任何一个镜像文件,其头2个字节必为字符串“MZ”,这是Mark Zbikowski的姓名缩写,他是最初的MS-DOS设计者之一。然后是一些在MS-DOS下的一些参数,这些参数是在MS-DOS下运行该程序时要用到的。在这些参数的末尾也就是文件的偏移0x3C(第60字节)处是是一个4字节的PE文件签名的偏移地址。该地址有一个专用名称叫做“E_lfanew”。这个签名是“PE00”(字母“P”和“E”后跟着两个空字节)。紧跟着E_lfanew的是一个MS-DOS程序。那是一个运行于MS-DOS下的合法应用程序。当可执行文件(一般指exe、com文件)运行于MS-DOS下时,这个程序显示“This program cannot be run in DOS mode(此程序不能在DOS模式下运行)”这条消息。用户也可以自己更改该程序,有些还原软件就是这么干的。同时,有些程序既能运行于DOS又能运行于Windows下就是这个原因。Notepad.exe整个DOS头大小为224个字节,大部分不能在DOS下运行的Win32文件都是这个值。MS-DOS程序是可有可无的,如果你想使文件大小尽可能的小可以省掉MS-DOS程序,同时把前面的参数都清0。

重要的就是第一个和最后一个,第一个标志了这个文件是哪种类型,最后一个就是PE头的偏移地址。

DOS Stub

由于DOS Stub的大小不固定,因此DOS头的大小也是不固定的。

NT头

PE头标识(Signature)

紧跟在DOS Stub后面的是PE头标识Signature。与大部分文件格式的头部结构一样,PE头部信息中有一个四字节的标识,该标识位于指针IMAGE_DOS_HEADER.e_lfanew指向的位置。其内容固定,对应于ASCⅡ码的字符串“PE\0\0”。

标准PE头(IMAGE_FILE_HEADER)

标准PE头IMAGE_FILE_HEADER紧跟在PE头标识后,即位于IMAGE_DOS_HEADER的e_lfanew值+4的位置。由此位置开始的20个字节为数据结构标准PE头IMAGE_FILE_HEADER的内容。该结构在微软的官方文档中被称为标准通用对象文件格式(Common Object File Format,COFF)头。它记录了PE文件的全局属性,如该PE文件运行的平台、PE文件类型(是EXE文件还是DLL文件)、文件中存在的节的总数等,其详细定义如下:

Machine

机器数,标识CPU的数字。

描述
0x0适用于任何类型处理器
0x1d3Matsushita AM33处理器
0x8664x64处理器
0x1c0ARM小尾处理器
0xebcEFI字节码处理器
0x14cIntel 386或后继处理器及其兼容处理器
0x200Intel Itanium处理器
0x9041Mitsubishi M32R小尾处理器
0x266MIPS16处理器
0x366带FPU的MIPS处理器
0x466带FPU的MIPS16处理器
0x1f0PowerPC小尾处理器
0x1f1带符点运算支持的PowerPC处理器
0x166MIPS小尾处理器
0x1a2Hitachi SH3处理器
0x1a3Hitachi SH3 DSP处理器
0x1a6Hitachi SH4处理器
0x1a6Hitachi SH5处理器
0x1c2Thumb处理器
0x169MIPS小尾WCE v2处理器

NumberOfSections

节数,节的数目,Windows加载器限制节的最大数目为96,如果将该值设置为0,则操作系统装载时会提示不是有效的Win32程序。如果想在PE中增加或删除节,必须变更此处的值。

TimeDateStamp

时间/日期标记,UTC时间1970年1月1日00:00起的总秒数的低32位,它指出文件何时被创建。低32位存放的值是自1970年1月1日00:00时开始到创建时间为止的总秒数。

PointerToSymbolTable

COFF符号表的文件偏移。如果不存在COFF符号表,此值为0。对于映像文件来说,此值为0,因为微软已经不赞成在PE中使用COFF调试信息了。

NumberOfSymbols

符号表中元素的数目。由于字符串表紧跟在符号表后,所以可以利用这个值来定位字符串表。对于映像文件来说,此值为0,主要用于调试。

SizeOfOptionalHeader

指定结构IMAGE_OPTIONAL_HEADER32的长度,默认情况下这个值等于00e0h(224);如果是64位PE文件,该结构的默认大小为00F0h(240)。

用户可以自己定义这个值的大小,不过需要注意两点:

  1. 更改完以后,需要自行将文件中IMAGE_OPITONAL_HEADER32的大小扩充为你指定的值(一般以0补足)。
  2. 扩充完以后,要维持文件中的对齐特性(比如在HelloWorld.exe中,此处增加了8个字节后,一定在后面相应地删除8个字节,以保证.text节起始位置处于0400h)。

Characteristics

文件属性标志字段,它的不同数据位定义了不同的文件属性。这是一个很重要的字段,不同的定义将影响系统对文件的装入方式。比如,当位13为1时,表示这是一个DLL文件,那么系统将使用调用DLL入口函数的方式执行文件入口函数;当位13为0时,表示这是一个普通的可执行文件,系统直接跳到入口处执行。对于普通的可执行PE文件来说,这个字段的值一般是010fh,而对于DLL文件来说,这个字段的值一般是210eh。

当第0位为1时,表明此文件不包含基址重定位信息,因此必须将其加载到文件头中指定的基地址字段位置。如果进程空间此处的基地址被占用,加载器会报错。在程序运行前如果发现文件中存在可重定位信息,链接器会执行移除可执行文件中的重定位信息的操作。

位置描述(为1时)
0它表明此文件不包含基址重定位信息,因此必须被加载到其首选基地址上。如果基地址不可用,加载器会报错
1它表明此镜像文件是合法的,可执行的
2不存在行信息,保留,必须为0
3不存在符号信息,保留,必须为0
4调整工作集,保留,必须为0
5应用程序可以处理大于2GB的地址
6保留,必须为0
7小尾方式, 保留,必须为0
8机器类型基于32位体系结构
9调试信息已经从此镜像文件中移除
10如果此镜像文件在可移动介质上,完全加载它并把它复制到交换文件中(不可从可移动盘运行)
11如果此镜像文件在网络介质上,完全加载它并把它复制到交换文件中(不能从网络上运行)
12此镜像文件是系统文件,而不是用户程序(如驱动程序,不能直接运行)
13此镜像文件是动态链接库(DLL)
14此文件只能运行于单处理器机器上
15大尾方式,保留,必须为0

当第1位为1时,表明此映像文件是合法的,可以运行。如果未设置此标志,表明出现了链接器错误。
当第7位为1时,表示文件为小尾方式,即内存中,最低有效位LSB位于最高有效位MSB的前面,与第15位的大尾方式(MSB在前,LSB在后)一样,都不赞成使用该标志,最好将其设置为0。
当第10位为1时,如果此映像文件在可移动存储介质上,那么加载器将完全加载它并把它复制到内存交换文件中。
当第11位为1时,如果此映像文件在网络上,那么加载器也将完全加载它并把它复制到内存交换文件中。
当第13位为1时,表明此映像文件是动态链接库(DLL)。这样的文件总被认为是可执行文件,尽管它们并不能直接运行。
可执行文件的标志位设置为010fh,即第0、1、2、3、8位分别被设置为1,表示该文件为可执行文件,不含重定位信息,不含符号和行号信息,文件只在32位平台运行。

扩展PE头(IMAGE_OPTIONAL_HEADER32)

偏移大小英文名中文名描述
02Magic魔数这个无符号整数指出了镜像文件的状态。如果为010BH,则表示该文件为PE32;如果为0107h,则表示文件为ROM映像;如果为020BH,则表示该文件为PE32+,即64位下的PE文件
21MajorLinkerVersion链接器的主版本号链接器的主版本号,对执行没有任何影响。
31MinorLinkerVersion链接器的次版本号链接器的次版本号,对执行没有任何影响。
44SizeOfCode代码节大小 所有代码节的总和 ,一般放在“.text”节里。如果有多个代码节的话,它是所有代码节的和。必须是FileAlignment的整数倍,是在文件里的大小。
84SizeOfInitializedData已初始化数大小 所有包含已经初始化的数据的节的总大小 ,一般放在“.data”节里。如果有多个这样的节话,它是所有这些节的和。必须是FileAlignment的整数倍,是在文件里的大小。
124SizeOfUninitializedData未初始化数大小所有包含未初始化的数据的节的总大小,一般放在“.bss”节里。如果有多个这样的节话,它是所有这些节的和。必须是FileAlignment的整数倍,是在文件里的大小。
164AddressOfEntryPoint入口点当可执行文件被加载进内存时其入口点RVA。对于一般程序镜像来说,它就是启动地址。为0则从ImageBase开始执行。对于dll文件是可选的。
204BaseOfCode代码基址当镜像被加载进内存时代码节的开头RVA。必须是SectionAlignment的整数倍。
244BaseOfData数据基址当镜像被加载进内存时数据节的开头RVA。(在64位文件中此处被并入紧随其后的ImageBase中。)必须是SectionAlignment的整数倍。
28/24 4 / 8 ImageBase镜像基址当加载进内存时镜像的第1个字节的首选地址。它必须是64K的倍数。DLL默认是10000000H。Windows CE 的EXE默认是00010000H。Windows 系列的EXE默认是00400000H。
324SectionAlignment内存对齐当加载进内存时节的对齐值(以字节计)。它必须≥FileAlignment。默认是相应系统的页面大小。
364FileAlignment文件对齐用来对齐镜像文件的节中的原始数据的对齐因子(以字节计)。它应该是界于512和64K之间的2的幂(包括这两个边界值)。默认是512。如果SectionAlignment小于相应系统的页面大小,那么FileAlignment必须与SectionAlignment相等。
402MajorOperatingSystemVersion主系统的主版本号操作系统的版本号可以从“我的电脑”→“帮助”里面看到,Windows XP是5.1。5是主版本号,1是次版本号
422MinorOperatingSystemVersion主系统的次版本号 
442MajorImageVersion镜像的主版本号 
462MinorImageVersion镜像的次版本号 
482MajorSubsystemVersion子系统的主版本号 
502MinorSubsystemVersion子系统的次版本号 
522Win32VersionValue保留,必须为0子系统版本的值,暂时保留未用,必须设置为0。比如将此处的值更改为696C6971h,程序运行将失败。 
564SizeOfImage镜像大小当镜像被加载进内存时的大小,包括所有的文件头。向上舍入为SectionAlignment的倍数。
604SizeOfHeaders头大小所有头的总大小,向上舍入为FileAlignment的倍数。可以以此值作为PE文件第一节的文件偏移量。
644CheckSum校验和镜像文件的校验和。计算校验和的算法被合并到了Imagehlp.DLL 中。以下程序在加载时被校验以确定其是否合法:所有的驱动程序、任何在引导时被加载的DLL以及加载进关键Windows进程中的DLL。
682Subsystem子系统类型运行此镜像所需的子系统
702DllCharacteristicsDLL标识DLL文件属性。它是一个标志集,不是针对DLL文件的,而是针对所有PE文件的。
724 / 8SizeOfStackReserve堆栈保留大小最大栈大小。CPU的堆栈。默认是1MB。
76/804 / 8SizeOfStackCommit堆栈提交大小初始提交的堆栈大小。默认是4KB。
80/88 4 / 8 SizeOfHeapReserve堆保留大小最大堆大小。编译器分配的。默认是1MB。这个堆的句柄可以通过调用GetProcessHeap函数获得。
84/96 4 / 8 SizeOfHeapCommit堆栈交大小初始提交的局部堆空间大小。默认是4KB。
88/1044LoaderFlags保留,必须为0加载标志
92/1084NumberOfRvaAndSizes目录项数目数据目录项的个数。由于以前发行的Windows NT的原因,它只能为16。

数据目录(DataDirectory)

偏移(PE32/PE32+)大小英文名描述
96/1128Export Table导出表的地址和大小。
104/1208Import Table导入目录表地址和大小
112/1288Resource Table资源表的地址和大小。
120/1368Exception Table异常表的地址和大小。
128/1448Certificate Table属性证书表的地址和大小。
136/1528Base Relocation Table基址重定位表的地址和大小。
144/1608Debug调试数据起始地址和大小。
152/1688Architecture保留,必须为0
160/1768Global Ptr将被存储在全局指针寄存器中的一个值的RVA。这个结构的Size域必须为0
168/1848TLS Table线程局部存储(TLS)表的地址和大小。
176/1928Load Config Table加载配置表的地址和大小。
184/2008Bound Import绑定导入查找表的地址和大小。
192/2088IAT导入地址表的地址和大小。
200/2168Delay Import Descriptor延迟导入描述符的地址和大小。
208/2248CLR Runtime HeaderCLR运行时头部的地址和大小。(已废除)
216/2328保留,必须为0保留,必须为0


I am very tired