首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

PE 檔案格式深度解析:從 DOS Header 到資源目錄樹的完整結構圖譜

     編輯:關於C++

可移植執行檔 (Portable Executable, PE) 是 Windows 核心執行檔格式 (EXE、DLL、SYS、OCX 等)。深入理解 PE 結構是逆向工程、病毒分析、載入器開發、二進位修補、編譯器後端優化的基石。本文系統梳理 PE 文件的物理佈局、核心結構體定義 (基於 winnt.h)、關鍵目錄表與區段表關係,並以資源目錄樹為例展示多層級資料組織。

PE 文件整體物理佈局


+------------------+  <-- 文件偏移 0
| DOS MZ Header    |  (IMAGE_DOS_HEADER) 含 e_lfanew 指向 PE Header
| DOS Stub         |  通常顯示 "This program cannot run in DOS mode"
+------------------+  <-- e_lfanew (通常 0x80 或 0x100)
| PE Signature     |  "PE\0\0" (4 bytes)
| COFF File Header |  (IMAGE_FILE_HEADER) 機器類型、區段數、時間戳...
| Optional Header  |  (IMAGE_OPTIONAL_HEADER) 邏輯佈局核心:入口點、映像基址、區段對齊、資料目錄表...
+------------------+
| Section Table    |  區段表陣列 (IMAGE_SECTION_HEADER[]) 每區段 40 bytes
| (Section Headers)|
+------------------+  <-- 區段原始數據起始 (PointerToRawData 對齊)
| Section 1 (.text)|  代碼區
| Section 2 (.data)|  初始化數據
| Section 3 (.rsrc)|  資源區
| ...              |
| Section n        |
+------------------+

核心結構體定義 (關鍵字段解析)

1. IMAGE_NT_HEADERS (PE 標誌 + 文件頭 + 可選頭)


typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;                    // "PE\0\0"
    IMAGE_FILE_HEADER FileHeader;       // 物理佈局信息
    IMAGE_OPTIONAL_HEADER32 OptionalHeader; // 邏輯佈局信息 (32位)
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

2. IMAGE_FILE_HEADER (物理屬性)


typedef struct _IMAGE_FILE_HEADER {
    WORD  Machine;              // 0x14c = Intel 386+, 0x8664 = x64
    WORD  NumberOfSections;     // 區段數量
    DWORD TimeDateStamp;        // 編譯時間戳
    DWORD PointerToSymbolTable; // COFF 符號表 (調試用)
    DWORD NumberOfSymbols;
    WORD  SizeOfOptionalHeader; // OptionalHeader 大小
    WORD  Characteristics;      // 0x0002=EXE, 0x2000=DLL, 0x0020=系統檔...
} IMAGE_FILE_HEADER;

3. IMAGE_OPTIONAL_HEADER32 (邏輯屬性核心)


typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD  Magic;                    // 0x10b = PE32, 0x20b = PE32+ (64位)
    BYTE  MajorLinkerVersion;
    BYTE  MinorLinkerVersion;
    DWORD SizeOfCode;               // 所有代碼區段大小總和
    DWORD SizeOfInitializedData;    // 初始化數據區段大小
    DWORD SizeOfUninitializedData;  // 未初始化數據 (.bss)
    DWORD AddressOfEntryPoint;      // 程式入口點 RVA (相對虛擬地址) **關鍵**
    DWORD BaseOfCode;               // 代碼區段起始 RVA
    DWORD BaseOfData;               // 數據區段起始 RVA
    DWORD ImageBase;                // 建議載入基址 (EXE通常0x400000, DLL 0x10000000)
    DWORD SectionAlignment;         // 記憶體中區段對齊粒度 (通常 0x1000 = 4KB)
    DWORD FileAlignment;            // 文件中區段對齊粒度 (通常 0x200 = 512B)
    WORD  MajorOperatingSystemVersion;
    WORD  MinorOperatingSystemVersion;
    WORD  MajorImageVersion;
    WORD  MinorImageVersion;
    WORD  MajorSubsystemVersion;    // 4.0 以上才有 3D 邊框
    WORD  MinorSubsystemVersion;
    DWORD Win32VersionValue;        // 保留 0
    DWORD SizeOfImage;              // 映像在記憶體中總大小 (含頭、區段對齊後)
    DWORD SizeOfHeaders;            // 所有頭 + 區段表大小 (FileAlignment 對齊)
    DWORD CheckSum;                 // 校驗和 (驅動程式/系統檔必填)
    WORD  Subsystem;                // 2=GUI, 3=CUI, 1=Native, 9=Windows CE
    WORD  DllCharacteristics;       // ASLR, DEP, 高熵VA 等旗標
    DWORD SizeOfStackReserve/Commit;
    DWORD SizeOfHeapReserve/Commit;
    DWORD LoaderFlags;              // 保留
    DWORD NumberOfRvaAndSizes;      // 固定 16
    IMAGE_DATA_DIRECTORY DataDirectory[16]; // **關鍵: 16個重要資料目錄**
} IMAGE_OPTIONAL_HEADER32;

4. IMAGE_DATA_DIRECTORY (資料目錄入口)


typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD VirtualAddress; // 目錄表 RVA
    DWORD Size;           // 目錄表大小
} IMAGE_DATA_DIRECTORY;

16 個預定義目錄索引 (部分):

索引宏定義用途
0IMAGE_DIRECTORY_ENTRY_EXPORT導出表 (DLL 導出函式)
1IMAGE_DIRECTORY_ENTRY_IMPORT導入表 (IAT, 引入函式)
2IMAGE_DIRECTORY_ENTRY_RESOURCE**資源表** (圖標、對話框、字串表...)
5IMAGE_DIRECTORY_ENTRY_BASERELOC基址重定位表 (ASLR 關鍵)
6IMAGE_DIRECTORY_ENTRY_DEBUG調試資訊目錄
9IMAGE_DIRECTORY_ENTRY_TLS執行緒區域儲存 (TLS) 回呼
12IMAGE_DIRECTORY_ENTRY_IAT導入地址表 (綁定後)
14IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR.NET 元資料 (CLI Header)

5. IMAGE_SECTION_HEADER (區段表)


typedef struct _IMAGE_SECTION_HEADER {
    BYTE  Name[8];            // 區段名 ".text", ".data", ".rsrc"...
    union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc;
    DWORD VirtualAddress;     // 區段 RVA (載入後相對 ImageBase 偏移)
    DWORD SizeOfRawData;      // 文件中區段大小 (FileAlignment 對齊)
    DWORD PointerToRawData;   // 文件偏移 **關鍵: 定位原始數據**
    DWORD PointerToRelocations;
    DWORD PointerToLinenumbers;
    WORD  NumberOfRelocations;
    WORD  NumberOfLinenumbers;
    DWORD Characteristics;    // 0x60000020=代碼/可執行/可讀, 0xC0000040=數據/可讀寫...
} IMAGE_SECTION_HEADER;

RVA 轉檔案偏移公式:
FileOffset = PointerToRawData + (RVA - VirtualAddress) (前提:RVA 在該區段範圍內)。

資源目錄樹結構深度剖析 (IMAGE_DIRECTORY_ENTRY_RESOURCE)

資源區 (.rsrc) 內部組織為三層樹狀結構:**類型 -> 名稱/ID -> 語言 -> 數據**。

層級 1:資源目錄根節點 (IMAGE_RESOURCE_DIRECTORY)


typedef struct _IMAGE_RESOURCE_DIRECTORY {
    DWORD Characteristics;        // 保留 0
    DWORD TimeDateStamp;          // 資源建立時間
    WORD  MajorVersion;
    WORD  MinorVersion;
    WORD  NumberOfNamedEntries;   // 使用字串名稱的項目數
    WORD  NumberOfIdEntries;      // 使用整數 ID 的項目數
    // 緊接著: IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY;

層級 2/3:目錄項目 (IMAGE_RESOURCE_DIRECTORY_ENTRY)


typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
    union {
        struct { DWORD NameOffset:31; DWORD NameIsString:1; }; // 高位=1 表示字串名
        DWORD Name;          // 整數 ID (低 16 位有效) 或字串 RVA
        WORD  Id;            // 別名
    };
    union {
        DWORD OffsetToData;  // 高位=0: 指向資源數據項; 高位=1: 指向子目錄
        struct { DWORD OffsetToDirectory:31; DWORD DataIsDirectory:1; };
    };
} IMAGE_RESOURCE_DIRECTORY_ENTRY;

關鍵位操作:

  • NameIsString (0x80000000):1=名稱是字串 (RVA 指向 IMAGE_RESOURCE_DIR_STRING_U),0=名稱是整數 ID。
  • DataIsDirectory (0x80000000):1=指向子目錄 (IMAGE_RESOURCE_DIRECTORY),0=指向數據項 (IMAGE_RESOURCE_DATA_ENTRY)。
  • 去除高位掩碼:0x7FFFFFFF。

資源目錄字串 (IMAGE_RESOURCE_DIR_STRING_U)


typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
    WORD  Length;           // 字元數 (不含 NULL)
    WCHAR NameString[1];    // Unicode 字串 (變長)
} IMAGE_RESOURCE_DIR_STRING_U;

葉子節點:資源數據項 (IMAGE_RESOURCE_DATA_ENTRY)


typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
    DWORD OffsetToData; // 資源數據 RVA (相對 ImageBase,**非**檔案偏移)
    DWORD Size;         // 資源數據大小
    DWORD CodePage;     // 代碼頁 (通常 0 或 1252/65001)
    DWORD Reserved;     // 0
} IMAGE_RESOURCE_DATA_ENTRY;

標準資源類型 ID (根目錄層級)

1: RT_CURSOR2: RT_BITMAP3: RT_ICON4: RT_MENU5: RT_DIALOG
6: RT_STRING7: RT_FONTDIR8: RT_FONT9: RT_ACCELERATOR10: RT_RCDATA
11: RT_MESSAGETABLE12: RT_GROUP_CURSOR14: RT_GROUP_ICON16: RT_VERSION17: RT_DLGINCLUDE
19: RT_PLUGPLAY20: RT_VXD21: RT_ANICURSOR22: RT_ANIICON23: RT_HTML
24: RT_MANIFEST

實用工具與延伸閱讀

  • 工具:dumpbin /headers (VC)、PEview、CFF Explorer、LordPE、010 Editor PE Template。
  • 規格書:Microsoft "PE/COFF Specification" (官方權威)、winnt.h (原始碼定義)。
  • 進階主題:導入表綁定 (Bound Import)、延遲載入 (Delay Load)、TLS 回呼、.NET CLI Header 結構、簽名驗證 (Authenticode)、PE 封裝/加殼原理。

總結

PE 格式雖然結構繁複,但邏輯清晰:DOS 頭定位 PE 頭 -> 可選頭提供邏輯佈局鍵值 (入口點、基址、對齊、資料目錄) -> 區段表映射物理文件到虛擬記憶體 -> 資料目錄索引關鍵子系統 (導入、導出、資源、重定位、TLS...)。掌握上述核心結構體與 RVA/檔案偏移轉換,即可編寫 PE 解析器、修補器、載入器,進行深度二進位分析與工程開發。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved