文章

WindowsPE指南

WindowsPE指南

该文主要介绍 Windows PE 结构。

WindowsPE指南

1. PE 文件头

1.1. 基本概念

1.1.1. 地址

虚拟内存地址(Virtual Address, VA):用户的PE文件被操作系统加载进内存后,PE对应的进程支配了自己独立的虚拟空间;进程本身的VA被解释为:进程的基地址+相对虚拟内存地址;

相对虚拟内存地址(Reverse Virtual Address, RVA):RVA是相对于模块而言的,VA是相对于整个地址空间而言的。RVA与具体模块相关,它有一个范围,该范围从模块的开始到模块结束,脱离开这个范围的RVA是无效的,称为越界。

文件偏移地址(File Offset Address, FOA):和内存无关,它是指某个位置距离文件头的偏移。

特殊地址:在PE结构中还有一种特殊地址,其计算方法并不是从文件头算起,也不是从内存的某个模块的基地址算起,而是从某个特定的位置算起。

1.1.2. 指针

PE数据结构中的指针的定义:如果数据结构中某个字段存储的值为一个地址,那么这个字段就是一个指针。

1.1.3. 数据目录

PE中有一个数据结构称为数据目录,其中记录了所有可能的数据类型。这些类型中,目前已定义的有15种,包括导出表、导入表、资源表、异常表、属性证书表、重定位表、调试数据、Architecture、Global Ptr、线程局部存储、加载配置表、绑定导入表、IAT、延迟导入表和CLR运行时头部。

1.1.4. 节

节就是存放不同类型数据(比如代码、数据、常量、资源等)的地方,不同的节具有不同的访问权限。节是PE文件中存放代码或数据的基本单元。

1.1.5. 对齐

PE中规定了三类对齐:数据在内存中的对齐、数据在文件中的对齐、资源文件中资源数据的对齐。

1.1.6. Unicode字符串

Unicode是继ASCII字符编码后的另一种新型字符编码。严格意义上讲,ASCII码的每个字符使用7位表示,Unicode则使用全16位表示一个字符。Unicode字符串中的每个字符均为双字节,所以又称为宽字符串。

1.2. PE文件结构

1.2.1. DOS头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _IMAGE_DOS_HEADER {
    WORD   e_magic;                             // 0000h - EXE标志,“MZ”
    WORD   e_cblp;                              // 0002h - 最后(部分)页中的字节数
    WORD   e_cp;                                // 0004h - 文件中的全部和部分页数
    WORD   e_crlc;                              // 0006h - 重定位表中的指针数
    WORD   e_cparhdr;                           // 0008h - 头部尺寸,以段落为单位
    WORD   e_minalloc;                          // 000ah - 所需的最小附加段
    WORD   e_maxalloc;                          // 000ch - 所需的最大附加段
    WORD   e_ss;                                // 000eh - 初始的SS值(相对偏移量)
    WORD   e_sp;                                // 0010h - 初始的SP值
    WORD   e_csum;                              // 0012h - 补码校验值
    WORD   e_ip;                                // 0014h - 初始的IP值
    WORD   e_cs;                                // 0016h - 初始的Cs值
    WORD   e_lfarlc;                            // 0018h - 重定位表的字节偏移量
    WORD   e_ovno;                              // 001ah - 覆盖号
    WORD   e_res[4];                            // 001ch - 保留字
    WORD   e_oemid;                             // 0024h - OEM标识符(相对e_oeminfo)
    WORD   e_oeminfo;                           // 0026h - OEM信息
    WORD   e_res2[10];                          // 0028h - 保留字
    LONG   e_lfanew;                            // 003ch - PE头相对于文件的偏移地址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

注释后的偏移是基于 IMAGE_DOS_HEADER 头的

1.2.2. PE头

1
2
3
4
5
typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;                            // PE 文件标识, "PE\0\0"
    IMAGE_FILE_HEADER FileHeader;               // PE 标准头
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;     // PE 扩展头
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
1.2.2.1. PE头标识

紧跟在 DOS Stub 后面的是PE头标识 Signature。与大部分文件格式的头部结构一样,PE 头部信息中有一个四字节的标识,该标识位于指针 IMAGE_DOS_HEADER.e_lfanew 指向的位置。其内容固定,对应于 ASCII 码的字符串 PE\0\0

1.2.2.2. 标准PE头

标准 PE 头 IMAGE_FILE_HEADER 紧跟在 PE 头标识后,即位于 IMAGE_DOS_HEADER的e_lfanew+4 的位置。由此位置开始的 20 个字节为数据结构标准 PE 头 IMAGE_FILE_HEADER 的内容。该结构在微软的官方文档中被称为标准通用对象文件格式(Common Object File Format,COFF)头。它记录了PE文件的全局属性,如该PE文件运行的平台、PE文件类型(是EXE文件还是DLL文件)、文件中存在的节的总数等

1
2
3
4
5
6
7
8
9
typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;                            // 0004h - 运行平台
    WORD    NumberOfSections;                   // 0006h - PE中节的数量
    DWORD   TimeDateStamp;                      // 0008h - 文件创建日期和时间
    DWORD   PointerToSymbolTable;               // 000ch - 指向符号表(用于调试)
    DWORD   NumberOfSymbols;                    // 0010h - 符号表中的符号数量(用于调试)
    WORD    SizeOfOptionalHeader;               // 0014h - 扩展头结构的长度
    WORD    Characteristics;                    // 0016h - 文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

注释后的偏移是基于 IMAGE_NT_HEADERS 头的

1.2.2.3. 扩展PE头

尽管从名字上看好像该部分数据是可选(optional)的,但在PE文件结构中,它却有着比标准PE头更多的内容,让人感觉似乎它才是真正的PE头。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD        Magic;                          // 0018h - 魔术字: 0x10B=32位PE,0x20B=64位PE,0x107=ROM镜像
    BYTE        MajorLinkerVersion;             // 001ah - 链接器主版本号
    BYTE        MinorLinkerVersion;             // 001bh - 链接器次版本号
    DWORD       SizeOfCode;                     // 001ch - 所有含代码的节的总大小
    DWORD       SizeOfInitializedData;          // 0020h - 所有含已初始化数据的节的总大小
    DWORD       SizeOfUninitializedData;        // 0024h - 所有含未初始化数据的节的总大小
    DWORD       AddressOfEntryPoint;            // 0028h - 程序执行入口RVA(相对虚拟地址)
    DWORD       BaseOfCode;                     // 002ch - 代码节的起始RVA
    ULONGLONG   ImageBase;                      // 0030h - 镜像首选加载基址(64位默认0x140000000)
    DWORD       SectionAlignment;               // 0038h - 内存中节的对齐粒度(典型0x1000=4KB)
    DWORD       FileAlignment;                  // 003ch - 文件中节的对齐粒度(典型0x200=512字节)
    WORD        MajorOperatingSystemVersion;    // 0040h - 所需操作系统主版本号
    WORD        MinorOperatingSystemVersion;    // 0042h - 所需操作系统次版本号
    WORD        MajorImageVersion;              // 0044h - 镜像主版本号(由开发者定义)
    WORD        MinorImageVersion;              // 0046h - 镜像次版本号(由开发者定义)
    WORD        MajorSubsystemVersion;          // 0048h - 所需子系统主版本号
    WORD        MinorSubsystemVersion;          // 004ah - 所需子系统次版本号
    DWORD       Win32VersionValue;              // 004ch - Win32版本值(保留,通常为0)
    DWORD       SizeOfImage;                    // 0050h - 内存中整个镜像的总大小(按SectionAlignment对齐)
    DWORD       SizeOfHeaders;                  // 0054h - 所有头部(DOS头+NT头+节表)按FileAlignment对齐后的大小
    DWORD       CheckSum;                       // 0058h - 校验和(用于驱动DLL和系统关键文件验证)
    WORD        Subsystem;                      // 005ch - 子系统类型(如2=GUI,3=控制台)
    WORD        DllCharacteristics;             // 005eh - DLL特征标志(如ASLR、DEP、NX兼容等)
    ULONGLONG   SizeOfStackReserve;             // 0060h - 线程栈保留大小(虚拟地址空间,非实际分配)
    ULONGLONG   SizeOfStackCommit;              // 0068h - 线程栈初始提交大小(实际分配的物理内存)
    ULONGLONG   SizeOfHeapReserve;              // 0070h - 堆保留大小(虚拟地址空间)
    ULONGLONG   SizeOfHeapCommit;               // 0078h - 堆初始提交大小
    DWORD       LoaderFlags;                    // 0080h - 加载器标志(已弃用,通常为0)
    DWORD       NumberOfRvaAndSizes;            // 0084h - 数据目录项数量(通常为16)
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 0088h - 数据目录表(导入/导出/资源/重定位等)
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

注释后的偏移是基于 IMAGE_NT_HEADERS 头的。

1.2.2.3.1. 数据目录项

该字段定义了PE文件中出现的所有不同类型的数据的目录信息。如前所述,应用程序中的数据被按照用途分成很多种类,如导出表、导入表、资源、重定位表等。在内存中,这些数据被操作系统以页为单位组织起来,并赋以不同的访问属性;在文件中,这些数据也同样被组织起来,按照不同类别分别存放在文件的指定位置。该结构就是用来描述这些不同类别的数据在文件(和内存)中的位置及大小的。

1
2
3
4
typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;                     // 0000h - 数据的起始RVA
    DWORD   Size;                               // 0004h - 数据块的长度
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
编号英文名称中文描述
0IMAGE_DIRECTORY_ENTRY_EXPORT导出表 - 存储 DLL/EXE 导出的函数、变量、类等信息
1IMAGE_DIRECTORY_ENTRY_IMPORT导入表 - 记录本模块需要调用的外部函数信息
2IMAGE_DIRECTORY_ENTRY_RESOURCE资源表 - 存储图标、菜单、对话框、字符串等资源数据
3IMAGE_DIRECTORY_ENTRY_EXCEPTION异常处理表 - 用于结构化异常处理(SEH),x64/ARM 平台的展开信息
4IMAGE_DIRECTORY_ENTRY_SECURITY安全证书目录 - 存储数字签名和证书信息(不映射到内存,仅存在于文件中)
5IMAGE_DIRECTORY_ENTRY_BASERELOC基址重定位表 - 当首选加载地址被占用时,需要修复的地址偏移列表
6IMAGE_DIRECTORY_ENTRY_DEBUG调试信息表 - 存储调试符号、PDB 文件路径等调试数据
7IMAGE_DIRECTORY_ENTRY_ARCHITECTURE架构保留表 - 已弃用,保留为 0
8IMAGE_DIRECTORY_ENTRY_GLOBALPTR全局指针表 - 用于 RISC 平台的全局指针相对寻址(MIPS/Alpha 架构)
9IMAGE_DIRECTORY_ENTRY_TLSTLS 表 - 线程局部存储,用于多线程环境下每线程独立的全局变量
10IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG加载配置表 - 包含安全特性配置(如 DEP、SEH、控制流保护 CFG、动态安全初始化等)
11IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT绑定导入表 - 预绑定的导入表,用于加速 DLL 加载(已较少使用)
12IMAGE_DIRECTORY_ENTRY_IAT导入地址表 - 导入函数实际地址表,由加载器填充
13IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT延迟导入表 - 延迟加载的 DLL 信息,只在首次调用时才真正加载
14IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTORCOM+ 描述符 - 用于 .NET / CLI 元数据描述符(CLR 头)
15IMAGE_DIRECTORY_ENTRY_RESERVED保留项 - 未使用,值为 0

1.2.3. 节表项

每个节表项记录了PE中与某个特定的节有关的信息,如节的属性、节的大小、在文件和内存中的起始位置等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _IMAGE_SECTION_HEADER {
    BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];      // 节名称(8字节,ANSI字符串,不足补0,不必以\0结尾)".text", ".data", ".rdata", ".rsrc"
    union {
        DWORD   PhysicalAddress;                // 物理地址(仅用于目标文件.obj,不用于PE)
        DWORD   VirtualSize;                    // 未对齐前的实际大小(内存中节的有效数据大小)
    } Misc;
    DWORD   VirtualAddress;                     // 节在内存中的RVA(相对虚拟地址,按SectionAlignment对齐)
    DWORD   SizeOfRawData;                      // 节在磁盘文件中的大小(按FileAlignment对齐,可能是VirtualSize的向上取整)
    DWORD   PointerToRawData;                   // 节在磁盘文件中的偏移量(相对于文件开头)
    DWORD   PointerToRelocations;               // 重定位表在文件中的偏移(仅用于.obj目标文件,PE文件中为0)
    DWORD   PointerToLinenumbers;               // 行号表在文件中的偏移(用于调试信息,PE中很少使用)
    WORD    NumberOfRelocations;                // 重定位表条目数量(仅用于.obj目标文件)
    WORD    NumberOfLinenumbers;                // 行号表条目数量(用于调试信息)
    DWORD   Characteristics;                    // 节属性标志位(描述该节的内存属性:可读/可写/可执行等)
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

注释后的偏移是基于 IMAGE_SECTION_HEADER 头的

2. 导入表

2.1. 导入表描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;                // 0 表示此为结束标记(最后一个导入描述符)
        DWORD   OriginalFirstThunk;             // RVA,指向原始的未绑定的IAT(即 INT,导入名称表)
    } DUMMYUNIONNAME;
    DWORD   TimeDateStamp;                      // 时间戳,用于绑定导入(Bound Import)
    DWORD   ForwarderChain;                     // 转发链索引
    DWORD   Name;                               // RVA,指向以 '\0' 结尾的 DLL 名称字符串
    DWORD   FirstThunk;                         // RVA,指向 IAT(导入地址表)
} IMAGE_IMPORT_DESCRIPTOR, *PIMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_THUNK_DATA64 {
    union {
        ULONGLONG ForwarderString;              // 转发器字符串的RVA(指向一个ASCII字符串)
        ULONGLONG Function;                     // 函数的内存地址(运行时填充)
        ULONGLONG Ordinal;                      // 导入函数的序号(按序号导入)
        ULONGLONG AddressOfData;                // 指向导入函数名称信息的RVA(按名称导入)
    } u1;
} IMAGE_THUNK_DATA64, *PIMAGE_THUNK_DATA64;

2.2. 延迟加载导入描述符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct _IMAGE_DELAYLOAD_DESCRIPTOR {
    union {
        DWORD AllAttributes;                // 所有属性标志的组合值
        struct {
            DWORD RvaBased : 1;             // 延迟导入版本标识(1表示版本2)
            DWORD ReservedAttributes : 31;  // 保留属性位(必须为0)
        } DUMMYSTRUCTNAME;
    } Attributes;
    DWORD DllNameRVA;                       // RVA,指向目标DLL名称字符串(以NULL结尾的ASCII)
    DWORD ModuleHandleRVA;                  // RVA,指向缓存DLL模块句柄的变量(PHMODULE)
    DWORD ImportAddressTableRVA;            // RVA,指向延迟导入IAT起始位置(PIMAGE_THUNK_DATA)
    DWORD ImportNameTableRVA;               // RVA,指向导入名称表起始位置(指向函数名称RVA)
    DWORD BoundImportAddressTableRVA;       // RVA,指向可选的绑定导入地址表
    DWORD UnloadInformationTableRVA;        // RVA,指向可选的卸载信息表
    DWORD TimeDateStamp;                    // 时间戳(0=未绑定,否则为目标DLL的日期/时间)
} IMAGE_DELAYLOAD_DESCRIPTOR, *PIMAGE_DELAYLOAD_DESCRIPTOR;

2.3. 绑定导入表

1
2
3
4
5
6
7
8
9
10
11
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
    DWORD   TimeDateStamp;                      // 绑定时所依赖 DLL 的时间戳
    WORD    OffsetModuleName;                   // 模块名称的偏移量(相对于本结构起始位置)
    WORD    NumberOfModuleForwarderRefs;        // 转发器引用的数量
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_BOUND_FORWARDER_REF {
    DWORD   TimeDateStamp;                      // 转发目标 DLL 的时间戳
    WORD    OffsetModuleName;                   // 转发目标 DLL 名称的偏移量(相对于主描述符起始)
    WORD    Reserved;                           // 保留字段(未使用,通常为 0)
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;

3. 导出表

导出表的主要作用是将PE中存在的函数引出到外部,以便其他人可以使用这些函数,实现代码的重用。

3.1. 导出目录

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;                    // 导出表属性标志(通常为0,保留未用)
    DWORD   TimeDateStamp;                      // 导出表创建时间戳(用于版本校验)
    WORD    MajorVersion;                       // 导出表主版本号
    WORD    MinorVersion;                       // 导出表次版本号
    DWORD   Name;                               // RVA,指向DLL名称字符串(如"kernel32.dll")
    DWORD   Base;                               // 导出函数序号的起始值(序号=Base+索引)
    DWORD   NumberOfFunctions;                  // 导出函数总数(AddressOfFunctions数组元素个数)
    DWORD   NumberOfNames;                      // 以名称导出的函数数量(AddressOfNames数组元素个数)
    DWORD   AddressOfFunctions;                 // RVA,指向导出函数地址数组(RVA数组)
    DWORD   AddressOfNames;                     // RVA,指向导出函数名称数组(字符串RVA数组)
    DWORD   AddressOfNameOrdinals;              // RVA,指向导出序号数组(与AddressOfNames一一对应)
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

4. 栈与重定位表

4.1. 重定位表项

1
2
3
4
5
typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;                     // 本重定位块所应用的内存页起始RVA(按页对齐)
    DWORD   SizeOfBlock;                        // 当前重定位块的总字节数(包含本结构体大小)
//  WORD    TypeOffset[1];                      // 可变长数组,每项高4位表示重定位类型,低12位表示偏移量
} IMAGE_BASE_RELOCATION;

5. 资源表

在程序设计中,总会涉及一些数据。这些数据可能是源代码内部需要用到的常量,比如菜单选项、界面描述等;也可能是源代码外部的,比如程序的图标文件、背景音乐文件、配置文件等,以上这些数据统称为资源。

5.1. 资源目录头

1
2
3
4
5
6
7
8
9
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, *PIMAGE_RESOURCE_DIRECTORY;

5.2. 资源目录项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
    union {
        struct {
            DWORD NameOffset:31;                // 名称字符串偏移量(相对于资源目录起始)
            DWORD NameIsString:1;               // 名称类型标志(1=使用字符串名称,0=使用整数ID)
        } DUMMYSTRUCTNAME;
        DWORD   Name;                           // 资源名称的RVA(高位标志决定解释方式)
        WORD    Id;                             // 资源整数ID(当高位标志为0时使用)
    } DUMMYUNIONNAME;
    union {
        DWORD   OffsetToData;                   // RVA,指向资源数据(当DataIsDirectory=0时)
        struct {
            DWORD   OffsetToDirectory:31;       // 子目录偏移量(相对于资源目录起始)
            DWORD   DataIsDirectory:1;          // 目录标志(1=指向子目录,0=指向资源数据)
        } DUMMYSTRUCTNAME2;
    } DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

5.3. 资源数据项

1
2
3
4
5
6
typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
    DWORD   OffsetToData;                       // 资源原始数据相对于文件起始的RVA
    DWORD   Size;                               // 资源数据的字节大小
    DWORD   CodePage;                           // 资源的代码页(用于字符串等文本资源,通常为0)
    DWORD   Reserved;                           // 保留字段(必须为0)
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;

6. 线程局部存储

6.1. TLS目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct _IMAGE_TLS_DIRECTORY64 {
    ULONGLONG StartAddressOfRawData;            // TLS模板数据起始的RVA(内存中)
    ULONGLONG EndAddressOfRawData;              // TLS模板数据结束的RVA(内存中)
    ULONGLONG AddressOfIndex;                   // RVA,指向TLS索引变量(DWORD)
    ULONGLONG AddressOfCallBacks;               // RVA,指向TLS回调函数指针数组(以NULL结尾)
    DWORD SizeOfZeroFill;                       // 初始化数据后填充零的大小(字节)
    union {
        DWORD Characteristics;                  // TLS目录特性标志
        struct {
            DWORD Reserved0 : 20;               // 保留位(必须为0)
            DWORD Alignment : 4;                // TLS数据对齐方式(2的幂次)
            DWORD Reserved1 : 8;                // 保留位(必须为0)
        } DUMMYSTRUCTNAME;
    } DUMMYUNIONNAME;

} IMAGE_TLS_DIRECTORY64, *PIMAGE_TLS_DIRECTORY64;

7. 加载配置信息

7.1. 加载配置目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
typedef struct _IMAGE_LOAD_CONFIG_DIRECTORY64 {
    DWORD      Size;                                        // 结构体总大小(用于版本识别)
    DWORD      TimeDateStamp;                               // 时间戳(通常为0)
    WORD       MajorVersion;                                // 主版本号
    WORD       MinorVersion;                                // 次版本号
    DWORD      GlobalFlagsClear;                            // 运行时需要清零的全局标志
    DWORD      GlobalFlagsSet;                              // 运行时需要设置的全局标志
    DWORD      CriticalSectionDefaultTimeout;               // 临界区默认超时时间
    ULONGLONG  DeCommitFreeBlockThreshold;                  // 释放内存块的解除提交阈值
    ULONGLONG  DeCommitTotalFreeThreshold;                  // 总释放内存的解除提交阈值
    ULONGLONG  LockPrefixTable;                             // VA,指向锁定前缀表(用于原子操作优化)
    ULONGLONG  MaximumAllocationSize;                       // 最大单次内存分配大小
    ULONGLONG  VirtualMemoryThreshold;                      // 虚拟内存阈值
    ULONGLONG  ProcessAffinityMask;                         // 进程CPU亲和性掩码
    DWORD      ProcessHeapFlags;                            // 进程堆标志
    WORD       CSDVersion;                                  // Service Pack版本号
    WORD       DependentLoadFlags;                          // 依赖项加载标志
    ULONGLONG  EditList;                                    // VA,指向编辑列表
    ULONGLONG  SecurityCookie;                              // VA,指向安全Cookie(/GS保护)
    ULONGLONG  SEHandlerTable;                              // VA,指向SEH异常处理表
    ULONGLONG  SEHandlerCount;                              // SEH异常处理表条目数量
    ULONGLONG  GuardCFCheckFunctionPointer;                 // VA,指向控制流检查函数(/guard:cf)
    ULONGLONG  GuardCFDispatchFunctionPointer;              // VA,指向控制流分发函数
    ULONGLONG  GuardCFFunctionTable;                        // VA,指向CFG有效函数地址表
    ULONGLONG  GuardCFFunctionCount;                        // CFG函数表条目数量
    DWORD      GuardFlags;                                  // CFG标志位
    IMAGE_LOAD_CONFIG_CODE_INTEGRITY CodeIntegrity;         // 代码完整性验证信息
    ULONGLONG  GuardAddressTakenIatEntryTable;              // VA,指向地址被获取的IAT条目表
    ULONGLONG  GuardAddressTakenIatEntryCount;              // 地址被获取的IAT条目数量
    ULONGLONG  GuardLongJumpTargetTable;                    // VA,指向longjmp目标地址表
    ULONGLONG  GuardLongJumpTargetCount;                    // longjmp目标表条目数量
    ULONGLONG  DynamicValueRelocTable;                      // VA,指向动态值重定位表
    ULONGLONG  CHPEMetadataPointer;                         // VA,指向CHPE元数据
    ULONGLONG  GuardRFFailureRoutine;                       // VA,指向返回地址失败处理函数
    ULONGLONG  GuardRFFailureRoutineFunctionPointer;        // VA,指向返回地址失败函数指针
    DWORD      DynamicValueRelocTableOffset;                // 动态值重定位表偏移量
    WORD       DynamicValueRelocTableSection;               // 动态值重定位表所在节索引
    WORD       Reserved2;                                   // 保留字段2
    ULONGLONG  GuardRFVerifyStackPointerFunctionPointer;    // VA,指向栈指针验证函数指针
    DWORD      HotPatchTableOffset;                         // 热补丁表偏移量
    DWORD      Reserved3;                                   // 保留字段3
    ULONGLONG  EnclaveConfigurationPointer;                 // VA,指向Enclave配置(VBS/CFG)
    ULONGLONG  VolatileMetadataPointer;                     // VA,指向易变元数据
    ULONGLONG  GuardEHContinuationTable;                    // VA,指向异常处理延续表
    ULONGLONG  GuardEHContinuationCount;                    // 异常处理延续表条目数量
    ULONGLONG  GuardXFGCheckFunctionPointer;                // VA,指向XFGC检查函数指针
    ULONGLONG  GuardXFGDispatchFunctionPointer;             // VA,指向XFGC分发函数指针
    ULONGLONG  GuardXFGTableDispatchFunctionPointer;        // VA,指向XFGC表分发函数指针
    ULONGLONG  CastGuardOsDeterminedFailureMode;            // VA,指向CastGuard失败处理模式
    ULONGLONG  GuardMemcpyFunctionPointer;                  // VA,指向安全memcpy函数指针
    ULONGLONG  UmaFunctionPointers;                         // VA,指向统一内存访问函数指针表
} IMAGE_LOAD_CONFIG_DIRECTORY64, *PIMAGE_LOAD_CONFIG_DIRECTORY64;

8. 总结

图上使用的是 32 位记录,目前是 64 位,部分结构体存在区别,以最新的为准。

参考

本文由作者按照 CC BY 4.0 进行授权