PE 文件结构剖析:手工压缩与注入实战
字数 1674 2025-08-22 12:22:15

PE文件结构剖析与手工压缩、注入实战

1. PE文件概述

PE (Portable Executable) 是可移植的可执行文件,是Windows操作系统下可执行文件的标准格式,包括:

  • 可执行文件 (exe, scr)
  • 动态链接库 (dll, ocx, cpl)
  • 驱动程序 (sys, vxd)

了解PE文件结构对于逆向工程、软件分析和安全研究非常重要。

2. PE文件结构详解

2.1 DOS头 (DOS Header + DOS Stub)

DOS Header (IMAGE_DOS_HEADER结构体,0x40字节):

typedef struct _IMAGE_DOS_HEADER {
    WORD e_magic;    // 标识符"MZ"(0x5A4D)
    // ...其他字段...
    LONG e_lfanew;   // 指向PE头的偏移地址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

关键字段:

  • e_magic:标识文件为可执行文件
  • e_lfanew:指向PE头的偏移地址

DOS Stub

  • 在DOS环境中运行的简单程序
  • 在Windows下不会运行
  • 大小不固定,通常可以删除

2.2 NT头 (NT Header)

IMAGE_NT_HEADERS结构体(32位下0xF8字节):

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;                 // PE签名"PE\0\0"
    IMAGE_FILE_HEADER FileHeader;    // PE文件头
    IMAGE_OPTIONAL_HEADER32 OptionalHeader; // PE可选头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

FileHeader (IMAGE_FILE_HEADER结构体,0x14字节):

typedef struct _IMAGE_FILE_HEADER {
    WORD Machine;            // 目标机器类型
    WORD NumberOfSections;   // 节区数量
    DWORD TimeDateStamp;     // 时间戳
    // ...其他字段...
    WORD Characteristics;    // 文件特性标志
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

OptionalHeader (IMAGE_OPTIONAL_HEADER32结构体):

typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD Magic;             // 可选头类型(0x10B=32位,0x20B=64位)
    // ...标准域...
    DWORD AddressOfEntryPoint;  // 程序入口点RVA
    DWORD ImageBase;        // 镜像基址
    DWORD SectionAlignment; // 内存对齐大小
    DWORD FileAlignment;    // 文件对齐大小
    // ...NT附加域...
    DWORD SizeOfImage;      // 镜像在内存中的大小
    DWORD SizeOfHeaders;    // PE头物理大小
    // ...数据目录...
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

2.3 节表区 (Section Table)

节表由一系列IMAGE_SECTION_HEADER结构组成,每个结构描述一个节区:

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文件依赖的模块(dll)及其函数
  • 每个依赖模块对应一个IMAGE_IMPORT_DESCRIPTOR结构(0x14字节)
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD Characteristics;
        DWORD OriginalFirstThunk; // 指向IAT(导入地址表)
    } DUMMYUNIONNAME;
    DWORD TimeDateStamp;
    DWORD ForwarderChain;
    DWORD Name;             // 指向DLL名称的RVA
    DWORD FirstThunk;       // 指向IAT
} IMAGE_IMPORT_DESCRIPTOR;

导出表

  • 记录DLL提供的导出函数
  • 包含函数地址、名称和序号
  • 可通过API获取导出函数:
    • LoadLibrary/GetModuleHandle获取模块句柄
    • GetProcAddress获取函数地址

3. PE文件手工压缩实战

3.1 压缩思路

  1. 删除冗余数据(DOS Stub等)
  2. 修改对齐方式(最小为0x4)
  3. 合并节区(如代码节和数据节)
  4. 见缝插针(将有用数据填充到空白区域)

3.2 具体步骤

  1. DOS头处理

    • 保留e_magice_lfanew
    • 删除DOS Stub
    • 修改e_lfanew指向新的PE头位置
  2. NT头处理

    • 修改对齐方式(SectionAlignmentFileAlignment为0x4)
    • 更新AddressOfEntryPointSizeOfImageSizeOfHeaders
    • 修正导入表和导入地址表的RVA和Size
  3. 节表处理

    • 修改各节表的VirtualSizeVirtualAddressSizeOfRawDataPointerToRawData
    • 保留节区属性(Characteristics)
  4. 节区数据处理

    • 删除多余填充数据
    • 修正代码中的硬编码地址
    • 确保字符串有\x00终止符

3.3 注意事项

  • SizeOfHeaders必须正确,否则无法定位节区
  • .rdata节中的字符串必须有终止符
  • 修改后需保持所有RVA引用的正确性
  • 合并节区需注意权限设置

4. PE文件代码注入实战

4.1 针对EXE的代码注入

  1. 在数据段找空白处插入字符串
  2. 在有执行权限的内存中注入指令
    push 0
    push 0x010051B0    ; 字符串地址
    push 0x010051BB    ; 标题地址
    push 0
    call MessageBoxA
    jmp original_entry ; 跳回原入口点
    
  3. 修改程序入口地址为注入代码的RVA

4.2 针对DLL的代码注入

  1. 使用Process Monitor查找目标程序依赖的系统DLL
  2. 使用AHeadLib.Net工具生成DLL源码模板
  3. __ExecuteUserCutomCodes函数中添加恶意代码
    system("calc.exe");
    
  4. 编译后将DLL放在目标程序同级目录(利用DLL搜索顺序)

5. 总结

PE文件结构复杂但规律性强,掌握其结构可以:

  1. 深入理解Windows程序加载执行机制
  2. 进行有效的文件压缩和优化
  3. 实现代码注入和功能扩展
  4. 开展逆向分析和安全研究

关键点在于理解各结构体的含义和相互关系,以及RVA与文件偏移的转换。实际操作中需结合调试工具(如IDA、010Editor)验证修改效果。

PE文件结构剖析与手工压缩、注入实战 1. PE文件概述 PE (Portable Executable) 是可移植的可执行文件,是Windows操作系统下可执行文件的标准格式,包括: 可执行文件 (exe, scr) 动态链接库 (dll, ocx, cpl) 驱动程序 (sys, vxd) 了解PE文件结构对于逆向工程、软件分析和安全研究非常重要。 2. PE文件结构详解 2.1 DOS头 (DOS Header + DOS Stub) DOS Header (IMAGE_ DOS_ HEADER结构体,0x40字节): 关键字段: e_magic :标识文件为可执行文件 e_lfanew :指向PE头的偏移地址 DOS Stub : 在DOS环境中运行的简单程序 在Windows下不会运行 大小不固定,通常可以删除 2.2 NT头 (NT Header) IMAGE_ NT_ HEADERS 结构体(32位下0xF8字节): FileHeader (IMAGE_ FILE_ HEADER结构体,0x14字节): OptionalHeader (IMAGE_ OPTIONAL_ HEADER32结构体): 2.3 节表区 (Section Table) 节表由一系列IMAGE_ SECTION_ HEADER结构组成,每个结构描述一个节区: 2.4 导入表与导出表 导入表 : 记录PE文件依赖的模块(dll)及其函数 每个依赖模块对应一个IMAGE_ IMPORT_ DESCRIPTOR结构(0x14字节) 导出表 : 记录DLL提供的导出函数 包含函数地址、名称和序号 可通过API获取导出函数: LoadLibrary / GetModuleHandle 获取模块句柄 GetProcAddress 获取函数地址 3. PE文件手工压缩实战 3.1 压缩思路 删除冗余数据(DOS Stub等) 修改对齐方式(最小为0x4) 合并节区(如代码节和数据节) 见缝插针(将有用数据填充到空白区域) 3.2 具体步骤 DOS头处理 : 保留 e_magic 和 e_lfanew 删除DOS Stub 修改 e_lfanew 指向新的PE头位置 NT头处理 : 修改对齐方式( SectionAlignment 和 FileAlignment 为0x4) 更新 AddressOfEntryPoint 、 SizeOfImage 和 SizeOfHeaders 修正导入表和导入地址表的RVA和Size 节表处理 : 修改各节表的 VirtualSize 、 VirtualAddress 、 SizeOfRawData 和 PointerToRawData 保留节区属性( Characteristics ) 节区数据处理 : 删除多余填充数据 修正代码中的硬编码地址 确保字符串有 \x00 终止符 3.3 注意事项 SizeOfHeaders 必须正确,否则无法定位节区 .rdata 节中的字符串必须有终止符 修改后需保持所有RVA引用的正确性 合并节区需注意权限设置 4. PE文件代码注入实战 4.1 针对EXE的代码注入 在数据段找空白处插入字符串 在有执行权限的内存中注入指令 修改程序入口地址为注入代码的RVA 4.2 针对DLL的代码注入 使用Process Monitor查找目标程序依赖的系统DLL 使用AHeadLib.Net工具生成DLL源码模板 在 __ExecuteUserCutomCodes 函数中添加恶意代码 编译后将DLL放在目标程序同级目录(利用DLL搜索顺序) 5. 总结 PE文件结构复杂但规律性强,掌握其结构可以: 深入理解Windows程序加载执行机制 进行有效的文件压缩和优化 实现代码注入和功能扩展 开展逆向分析和安全研究 关键点在于理解各结构体的含义和相互关系,以及RVA与文件偏移的转换。实际操作中需结合调试工具(如IDA、010Editor)验证修改效果。