PE可选头

本文深入解析PE文件格式中的可选头结构,包括其组成、字段含义及作用,并介绍了环境子系统的定义。

紧跟着PE文件头的,是一个叫做PE可选头(Optional Header)的数据结构。这个数据结构被叫做可选头是因为某些PE格式文件,比如目标文件(.OBJ)没有这个数据结构。但是对于一个可执行文件或动态链接库来说,这个数据结构是必须的。可选头由4部分组成,

第一部分是一个PE可选头签名。通过检查这个签名,我们可以知道这是一个32位的可选头还是64位的可选头。我们现在只介绍32位的可选头。 对于32位的PE文件来说,这个签名是0x10b

紧跟签名的第二部分是一个标准COFF标准数据。再接下来的第三部分是Windows对COFF头所做的一些扩展。最后一部分是PE文件中一些重要数据结构(导入表,导出表等)的索引。

在WINNT.h中,我们可以看到这个可选头的C语法声明:

typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

下面我们来看下这里最主要的几个字段。

Field

Description

Magic

一般 0x10b

SizeOfCode

段大

SizeOfInitializedData

初始化的数据大

SizeOfUninitializedData

未初始化数据大

AddressOfEntryPoint

程序入口点 .

BaseOfCode

段在文件中的起始位

 

Field

Description

ImageBase

文件基地址 ,必须 64K 对齐 . 假如应用程序被加载到这个地址,就不需要重定位了。

Win32VersionValue

Reserved, must be zero.

Subsystem

运行该程序需要的环境子系统

DllCharacteristics

DLL 属性

SizeOfStackReserve

默认堆栈大小

SizeOfHeapReserve

默认堆大小

NumberOfRvaAndSizes

数据目录表大小。(下一篇会描述)


环境子系统常量定义

Constant

Value

Description

IMAGE_SUBSYSTEM_UNKNOWN

  0

未知子系统

IMAGE_SUBSYSTEM_NATIVE

  1

Windows 驱动程序

IMAGE_SUBSYSTEM_WINDOWS_GUI

  2

WIN32 GUI

IMAGE_SUBSYSTEM_WINDOWS_CUI

  3

WIN32 控制台

IMAGE_SUBSYSTEM_POSIX_CUI

  7

POSIX

IMAGE_SUBSYSTEM_WINDOWS_CE_GUI

  9

Windows CE

IMAGE_SUBSYSTEM_XBOX

14

XBOX


<think> 嗯好的,用户这次想了解可选PE的结构,看来是在研究Windows可执行文件格式。从引用内容看,用户已经接触过PE文件的基础概念,现在需要深入理解可选的具体组成功能。 用户可能正在学习逆向工程或系统开发,需要掌握PE文件解析技术。引用[2][3][4]特别提到了可选的版本差异数据结构,这应该是重点内容。用户上次提到的是PE整体结构,这次聚焦可选说明学习在逐步深入。 需要特别注意几个技术要点:一是32位64位版本的魔术字差异(0x10b0x20b),这在引用[4]的宏定义里有明确体现;二是可选中的关键字段如AddressOfEntryPointImageBase,这些关系到程序执行入口内存映射;三是数据目录的结构,它指向导入导出等核心信息。 用户提供的引用[5]提到模块概念很有意思,虽然可选没有直接关系,但可以说明用户对PE文件的整体认知是清晰的。在回答时要注意区分文件(IMAGE_FILE_HEADER)可选(IMAGE_OPTIONAL_HEADER)的界限,引用[4]特别强调过前者描述文件特征后者管理装载信息。 考虑到用户的技术水平,准备用格形式呈现字段偏移量会更直观。由于涉及指针位宽差异,要明确标注32/64位版本的不同字段大小。DataDirectory数组的解析需要单独说明,因为它是嵌套结构。 最后生成的问题需要考虑用户可能的后续研究方向:结构内存映射关系、实际解析工具的使用、安全领域的应用场景等。特别要包含区块对齐这类难点问题,引用[3]提到过区块(section header)与可选的关联性。 </think> 以下是关于 PE 可选(`IMAGE_OPTIONAL_HEADER`)的详细解析,结合引用内容整理关键信息: --- ### **一、可选的作用** 可选是 NT (`IMAGE_NT_HEADERS`)的重要组成部分,紧跟在**文件(`IMAGE_FILE_HEADER`)**之后。它的核心作用是**管理 PE 文件被操作系统加载到内存时的关键信息**,包括: - 程序入口点(EntryPoint) - 内存对齐方式 - 所需系统版本 - 堆栈大小 - 数据目录(指向导入、导出等)[^2][^4] > 引用说明:*“文件描述文件基本信息,可选管理装载信息”*[^4]。 --- ### **二、32位与64位版本的区别** 可选分为 **`IMAGE_OPTIONAL_HEADER32`(32位)** **`IMAGE_OPTIONAL_HEADER64`(64位)**,通过**魔术字(Magic)** 区分: - `0x10B` → 32 位版本 - `0x20B` → 64 位版本 - `0x107` → ROM 镜像(较少见)[^4] ```cpp // 系统宏定义(引用[4]) #define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b ``` > **关键差异**: > - 64 位版本**删除**了 `BaseOfData` 字段(32位独有)。 > - 地址字段(如 `ImageBase`)从 `DWORD`(4字节)扩展为 `ULONGLONG`(8字节)。 --- ### **三、数据结构与关键字段解析** #### **1. 通用字段(32/64位共用)** | 字段名 | 偏移 | 大小 | 说明 | |----------------------|------|------|------| | **Magic** | 0x00 | 2 | 标识版本(0x10B/0x20B) | | **AddressOfEntryPoint** | 0x10 | 4 | **程序执行入口地址**(RVA) | | **ImageBase** | 0x1C | 4/8 | 文件加载到内存的基地址 | | **SectionAlignment** | 0x24 | 4 | 内存中区块的对齐值(通常 0x1000) | | **FileAlignment** | 0x28 | 4 | 文件中区块的对齐值(通常 0x200) | | **SizeOfImage** | 0x38 | 4 | 加载到内存后的总大小(对齐后) | | **Subsystem** | 0x44 | 2 | 文件类型(GUI=2, CONSOLE=3) | #### **2. 32位特有字段** | 字段名 | 偏移 | 大小 | 说明 | |-----------------|------|------|------| | **BaseOfData** | 0x14 | 4 | 数据段的起始 RVA(64位已移除) | #### **3. 数据目录(DataDirectory)** 位于可选末尾(32位偏移 `0x60`,64位偏移 `0x70`),包含 **16 个结构体数组**,每个结构体定义: ```c typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; // 数据起始 RVA DWORD Size; // 数据大小 } IMAGE_DATA_DIRECTORY; ``` **重要目录索引**: - 0: 导出(`Export Table`) - 1: 导入(`Import Table`) - 2: 资源(`Resource Table`) - 5: 重定位(`Base Relocation Table`) > 引用说明:*“数据目录指向导入、导出等核心结构”*[^4]。 --- ### **四、内存映射关系** 可选中的字段直接影响 PE 加载行为: 1. **`ImageBase` + `AddressOfEntryPoint`** → 程序第一条指令地址。 2. **`SectionAlignment`** **`FileAlignment`** 共同决定区块在磁盘文件与内存中的布局差异。 3. **`SizeOfImage`** 确保系统预留足够内存空间[^3][^4]。 --- ### **五、解析示例(C++伪代码)** ```cpp // 读取 NT IMAGE_NT_HEADERS* pNtHeader = (IMAGE_NT_HEADERS*)(pDosHeader + pDosHeader->e_lfanew); // 获取可选指针(兼容32/64位) if (pNtHeader->OptionalHeader.Magic == 0x10B) { IMAGE_OPTIONAL_HEADER32* pOptHeader = &(pNtHeader->OptionalHeader32); DWORD entryPoint = pOptHeader->AddressOfEntryPoint; // 入口点 RVA DWORD imageBase = pOptHeader->ImageBase; // 基址 } else if (pNtHeader->OptionalHeader.Magic == 0x20B) { IMAGE_OPTIONAL_HEADER64* pOptHeader = &(pNtHeader->OptionalHeader64); // 64位字段处理... } ``` > 完整源码可参考引用[^3][^4]。 --- ### **总结** 可选是连接**文件结构**与**内存执行**的桥梁,其字段精确控制了 PE 文件如何被操作系统加载运行。掌握其结构对逆向分析、漏洞挖掘及安全加固至关重要[^1][^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值