PE文件结构入门
字数 1278 2025-08-09 17:09:26

PE文件结构详解

一、PE文件概述

1.1 可执行文件类型

  • Windows平台: PE(Portable Executable)文件结构
  • Linux平台: ELF(Executable and Linking Format)文件结构

1.2 PE文件应用领域

  • 病毒与反病毒
  • 外挂与反外挂
  • 加壳与脱壳
  • 无源码修改功能/汉化

1.3 PE文件识别特征

  • 前两字节ASCII是"MZ"
  • 0x3C位置的十六进制对应ASCII是"PE"

二、PE文件结构详解

2.1 DOS部分

typedef struct _IMAGE_DOS_HEADER {
    WORD e_magic;    // Magic number "MZ" (0x4D5A)
    // ...其他DOS头字段...
    LONG e_lfanew;   // PE头相对文件起始地址的偏移
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
  • DOS头大小: 64字节(18个WORD + 1个LONG)
  • 关键字段:
    • e_magic: 必须为"MZ"(0x4D5A)
    • e_lfanew: 指向PE头的偏移

2.2 PE文件头

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;             // "PE\0\0" (0x00004550)
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

2.2.1 标准PE头(IMAGE_FILE_HEADER)

typedef struct _IMAGE_FILE_HEADER {
    WORD Machine;               // 运行平台标识
    WORD NumberOfSections;      // 节表数量
    DWORD TimeDateStamp;        // 创建时间戳
    DWORD PointerToSymbolTable; // COFF符号表偏移
    DWORD NumberOfSymbols;      // 符号表数量
    WORD SizeOfOptionalHeader;  // 扩展PE头大小
    WORD Characteristics;       // 文件属性标志
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

2.2.2 扩展PE头(IMAGE_OPTIONAL_HEADER)

typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD Magic;                 // 标识32/64位(0x10B/0x20B)
    // ...其他字段...
    DWORD AddressOfEntryPoint;  // 程序执行入口RVA
    DWORD ImageBase;            // 首选装载地址
    DWORD SectionAlignment;     // 内存对齐大小
    DWORD FileAlignment;        // 文件对齐大小
    // ...其他字段...
    IMAGE_DATA_DIRECTORY DataDirectory[16]; // 数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

2.2.3 数据目录表

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD VirtualAddress;  // RVA
    DWORD Size;           // 大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

重要目录项:

  • 0: 导出表
  • 1: 导入表
  • 2: 资源表
  • 5: 重定位表
  • 9: TLS目录
  • 12: 导入地址表(IAT)

2.3 节表

typedef struct _IMAGE_SECTION_HEADER {
    BYTE Name[8];           // 节区名称
    union {
        DWORD PhysicalAddress;
        DWORD VirtualSize;  // 节区的实际尺寸
    } Misc;
    DWORD VirtualAddress;   // 节区的RVA地址
    DWORD SizeOfRawData;    // 文件中对齐后的尺寸
    DWORD PointerToRawData; // 文件中的偏移量
    // ...其他字段...
    DWORD Characteristics;  // 节属性(可读/可写/可执行等)
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

2.4 节数据

  • 包含实际的代码、数据等内容
  • 按照节表定义的位置和大小排列

三、PE文件两种状态

3.1 磁盘状态

  • 节按照FileAlignment对齐(通常200h)
  • 未初始化数据不占用磁盘空间

3.2 内存状态

  • 节按照SectionAlignment对齐(通常1000h)
  • 未初始化数据分配内存空间
  • 部分节(.reloc等)不映射到内存

四、关键操作技术

4.1 RVA与FOA转换

  1. 判断RVA是否位于PE头:
    • 是: FOA = RVA
  2. 定位所属节:
    • RVA >= 节.VirtualAddress
    • RVA <= 节.VirtualAddress + 内存对齐后大小
  3. 计算差值 = RVA - 节.VirtualAddress
  4. FOA = 节.PointerToRawData + 差值

4.2 空白区添加代码

步骤:

  1. 构造要写入的代码
  2. 在PE空白区插入代码
  3. 修改入口地址指向新增代码
  4. 新增代码执行后跳回原入口

4.3 扩大节

步骤:

  1. 分配新空间(大小S)
  2. 修改最后一个节的SizeOfRawData和VirtualSize
    • N = 原值(对齐后) + S
  3. 修改SizeOfImage的值

4.4 新增节

步骤:

  1. 检查是否有足够空间添加节表
  2. 在节表中新增成员
  3. 修改PE头中节的数量
  4. 修改SizeOfImage大小
  5. 在文件末尾添加节数据(内存对齐整数倍)
  6. 设置新增节表属性

五、重要数据表

5.1 导出表

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD Characteristics;
    DWORD TimeDateStamp;
    WORD MajorVersion;
    WORD MinorVersion;
    DWORD Name;               // 模块名RVA
    DWORD Base;               // 序号基数
    DWORD NumberOfFunctions;  // 导出函数总数
    DWORD NumberOfNames;      // 按名导出函数数
    DWORD AddressOfFunctions; // 函数地址表RVA
    DWORD AddressOfNames;     // 函数名指针表RVA
    DWORD AddressOfNameOrdinals; // 序号表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

5.2 导入表

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD Characteristics;
        DWORD OriginalFirstThunk; // 导入名称表(INT)RVA
    };
    DWORD TimeDateStamp;
    DWORD ForwarderChain;
    DWORD Name;                // DLL名RVA
    DWORD FirstThunk;          // 导入地址表(IAT)RVA
} IMAGE_IMPORT_DESCRIPTOR;

5.3 重定位表

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD VirtualAddress;      // 页起始RVA
    DWORD SizeOfBlock;         // 该分组大小
    WORD TypeOffset[1];        // 重定位项数组
} IMAGE_BASE_RELOCATION;
  • 重定位类型: IMAGE_REL_BASED_HIGHLOW(3)
  • 每个WORD: 高4位为类型,低12位为偏移

六、总结

PE文件结构是Windows平台可执行文件的核心组织形式,理解其结构对于软件安全分析、逆向工程、病毒分析等领域至关重要。掌握DOS头、PE头、节表及各数据目录的结构和相互关系,能够帮助深入分析PE文件的行为和特性。

PE文件结构详解 一、PE文件概述 1.1 可执行文件类型 Windows平台 : PE(Portable Executable)文件结构 Linux平台 : ELF(Executable and Linking Format)文件结构 1.2 PE文件应用领域 病毒与反病毒 外挂与反外挂 加壳与脱壳 无源码修改功能/汉化 1.3 PE文件识别特征 前两字节ASCII是"MZ" 0x3C位置的十六进制对应ASCII是"PE" 二、PE文件结构详解 2.1 DOS部分 DOS头大小 : 64字节(18个WORD + 1个LONG) 关键字段 : e_magic : 必须为"MZ"(0x4D5A) e_lfanew : 指向PE头的偏移 2.2 PE文件头 2.2.1 标准PE头(IMAGE_ FILE_ HEADER) 2.2.2 扩展PE头(IMAGE_ OPTIONAL_ HEADER) 2.2.3 数据目录表 重要目录项: 0: 导出表 1: 导入表 2: 资源表 5: 重定位表 9: TLS目录 12: 导入地址表(IAT) 2.3 节表 2.4 节数据 包含实际的代码、数据等内容 按照节表定义的位置和大小排列 三、PE文件两种状态 3.1 磁盘状态 节按照 FileAlignment 对齐(通常200h) 未初始化数据不占用磁盘空间 3.2 内存状态 节按照 SectionAlignment 对齐(通常1000h) 未初始化数据分配内存空间 部分节(.reloc等)不映射到内存 四、关键操作技术 4.1 RVA与FOA转换 判断RVA是否位于PE头: 是: FOA = RVA 定位所属节: RVA >= 节.VirtualAddress RVA <= 节.VirtualAddress + 内存对齐后大小 计算差值 = RVA - 节.VirtualAddress FOA = 节.PointerToRawData + 差值 4.2 空白区添加代码 步骤: 构造要写入的代码 在PE空白区插入代码 修改入口地址指向新增代码 新增代码执行后跳回原入口 4.3 扩大节 步骤: 分配新空间(大小S) 修改最后一个节的SizeOfRawData和VirtualSize N = 原值(对齐后) + S 修改SizeOfImage的值 4.4 新增节 步骤: 检查是否有足够空间添加节表 在节表中新增成员 修改PE头中节的数量 修改SizeOfImage大小 在文件末尾添加节数据(内存对齐整数倍) 设置新增节表属性 五、重要数据表 5.1 导出表 5.2 导入表 5.3 重定位表 重定位类型: IMAGE_ REL_ BASED_ HIGHLOW(3) 每个WORD: 高4位为类型,低12位为偏移 六、总结 PE文件结构是Windows平台可执行文件的核心组织形式,理解其结构对于软件安全分析、逆向工程、病毒分析等领域至关重要。掌握DOS头、PE头、节表及各数据目录的结构和相互关系,能够帮助深入分析PE文件的行为和特性。