PE结构(1)
DOS头
DOS头是为了兼容DOS系统遗留
MZ头
大小为64字节
偏移地址 (十六进制) | 字段长度 (字节) | 字段名称 | 描述 | |
---|---|---|---|---|
00H |
2 | e_magic |
文件标识("MZ")。必须是 4DH 5AH (ASCII "MZ")。 |
|
02H |
2 | e_cblp |
文件中最后512字节块的字节数。如果文件大小是512的倍数,则为0。如果不是,则表示最后一个块中包含的字节数。 | |
04H |
2 | e_cp |
文件中的512字节块数。文件的总大小可以通过 (e_cp - 1) * 512 + e_cblp 计算。 |
|
06H |
2 | e_crlc |
重定位项的数目。重定位表用于调整程序中对内存地址的引用,以便程序可以在内存中的任何位置加载和运行。 | |
08H |
2 | e_cparhdr |
头部大小(以段为单位)。指定MZ头以及其后的重定位表所占用的段数(每个段16字节)。程序实际代码和数据从这个偏移量开始。 | |
0AH |
2 | e_minalloc |
所需的最小额外内存(以段为单位)。程序运行时除了其本身大小外,至少需要额外分配的内存量。 | |
0CH |
2 | e_maxalloc |
所需的最大额外内存(以段为单位)。程序运行时除了其本身大小外,最多可以额外分配的内存量。通常为 FFFFH ,表示尽可能多。 |
|
0EH |
2 | e_ss |
初始栈段(SS)寄存器的值。相对于加载模块的起始地址。 | |
10H |
2 | e_sp |
初始栈指针(SP)寄存器的值。 | |
12H |
2 | e_csum |
文件校验和。用于验证文件的完整性,但在实际中很少被使用或校验。 | |
14H |
2 | e_ip |
初始指令指针(IP)寄存器的值。 | |
16H |
2 | e_cs |
初始代码段(CS)寄存器的值。相对于加载模块的起始地址。 | |
18H |
2 | e_lfarlc |
重定位表在文件中的偏移地址。指明了重定位表在文件中的起始位置。 | |
1AH |
2 | e_ovno |
覆盖号(Overlay Number)。对于非覆盖程序,此字段通常为0。 |
e_magic
用于标注该文件
e_lfarlc
用于标注PE头偏移
DOS块
大小为16字节
如果不在DOS系统上执行可以修改此部分代码
PE头
PE签名
大小为4字节,标识文件为PE文件
标准PE头
大小为20字节
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; // 目标 CPU 架构
WORD NumberOfSections; // 节(Section)的数量
DWORD TimeDateStamp; // 文件创建/修改时间戳
DWORD PointerToSymbolTable; // COFF 符号表的文件偏移
DWORD NumberOfSymbols; // COFF 符号表中的符号数量
WORD SizeOfOptionalHeader; // 可选头(Optional Header)的大小
WORD Characteristics; // 文件特性标志
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
Characteristics 字段的标志位详解
值 (十六进制) | 符号常量 | 描述 |
---|---|---|
0x0001 |
IMAGE_FILE_RELOCS_STRIPPED |
重定位信息已剥离。表示文件不包含基址重定位信息。如果文件不能加载到其首选基址,加载器会报错。链接器的默认行为通常是剥离基址重定位。 |
0x0002 |
IMAGE_FILE_EXECUTABLE_IMAGE |
可执行映像。表示文件是有效的可执行映像(即 .exe 或 .dll ),而不是对象文件 (.obj )。如果此标志未设置,通常表示链接器错误。 |
0x0004 |
IMAGE_FILE_LINE_NUMS_STRIPPED |
行号信息已剥离。表示 COFF 行号信息已从文件中移除。此标志已弃用,通常应为 0。 |
0x0008 |
IMAGE_FILE_LOCAL_SYMS_STRIPPED |
本地符号信息已剥离。表示 COFF 符号表中的本地符号条目已从文件中移除。此标志已弃用,通常应为 0。 |
0x0010 |
IMAGE_FILE_AGGRESIVE_WS_TRIM |
积极裁剪工作集。此值已废弃,且在 Windows 2000 及更高版本中必须为 0。 |
0x0020 |
IMAGE_FILE_LARGE_ADDRESS_AWARE |
大地址感知。表示应用程序能够处理大于 2 GB 的地址(对于 32 位程序,这意味着它在启用 PAE 的系统上可以访问超过 2GB 的虚拟内存空间)。 |
0x0040 |
(未使用) | 未使用。 |
0x0080 |
IMAGE_FILE_BYTES_REVERSED_LO |
字节序反转(低位在前)。此标志已废弃,通常应为 0。 |
0x0100 |
IMAGE_FILE_32BIT_MACHINE |
32 位机器。表示文件是为 32 位字长架构的计算机设计的。对于 x64 PE 文件,此标志通常不设置。 |
0x0200 |
IMAGE_FILE_DEBUG_STRIPPED |
调试信息已剥离。表示调试信息已从映像文件中移除,可能已存储在单独的调试文件(如 .pdb )中。 |
0x0400 |
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP |
从可移动媒体运行,复制到交换文件。如果映像在可移动媒体上,则将其完全加载并复制到交换文件中运行。 |
0x0800 |
IMAGE_FILE_NET_RUN_FROM_SWAP |
从网络运行,复制到交换文件。如果映像在网络媒体上,则将其完全加载并复制到交换文件中运行。 |
0x1000 |
IMAGE_FILE_SYSTEM |
系统文件。表示映像文件是一个系统文件(例如驱动程序),而不是用户应用程序。 |
0x2000 |
IMAGE_FILE_DLL |
DLL 文件。表示映像文件是一个动态链接库 (DLL)。这类文件在大多数情况下被视为可执行文件,但不能直接运行,通常由其他程序加载。 |
0x4000 |
IMAGE_FILE_UP_SYSTEM_ONLY |
仅限于单处理器系统。表示文件应仅在单处理器计算机上运行。在实践中,它可能会导致程序在多核系统上运行时,其进程的 CPU 亲和性被设置为单个 CPU 核心,且该核心可能轮换。 |
0x8000 |
IMAGE_FILE_BYTES_REVERSED_HI |
字节序反转(高位在前)。此标志已废弃,通常应为 0。 |
拓展PE头
大小不固定,具体大小由SizeOfOptionalHeader
字段决定
标准头
字段名称 | 大小 (32位/64位) | 描述 |
---|---|---|
Magic |
2 字节 | 魔法数字。指示可选头的类型和 PE 文件的位宽。加载器根据此值判断文件是 32 位还是 64 位。 - 0x10B (IMAGE_NT_OPTIONAL_HDR32_MAGIC ): 32 位 PE (PE32) 文件- 0x20B (IMAGE_NT_OPTIONAL_HDR64_MAGIC ): 64 位 PE (PE32+) 文件- 0x107 (IMAGE_ROM_OPTIONAL_HDR_MAGIC ): ROM 映像 |
MajorLinkerVersion |
1 字节 | 创建此 PE 文件的链接器的主版本号。 |
MinorLinkerVersion |
1 字节 | 创建此 PE 文件的链接器的次版本号。 |
SizeOfCode |
4 字节 | 所有可执行代码节的总大小(例如 .text 节)。此大小指的是文件中的原始字节数。 |
SizeOfInitializedData |
4 字节 | 所有已初始化数据节的总大小(例如 .data 节)。此大小指的是文件中的原始字节数。 |
SizeOfUninitializedData |
4 字节 | 所有未初始化数据节的总大小(例如 .bss 节)。此大小指的是在内存中所需的空间,在文件中通常不占用实际存储空间。 |
AddressOfEntryPoint |
4 字节 | 程序执行的入口点 RVA (Relative Virtual Address,相对虚拟地址)。这是加载器将控制权交给程序的第一个指令的内存地址。对于 DLL,此字段可能是 0,表示没有一个标准的入口函数。 |
BaseOfCode |
4 字节 | 代码节的基地址 RVA。这是所有代码节在内存中的起始相对地址。 |
BaseOfData |
4 字节 (仅限 32 位) | 数据节的基地址 RVA。这是所有数据节在内存中的起始相对地址。在 64 位 PE 文件(PE32+)中,此字段已移除,因为在 64 位寻址下,数据节通常不需要独立的基地址。 |
Windows特定字段
字段名称 | 大小 (32位/64位) | 描述 |
---|---|---|
ImageBase |
4/8 字节 | 映像加载到内存中的首选基地址。这是加载器尝试将 PE 文件(EXE 或 DLL)加载到的虚拟内存地址。如果该地址已被占用,加载器会寻找其他可用地址,并使用基址重定位表 (IMAGE_DIRECTORY_ENTRY_BASERELOC ) 来调整程序内部的绝对地址引用。这对于 DLL 文件尤其重要,因为它们可能被加载到任意地址。- EXE 文件的默认值通常是 0x00400000 。- DLL 文件的默认值通常是 0x10000000 。 |
SectionAlignment |
4 字节 | 内存中节的对齐粒度。当 PE 文件的各个节(如 .text , .data )被加载到内存中时,它们的起始地址必须是这个值的倍数。通常,这个值与系统页大小相同(如 4KB 或 0x1000 字节),以优化内存管理器的性能。 |
FileAlignment |
4 字节 | 文件中节的对齐粒度。这是节在磁盘上的原始数据在文件中的对齐方式。节的原始数据大小(SizeOfRawData )和文件偏移量(PointerToRawData )都必须是这个值的倍数。常见的 FileAlignment 值是 512 字节 (0x200 ) 或 4KB (0x1000 )。 |
MajorOperatingSystemVersion |
2 字节 | 程序运行所需的最低操作系统主版本号。例如,如果程序需要 Windows XP 或更高版本,此值可能是 5。 |
MinorOperatingSystemVersion |
2 字节 | 程序运行所需的最低操作系统次版本号。例如,如果程序需要 Windows XP (5.1),则此值可能是 1。 |
MajorImageVersion |
2 字节 | PE 映像(文件本身)的主版本号。由链接器设置,通常反映程序的内部版本。 |
MinorImageVersion |
2 字节 | PE 映像的次版本号。 |
MajorSubsystemVersion |
2 字节 | 程序所需的子系统主版本号。例如,如果程序是为 Windows GUI 子系统编写的,则会有对应的版本号。 |
MinorSubsystemVersion |
2 字节 | 程序所需的子系统次版本号。 |
Win32VersionValue |
4 字节 | 保留字段,目前通常为 0。 |
SizeOfImage |
4 字节 | 整个 PE 映像加载到内存中后所占用的总大小。这个值必须是 SectionAlignment 的倍数。它包括了所有的头、节以及所有节在内存中对齐后所需的填充空间。 |
SizeOfHeaders |
4 字节 | 所有头部(DOS 头、PE 签名、COFF 文件头、可选头和节表)在文件中的总大小。这个值必须是 FileAlignment 的倍数。它也指示了文件中第一个实际的节(代码或数据)的原始数据开始的偏移量。 |
CheckSum |
4 字节 | 映像文件的校验和。对于某些系统文件(如 DLL 和驱动程序),Windows 加载器在加载前会计算并验证此校验和,以确保文件在传输过程中未被损坏或篡改。对于 EXE 文件,校验和通常是可选的。 |
Subsystem |
2 字节 | 运行程序所需的子系统类型。这个字段决定了 Windows 如何启动和管理该进程。 - IMAGE_SUBSYSTEM_WINDOWS_GUI (2) : Windows 图形用户界面 (GUI) 应用程序。- IMAGE_SUBSYSTEM_WINDOWS_CUI (3) : Windows 字符用户界面 (CUI) 应用程序(控制台应用程序)。- IMAGE_SUBSYSTEM_NATIVE (1) : 不需任何子系统(例如内核模式驱动程序,直接与操作系统内核交互)。 - 还有其他用于 EFI、Posix、OS/2 等的子系统类型。 |
DllCharacteristics |
2 字节 | DLL/可执行文件的特性标志。这是一组位标志,指示映像支持的特定特性或行为。它们是加载器和操作系统在加载时要考虑的重要标志。 - IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE (0x0040) : 动态基址重定位 (ASLR)。表示 DLL 或 EXE 可以在任意地址加载,支持地址空间布局随机化。强烈推荐设置此标志以提高安全性。 - IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY (0x0080) : 强制代码完整性检查。要求在加载时对映像进行数字签名验证。 - IMAGE_DLLCHARACTERISTICS_NX_COMPAT (0x0100) : 数据执行保护 (DEP) 兼容。表示映像与 DEP 兼容,其数据区不会被执行代码。几乎所有现代可执行文件都设置此标志。 - IMAGE_DLLCHARACTERISTICS_NO_SEH (0x0400) : 不使用结构化异常处理 (SEH)。表示映像不使用传统的 SEH 机制(通常用于 Windows CE 或一些安全强化代码)。 - IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE (0x8000) : 终端服务器感知。表示应用程序在多用户(如远程桌面服务)环境下能正常运行,不会出现资源冲突。 |
SizeOfStackReserve |
4/8 字节 | 线程初始堆栈的保留大小。这是为堆栈预留的虚拟内存空间大小。 |
SizeOfStackCommit |
4/8 字节 | 线程初始堆栈的提交大小。这是为堆栈实际分配的物理内存(RAM)大小。 |
SizeOfHeapReserve |
4/8 字节 | 进程默认堆的保留大小。这是为进程的默认堆预留的虚拟内存空间大小。 |
SizeOfHeapCommit |
4/8 字节 | 进程默认堆的提交大小。这是为进程的默认堆实际分配的物理内存大小。 |
LoaderFlags |
4 字节 | 加载器标志。此字段现在已弃用,通常为 0。在过去用于一些特殊的加载器行为。 |
NumberOfRvaAndSizes |
4 字节 | 数据目录数组中包含的条目数量。这个值通常是 IMAGE_NUMBEROF_DIRECTORY_ENTRIES (即 16)。它指明了 DataDirectory 数组的有效条目个数。 |
SizeOfImage
文件从磁盘映射到内存后的大小
SizeOfHeaders
文件从磁盘映射到内存后的文件头大小,包括DOS头,标准PE头,拓展PE头
数据目录
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // 相对虚拟地址 (RVA)
DWORD Size; // 数据结构的大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
节表
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 节的名称,8 字节
union {
DWORD PhysicalAddress;
DWORD VirtualSize; // 节在内存中的实际大小(字节)
} Misc;
DWORD VirtualAddress; // 节在内存中的相对虚拟地址 (RVA)
DWORD SizeOfRawData; // 节在文件中原始数据的大小(字节)
DWORD PointerToRawData; // 节在文件中的原始数据偏移量(字节)
DWORD PointerToRelocations; // 重定位表的偏移量(对于目标文件)
DWORD PointerToLinenumbers; // 行号信息的偏移量(对于目标文件)
WORD NumberOfRelocations; // 重定位项的数量
WORD NumberOfLinenumbers; // 行号信息的数量
DWORD Characteristics; // 节的属性标志
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;