PE Header 是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称。里面包含了许多PE装载器用到的重要字段。
先看看该结构体:
1. typedef struct _IMAGE_NT_HEADERS {
2. DWORD Signature;
3. IMAGE_FILE_HEADER FileHeader;
4. IMAGE_OPTIONAL_HEADER OptionalHeader;
5. } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
第一个参数是一个标志。在一个有效的PE文件里,
Signature
字段被设置为
00004550h
, ASCI|| 字符码是:
PE00
。标志着PE文件的开头。
看看下面的这个例子,使用UE查看的16进制代码:
第二个参数是:IMAGE_FILE_HEADER。如下图:
IMAGE_FILE_HEADER 结构,那么接下来就是第三个参数OptionalHeader。
先看看这个结构体的定义吧。
1. typedef struct _IMAGE_OPTIONAL_HEADER
2. {
3. //
4. // Standard fields.
5. //
6. +18h WORD Magic; // 标志字, ROM 映像(0107h),普通可执行文件(010Bh)
7. +1Ah BYTE MajorLinkerVersion; // 链接程序的主版本号
8. +1Bh BYTE MinorLinkerVersion; // 链接程序的次版本号
9. +1Ch DWORD SizeOfCode; // 所有含代码的节的总大小
10. +20h DWORD SizeOfInitializedData; // 所有含已初始化数据的节的总大小
11. +24h DWORD SizeOfUninitializedData; // 所有含未初始化数据的节的大小
12. +28h DWORD AddressOfEntryPoint; //----!!!!!! 程序执行入口RVA
13. +2Ch DWORD BaseOfCode; // 代码的区块的起始RVA
14. +30h DWORD BaseOfData; // 数据的区块的起始RVA
15. //
16. // NT additional fields. 以下是属于NT结构增加的领域。
17. //
18. +34h DWORD ImageBase; // ----!!!!!!程序的首选装载地址
19. +38h DWORD SectionAlignment; // 内存中的区块的对齐大小
20. +3Ch DWORD FileAlignment; // 文件中的区块的对齐大小
21. +40h WORD MajorOperatingSystemVersion; // 要求操作系统最低版本号的主版本号
22. +42h WORD MinorOperatingSystemVersion; // 要求操作系统最低版本号的副版本号
23. +44h WORD MajorImageVersion; // 可运行于操作系统的主版本号
24. +46h WORD MinorImageVersion; // 可运行于操作系统的次版本号
25. +48h WORD MajorSubsystemVersion; // 要求最低子系统版本的主版本号
26. +4Ah WORD MinorSubsystemVersion; // 要求最低子系统版本的次版本号
27. +4Ch DWORD Win32VersionValue; // 莫须有字段,不被病毒利用的话一般为0
28. +50h DWORD SizeOfImage; // 映像装入内存后的总尺寸
29. +54h DWORD SizeOfHeaders; // 所有头 + 区块表的尺寸大小
30. +58h DWORD CheckSum; // 映像的校检和
31. +5Ch WORD Subsystem; // 可执行文件期望的子系统
32. +5Eh WORD DllCharacteristics; // DllMain()函数何时被调用,默认为 0
33. +60h DWORD SizeOfStackReserve; // 初始化时的栈大小
34. +64h DWORD SizeOfStackCommit; // 初始化时实际提交的栈大小
35. +68h DWORD SizeOfHeapReserve; // 初始化时保留的堆大小
36. +6Ch DWORD SizeOfHeapCommit; // 初始化时实际提交的堆大小
37. +70h DWORD LoaderFlags; // 与调试有关,默认为 0
38. +74h DWORD NumberOfRvaAndSizes; // 下边数据目录的项数,这个字段自Windows NT 发布以来 // 一直是16
39. +78h IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
40. //----!!!!!!!! 数据目录表
41. } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
这是个可选映像头,是一个可选的结构体。里面定义了很多PE文件的属性。
然而 这个结构体的大部分字段不重要的,虽然这么多。重点掌握上面的注释以 “//---!!!!!!*****”的字段。
下面是各字段的解释:
AddressOfEntryPoint 字段
指出文件被执行时的入口地址,这是一个RVA地址。若在一个可执行文件上附加了一段代码并想让这段代码首先被执行,呢么只需要将这个入口地址指向附加的代码就行了。
ImageBase 字段:
之初文件的有点先装入地址,也就是当文件被执行时,若可能,Windows有限将文件装入到ImageBase 字段指向的地址中,只有指定的地址已经被其他的模块使用时,文件才被装入到其他地址中,链接器产生可执行文件的时候对应 这个 地址来生成机器码。当文件被装入这个地址时不需要进行重定位操作,装入速度最快,若文件被装载到其他的地址,将不得不进行重定位操作。
在链接的时候,可以自定义优先装载地址,如果不指定这个选项话,一般地EXE文件的默认装载地址是00400 0000h,而DLL文件的默认优先装土地址被定位1000 0000h。
SectionAlignment 和 FileAlignment字段
SectionAlignment字段指定了字节被装入内存 后的对齐的单位,每个字节被装入的地址必定是本子段指定的数值的整数倍。而FileAlignment字段指定了存储在磁盘文件中的对齐的单位。通常来讲X86CPU的内存对齐页面大小是4KB。
Subsystem 字段
指定使用界面的子系统。这个字段决定了系统如何为程序建立初始化的界面,连接时的/subsystem:xxx 选项指定的就是这个字段的值。
DataDirectory 字段
数据目录表。这个字段是一个指针,它由16个相同的 IMAGE_DATA_DIRECTORY,结构组成。虽然PE文件中的数据是按照装入内存后的页属性归类而被放在不同的字节中,但是这些处于各个字节中的数据按照用途可以被分为导出表,导入表,资源,重定位表等数据块,这个16个IMAGE_DATA_DIRECTORY 结构就是用来定义多种不同的数据块。它的定义很简单,仅仅指出了某种数据块的位置和长度:
1. typedef struct _IMAGE_DATA_DIRECTORY {
2. DWORD VirtualAddress;
3. DWORD Size;
4. } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
在PE文件中寻找特定的数据时,就是从这些IMAGE_DATA_DIRECTORY 结构开始的。