分享

PE 格式

 羊玉wngbx 2022-09-24 发布于云南
  •  项目

此规范介绍操作系统Windows系列下可执行 (映像) 文件和对象文件的结构。 这些文件分别称为可移植可执行文件 (PE) 和通用对象文件格式 (COFF) 文件。

备注

本文档旨在帮助开发用于Windows的工具和应用程序,但不能保证在所有方面都是完整的规范。 Microsoft 保留未经通知更改此文档的权利。

Microsoft 可移植可执行文件和通用对象文件格式规范的此修订版将替换此规范的所有以前的修订。

一般概念

本文档指定 Microsoft Windows 操作系统系列下的可执行 (映像) 文件和对象文件的结构。 这些文件分别称为可移植可执行文件 (PE) 和通用对象文件格式 (COFF) 文件。 名称“可移植可执行文件”是指格式不是特定于体系结构的事实。

下表描述了在整个此规范中显示的某些概念:

名称 说明
属性证书
用于将可验证语句与映像关联的证书。 许多不同的可验证语句可以与文件相关联;最有用的声明之一是软件制造商的声明,指示图像的消息摘要应是什么。 消息摘要类似于校验和,但很难伪造。 因此,很难修改文件,使其与原始文件具有相同的消息摘要。 可以使用公钥或私钥加密方案验证该语句是否由制造商进行验证。 本文档介绍属性证书的详细信息,但不允许将其插入到图像文件中。
日期/时间戳
用于 PE 或 COFF 文件中多个位置的不同用途的标记。 在大多数情况下,每个标记的格式与 C 运行时库中的时间函数所使用的格式相同。 有关异常,请参阅 调试类型中IMAGE_DEBUG_TYPE_REPRO的描述符。 如果印章值为 0 或0xFFFFFFFF,则它不表示实际或有意义的日期/时间戳。
文件指针
文件本身中项的位置,在链接器 (在对象文件) 或加载程序在图像文件) 的情况下 (进行处理。 换句话说,这是存储在磁盘上的文件中的位置。
链接器
对随Microsoft Visual Studio提供的链接器的引用。
对象文件
作为链接器输入的文件。 链接器生成图像文件,该文件反过来又用作加载者的输入。 术语“object file”不一定意味着任何与面向对象的编程的连接。
保留,必须为 0
指示字段的值必须为零的字段的说明,并且使用者必须忽略该字段。
RVA) (相对虚拟地址
在图像文件中,这是将项加载到内存中的地址,图像文件的基址从中减去。 项的 RVA 几乎总是与磁盘上的文件位置不同, (文件指针) 。
在对象文件中,RVA 不太有意义,因为未分配内存位置。 在这种情况下,RVA 将是此表后面) 中所述的节 (中的地址,稍后会在链接期间应用重定位。 为简单起见,编译器应将每个部分中的第一个 RVA 设置为零。
section
PE 或 COFF 文件中的基本代码或数据单元。 例如,对象文件中的所有代码都可以在单个节或 (内组合,具体取决于编译器行为,) 每个函数可以占据自己的节。 通过更多部分,文件开销更大,但链接器能够以更选择性的方式在代码中链接。 一个部分类似于 Intel 8086 体系结构中的段。 必须连续加载节中的所有原始数据。 此外,映像文件可以包含许多部分,例如 .tls 或 .reloc,这些节具有特殊用途。
虚拟地址 (VA)
与 RVA 相同,只是图像文件的基址未减去。 该地址称为 VA,因为Windows为每个进程(独立于物理内存)创建不同的 VA 空间。 对于几乎所有目的,VA 应只被视为一个地址。 VA 不如 RVA 那么可预测,因为加载程序可能不会在其首选位置加载映像。

概述

以下列表介绍 Microsoft PE 可执行文件格式,其基础位于顶部的图像标头。 从 MS-DOS 2.0 兼容 EXE 标头到 PE 标头之前未使用的分区的部分是 MS-DOS 2.0 部分,仅用于 MS-DOS 兼容性。

  • MS-DOS 2.0 兼容的 EXE 标头

  • unused

  • OEM 标识符

    OEM 信息

    偏移到 PE 标头

  • MS-DOS 2.0 存根计划和重定位表

  • unused

  • PE 标头 (与 8 字节边界) 对齐

  • 节标头

  • 图像页:

    导入信息

    导出信息

    基本重定位

    资源信息

以下列表描述了 Microsoft COFF 对象模块格式:

  • Microsoft COFF 标头

  • 节标头

  • 原始数据:

    code

    数据

    调试信息

    重定位

文件标头

PE 文件标头由 Microsoft MS-DOS 存根、PE 签名、COFF 文件标头和可选标头组成。 COFF 对象文件标头由 COFF 文件标头和可选标头组成。 在这两种情况下,文件标头紧随节标头。

仅 MS-DOS 存根 (映像)

MS-DOS 存根是一个在 MS-DOS 下运行的有效应用程序。 它放置在 EXE 映像的前面。 链接器在此处放置默认存根,当映像在 MS-DOS 中运行时,输出消息“此程序不能在 DOS 模式下运行”。 用户可以使用 /STUB 链接器选项指定不同的存根。

在位置0x3c,存根具有 PE 签名的文件偏移量。 此信息使Windows能够正确执行映像文件,即使它具有 MS-DOS 存根。 此文件偏移量放置在链接期间的位置0x3c。

仅签名 (映像)

MS-DOS 存根后,在偏移量指定的0x3c的文件偏移量处,是一个 4 字节签名,用于将该文件标识为 PE 格式图像文件。 此签名是“PE\0\0” (字母“P”和“E”,后跟两个 null 字节) 。

COFF 文件头 (对象和图像)

在对象文件的开头或图像文件的签名之后,是采用以下格式的标准 COFF 文件标头。 请注意,Windows加载程序将分区数限制为 96。

Offset 大小 字段 说明
0
2
计算机
标识目标计算机类型的数字。 有关详细信息,请参阅 计算机类型
2
2
NumberOfSections
节数。 这指示部分表的大小,该表紧跟在标头之后。
4
4
TimeDateStamp
自 1970 年 1 月 1 日 00:00 起的秒数的低 32 位 (C 运行时time_t值) ,该值指示文件创建时间。
8
4
PointerToSymbolTable
COFF 符号表的文件偏移量,如果没有 COFF 符号表,则为零。 映像的此值应为零,因为 COFF 调试信息已弃用。
12
4
NumberOfSymbols
符号表中的条目数。 此数据可用于查找紧跟在符号表后面的字符串表。 映像的此值应为零,因为 COFF 调试信息已弃用。
16
2
SizeOfOptionalHeader
可选标头的大小,这是可执行文件所必需的,但不适用于对象文件。 对于对象文件,此值应为零。 有关标头格式的说明,请参阅 仅) 的可选标头 (图像
18
2
特征
指示文件属性的标志。 有关特定标志值,请参阅 特征

计算机类型

“计算机”字段具有以下值之一,用于指定 CPU 类型。 映像文件只能在指定计算机或模拟指定计算机的系统上运行。

返回的常量 Value 说明
IMAGE_FILE_MACHINE_UNKNOWN
0x0
假定此字段的内容适用于任何计算机类型
IMAGE_FILE_MACHINE_AM33
0x1d3
松田 AM33
IMAGE_FILE_MACHINE_AMD64
0x8664
X64
IMAGE_FILE_MACHINE_ARM
0x1c0
ARM 小 endian
IMAGE_FILE_MACHINE_ARM64
0xaa64
ARM64 小 endian
IMAGE_FILE_MACHINE_ARMNT
0x1c4
ARM Thumb-2 小 endian
IMAGE_FILE_MACHINE_EBC
0xebc
EFI 字节代码
IMAGE_FILE_MACHINE_I386
0x14c
Intel 386 或更高版本的处理器和兼容的处理器
IMAGE_FILE_MACHINE_IA64
0x200
Intel Itanium 处理器系列
IMAGE_FILE_MACHINE_LOONGARCH32
0x6232
LoongArch 32 位处理器系列
IMAGE_FILE_MACHINE_LOONGARCH64
0x6264
LoongArch 64 位处理器系列
IMAGE_FILE_MACHINE_M32R
0x9041
三菱M32R小尾声
IMAGE_FILE_MACHINE_MIPS16
0x266
MIPS16
IMAGE_FILE_MACHINE_MIPSFPU
0x366
使用 FPU 的 MIPS
IMAGE_FILE_MACHINE_MIPSFPU16
0x466
具有 FPU 的 MIPS16
IMAGE_FILE_MACHINE_POWERPC
0x1f0
Power PC 小 endian
IMAGE_FILE_MACHINE_POWERPCFP
0x1f1
支持浮点支持的电源电脑
IMAGE_FILE_MACHINE_R4000
0x166
MIPS 小 endian
IMAGE_FILE_MACHINE_RISCV32
0x5032
RISC-V 32 位地址空间
IMAGE_FILE_MACHINE_RISCV64
0x5064
RISC-V 64 位地址空间
IMAGE_FILE_MACHINE_RISCV128
0x5128
RISC-V 128 位地址空间
IMAGE_FILE_MACHINE_SH3
0x1a2
Hitachi SH3
IMAGE_FILE_MACHINE_SH3DSP
0x1a3
Hitachi SH3 DSP
IMAGE_FILE_MACHINE_SH4
0x1a6
Hitachi SH4
IMAGE_FILE_MACHINE_SH5
0x1a8
Hitachi SH5
IMAGE_FILE_MACHINE_THUMB
0x1c2
Thumb
IMAGE_FILE_MACHINE_WCEMIPSV2
0x169
MIPS 小端 WCE v2

特征

“特征”字段包含指示对象或图像文件属性的标志。 当前定义了以下标志:

标志 说明
IMAGE_FILE_RELOCS_STRIPPED
0x0001
仅映像、Windows CE和 Microsoft Windows NT 及更高版本。 这表示该文件不包含基本重定位,因此必须加载在其首选基址处。 如果基址不可用,加载程序将报告错误。 链接器的默认行为是从可执行文件 (EXE) 文件中去除基本重定位。
IMAGE_FILE_EXECUTABLE_IMAGE
0x0002
仅图像。 这表示映像文件有效且可以运行。 如果未设置此标志,则表示链接器错误。
IMAGE_FILE_LINE_NUMS_STRIPPED
0x0004
COFF 行号已删除。 此标志已弃用,应为零。
IMAGE_FILE_LOCAL_SYMS_STRIPPED
0x0008
已删除本地符号的 COFF 符号表条目。 此标志已弃用,应为零。
IMAGE_FILE_AGGRESSIVE_WS_TRIM
0x0010
已过时。 积极剪裁工作集。 此标志已弃用 Windows 2000 及更高版本,并且必须为零。
IMAGE_FILE_LARGE_ADDRESS_ AWARE
0x0020
应用程序可以处理 > 2 GB 地址。
0x0040
此标志保留供将来使用。
IMAGE_FILE_BYTES_REVERSED_LO
0x0080
小尾数:LSB) 最不重要的位 (LSB) 位于内存中最重要的位 (MSB) 。 此标志已弃用,应为零。
IMAGE_FILE_32BIT_MACHINE
0x0100
计算机基于 32 位体系结构。
IMAGE_FILE_DEBUG_STRIPPED
0x0200
从映像文件中删除调试信息。
IMAGE_FILE_REMOVABLE_RUN_ FROM_SWAP
0x0400
如果映像位于可移动媒体上,请完全加载该映像并将其复制到交换文件。
IMAGE_FILE_NET_RUN_FROM_SWAP
0x0800
如果映像位于网络媒体上,请完全加载映像并将其复制到交换文件。
IMAGE_FILE_SYSTEM
0x1000
映像文件是系统文件,而不是用户程序。
IMAGE_FILE_DLL
0x2000
映像文件是动态链接库 (DLL) 。 此类文件被视为几乎所有目的的可执行文件,尽管无法直接运行这些文件。
IMAGE_FILE_UP_SYSTEM_ONLY
0x4000
该文件应仅在单处理器计算机上运行。
IMAGE_FILE_BYTES_REVERSED_HI
0x8000
大尾号:MSB 位于内存中的 LSB 之前。 此标志已弃用,应为零。

仅) 可选标头 (图像

每个图像文件都有一个可选标头,用于向加载程序提供信息。 此标头是可选的,因为某些文件 (具体来说,对象文件) 没有它。 对于图像文件,此标头是必需的。 对象文件可以有一个可选的标头,但通常此标头在对象文件中没有函数,只是为了增大其大小。

请注意,可选标头的大小未固定。 COFF 标头中的 SizeOfOptionalHeader 字段必须用于验证对特定数据目录的文件探测是否超出 SizeOfOptionalHeader。 有关详细信息,请参阅 COFF 文件头 (对象和图像)

还应使用可选标头的 NumberOfRvaAndSizes 字段来确保不会探测特定数据目录条目超出可选标头。 此外,请务必验证可选的标头 magic 编号,以确保格式兼容性。

可选的标头 magic 号确定图像是 PE32 还是 PE32+ 可执行文件。

Magic 数字 PE 格式
0x10b
PE32
0x20b
PE32+

PE32+ 图像允许 64 位地址空间,同时将图像大小限制为 2 GB。 其他 PE32+ 修改在各自的部分中得到解决。

可选标头本身有三个主要部分。

偏移量 (PE32/PE32+) 大小 (PE32/PE32+) 标头部件 说明
0
28/24
标准字段
为 COFF 的所有实现定义的字段,包括UNIX。
28/24
68/88
特定于Windows字段
支持Windows (的特定功能的其他字段,例如子系统) 。
96/112
变量
数据目录
映像文件中找到的特殊表的地址/大小对,由操作系统 (使用,例如导入表和导出表) 。

仅图像 (可选标头标准字段)

可选标头的前八个字段是为每个 COFF 实现定义的标准字段。 这些字段包含用于加载和运行可执行文件的常规信息。 对于 PE32+ 格式,它们保持不变。

Offset 大小 字段 说明
0
2
Magic
标识映像文件状态的无符号整数。 最常见的数字是0x10B,它将其标识为普通可执行文件。 0x107将其标识为 ROM 映像,0x20B将其标识为 PE32+ 可执行文件。
2
1
MajorLinkerVersion
链接器主版本号。
3
1
MinorLinkerVersion
链接器次要版本号。
4
4
SizeOfCode
代码 (文本) 节的大小,或者存在多个节时所有代码节的总和。
8
4
SizeOfInitializedData
初始化的数据节的大小;如果有多个数据节,则为所有此类节的总和。
12
4
SizeOfUninitializedData
未初始化的数据部分的大小 (BSS) ,或存在多个 BSS 节时所有此类节的总和。
16
4
AddressOfEntryPoint
当可执行文件加载到内存中时,入口点相对于映像基的地址。 对于程序映像,这是起始地址。 对于设备驱动程序,这是初始化函数的地址。 DLL 的入口点是可选的。 当不存在入口点时,此字段必须为零。
20
4
BaseOfCode
当它加载到内存中时,相对于代码开头部分的图像基的地址。

PE32 包含此附加字段,此字段在 PE32+ 中不存在,遵循 BaseOfCode。

Offset 大小 字段 说明
24
4
BaseOfData
当数据开始部分加载到内存中时,相对于其图像基的地址。

仅) (图像的可选标头Windows-Specific字段

接下来的 21 个字段是 COFF 可选标头格式的扩展。 它们包含链接器和加载程序在 Windows 中所需的其他信息。

偏移量 (PE32/PE32+) 大小 (PE32/PE32+) 字段 说明
28/24
4/8
ImageBase
加载到内存中的第一个图像字节的首选地址;必须是 64 K 的倍数。DLL 的默认值为0x10000000。 Windows CE EXE 的默认值为 0x00010000。 Windows NT、Windows 2000、Windows XP、Windows 95、Windows 98 和 Windows Me 的默认值为 0x00400000。
32/32
4
SectionAlignment
各部分加载到内存中时的对齐值(以字节为单位)。 它必须大于或等于 FileAlignment。 默认值为体系结构的页面大小。
36/36
4
FileAlignment
用于使映像文件中各部分的原始数据一致的对齐系数(以字节为单位)。 该值应为 2(含 512 到 64 K)之间的幂。 默认值为 512。 如果 SectionAlignment 小于体系结构的页面大小,则 FileAlignment 必须与 SectionAlignment 匹配。
40/40
2
MajorOperatingSystemVersion
所需操作系统的主版本号。
42/42
2
MinorOperatingSystemVersion
所需操作系统的次要版本号。
44/44
2
MajorImageVersion
映像的主版本号。
46/46
2
MinorImageVersion
映像的次要版本号。
48/48
2
MajorSubsystemVersion
子系统的主版本号。
50/50
2
MinorSubsystemVersion
子系统的次要版本号。
52/52
4
Win32VersionValue
保留,必须为零。
56/56
4
SizeOfImage
图像的大小 (字节) ,包括所有标头,因为图像在内存中加载。 它必须是 SectionAlignment 的倍数。
60/60
4
SizeOfHeaders
MS-DOS 存根、PE 标头和节标头的组合大小向上舍入为 FileAlignment 的倍数。
64/64
4
校验
图像文件校验和。 用于计算校验和的算法合并到IMAGHELP.DLL中。 以下代码在加载时检查验证:所有驱动程序、在启动时加载的任何 DLL,以及加载到关键Windows进程中的任何 DLL。
68/68
2
子系统
运行此映像所需的子系统。 有关详细信息,请参阅Windows子系统
70/70
2
DllCharacteristics
有关详细信息,请参阅此规范后面的 DLL 特征
72/72
4/8
SizeOfStackReserve
要保留的堆栈的大小。 仅提交 SizeOfStackCommit;其余部分一次可用一页,直到达到预留大小。
76/80
4/8
SizeOfStackCommit
要提交的堆栈的大小。
80/88
4/8
SizeOfHeapReserve
要保留的本地堆空间的大小。 仅提交 SizeOfHeapCommit;其余部分一次可用一页,直到达到预留大小。
84/96
4/8
SizeOfHeapCommit
要提交的本地堆空间的大小。
88/104
4
LoaderFlags
保留,必须为零。
92/108
4
NumberOfRvaAndSizes
可选标头的其余部分中的数据目录条目数。 每项都描述位置和大小。
Windows子系统

为可选标头的子系统字段定义的以下值确定运行映像是否需要任何) (哪个Windows子系统。

返回的常量 Value 说明
IMAGE_SUBSYSTEM_UNKNOWN
0
未知子系统
IMAGE_SUBSYSTEM_NATIVE
1
设备驱动程序和本机Windows进程
IMAGE_SUBSYSTEM_WINDOWS_GUI
2
Windows图形用户界面 (GUI) 子系统
IMAGE_SUBSYSTEM_WINDOWS_CUI
3
Windows字符子系统
IMAGE_SUBSYSTEM_OS2_CUI
5
OS/2 字符子系统
IMAGE_SUBSYSTEM_POSIX_CUI
7
Posix 字符子系统
IMAGE_SUBSYSTEM_NATIVE_WINDOWS
8
本机 Win9x 驱动程序
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI
9
Windows CE
IMAGE_SUBSYSTEM_EFI_APPLICATION
10
可扩展固件接口 (EFI) 应用程序
IMAGE_SUBSYSTEM_EFI_BOOT_ SERVICE_DRIVER
11
具有启动服务的 EFI 驱动程序
IMAGE_SUBSYSTEM_EFI_RUNTIME_ DRIVER
12
具有运行时服务的 EFI 驱动程序
IMAGE_SUBSYSTEM_EFI_ROM
13
EFI ROM 映像
IMAGE_SUBSYSTEM_XBOX
14
XBOX
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION
16
Windows启动应用程序。
DLL 特征

为可选标头的 DllCharacteristics 字段定义了以下值。

返回的常量 Value 说明
0x0001
保留,必须为零。
0x0002
保留,必须为零。
0x0004
保留,必须为零。
0x0008
保留,必须为零。
IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA
0x0020
图像可以处理高萎缩 64 位虚拟地址空间。
IMAGE_DLLCHARACTERISTICS_
DYNAMIC_BASE
0x0040
DLL 可以在加载时重新定位。
IMAGE_DLLCHARACTERISTICS_
FORCE_INTEGRITY
0x0080
强制实施代码完整性检查。
IMAGE_DLLCHARACTERISTICS_
NX_COMPAT
0x0100
映像与 NX 兼容。
IMAGE_DLLCHARACTERISTICS_ NO_ISOLATION
0x0200
隔离感知,但不隔离映像。
IMAGE_DLLCHARACTERISTICS_ NO_SEH
0x0400
不使用结构化异常 (标准版) 处理。 此映像中无法调用任何标准版处理程序。
IMAGE_DLLCHARACTERISTICS_ NO_BIND
0x0800
不要绑定映像。
IMAGE_DLLCHARACTERISTICS_APPCONTAINER
0x1000
映像必须在 AppContainer 中执行。
IMAGE_DLLCHARACTERISTICS_ WDM_DRIVER
0x2000
WDM 驱动程序。
IMAGE_DLLCHARACTERISTICS_GUARD_CF
0x4000
映像支持 Control Flow Guard。
IMAGE_DLLCHARACTERISTICS_ TERMINAL_SERVER_AWARE
0x8000
终端服务器感知。

仅) (映像的可选标头数据目录

每个数据目录都提供Windows使用的表或字符串的地址和大小。 这些数据目录条目全部加载到内存中,以便系统可以在运行时使用它们。 数据目录是具有以下声明的 8 字节字段:

C++
typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

第一个字段 VirtualAddress 实际上是表的 RVA。 RVA 是加载表时相对于映像基址的表的地址。 第二个字段提供大小(以字节为单位)。 下表列出了构成可选标头最后一部分的数据目录。

请注意,目录数未固定。 在查找特定目录之前,请检查可选标头中的 NumberOfRvaAndSizes 字段。

此外,不要假设此表中的 RVA 指向节的开头,或者包含特定表的节具有特定名称。

偏移量 (PE/PE32+) 大小 字段 说明
96/112
8
导出表
导出表地址和大小。 有关详细信息,请参阅 .edata Section (Image Only)
104/120
8
导入表
导入表地址和大小。 有关详细信息,请参阅 .idata 节
112/128
8
资源表
资源表地址和大小。 有关详细信息,请参阅 .rsrc 部分
120/136
8
异常表
异常表地址和大小。 有关详细信息,请参阅 .pdata 节
128/144
8
证书表
属性证书表地址和大小。 有关详细信息,请参阅 “属性证书表” (映像)
136/152
8
基本重定位表
基重定位表地址和大小。 有关详细信息,请参阅仅) .reloc 部分 (映像
144/160
8
调试
调试数据起始地址和大小。 有关详细信息,请参阅 .debug 节
152/168
8
体系结构
保留,必须为 0
160/176
8
Global Ptr
要存储在全局指针寄存器中的值的 RVA。 此结构的大小成员必须设置为零。
168/184
8
TLS 表
线程本地存储 (TLS) 表地址和大小。 有关详细信息,请参阅 .tls 部分
176/192
8
加载配置表
加载配置表地址和大小。 有关详细信息,请参阅 “仅加载配置结构” (映像)
184/200
8
绑定导入
绑定导入表地址和大小。
192/208
8
IAT
导入地址表地址和大小。 有关详细信息,请参阅 导入地址表
200/216
8
延迟导入描述符
延迟导入描述符地址和大小。 有关详细信息,请参阅 仅) (映像延迟加载导入表
208/224
8
CLR 运行时标头
CLR 运行时标头地址和大小。 有关详细信息,请参阅仅) .cormeta 节 (对象
216/232
8
保留,必须为零

证书表入口指向属性证书表。 这些证书不会作为映像的一部分加载到内存中。 因此,此条目的第一个字段通常是 RVA,而是文件指针。

节表 (节标头)

节表的每一行实际上都是节标题。 此表紧跟可选标头(如果有)。 此定位是必需的,因为文件头不包含指向节表的直接指针。 相反,节表的位置是通过计算标头后第一个字节的位置来确定的。 请确保使用文件标头中指定的可选标头的大小。

节表中的条目数由文件标头中的 NumberOfSections 字段提供。 节表中的条目从一个 (1 个) 开始编号。 代码和数据内存节条目按链接器选择的顺序。

在图像文件中,节的 VA 必须由链接器分配,以便它们按升序和相邻顺序分配,并且它们必须是可选标头中的 SectionAlignment 值的倍数。

每个节标头 (节表项) 具有以下格式,每个条目总共 40 个字节。

Offset 大小 字段 说明
0
8
名称
一个 8 字节、null 填充的 UTF-8 编码字符串。 如果字符串长度正好为 8 个字符,则不会终止 null。 对于较长的名称,此字段包含一个斜杠 (/) ,后跟十进制数字的 ASCII 表示形式,该数字是字符串表中的偏移量。 可执行图像不使用字符串表,并且不支持长度超过 8 个字符的节名称。 如果对象文件中的长名称被发送到可执行文件,则会截断它们。
8
4
VirtualSize
加载到内存中的节的总大小。 如果此值大于 SizeOfRawData,则节为零填充。 此字段仅对可执行图像有效,对于对象文件应设置为零。
12
4
VirtualAddress
对于可执行映像,当分区加载到内存中时,该节相对于映像基的第一个字节的地址。 对于对象文件,此字段是应用重定位前第一个字节的地址;为简单起见,编译器应将此设置为零。 否则,它是在重定位期间从偏移量中减去的任意值。
16
4
SizeOfRawData
对象文件分区 (的大小) 或磁盘上初始化的数据的大小 (映像文件) 。 对于可执行映像,这必须是可选标头中的 FileAlignment 的倍数。 如果这小于 VirtualSize,则部分的其余部分为零填充。 由于 SizeOfRawData 字段是舍入的,但 VirtualSize 字段不是,因此 SizeOfRawData 也有可能大于 VirtualSize。 当节仅包含未初始化的数据时,此字段应为零。
20
4
PointerToRawData
文件指针指向 COFF 文件中节的第一页。 对于可执行映像,这必须是可选标头中的 FileAlignment 的倍数。 对于对象文件,该值应在 4 字节边界上对齐,以获得最佳性能。 当节仅包含未初始化的数据时,此字段应为零。
24
4
PointerToRelocations
文件指针指向分区的重定位条目的开头。 对于可执行映像或没有重定位,这设置为零。
28
4
PointerToLinenumbers
文件指针指向节的行号条目的开头。 如果没有 COFF 行号,则设置为零。 映像的此值应为零,因为 COFF 调试信息已弃用。
32
2
NumberOfRelocations
分区的重定位条目数。 对于可执行映像,这设置为零。
34
2
NumberOfLinenumbers
节的行号条目数。 映像的此值应为零,因为 COFF 调试信息已弃用。
36
4
特征
描述节特征的标志。 有关详细信息,请参阅 节标志

 

节标志

节标头的“特征”字段中的节标志指示节的特征。

标志 说明
0x00000000
保留供将来使用。
0x00000001
保留供将来使用。
0x00000002
保留供将来使用。
0x00000004
保留供将来使用。
IMAGE_SCN_TYPE_NO_PAD
0x00000008
不应将节填充到下一个边界。 此标志已过时,并替换为IMAGE_SCN_ALIGN_1BYTES。 这仅适用于对象文件。
0x00000010
保留供将来使用。
IMAGE_SCN_CNT_CODE
0x00000020
该节包含可执行代码。
IMAGE_SCN_CNT_INITIALIZED_DATA
0x00000040
该节包含已初始化的数据。
IMAGE_SCN_CNT_UNINITIALIZED_ DATA
0x00000080
该节包含未初始化的数据。
IMAGE_SCN_LNK_OTHER
0x00000100
保留供将来使用。
IMAGE_SCN_LNK_INFO
0x00000200
该部分包含注释或其他信息。 .drectve 节具有此类型。 这仅适用于对象文件。
0x00000400
保留供将来使用。
IMAGE_SCN_LNK_REMOVE
0x00000800
该部分不会成为图像的一部分。 这仅适用于对象文件。
IMAGE_SCN_LNK_COMDAT
0x00001000
该节包含 COMDAT 数据。 有关详细信息,请参阅 COMDAT 节 (对象仅) 。 这仅适用于对象文件。
IMAGE_SCN_GPREL
0x00008000
该节包含通过全局指针 (GP) 引用的数据。
IMAGE_SCN_MEM_PURGEABLE
0x00020000
保留供将来使用。
IMAGE_SCN_MEM_16BIT
0x00020000
保留供将来使用。
IMAGE_SCN_MEM_LOCKED
0x00040000
保留供将来使用。
IMAGE_SCN_MEM_PRELOAD
0x00080000
保留供将来使用。
IMAGE_SCN_ALIGN_1BYTES
0x00100000
在 1 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_2BYTES
0x00200000
在 2 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_4BYTES
0x00300000
在 4 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_8BYTES
0x00400000
对齐 8 字节边界上的数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_16BYTES
0x00500000
在 16 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_32BYTES
0x00600000
在 32 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_64BYTES
0x00700000
在 64 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_128BYTES
0x00800000
在 128 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_256BYTES
0x00900000
在 256 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_512BYTES
0x00A00000
在 512 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_1024BYTES
0x00B00000
在 1024 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_2048BYTES
0x00C00000
在 2048 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_4096BYTES
0x00D00000
在 4096 字节边界上对齐数据。 仅适用于对象文件。
IMAGE_SCN_ALIGN_8192BYTES
0x00E00000
对齐 8192 字节边界上的数据。 仅适用于对象文件。
IMAGE_SCN_LNK_NRELOC_OVFL
0x01000000
该部分包含扩展的重定位。
IMAGE_SCN_MEM_DISCARDABLE
0x02000000
可以根据需要放弃该节。
IMAGE_SCN_MEM_NOT_CACHED
0x04000000
无法缓存节。
IMAGE_SCN_MEM_NOT_PAGED
0x08000000
该节不可分页。
IMAGE_SCN_MEM_SHARED
0x10000000
该部分可以在内存中共享。
IMAGE_SCN_MEM_EXECUTE
0x20000000
该节可以作为代码执行。
IMAGE_SCN_MEM_READ
0x40000000
可以读取该节。
IMAGE_SCN_MEM_WRITE
0x80000000
可以写入该节。

 

IMAGE_SCN_LNK_NRELOC_OVFL指示分区的重定位计数超过节标头中保留的 16 位。 如果设置了位,并且节标头中的 NumberOfRelocations 字段0xffff,则实际重定位计数将存储在第一次重定位的 32 位 VirtualAddress 字段中。 如果设置了IMAGE_SCN_LNK_NRELOC_OVFL,并且分区中的重定位次数少于0xffff,则会出现错误。

仅限) (对象的分组节

“$”? 字符 (美元符号) 在对象文件中的节名称中具有特殊解释。

确定将包含对象节内容的图像节时,链接器将放弃“$”? 及其后面的所有字符。 因此,名为 ..text$X 实际上为图像中的 .text 节做出贡献。

但是,“$”后面的字符? 确定对图像部分的贡献的排序。 具有相同对象节名称的所有贡献在图像中连续分配,并且按对象节名称按词法顺序对贡献块进行排序。 因此,在 .text$W 贡献和 .text$Y 贡献之前,具有节名称 .text$X 的对象文件中的所有内容最终一起。

图像文件中的节名称永远不会包含“$”? 字符。

文件的其他内容

到目前为止描述的数据结构(最多包括可选标头)均位于文件开头 (或 PE 标头的固定偏移量(如果该文件是包含 MS-DOS 存根) 的图像)。

COFF 对象或图像文件的其余部分包含不一定在任何特定文件偏移量处的数据块。 相反,位置由可选标头或节标头中的指针定义。

例外情况是,对于 SectionAlignment 值小于体系结构的页面大小的图像, (4 K for Intel x86 和 MIPS,以及 8 K 表示 Itanium) 。 有关 SectionAlignment 的说明,请参阅 仅) 的可选标头 (图像 。 在这种情况下,节数据的文件偏移量存在约束,如第 5.1 节“节数据”中所述。另一个例外是,属性证书和调试信息必须放置在映像文件的末尾,并且属性证书表紧接在调试节前面,因为加载程序不会将这些信息映射到内存中。 但是,有关属性证书和调试信息的规则不适用于对象文件。

节数据

节的初始化数据由简单的字节块组成。 但是,对于包含所有零的分区,不需要包含节数据。

每个节的数据位于节标头中的 PointerToRawData 字段给出的文件偏移量处。 文件中此数据的大小由 SizeOfRawData 字段指示。 如果 SizeOfRawData 小于 VirtualSize,则余数填充为零。

在图像文件中,节数据必须在由可选标头中的 FileAlignment 字段指定的边界上对齐。 节数据必须按相应节的 RVA 值的顺序显示, (节表中的各个节标题) 。

如果可选标头中的 SectionAlignment 值小于体系结构的页面大小,则图像文件还有其他限制。 对于此类文件,文件中分区数据的位置必须与加载图像时内存中的位置匹配,以便分区数据的物理偏移量与 RVA 相同。

仅限 COFF 重定位 (对象)

对象文件包含 COFF 重定位,指定在图像文件中放置并随后加载到内存中时应如何修改节数据。

图像文件不包含 COFF 重定位,因为所有引用的符号都已分配给平面地址空间中的地址。 除非映像具有) IMAGE_FILE_RELOCS_STRIPPED属性,否则映像包含以 .reloc 节中基重定位的形式 (形式的重定位信息。 有关详细信息,请参阅 .reloc 节 (仅) 映像

对于对象文件中的每个节,固定长度记录的数组保留该节的 COFF 重定位。 数组的位置和长度在节标头中指定。 数组的每个元素具有以下格式。

Offset 大小 字段 说明
0
4
VirtualAddress
要对其应用重定位的项目的地址。 这是节开头的偏移量,以及节的 RVA/Offset 字段的值。 请参阅 节表 (节标头) 。 例如,如果节的第一个字节的地址为 0x10,则第三个字节的地址为 0x12。
4
4
SymbolTableIndex
符号表中的从零开始的索引。 此符号提供要用于重定位的地址。 如果指定的符号具有节存储类,则符号的地址是具有相同名称的第一节的地址。
8
2
类型
一个值,该值指示应执行的重定位类型。 有效的重定位类型取决于计算机类型。 请参阅 类型指示器

 

如果 SymbolTableIndex 字段引用的符号具有存储类IMAGE_SYM_CLASS_SECTION,则符号的地址是节的开头。 该节通常位于同一文件中,除非对象文件是存档库 (库) 的一部分。 在这种情况下,可以在存档中具有与当前对象文件相同的存档成员名称的任何其他对象文件中找到该节。 (导入表的链接(即 .idata section.) )中使用与存档成员名称的关系

类型指示器

重定位记录的“类型”字段指示应执行哪种类型的重定位。 为每个类型的计算机定义不同的重定位类型。

x64 处理器

为 x64 和兼容的处理器定义以下重定位类型指示器。

返回的常量 Value 说明
IMAGE_REL_AMD64_ABSOLUTE
0x0000
将忽略重定位。
IMAGE_REL_AMD64_ADDR64
0x0001
重定位目标的 64 位 VA。
IMAGE_REL_AMD64_ADDR32
0x0002
重定位目标的 32 位 VA。
IMAGE_REL_AMD64_ADDR32NB
0x0003
没有映像基 (RVA) 的 32 位地址。
IMAGE_REL_AMD64_REL32
0x0004
重定位后的字节中的 32 位相对地址。
IMAGE_REL_AMD64_REL32_1
0x0005
相对于重定位的字节距离 1 的 32 位地址。
IMAGE_REL_AMD64_REL32_2
0x0006
相对于重定位的字节距离 2 的 32 位地址。
IMAGE_REL_AMD64_REL32_3
0x0007
32 位地址相对于重定位的字节距离 3。
IMAGE_REL_AMD64_REL32_4
0x0008
32 位地址相对于重定位的字节距离 4。
IMAGE_REL_AMD64_REL32_5
0x0009
32 位地址相对于离重定位的字节距离 5。
IMAGE_REL_AMD64_SECTION
0x000A
包含目标的节的 16 位节索引。 这用于支持调试信息。
IMAGE_REL_AMD64_SECREL
0x000B
目标从其部分开头的 32 位偏移量。 这用于支持调试信息和静态线程本地存储。
IMAGE_REL_AMD64_SECREL7
0x000C
与包含目标的节基的 7 位无符号偏移量。
IMAGE_REL_AMD64_TOKEN
0x000D
CLR 令牌。
IMAGE_REL_AMD64_SREL32
0x000E
向对象发出的 32 位有符号跨度依赖值。
IMAGE_REL_AMD64_PAIR
0x000F
必须立即遵循每个跨度依赖值的配对。
IMAGE_REL_AMD64_SSPAN32
0x0010
在链接时应用的 32 位有符号跨度依赖值。

 

ARM 处理器

为 ARM 处理器定义以下重定位类型指示器。

返回的常量 Value 说明
IMAGE_REL_ARM_ABSOLUTE
0x0000
将忽略重定位。
IMAGE_REL_ARM_ADDR32
0x0001
目标的 32 位 VA。
IMAGE_REL_ARM_ADDR32NB
0x0002
目标的 32 位 RVA。
IMAGE_REL_ARM_BRANCH24
0x0003
目标 24 位相对偏移量。
IMAGE_REL_ARM_BRANCH11
0x0004
对子例程调用的引用。 该引用包含两个 16 位指令,其中包含 11 位偏移量。
IMAGE_REL_ARM_REL32
0x000A
重定位后的字节中的 32 位相对地址。
IMAGE_REL_ARM_SECTION
0x000E
包含目标的节的 16 位节索引。 这用于支持调试信息。
IMAGE_REL_ARM_SECREL
0x000F
目标从其部分开头的 32 位偏移量。 这用于支持调试信息和静态线程本地存储。
IMAGE_REL_ARM_MOV32
0x0010
目标的 32 位 VA。 此重定位使用 MOVW 指令对低 16 位应用,后跟高 16 位的 MOVT。
IMAGE_REL_THUMB_MOV32
0x0011
目标的 32 位 VA。 此重定位使用 MOVW 指令对低 16 位应用,后跟高 16 位的 MOVT。
IMAGE_REL_THUMB_BRANCH20
0x0012
该指令使用 21 位相对于 2 字节对齐的目标的位移进行修复。 位移的最小有效位始终为零,且不存储。 此重定位对应于 Thumb-2 32 位条件 B 指令。
未使用
0x0013
IMAGE_REL_THUMB_BRANCH24
0x0014
该指令修复了 25 位相对于 2 字节对齐目标的相对偏移量。 位移的最小有效位为零,并且不存储。此重定位对应于 Thumb-2 B 指令。
IMAGE_REL_THUMB_BLX23
0x0015
该指令修复了 25 位相对于 4 字节对齐目标的相对偏移量。 位移的低 2 位为零,并且不存储。
此重定位对应于 Thumb-2 BLX 指令。
IMAGE_REL_ARM_PAIR
0x0016
仅当重定位紧跟ARM_REFHI或THUMB_REFHI时才有效。 它的 SymbolTableIndex 包含位移,而不是符号表中的索引。

 

ARM64 处理器

为 ARM64 处理器定义了以下重定位类型指示器。

返回的常量 Value 说明
IMAGE_REL_ARM64_ABSOLUTE
0x0000
将忽略重定位。
IMAGE_REL_ARM64_ADDR32
0x0001
目标的 32 位 VA。
IMAGE_REL_ARM64_ADDR32NB
0x0002
目标的 32 位 RVA。
IMAGE_REL_ARM64_BRANCH26
0x0003
B 和 BL 指令的 26 位相对偏移量。
IMAGE_REL_ARM64_PAGEBASE_REL21
0x0004
目标的页面基,用于 ADRP 指令。
IMAGE_REL_ARM64_REL21
0x0005
指令 ADR 的 12 位相对偏移量
IMAGE_REL_ARM64_PAGEOFFSET_12A
0x0006
目标的 12 位页偏移量,说明 ADD/ADDS (立即) 零班次。
IMAGE_REL_ARM64_PAGEOFFSET_12L
0x0007
目标 12 位页偏移量,指示 LDR (索引的无符号即时) 。
IMAGE_REL_ARM64_SECREL
0x0008
目标从其部分开头的 32 位偏移量。 这用于支持调试信息和静态线程本地存储。
IMAGE_REL_ARM64_SECREL_LOW12A
0x0009
目标节偏移量的位 0:11,有关说明 ADD/ADDS (立即) 零移位。
IMAGE_REL_ARM64_SECREL_HIGH12A
0x000A
目标节偏移量的位 12:23,有关说明 ADD/ADDS (立即) 零移位。
IMAGE_REL_ARM64_SECREL_LOW12L
0x000B
目标部分偏移量的位 0:11,用于指令 LDR (索引的无符号即时) 。
IMAGE_REL_ARM64_TOKEN
0x000C
CLR 令牌。
IMAGE_REL_ARM64_SECTION
0x000D
包含目标的节的 16 位节索引。 这用于支持调试信息。
IMAGE_REL_ARM64_ADDR64
0x000E
重定位目标的 64 位 VA。
IMAGE_REL_ARM64_BRANCH19
0x000F
条件 B 指令的重定位目标的 19 位偏移量。
IMAGE_REL_ARM64_BRANCH14
0x0010
有关 TBZ 和 TBNZ 的说明,14 位偏移量到重定位目标。
IMAGE_REL_ARM64_REL32
0x0011
重定位后的字节中的 32 位相对地址。
Hitachi SuperH 处理器

为 SH3 和 SH4 处理器定义了以下重定位类型指示器。 SH5 特定的重定位称为 SHM (SH Media) 。

返回的常量 Value 说明
IMAGE_REL_SH3_ABSOLUTE
0x0000
将忽略重定位。
IMAGE_REL_SH3_DIRECT16
0x0001
对包含目标符号 VA 的 16 位位置的引用。
IMAGE_REL_SH3_DIRECT32
0x0002
目标符号的 32 位 VA。
IMAGE_REL_SH3_DIRECT8
0x0003
对包含目标符号 VA 的 8 位位置的引用。
IMAGE_REL_SH3_DIRECT8_WORD
0x0004
对包含目标符号的有效 16 位 VA 的 8 位指令的引用。
IMAGE_REL_SH3_DIRECT8_LONG
0x0005
对包含目标符号的有效 32 位 VA 的 8 位指令的引用。
IMAGE_REL_SH3_DIRECT4
0x0006
对 8 位位置的引用,其低 4 位包含目标符号的 VA。
IMAGE_REL_SH3_DIRECT4_WORD
0x0007
对 8 位指令的引用,其低 4 位包含目标符号的有效 16 位 VA。
IMAGE_REL_SH3_DIRECT4_LONG
0x0008
对 8 位指令的引用,其低 4 位包含目标符号的有效 32 位 VA。
IMAGE_REL_SH3_PCREL8_WORD
0x0009
对包含目标符号的有效 16 位相对偏移量的 8 位指令的引用。
IMAGE_REL_SH3_PCREL8_LONG
0x000A
对包含目标符号的有效 32 位相对偏移量的 8 位指令的引用。
IMAGE_REL_SH3_PCREL12_WORD
0x000B
对 16 位指令的引用,其低 12 位包含目标符号的有效 16 位相对偏移量。
IMAGE_REL_SH3_STARTOF_SECTION
0x000C
对 32 位位置的引用,该位置是包含目标符号的部分的 VA。
IMAGE_REL_SH3_SIZEOF_SECTION
0x000D
对包含目标符号的节大小的 32 位位置的引用。
IMAGE_REL_SH3_SECTION
0x000E
包含目标的节的 16 位节索引。 这用于支持调试信息。
IMAGE_REL_SH3_SECREL
0x000F
目标从其部分开头的 32 位偏移量。 这用于支持调试信息和静态线程本地存储。
IMAGE_REL_SH3_DIRECT32_NB
0x0010
目标符号的 32 位 RVA。
IMAGE_REL_SH3_GPREL4_LONG
0x0011
GP 相对。
IMAGE_REL_SH3_TOKEN
0x0012
CLR 令牌。
IMAGE_REL_SHM_PCRELPT
0x0013
长字中当前指令的偏移量。 如果未设置 NOMODE 位,请在位 32 处插入低位的反转位以选择 PTA 或 PTB。
IMAGE_REL_SHM_REFLO
0x0014
32 位地址的低 16 位。
IMAGE_REL_SHM_REFHALF
0x0015
32 位地址的高 16 位。
IMAGE_REL_SHM_RELLO
0x0016
相对地址的低 16 位。
IMAGE_REL_SHM_RELHALF
0x0017
相对地址的高 16 位。
IMAGE_REL_SHM_PAIR
0x0018
仅当重新定位紧跟 REFHALF、RELHALF 或 RELLO 重定位时才有效。 重定位的 SymbolTableIndex 字段包含位移,而不是符号表中的索引。
IMAGE_REL_SHM_NOMODE
0x8000
重定位将忽略分区模式。

 

IBM PowerPC 处理器

为PowerPC处理器定义以下重定位类型指示器。

返回的常量 Value 说明
IMAGE_REL_PPC_ABSOLUTE
0x0000
将忽略重定位。
IMAGE_REL_PPC_ADDR64
0x0001
目标的 64 位 VA。
IMAGE_REL_PPC_ADDR32
0x0002
目标的 32 位 VA。
IMAGE_REL_PPC_ADDR24
0x0003
目标 VA 的低 24 位。 仅当目标符号是绝对的并且可以将其原始值进行签名时,才有效。
IMAGE_REL_PPC_ADDR16
0x0004
目标 VA 的低 16 位。
IMAGE_REL_PPC_ADDR14
0x0005
目标 VA 的低 14 位。 仅当目标符号是绝对的并且可以将其原始值进行签名时,才有效。
IMAGE_REL_PPC_REL24
0x0006
相对于符号位置的 24 位 PC 相对偏移量。
IMAGE_REL_PPC_REL14
0x0007
符号位置的 14 位相对于电脑的偏移量。
IMAGE_REL_PPC_ADDR32NB
0x000A
目标的 32 位 RVA。
IMAGE_REL_PPC_SECREL
0x000B
目标从其部分开头的 32 位偏移量。 这用于支持调试信息和静态线程本地存储。
IMAGE_REL_PPC_SECTION
0x000C
包含目标的节的 16 位节索引。 这用于支持调试信息。
IMAGE_REL_PPC_SECREL16
0x000F
目标从其部分开头的 16 位偏移量。 这用于支持调试信息和静态线程本地存储。
IMAGE_REL_PPC_REFHI
0x0010
目标 32 位 VA 的高 16 位。 这用于加载完整地址的双指令序列中的第一个指令。 此重定位必须紧接着是一个 PAIR 重定位,其 SymbolTableIndex 包含已签名的 16 位偏移量,该偏移量将添加到从要重新定位的位置获取的上限 16 位。
IMAGE_REL_PPC_REFLO
0x0011
目标 VA 的低 16 位。
IMAGE_REL_PPC_PAIR
0x0012
仅当重定位紧跟 REFHI 或 SECRELHI 重定位时才有效。 其 SymbolTableIndex 包含一个偏移量,而不是符号表中的索引。
IMAGE_REL_PPC_SECRELLO
0x0013
目标的 32 位偏移量(从其部分的开头开始)的低 16 位。
IMAGE_REL_PPC_GPREL
0x0015
目标相对于 GP 寄存器的 16 位有符号偏移量。
IMAGE_REL_PPC_TOKEN
0x0016
CLR 令牌。

 

Intel 386 处理器

为 Intel 386 和兼容的处理器定义了以下重定位类型指示器。

返回的常量 Value 说明
IMAGE_REL_I386_ABSOLUTE
0x0000
将忽略重定位。
IMAGE_REL_I386_DIR16
0x0001
不支持。
IMAGE_REL_I386_REL16
0x0002
不支持。
IMAGE_REL_I386_DIR32
0x0006
目标的 32 位 VA。
IMAGE_REL_I386_DIR32NB
0x0007
目标的 32 位 RVA。
IMAGE_REL_I386_SEG12
0x0009
不支持。
IMAGE_REL_I386_SECTION
0x000A
包含目标的节的 16 位节索引。 这用于支持调试信息。
IMAGE_REL_I386_SECREL
0x000B
目标从其部分开头的 32 位偏移量。 这用于支持调试信息和静态线程本地存储。
IMAGE_REL_I386_TOKEN
0x000C
CLR 令牌。
IMAGE_REL_I386_SECREL7
0x000D
与包含目标的节基的 7 位偏移量。
IMAGE_REL_I386_REL32
0x0014
目标 32 位相对偏移量。 这支持 x86 相对分支和调用说明。

 

Intel Itanium 处理器系列 (IPF)

为 Intel Itanium 处理器系列和兼容的处理器定义了以下重定位类型指示器。 请注意,指令上的重定位使用捆绑包的偏移量和槽号进行重定位偏移。

返回的常量 Value 说明
IMAGE_REL_IA64_ABSOLUTE
0x0000
将忽略重定位。
IMAGE_REL_IA64_IMM14
0x0001
指令重定位后跟一个 ADDEND 重定位,其值将添加到目标地址,然后再将其插入 IMM14 捆绑包中的指定槽。 重定位目标必须是绝对目标,或者必须修复映像。
IMAGE_REL_IA64_IMM22
0x0002
指令重定位后,可以添加一个 ADDEND 重定位,其值将添加到目标地址,然后再将其插入到 IMM22 捆绑包中的指定槽。 重定位目标必须是绝对目标,或者必须修复映像。
IMAGE_REL_IA64_IMM64
0x0003
此重定位的槽号必须是 1 (1) 。 重新定位后跟一个 ADDEND 重定位,其值将添加到目标地址,然后再将其存储在 IMM64 捆绑包的所有三个槽中。
IMAGE_REL_IA64_DIR32
0x0004
目标的 32 位 VA。 仅支持 /LARGEADDRESSAWARE:NO 映像。
IMAGE_REL_IA64_DIR64
0x0005
目标的 64 位 VA。
IMAGE_REL_IA64_PCREL21B
0x0006
该指令修复了 25 位相对于偏移量到 16 位对齐目标。 位移的低 4 位为零,并且不存储。
IMAGE_REL_IA64_PCREL21M
0x0007
该指令修复了 25 位相对于偏移量到 16 位对齐目标。 位移的低 4 位(为零)不存储。
IMAGE_REL_IA64_PCREL21F
0x0008
此重定位偏移量的 LSB 必须包含槽号,而其余的是捆绑地址。 捆绑包与 25 位相对偏移量固定到 16 位对齐的目标。 位移的低 4 位为零,并且不存储。
IMAGE_REL_IA64_GPREL22
0x0009
指令重定位后跟一个 ADDEND 重定位,其值将添加到目标地址,然后是计算并应用于 GPREL22 捆绑包的 22 位 GP 相对偏移量。
IMAGE_REL_IA64_LTOFF22
0x000A
该指令使用目标符号文本表项的 22 位 GP 相对偏移量修复。 链接器基于此重定位和可能遵循的 ADDEND 重定位创建此文本表条目。
IMAGE_REL_IA64_SECTION
0x000B
节的 16 位节索引包含目标。 这用于支持调试信息。
IMAGE_REL_IA64_SECREL22
0x000C
该指令已修复,其部分开头的 22 位目标偏移量已修复。 此重定位后,可以立即执行 ADDEND 重定位,其 Value 字段包含从节开头开始的目标的 32 位无符号偏移量。
IMAGE_REL_IA64_SECREL64I
0x000D
此重定位的槽号必须是 1 (1) 。 该指令与目标从其部分开头的 64 位偏移量一起修复。 此重定位后,可以立即执行 ADDEND 重定位,其 Value 字段包含该目标的 32 位无符号偏移量,该偏移量从节的开头开始。
IMAGE_REL_IA64_SECREL32
0x000E
要修复的数据地址,其部分开头的 32 位目标偏移量。
IMAGE_REL_IA64_DIR32NB
0x0010
目标的 32 位 RVA。
IMAGE_REL_IA64_SREL14
0x0011
这应用于一个签名的 14 位即时,其中包含两个可重定位目标之间的差异。 这是链接器声明性字段,指示编译器已发出此值。
IMAGE_REL_IA64_SREL22
0x0012
这应用于一个签名的 22 位即时,其中包含两个可重定位目标之间的差异。 这是链接器声明性字段,指示编译器已发出此值。
IMAGE_REL_IA64_SREL32
0x0013
这应用于一个有符号的 32 位即时值,其中包含两个可重定位值之间的差异。 这是链接器声明性字段,指示编译器已发出此值。
IMAGE_REL_IA64_UREL32
0x0014
这应用于一个无符号的 32 位即时值,其中包含两个可重定位值之间的差异。 这是链接器声明性字段,指示编译器已发出此值。
IMAGE_REL_IA64_PCREL60X
0x0015
一个 60 位电脑相对修复程序,始终作为 MLX 捆绑包的一个 MLX 指令保留。
IMAGE_REL_IA64_PCREL60B
0x0016
60 位电脑相对修复。 如果目标偏移量适合有符号的 25 位字段,请将整个捆绑包转换为具有 NOP 的 MBB 捆绑包。槽 1 中的 B 和 25 位 BR 指令 (,其中 4 个最低位全部为零,并在槽 2 中丢弃) 。
IMAGE_REL_IA64_PCREL60F
0x0017
60 位电脑相对修复。 如果目标偏移量适合有符号的 25 位字段,请将整个捆绑包转换为具有 NOP 的 MFB 捆绑包。槽 1 中的 F 和 25 位 (4 个最低位全部为零,并在槽 2 中删除了) BR 指令。
IMAGE_REL_IA64_PCREL60I
0x0018
60 位电脑相对修复。 如果目标偏移量适合有符号的 25 位字段,请将整个捆绑包转换为具有 NOP 的 MIB 捆绑包。我位于槽 1 和 25 位 (4 个最低位,全部为零,并在槽 2 中删除了) BR 指令。
IMAGE_REL_IA64_PCREL60M
0x0019
60 位电脑相对修复。 如果目标偏移量适合有符号的 25 位字段,请将整个捆绑包转换为具有 NOP 的 MMB 捆绑包。槽 1 中的 M 和 25 位 (4 个最低位全部为零,并删除了槽 2 中的 BR 指令) 。
IMAGE_REL_IA64_IMMGPREL64
0x001a
64 位 GP 相对修复。
IMAGE_REL_IA64_TOKEN
0x001b
CLR 令牌。
IMAGE_REL_IA64_GPREL32
0x001c
32 位 GP 相对修复。
IMAGE_REL_IA64_ADDEND
0x001F
仅当重定位紧跟以下搬迁之一时,该重定位才有效:IMM14、IMM22、IMM64、GPREL22、LTOFF22、LTOFF64、SECREL22、SECREL64I 或 SECREL32。 其值包含要应用于捆绑包内指令的附录,不适用于数据。

 

MIPS 处理器

为 MIPS 处理器定义了以下重定位类型指示器。

返回的常量 Value 说明
IMAGE_REL_MIPS_ABSOLUTE
0x0000
将忽略重定位。
IMAGE_REL_MIPS_REFHALF
0x0001
目标 32 位 VA 的高 16 位。
IMAGE_REL_MIPS_REFWORD
0x0002
目标的 32 位 VA。
IMAGE_REL_MIPS_JMPADDR
0x0003
目标 VA 的低 26 位。 这支持 MIPS J 和 JAL 指令。
IMAGE_REL_MIPS_REFHI
0x0004
目标 32 位 VA 的高 16 位。 这用于加载完整地址的双指令序列中的第一个指令。 此重定位必须紧接着一个 PAIR 重定位,其 SymbolTableIndex 包含一个已签名的 16 位位位移,该位已添加到从要重新定位的位置获取的上 16 位位。
IMAGE_REL_MIPS_REFLO
0x0005
目标 VA 的低 16 位。
IMAGE_REL_MIPS_GPREL
0x0006
相对于 GP 寄存器的目标的 16 位有符号位移。
IMAGE_REL_MIPS_LITERAL
0x0007
与IMAGE_REL_MIPS_GPREL相同。
IMAGE_REL_MIPS_SECTION
0x000A
节的 16 位节索引包含目标。 这用于支持调试信息。
IMAGE_REL_MIPS_SECREL
0x000B
目标从其部分开头的 32 位偏移量。 这用于支持调试信息和静态线程本地存储。
IMAGE_REL_MIPS_SECRELLO
0x000C
目标 32 位偏移量从其部分开头的低 16 位。
IMAGE_REL_MIPS_SECRELHI
0x000D
目标从部分开头的 32 位偏移量的高 16 位。 IMAGE_REL_MIPS_PAIR重定位必须立即遵循此重定位。 PAIR 重定位的 SymbolTableIndex 包含一个已签名的 16 位位位移,该位已添加到从要重新定位的位置获取的上限 16 位。
IMAGE_REL_MIPS_JMPADDR16
0x0010
目标 VA 的低 26 位。 这支持 MIPS16 JAL 指令。
IMAGE_REL_MIPS_REFWORDNB
0x0022
目标的 32 位 RVA。
IMAGE_REL_MIPS_PAIR
0x0025
仅当重定位紧跟 REFHI 或 SECRELHI 重定位时,重定位才有效。 它的 SymbolTableIndex 包含位移,而不是符号表中的索引。

 

三菱 M32R

为三菱 M32R 处理器定义了以下重定位类型指示器。

返回的常量 Value 说明
IMAGE_REL_M32R_ABSOLUTE
0x0000
将忽略重定位。
IMAGE_REL_M32R_ADDR32
0x0001
目标的 32 位 VA。
IMAGE_REL_M32R_ADDR32NB
0x0002
目标的 32 位 RVA。
IMAGE_REL_M32R_ADDR24
0x0003
目标的 24 位 VA。
IMAGE_REL_M32R_GPREL16
0x0004
目标与 GP 寄存器的 16 位偏移量。
IMAGE_REL_M32R_PCREL24
0x0005
目标从程序计数器的 24 位偏移量 (PC) ,左移 2 位并进行签名扩展
IMAGE_REL_M32R_PCREL16
0x0006
目标与电脑的 16 位偏移量,左移 2 位并扩展符号
IMAGE_REL_M32R_PCREL8
0x0007
目标与电脑的 8 位偏移量,左移 2 位并扩展签名
IMAGE_REL_M32R_REFHALF
0x0008
目标 VA 的 16 个 MSB。
IMAGE_REL_M32R_REFHI
0x0009
针对 LSB 符号扩展调整的目标 VA 的 16 个 MSB。 这用于加载完整 32 位地址的双指令序列中的第一个指令。 此重定位必须紧接着一个 PAIR 重定位,其 SymbolTableIndex 包含一个已签名的 16 位位位移,该位已添加到从要重新定位的位置获取的上 16 位位。
IMAGE_REL_M32R_REFLO
0x000A
目标 VA 的 16 个 LSB。
IMAGE_REL_M32R_PAIR
0x000B
重定位必须遵循 REFHI 重定位。 其 SymbolTableIndex 包含一个偏移量,而不是符号表中的索引。
IMAGE_REL_M32R_SECTION
0x000C
包含目标的节的 16 位节索引。 这用于支持调试信息。
IMAGE_REL_M32R_SECREL
0x000D
目标从其部分开头的 32 位偏移量。 这用于支持调试信息和静态线程本地存储。
IMAGE_REL_M32R_TOKEN
0x000E
CLR 令牌。

 

COFF 行号 (弃用)

COFF 行号不再生成,将来不会使用。

COFF 行号指示源文件中的代码和行号之间的关系。 COFF 行号的 Microsoft 格式类似于标准 COFF,但已扩展,允许单个节与多个源文件中的行号相关。

COFF 行号由固定长度记录数组组成。 (文件偏移量的位置) 和数组的大小在节标头中指定。 每条行号记录的格式如下。

Offset 大小 字段 说明
0
4
类型 (*)
这是两个字段的联合:SymbolTableIndex 和 VirtualAddress。 是使用 SymbolTableIndex 还是 RVA 取决于 Linenumber 的值。
4
2
布隆伯
非零时,此字段指定基于一个行号。 为零时,Type 字段将解释为函数的符号表索引。

 

Type 字段是两个 4 字节字段的联合:SymbolTableIndex 和 VirtualAddress。

Offset 大小 字段 说明
0
4
SymbolTableIndex
当 Linenumber 为零时使用:为函数符号表项的索引。 此格式用于指示一组行号记录引用的函数。
0
4
VirtualAddress
当 Linenumber 为非零时使用:对应于所指示源行的可执行代码的 RVA。 在对象文件中,它包含节中的 VA。

 

行号记录可以将“Linenumber”字段设置为零,并指向符号表中的函数定义,也可以通过提供正整数 (行号) 和对象代码中的相应地址来充当标准行号条目。

一组行号条目始终以第一种格式开头:函数符号的索引。 如果这是节中的第一行号记录,则如果设置了该节的 COMDAT 标志,则它也是函数的 COMDAT 符号名称。 请参阅 COMDAT 节 (对象) 。 符号表中函数的辅助记录具有指向此相同行号记录的 Linenumber 字段的指针。

标识函数的记录后跟任意数量的行号条目,这些条目提供实际行号信息 (,即具有大于零) 的项。 这些条目是一个基于一个的,相对于函数的开头,并表示函数中除第一行以外的每个源行。

例如,以下示例的第一个行号记录将指定 ReverseSign 函数 (ReverseSign 的 SymbolTableIndex 和 Linenumber 设置为零) 。 然后,带有 1、2 和 3 的包含 1、2 和 3 的记录将遵循,对应于源行,如下所示:

C++
// some code precedes ReverseSign function
int ReverseSign(int i)
1: {
2:  return -1 * i;
3: }

COFF 符号表

本节中的符号表继承自传统的 COFF 格式。 它不同于Microsoft Visual C++调试信息。 文件可以同时包含 COFF 符号表和 Visual C++ 调试信息,两者保持独立。 某些 Microsoft 工具将符号表用于有限但重要的用途,例如将 COMDAT 信息传达给链接器。 节名称和文件名以及代码和数据符号在符号表中列出。

符号表的位置在 COFF 标头中指示。

符号表是一个记录数组,每 18 个字节长。 每个记录都是标准符号或辅助符号表记录。 标准记录定义符号或名称,并具有以下格式。

Offset 大小 字段 说明
0
8
名称 (*)
符号的名称,由三个结构的联合表示。 如果名称长度不超过 8 个字节,则使用 8 个字节的数组。 有关详细信息,请参阅 符号名称表示形式
8
4

与符号关联的值。 此字段的解释取决于 SectionNumber 和 StorageClass。 典型含义是可重定位地址。
12
2
SectionNumber
使用基于一个索引的节表中标识节的带符号整数。 某些值具有特殊含义,如第 5.4.2 节“节编号值”中所述。
14
2
类型
表示类型的数字。 Microsoft 工具将此字段设置为0x20 (函数) 或0x0 (不是函数) 。 有关详细信息,请参阅 类型表示形式
16
1
StorageClass
一个表示存储类的枚举值。 有关详细信息,请参阅存储类
17
1
NumberOfAuxSymbols
此记录后面的辅助符号表条目数。

 

零个或多个辅助符号表记录立即遵循每个标准符号表记录。 但是,通常不超过一条辅助符号表记录遵循标准符号表记录 (,但具有长文件名的 .file 记录) 除外。 每个辅助记录的大小与标准符号表记录的大小相同, (18 字节) ,但不定义新符号,辅助记录提供有关定义的最后一个符号的其他信息。 要使用的多种格式的选择取决于 StorageClass 字段。 辅助符号表记录的当前定义格式显示在第 5.5 节“辅助符号记录”。

读取 COFF 符号表的工具必须忽略解释未知的辅助符号记录。 这允许扩展符号表格式以添加新的辅助记录,而无需中断现有工具。

符号名称表示形式

符号表中的 ShortName 字段包含 8 个字节,如果名称长度不超过 8 个字节,则 ShortName 字段将偏移到字符串表中。 若要确定是否给定名称本身或偏移量,请测试前 4 个字节是否等于零。

根据约定,名称被视为零终止的 UTF-8 编码字符串。

Offset 大小 字段 说明
0
8
ShortName
8 个字节的数组。 如果名称长度小于 8 字节,则此数组将用右侧的 null 填充。
0
4

如果名称超过 8 个字节,则设置为所有零的字段。
4
4
Offset
字符串表中的偏移量。

 

节号值

通常,符号表项中的“节值”字段是节表中的一个基于索引的索引。 但是,此字段是带符号整数,可以采用负值。 以下值小于一个,具有特殊含义。

返回的常量 Value 说明
IMAGE_SYM_UNDEFINED
0
符号记录尚未分配节。 值为零表示对外部符号的引用在别处定义。 非零的值是一个公共符号,其大小由值指定。
IMAGE_SYM_ABSOLUTE
-1
符号具有绝对 (不可重定位) 值,不是地址。
IMAGE_SYM_DEBUG
-2
符号提供常规类型或调试信息,但与节不对应。 Microsoft 工具将此设置与 .file 记录一起使用, (存储类 FILE) 。

 

类型表示形式

符号表项的 Type 字段包含 2 个字节,其中每个字节表示类型信息。 LSB 表示简单 (基) 数据类型,MSB 表示复杂类型(如果有):

MSB LSB
复杂类型:无、指针、函数、数组。
基类型:整数、浮点等。

 

以下值是为基类型定义的,尽管 Microsoft 工具通常不使用此字段并将 LSB 设置为 0。 相反,Visual C++ 调试信息用于指示类型。 但是,此处列出了可能的 COFF 值,以便完成。

返回的常量 Value 说明
IMAGE_SYM_TYPE_NULL
0
没有类型信息或未知基类型。 Microsoft 工具使用此设置
IMAGE_SYM_TYPE_VOID
1
无有效类型;与 void 指针和函数一起使用
IMAGE_SYM_TYPE_CHAR
2
带符号字节) 的字符 (
IMAGE_SYM_TYPE_SHORT
3
2 字节有符号整数
IMAGE_SYM_TYPE_INT
4
自然整数类型 (通常为 4 个字节,Windows)
IMAGE_SYM_TYPE_LONG
5
4 字节有符号整数
IMAGE_SYM_TYPE_FLOAT
6
4 字节浮点数
IMAGE_SYM_TYPE_DOUBLE
7
8 字节浮点数
IMAGE_SYM_TYPE_STRUCT
8
结构
IMAGE_SYM_TYPE_UNION
9
联合
IMAGE_SYM_TYPE_ENUM
10
枚举类型
IMAGE_SYM_TYPE_MOE
11
枚举的成员 (特定值)
IMAGE_SYM_TYPE_BYTE
12
字节;无符号 1 字节整数
IMAGE_SYM_TYPE_WORD
13
一个词;无符号 2 字节整数
IMAGE_SYM_TYPE_UINT
14
自然大小的无符号整数通常 (为 4 个字节)
IMAGE_SYM_TYPE_DWORD
15
无符号 4 字节整数

 

最重要的字节指定符号是指向、函数返回还是 LSB 中指定的基类型的数组的指针。 Microsoft 工具仅使用此字段来指示符号是否为函数,以便只有两个生成的值0x0和类型字段的0x20。 但是,其他工具可以使用此字段来传达更多信息。

正确指定函数属性非常重要。 增量链接需要此信息才能正常工作。 对于某些体系结构,可能需要这些信息才能用于其他目的。

返回的常量 Value 说明
IMAGE_SYM_DTYPE_NULL
0
无派生类型;符号是一个简单的标量变量。
IMAGE_SYM_DTYPE_POINTER
1
符号是指向基类型的指针。
IMAGE_SYM_DTYPE_FUNCTION
2
符号是返回基类型的函数。
IMAGE_SYM_DTYPE_ARRAY
3
符号是基类型的数组。

 

存储类

符号表的 StorageClass 字段指示符号表示的定义类型。 下表显示了可能的值。 请注意,StorageClass 字段是一个无符号 1 字节整数。 因此,应采用特殊值 -1 表示其无符号等效值,0xFF。

尽管传统的 COFF 格式使用许多存储类值,但 Microsoft 工具依赖于 Visual C++ 调试格式来获取大多数符号信息,并且通常只使用四个存储类值:EXTERNAL (2) 、STATIC (3) 、FUNCTION (101) 和 FILE (103) 。 除了下面的第二列标题中,应采用“值”来表示符号记录的值字段 (其解释取决于作为存储类) 找到的数字。

返回的常量 Value “值”字段的说明/解释
IMAGE_SYM_CLASS_END_OF_FUNCTION
-1 (0xFF)
表示函数末尾的特殊符号,用于调试。
IMAGE_SYM_CLASS_NULL
0
没有分配的存储类。
IMAGE_SYM_CLASS_AUTOMATIC
1
自动 (堆栈) 变量。 “值”字段指定堆栈帧偏移量。
IMAGE_SYM_CLASS_EXTERNAL
2
Microsoft 工具用于外部符号的值。 “值”字段指示节号是否IMAGE_SYM_UNDEFINED (0) 的大小。 如果节号不为零,则 Value 字段指定节内的偏移量。
IMAGE_SYM_CLASS_STATIC
3
节中符号的偏移量。 如果 Value 字段为零,则符号表示节名称。
IMAGE_SYM_CLASS_REGISTER
4
寄存器变量。 “值”字段指定寄存器编号。
IMAGE_SYM_CLASS_EXTERNAL_DEF
5
外部定义的符号。
IMAGE_SYM_CLASS_LABEL
6
在模块中定义的代码标签。 “值”字段指定节中符号的偏移量。
IMAGE_SYM_CLASS_UNDEFINED_LABEL
7
对未定义的代码标签的引用。
IMAGE_SYM_CLASS_MEMBER_OF_STRUCT
8
结构成员。 Value 字段指定第 n 个成员。
IMAGE_SYM_CLASS_ARGUMENT
9
函数的正式参数 (参数) 。 Value 字段指定第 n 个参数。
IMAGE_SYM_CLASS_STRUCT_TAG
10
结构标记名称条目。
IMAGE_SYM_CLASS_MEMBER_OF_UNION
11
联合成员。 Value 字段指定第 n 个成员。
IMAGE_SYM_CLASS_UNION_TAG
12
Union 标记名称条目。
IMAGE_SYM_CLASS_TYPE_DEFINITION
13
Typedef 条目。
IMAGE_SYM_CLASS_UNDEFINED_STATIC
14
静态数据声明。
IMAGE_SYM_CLASS_ENUM_TAG
15
枚举类型 tagname 条目。
IMAGE_SYM_CLASS_MEMBER_OF_ENUM
16
枚举的成员。 Value 字段指定第 n 个成员。
IMAGE_SYM_CLASS_REGISTER_PARAM
17
寄存器参数。
IMAGE_SYM_CLASS_BIT_FIELD
18
位字段引用。 “值”字段指定位字段中的第 n 位。
IMAGE_SYM_CLASS_BLOCK
100
块) 或 .eb (块) 记录的开头的 .bb (。 “值”字段是代码位置的可重新定位地址。
IMAGE_SYM_CLASS_FUNCTION
101
Microsoft 工具用于定义函数范围的符号记录:begin 函数 (.bf ) 、end function ( .ef ) 和函数 ( .lf ) 中的行。 对于 .lf 记录,“值”字段提供函数中的源行数。 对于 .ef 记录,Value 字段提供函数代码的大小。
IMAGE_SYM_CLASS_END_OF_STRUCT
102
结构结束项。
IMAGE_SYM_CLASS_FILE
103
Microsoft 工具以及传统 COFF 格式的值用于源文件符号记录。 符号后跟命名文件的辅助记录。
IMAGE_SYM_CLASS_SECTION
104
Microsoft 工具 (节的定义使用静态存储类,而不是) 。
IMAGE_SYM_CLASS_WEAK_EXTERNAL
105
外部弱。 有关详细信息,请参阅 辅助格式 3:弱外部
IMAGE_SYM_CLASS_CLR_TOKEN
107
CLR 标记符号。 该名称是一个 ASCII 字符串,由令牌的十六进制值组成。 有关详细信息,请参阅 CLR 令牌定义 (对象)

 

辅助符号记录

辅助符号表记录始终遵循并应用于某些标准符号表记录。 辅助记录可以识别任何格式,但必须为其分配 18 个字节,以便符号表作为常规大小的数组进行维护。 目前,Microsoft 工具识别以下类型的记录的辅助格式:函数定义、函数开始和结束符号 (.bf 和 .ef) 、弱外部、文件名和节定义。

传统的 COFF 设计还包括数组和结构的辅助记录格式。 Microsoft 工具不使用它们,而是在调试部分中将符号信息置于 Visual C++ 调试格式中。

辅助格式 1:函数定义

符号表记录将标记函数定义的开头(如果函数定义具有以下所有条件):EXTERNAL (2) 的存储类、指示它是函数 (0x20) 的类型值,以及大于零的节数。 请注意,符号表记录的节数为 UNDEFINED (0) 不定义函数,并且没有辅助记录。 函数定义符号记录后跟以下格式的辅助记录:

Offset 大小 字段 说明
0
4
TagIndex
相应 .bf 的符号表索引 (开始函数) 符号记录。
4
4
TotalSize
函数本身的可执行代码的大小。 如果函数位于自己的节中,则节标头中的 SizeOfRawData 大于或等于此字段,具体取决于对齐注意事项。
8
4
PointerToLinenumber
函数的第一个 COFF 行号条目的文件偏移量,如果不存在,则为零。 有关详细信息,请参阅 COFF 行号 (已弃用)
12
4
PointerToNextFunction
下一个函数的记录的符号表索引。 如果函数是符号表中的最后一个,则此字段设置为零。
16
2
未使用

 

辅助格式 2:.bf 和 .ef 符号

对于符号表中的每个函数定义,三个项描述了行的开始、结束和行数。 每个符号都有存储类 FUNCTION (101) :

名为 .bf (begin 函数) 的符号记录。 “值”字段未使用。

函数) 中名为 .lf 的符号记录 (行。 “值”字段提供函数中的行数。

名为 .ef (函数) 末尾的符号记录。 “值”字段的数字与函数定义符号记录中的“总大小”字段相同。

.bf 和 .ef 符号记录 (但不是 .lf 记录) 后跟具有以下格式的辅助记录:

Offset 大小 字段 说明
0
4
未使用
4
2
布鲁姆伯
与 .bf 或 .ef 记录相对应的源文件内的实际序号 (1、2、3 等) 。
6
6
未使用
12
4
PointerToNextFunction ( .bf 仅)
下一个 .bf 符号记录的符号表索引。 如果该函数是符号表中的最后一个,则此字段设置为零。 它不用于 .ef 记录。
16
2
未使用

 

辅助格式 3:弱外部

“弱外部”是对象文件的一种机制,允许在链接时灵活。 模块可以包含未解析的外部符号 (sym1) ,但它还可以包含一个辅助记录,指示如果符号 1 在链接时不存在,则使用另一个外部符号 (sym2) 解析引用。

如果符号1 的定义已链接,则通常解析对符号的外部引用。 如果未链接 sym1 的定义,则对 sym1 弱外部的所有引用都指 sym2。 外部符号 sym2 必须始终链接;通常,它在模块中定义,其中包含对 sym1 的弱引用。

弱外部由具有 EXTERNAL 存储类、UNDEF 节号和值为零的符号表记录表示。 弱外部符号记录后跟具有以下格式的辅助记录:

Offset 大小 字段 说明
0
4
TagIndex
sym2 的符号表索引,如果未找到 sym1,则要链接的符号。
4
4
特征
IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY的值指示不应执行符号1 的库搜索。
IMAGE_WEAK_EXTERN_SEARCH_LIBRARY值指示应执行符号1 的库搜索。
IMAGE_WEAK_EXTERN_SEARCH_ALIAS值指示 sym1 是 sym2 的别名。
8
10
未使用

 

请注意,“特征”字段未在 WINNT 中定义。H;而是使用“总大小”字段。

辅助格式 4:文件

此格式遵循存储类 FILE (103) 的符号表记录。 符号名称本身应为 .file,后面的辅助记录提供源代码文件的名称。

Offset 大小 字段 说明
0
18
文件名
一个 ANSI 字符串,该字符串提供源文件的名称。 如果该值小于最大长度,则用 null 填充。

 

辅助格式 5:节定义

此格式遵循定义节的符号表记录。 此类记录具有符号名称,该符号名称是 .text 或 .drectve) 等 (节的名称,并且存储类 STATIC (3) 。 辅助记录提供有关其引用部分的信息。 因此,它会复制节标头中的一些信息。

Offset 大小 字段 说明
0
4
长度
节数据的大小;与节标头中的 SizeOfRawData 相同。
4
2
NumberOfRelocations
分区的重定位条目数。
6
2
NumberOfLinenumbers
节的行号条目数。
8
4
校验
公共数据的校验和。 如果在节标头中设置了IMAGE_SCN_LNK_COMDAT标志,则适用。 有关详细信息,请参阅 COMDAT 节 (对象仅)
12
2
Number
关联节的节表中基于一个索引。 这在 COMDAT 选择设置为 5 时使用。
14
1
选择
COMDAT 选择编号。 如果分区是 COMDAT 部分,则此部分适用。
15
3
未使用

 

仅 COMDAT 节 (对象)

如果节是 COMDAT 节,则节定义辅助格式的“选择”字段适用。 COMDAT 节是由多个对象文件定义的节。 (在节标头的“节标志”字段中设置标志IMAGE_SCN_LNK_COMDAT。) “选择”字段确定链接器解析 COMDAT 节的多个定义的方式。

具有 COMDAT 节的节值的第一个符号必须是节符号。 此符号具有节的名称、Value 字段等于零、相关 COMDAT 节的节号、类型字段等于IMAGE_SYM_TYPE_NULL、类字段等于IMAGE_SYM_CLASS_STATIC和一条辅助记录。 第二个符号称为“COMDAT 符号”,链接器与“选择”字段结合使用。

“选择”字段的值如下所示。

返回的常量 Value 说明
IMAGE_COMDAT_SELECT_NODUPLICATES
1
如果已定义此符号,则链接器会发出“乘积符号”错误。
IMAGE_COMDAT_SELECT_ANY
2
定义同一 COMDAT 符号的任何节都可以链接;将删除其余部分。
IMAGE_COMDAT_SELECT_SAME_SIZE
3
链接器在此符号的定义中选择任意部分。 如果所有定义的大小都不同,则会发出“乘数定义的符号”错误。
IMAGE_COMDAT_SELECT_EXACT_MATCH
4
链接器在此符号的定义中选择任意部分。 如果所有定义不完全匹配,则会发出“乘数定义的符号”错误。
IMAGE_COMDAT_SELECT_ASSOCIATIVE
5
如果链接了某些其他 COMDAT 节,则此节会链接。 此其他节由分区定义的辅助符号记录的“数字”字段指示。 此设置对于具有多个部分中的组件的定义非常有用,例如,一 (个部分中的代码和另一个) 中的数据,但必须链接或丢弃所有组件作为一个集。 本部分关联的另一部分必须是 COMDAT 节,可以是另一个关联 COMDAT 节。 关联 COMDAT 节的节关联链无法形成循环。 节关联链最终必须指向未设置IMAGE_COMDAT_SELECT_ASSOCIATIVE的 COMDAT 节。
IMAGE_COMDAT_SELECT_LARGEST
6
链接器从此符号的所有定义中选择最大定义。 如果多个定义具有此大小,则它们之间的选择是任意的。

 

仅) 对象 (CLR 令牌定义

此辅助符号通常遵循IMAGE_SYM_CLASS_CLR_TOKEN。 它用于将令牌与 COFF 符号表的命名空间相关联。

Offset 大小 字段 说明
0
1
bAuxType
必须IMAGE_AUX_SYMBOL_TYPE_TOKEN_DEF (1) 。
1
1
bReserved
保留,必须为零。
2
4
SymbolTableIndex
此 CLR 令牌定义所引用的 COFF 符号的符号索引。
6
12
保留,必须为零。

 

COFF 字符串表

紧跟 COFF 符号表的是 COFF 字符串表。 可以通过在 COFF 标头中获取符号表地址并添加符号数乘以符号的大小来找到此表的位置。

COFF 字符串表的开头是 4 个字节,其中包含字符串表其余部分的总大小 (字节) 。 此大小包括大小字段本身,因此如果不存在字符串,则此位置中的值将为 4。

大小之后是 COFF 符号表中的符号指向的以 null 结尾的字符串。

仅) 属性证书表 (映像

可以通过添加属性证书表来与映像关联属性证书。 属性证书表由一组连续、四字对齐的属性证书条目组成。 在文件的原始末尾和属性证书表的开头之间插入零填充,以实现此对齐。 每个属性证书条目包含以下字段。

Offset 大小 字段 说明
0
4
dwLength
指定属性证书条目的长度。
4
2
wRevision
包含证书版本号。 有关详细信息,请参阅以下文本。
6
2
wCertificateType
指定 bCertificate 中内容类型。 有关详细信息,请参阅以下文本。
8
请参阅以下资源
bCertificate
包含证书,例如验证码签名。 有关详细信息,请参阅以下文本。

 

可选标头数据目录中证书表条目中的虚拟地址值是第一个属性证书条目的文件偏移量。 后续条目通过从当前属性证书条目开头向上舍入到 8 字节倍数的 dwLength 字节来访问。 这一点一直持续到舍入 dwLength 值之和等于可选标头数据目录中“证书表”条目中的 Size 值。 如果舍入 dwLength 值的总和不等于 Size 值,则属性证书表或 Size 字段已损坏。

例如,如果可选标头数据目录的证书表项包含:

C++
virtual address = 0x5000
size = 0x1000

第一个证书从磁盘上文件的开头偏移量0x5000开始。 若要推进所有属性证书条目,请执行以下操作:

  1. 将第一个属性证书的 dwLength 值添加到起始偏移量。
  2. 将步骤 1 中的值向上舍入到最接近的 8 字节倍数,以查找第二个属性证书条目的偏移量。
  3. 将偏移值从步骤 2 添加到第二个属性证书条目的 dwLength 值,并向上舍入到最接近的 8 字节倍数,以确定第三个属性证书条目的偏移量。
  4. 对每个连续证书重复步骤 3,直到计算偏移量等于0x6000 (0x5000开始 + 0x1000总大小) ,这表示已遍历整个表。

或者,可以通过在循环中调用 Win32 ImageEnumerateCertificates 函数来枚举证书条目。 有关函数引用页的链接,请参阅 “引用”。

只要条目具有正确的 dwLength 值、唯一 wRevision 值和唯一 wCertificateType 值,属性证书表条目就可以包含任何证书类型。 最常见的证书表条目类型是一种WIN_CERTIFICATE结构,该结构记录在 Wintrust.h 中,在本部分的其余部分进行了讨论。

WIN_CERTIFICATE wRevision 成员的选项包括 (,但不限于) 以下内容。

名称 说明
0x0100
WIN_CERT_REVISION_1_0
版本 1,Win_Certificate结构的旧版本。 仅出于验证旧验证码签名的目的,才支持它
0x0200
WIN_CERT_REVISION_2_0
版本 2 是Win_Certificate结构的当前版本。

 

WIN_CERTIFICATE wCertificateType 成员的选项包括 (,但不限于) 下表中的项。 请注意,当前不支持某些值。

名称 说明
0x0001
WIN_CERT_TYPE_X509
bCertificate 包含 X.509 证书
不支持
0x0002
WIN_CERT_TYPE_PKCS_SIGNED_DATA
bCertificate 包含 PKCS#7 SignedData 结构
0x0003
WIN_CERT_TYPE_RESERVED_1
保留
0x0004
WIN_CERT_TYPE_TS_STACK_SIGNED
终端服务器协议堆栈证书签名
不支持

 

WIN_CERTIFICATE结构的 bCertificate 成员包含具有 wCertificateType 指定的内容类型的可变长度字节数组。 Authenticode 支持的类型为 PKCS#7 SignedData 结构WIN_CERT_TYPE_PKCS_SIGNED_DATA。 有关 Authenticode 数字签名格式的详细信息,请参阅 Windows Authenticode 可移植可执行签名格式

如果 bCertificate 内容不以四字边界结尾,则从 bCertificate 的末尾到下一个四字边界,属性证书条目用零填充。

dwLength 值是最终WIN_CERTIFICATE结构的长度,并计算为:

dwLength = offsetof(WIN_CERTIFICATE, bCertificate) + (size of the variable-length binary array contained within bCertificate)

此长度应包括用于满足每个WIN_CERTIFICATE结构的四字对齐要求的任何填充的大小:

dwLength += (8 - (dwLength & 7)) & 7;

可选标头数据目录“证书表”条目中指定的“证书表”大小 (仅图像) - 包括填充。

有关使用 ImageHlp API 枚举、添加和删除 PE 文件中的证书的详细信息,请参阅 ImageHlp Functions

证书数据

如上一部分所述,属性证书表中的证书可以包含任何证书类型。 确保 PE 文件完整性的证书可能包括 PE 映像哈希。

PE 图像哈希 (或文件哈希) 类似于文件校验和,该哈希算法生成与文件完整性相关的消息摘要。 但是,校验和是由简单的算法生成的,主要用于检测磁盘上的内存块是否已损坏,并且存储的值已损坏。 文件哈希类似于校验和,因为它还会检测文件损坏。 但是,与大多数校验和算法不同,无需更改文件哈希,就很难修改文件,而不更改其原始未修改的值。 因此,文件哈希可用于检测对文件的有意甚至微妙的修改,例如病毒、黑客或特洛伊木马程序引入的文件。

在证书中包含时,映像摘要必须排除 PE 映像中的某些字段,例如可选标头数据目录中的校验和和证书表条目。 这是因为添加证书的行为会更改这些字段,并会导致计算不同的哈希值。

Win32 ImageGetDigestStream 函数提供目标 PE 文件中用于哈希函数的数据流。 向 PE 文件添加或删除证书时,此数据流保持一致。 根据传递给 ImageGetDigestStream 的参数,可以从哈希计算中省略 PE 映像中的其他数据。 有关函数引用页的链接,请参阅 “引用”。

Delay-Load仅) 导入表 (映像

这些表已添加到映像中,以支持应用程序在首次调用该 DLL 之前延迟加载 DLL 的统一机制。 表的布局与第 6.4 节. idata 节中所述的传统导入表的布局匹配。此处只讨论一些详细信息。

Delay-Load目录表

延迟加载目录表与导入目录表对应。 可以通过可选标头数据目录列表中的延迟导入描述符条目来检索它, (偏移量 200) 。 表按如下所示排列:

Offset 大小 字段 说明
0
4
特性
必须为零。
4
4
名称
要加载的 DLL 名称的 RVA。 该名称驻留在映像的只读数据部分中。
8
4
模块句柄
模块的 RVA 在要延迟加载的 DLL 的映像) 的数据部分中 (。 它由提供的例程用于存储,用于管理延迟加载。
12
4
延迟导入地址表
延迟加载导入地址表的 RVA。 有关详细信息,请参阅 延迟导入地址表 (IAT)
16
4
延迟导入名称表
延迟加载名称表的 RVA,其中包含可能需要加载的导入的名称。 这与导入名称表的布局匹配。 有关详细信息,请参阅 提示/名称表
20
4
绑定延迟导入表
绑定延迟加载地址表的 RVA(如果存在)。
24
4
卸载延迟导入表
卸载延迟加载地址表的 RVA(如果存在)。 这是延迟导入地址表的确切副本。 如果调用方卸载 DLL,则应将此表复制回延迟导入地址表,以便对 DLL 的后续调用继续正确使用指纹机制。
28
4
时间戳
此映像已绑定到的 DLL 的时间戳。

 

此数据结构中引用的表组织并排序,就像其对应项用于传统导入一样。 有关详细信息,请参阅 .idata 节

特性

但尚未定义任何属性标志。 链接器将此字段设置为图像中的零。 此字段可用于通过指示新字段的存在来扩展记录,或者可用于指示延迟或卸载帮助程序函数的行为。

名称

要延迟加载的 DLL 的名称驻留在图像的只读数据部分中。 它通过 szName 字段引用。

模块句柄

要延迟加载的 DLL 的句柄位于图像的数据部分。 phmod 字段指向句柄。 提供的延迟加载帮助程序使用此位置将句柄存储到加载的 DLL。

延迟导入地址表

延迟导入地址表 (IAT) 由延迟导入描述符通过 pIAT 字段引用。 延迟加载帮助程序使用实际入口点更新这些指针,以便 thunk 不再位于调用循环中。 使用表达式 pINT->u1.Function访问函数指针。

延迟导入名称表

延迟导入名称表 (INT) 包含可能需要加载的导入的名称。 它们按与 IAT 中的函数指针相同的方式排序。 它们包含与标准 INT 相同的结构,并使用表达式 pINT->u1.AddressOfData->Name[0]进行访问。

延迟绑定导入地址表和时间戳

延迟绑定导入地址表 (BIAT) 是IMAGE_THUNK_DATA项的可选表,该表由帖子进程绑定阶段与延迟加载目录表的时间戳字段一起使用。

延迟卸载导入地址表

延迟卸载导入地址表 (UIAT) 是卸载代码用于处理显式卸载请求的可选IMAGE_THUNK_DATA项表。 它由只读部分中的初始化数据组成,它是将代码引用到延迟加载 thunk 的原始 IAT 的确切副本。 在卸载请求中,可以释放库、清除 *phmod 以及通过 IAT 编写的 UIAT,以将所有内容还原到其预加载状态。

特殊部分

典型的 COFF 节包含链接器和 Microsoft Win32 加载程序处理的代码或数据,而不知道节内容。 内容仅与正在链接或执行的应用程序相关。

但是,某些 COFF 部分在对象文件或图像文件中找到时具有特殊含义。 工具和加载程序会识别这些节,因为它们在节标头中设置了特殊标志,因为图像中的特殊位置可选标头指向它们,或者节名称本身表示节的特殊函数。 (即使节名称本身未指示节的特殊功能,节名称也由约定决定,因此此规范的作者可以在所有情况下引用节名称。)

下表描述了保留节及其属性,后面是保存到可执行文件中的节类型的详细说明,以及包含扩展元数据的节类型。

部分名称 内容 特征
.bss
未初始化的数据 (免费格式)
IMAGE_SCN_CNT_UNINITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_WRITE
.cormeta
指示对象文件包含托管代码的 CLR 元数据
IMAGE_SCN_LNK_INFO
.data
初始化的数据 (免费格式)
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_WRITE
.debug$F
仅生成 FPO 调试信息 (对象,仅 x86 体系结构,现在已过时)
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_DISCARDABLE
.debug$P
仅) (对象的预编译调试类型
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_DISCARDABLE
.debug$S
仅) 调试符号 (对象
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_DISCARDABLE
.debug$T
仅) 调试对象 (类型
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_DISCARDABLE
.drective
链接器选项
IMAGE_SCN_LNK_INFO
.edata
导出表
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ
.idata
导入表
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_WRITE
.idlsym
仅包括已注册的 SEH (映像,) 支持 IDL 属性。 有关信息,请参阅本主题末尾的 引用 中的“IDL 属性”。
IMAGE_SCN_LNK_INFO
.pdata
异常信息
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ
.rdata
只读初始化的数据
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ
.reloc
图像重定位
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_DISCARDABLE
.rsrc
资源目录
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ
.sbss
GP 相对未初始化的数据 (免费格式)
IMAGE_SCN_CNT_UNINITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_WRITE |IMAGE _SCN_GPREL 应仅为 IA64 体系结构设置IMAGE_SCN_GPREL标志;此标志对于其他体系结构无效。 IMAGE_SCN_GPREL标志仅适用于对象文件;当此节类型出现在图像文件中时,不得设置IMAGE_SCN_GPREL标志。
.sdata
GP 相对初始化的数据 (免费格式)
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_WRITE |IMAGE _SCN_GPREL 应仅为 IA64 体系结构设置IMAGE_SCN_GPREL标志;此标志对于其他体系结构无效。 IMAGE_SCN_GPREL标志仅适用于对象文件;当此节类型出现在图像文件中时,不得设置IMAGE_SCN_GPREL标志。
.srdata
GP 相对只读数据 (免费格式)
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE _SCN_GPREL 应仅为 IA64 体系结构设置IMAGE_SCN_GPREL标志;此标志对于其他体系结构无效。 IMAGE_SCN_GPREL标志仅适用于对象文件;当此节类型出现在图像文件中时,不得设置IMAGE_SCN_GPREL标志。
.sxdata
已注册的异常处理程序数据 (免费格式和仅 x86/object)
IMAGE_SCN_LNK_INFO包含该对象文件中代码所引用的每个异常处理程序的符号索引。 该符号可以是 UNDEF 符号,也可以是该模块中定义的符号。
.text
可执行代码 (免费格式)
IMAGE_SCN_CNT_CODE |IMAGE_SCN_MEM_EXECUTE |IIMAGE_SCN_MEM_READ
.tls
仅) 线程本地存储 (对象
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_WRITE
.tls$
仅) 线程本地存储 (对象
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_WRITE
.vsdata
GP 相对初始化的数据 (免费格式,仅适用于 ARM、SH4 和 Thumb 体系结构)
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ |IMAGE_SCN_MEM_WRITE
.xdata
异常信息 (免费格式)
IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_MEM_READ

 

此处列出的部分标记为“仅对象”或“仅图像”,以指示其特殊语义仅与对象文件或图像文件相关。 标记为“仅图像”的分区可能仍以进入图像文件的方式出现在对象文件中,但该部分对链接器没有特殊含义,而只对图像文件加载程序。

.debug 节

.debug 节在对象文件中用于包含编译器生成的调试信息和图像文件中,以包含生成的所有调试信息。 本部分介绍在对象和图像文件中打包调试信息。

下一部分介绍调试目录的格式,该目录可以是映像中的任意位置。 后续部分介绍包含调试信息的对象文件中的“组”。

链接器的默认是调试信息不会映射到图像的地址空间。 仅当调试信息在地址空间中映射时,才存在 .debug 节。

仅调试目录 (映像)

图像文件包含可选调试目录,指示存在何种形式的调试信息及其所在位置。 此目录由一组调试目录条目组成,其位置和大小在图像可选标头中指示。

调试目录可以位于可丢弃的 .debug 节 (如果存在) ,也可以包含在映像文件的任何其他部分,或者根本不包含在分区中。

每个调试目录条目标识调试信息块的位置和大小。 如果节标头 (未涵盖调试信息,则指定的 RVA 可以为零,即它驻留在映像文件中,并且不会映射到运行时地址空间) 。 如果映射它,则 RVA 是其地址。

调试目录条目具有以下格式:

Offset 大小 字段 说明
0
4
特征
保留,必须为零。
4
4
TimeDateStamp
创建调试数据的时间和日期。
8
2
MajorVersion
调试数据格式的主要版本号。
10
2
MinorVersion
调试数据格式的次要版本号。
12
4
类型
调试信息的格式。 此字段支持多个调试器。 有关详细信息,请参阅 调试类型
16
4
SizeOfData
调试数据的大小 (不包括调试目录本身) 。
20
4
AddressOfRawData
加载时调试数据的地址,相对于映像基。
24
4
PointerToRawData
指向调试数据的文件指针。

 

调试类型

为调试目录条目的 Type 字段定义了以下值:

返回的常量 Value 说明
IMAGE_DEBUG_TYPE_UNKNOWN
0
所有工具忽略的未知值。
IMAGE_DEBUG_TYPE_COFF
1
COFF 调试信息(行号、符号表和字符串表)。 此类调试信息也由文件标头中的字段指向。
IMAGE_DEBUG_TYPE_CODEVIEW
2
Visual C++ 调试信息。
IMAGE_DEBUG_TYPE_FPO
3
帧指针遗漏 (FPO) 信息。 此信息告知调试器如何解释非标准堆栈帧,该帧使用 EBP 寄存器作为帧指针以外的用途。
IMAGE_DEBUG_TYPE_MISC
4
DBG 文件的位置。
IMAGE_DEBUG_TYPE_EXCEPTION
5
.pdata 节的副本。
IMAGE_DEBUG_TYPE_FIXUP
6
保留。
IMAGE_DEBUG_TYPE_OMAP_TO_SRC
7
从映像中的 RVA 到源映像中的 RVA 的映射。
IMAGE_DEBUG_TYPE_OMAP_FROM_SRC
8
从源映像中的 RVA 映射到映像中的 RVA。
IMAGE_DEBUG_TYPE_BORLAND
9
保留为博兰。
IMAGE_DEBUG_TYPE_RESERVED10
10
保留。
IMAGE_DEBUG_TYPE_CLSID
11
保留。
IMAGE_DEBUG_TYPE_REPRO
16
PE 确定性或可重现性。
IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS 20 扩展 DLL 特征位。

 

如果 Type 字段设置为IMAGE_DEBUG_TYPE_FPO,则调试原始数据是一个数组,其中每个成员描述函数的堆栈帧。 映像文件中的每一个函数都必须为其定义 FPO 信息,即使调试类型为 FPO 也是如此。 假定没有 FPO 信息的函数具有普通堆栈帧。 FPO 信息的格式如下所示:

C++
#define FRAME_FPO   0               
#define FRAME_TRAP  1
#define FRAME_TSS   2
               
typedef struct _FPO_DATA {
    DWORD       ulOffStart;            // offset 1st byte of function code
    DWORD       cbProcSize;            // # bytes in function
    DWORD       cdwLocals;             // # bytes in locals/4
    WORD        cdwParams;             // # bytes in params/4
    WORD        cbProlog : 8;          // # bytes in prolog
    WORD        cbRegs   : 3;          // # regs saved
    WORD        fHasSEH  : 1;          // TRUE if SEH in func
    WORD        fUseBP   : 1;          // TRUE if EBP has been allocated
    WORD        reserved : 1;          // reserved for future use
    WORD        cbFrame  : 2;          // frame type
} FPO_DATA;

IMAGE_DEBUG_TYPE_REPRO类型的条目的存在表明 PE 文件是构建的,用于实现确定性或可重现性。 如果输入未更改,则无论何时或何处生成 PE,输出 PE 文件都保证位到位相同。 PE 文件中的各种日期/时间戳字段都填充了使用 PE 文件内容作为输入的计算哈希值的一部分或所有位,因此不再表示生成 PE 文件或 PE 中相关特定数据的实际日期和时间。 此调试条目的原始数据可能为空,或者可能包含一个计算哈希值,前面有一个表示哈希值长度的四字节值。

如果类型字段设置为IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS,则调试原始数据包含扩展的 DLL 特征位,除了可以在图像的可选标头中设置的字段之外。 请参阅可选标头Windows-Specific字段 (图像) 部分中的 DLL 特征

扩展 DLL 特征

为扩展 DLL 特征位定义了以下值。

返回的常量 Value 说明
IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT 0x0001 映像与 CET 兼容。

仅) .debug$F (对象

本节中的数据已被 Visual C++ 版本 7.0 及更高版本取代,该数据集已发出到 .debug$S 子节中。

对象文件可以包含 .debug$F 节,其内容是一个或多个FPO_DATA记录, (帧指针遗漏信息) 。 请参阅 调试类型中的“IMAGE_DEBUG_TYPE_FPO”。

链接器可识别这些 .debug$F 记录。 如果正在生成调试信息,链接器会按过程 RVA 对FPO_DATA记录进行排序,并为其生成调试目录条目。

编译器不应为具有标准帧格式的过程生成 FPO 记录。

仅 .debug$S (对象)

本部分包含 Visual C++ 调试信息 (符号信息) 。

仅限 .debug$P (对象)

本部分包含 visual C++ 调试信息 (预编译信息) 。 这些是使用此对象生成的预编译标头编译的所有对象的共享类型。

仅 .debug$T (对象)

本部分包含 Visual C++ 调试信息 (类型信息) 。

链接器对 Microsoft 调试信息的支持

若要支持调试信息,链接器:

  • .debug$Fdebug$S.debug$P.debug$T 部分收集所有相关调试数据。

  • 处理该数据以及链接器生成的调试信息到 PDB 文件中,并创建一个调试目录条目来引用它。

仅限 .drectve Section (对象)

如果节在节标头中设置了IMAGE_SCN_LNK_INFO标志,并且具有 .drectve 节名称,则节是指令节。 链接器在处理信息后删除 .drectve 节,因此该节不会显示在正在链接的图像文件中。

.drectve 节由可编码为 ANSI 或 UTF-8 的文本字符串组成。 如果 UTF-8 字节顺序标记 (BOM,则由 0xEF、0xBB 和 0xBF) 组成的三字节前缀解释为 ANSI。 指令字符串是由空格分隔的一系列链接器选项。 每个选项都包含连字符、选项名称和任何适当的属性。 如果某个选项包含空格,则必须用引号括住该选项。 .drectve 节不得具有重定位或行号。

仅限 .edata 节 (映像)

名为 .edata 的导出数据部分包含有关其他图像可通过动态链接访问的符号的信息。 导出的符号通常位于 DLL 中,但 DLL 也可以导入符号。

下面概述了导出部分的一般结构。 描述的表通常以 (所示的顺序在文件中连续,但这不是) 所必需的。 只有导出目录表和导出地址表才能将符号导出为序号。 (序号是直接通过其导出地址表索引进行访问的导出。) 名称指针表、序号表和导出名称表都存在以支持使用导出名称。

表名称 说明
导出目录表
只有一行的表 (与调试目录) 不同。 此表指示其他导出表的位置和大小。
导出地址表
导出符号的 RVA 数组。 这些是可执行代码和数据部分中导出的函数和数据的实际地址。 其他图像文件可以使用索引将此表 (序号) ,或者(可选)通过使用与序号相对应的公共名称(如果定义了公共名称)来导入符号。
名称指针表
指向公共导出名称的指针数组,按升序排序。
序号表
对应于名称指针表成员的序号数组。 通信按位置排列:因此,名称指针表和序号表必须具有相同的成员数。 每个序号是导出地址表中的索引。
导出名称表
一系列以 null 结尾的 ASCII 字符串。 名称指针表指向此区域的成员。 这些名称是导入和导出符号的公共名称;它们不一定与映像文件中使用的专用名称相同。

 

当另一个图像文件按名称导入符号时,Win32 加载程序会在名称指针表中搜索匹配字符串。 如果找到匹配的字符串,则通过查找序号表中的相应成员来标识关联的序号 (,即与名称指针表中找到的字符串指针具有相同索引的序号表的成员) 。 生成的序号是导出地址表中的索引,它提供所需符号的实际位置。 每个导出符号都可以按序号访问。

当另一个图像文件按序号导入符号时,无需搜索名称指针表以查找匹配字符串。 因此,直接使用序号更高效。 但是,导出名称更易于记住,并且不需要用户知道符号的表索引。

导出目录表

导出符号信息以导出目录表开头,该表描述导出符号信息的其余部分。 导出目录表包含用于解析此映像中入口点的导入的地址信息。

Offset 大小 字段 说明
0
4
导出标志
保留,必须为 0。
4
4
Time/Date Stamp
创建导出数据的时间和日期。
8
2
主要版本
主版本号。 主要版本号和次要版本号可由用户设置。
10
2
次要版本
次版本号。
12
4
名称 RVA
包含 DLL 名称的 ASCII 字符串的地址。 此地址相对于映像基。
16
4
序号基数
此映像中导出的起始序号。 此字段指定导出地址表的起始序号。 它通常设置为 1。
20
4
地址表条目
导出地址表中的条目数。
24
4
名称指针数
名称指针表中的条目数。 这也是序号表中的条目数。
28
4
导出地址表 RVA
相对于映像基的导出地址表的地址。
32
4
名称指针 RVA
相对于图像基的导出名称指针表的地址。 表大小由“名称指针数”字段提供。
36
4
序号表 RVA
序号表的地址,相对于映像基。

 

导出地址表

导出地址表包含导出入口点的地址和导出的数据和绝对值。 序号用作导出地址表中的索引。

导出地址表中的每个条目都是使用下表中两种格式之一的字段。 如果指定的地址不在导出节中, (由可选标头) 中指示的地址和长度定义,则字段是导出 RVA,这是代码或数据中的实际地址。 否则,该字段是一个转发器 RVA,它为另一个 DLL 中的符号命名。

Offset 大小 字段 说明
0
4
导出 RVA
加载到内存中的已导出符号的地址,相对于映像基。 例如,导出函数的地址。
0
4
转发器 RVA
指向导出节中以 null 结尾的 ASCII 字符串的指针。 此字符串必须位于导出表数据目录条目提供的范围内。 请参阅 仅) (映像的可选标头数据目录 。 此字符串提供 DLL 名称和导出 (的名称,例如“MYDLL.expfunc”) 或 DLL 名称和导出 (的序号,例如“MYDLL.#27”) 。

 

转发器 RVA 从一些其他映像导出定义,使其看起来就像当前映像正在导出一样。 因此,符号同时导入和导出。

例如,在 Windows XP 中的Kernel32.dll中,名为“HeapAlloc”的导出将转发到字符串“NTDLL”。RtlAllocateHeap。”这样,应用程序就可以使用特定于 XP 的Windows模块Ntdll.dll,而无需实际包含对其的导入引用。 应用程序的导入表仅引用Kernel32.dll。 因此,应用程序不特定于 Windows XP,可以在任何 Win32 系统上运行。

导出名称指针表

导出名称指针表是导出名称表中 (RVA) 地址数组。 每个指针为 32 位,相对于图像基础。 指针按词法排序,以允许二进制搜索。

仅当导出名称指针表包含指向它的指针时,才定义导出名称。

导出序号表

导出序号表是导出地址表中 16 位无偏差索引的数组。 序号因导出目录表的序号基字段而有偏差。 换句话说,必须从序号中减去序号,才能将真正的索引导出到导出地址表中。

导出名称指针表和导出序号表形成两个并行数组,以允许自然字段对齐。 这两个表实际上充当一个表,其中“导出名称指针”列指向) 名称导出的公共 (,“导出序号”列为该公共名称提供相应的序号。 导出名称指针表的成员和导出序号表的成员通过在其各自的数组中具有相同的位置 (索引) 关联。

因此,当搜索导出名称指针表并在位置 i 找到匹配字符串时,用于查找符号的 RVA 和偏置序号的算法为:

C++
i = Search_ExportNamePointerTable (name);
ordinal = ExportOrdinalTable [i];

rva = ExportAddressTable [ordinal];
biased_ordinal = ordinal + OrdinalBase;

通过 (偏差) 序号搜索符号时,用于查找符号的 RVA 和名称的算法为:

C++
ordinal = biased_ordinal - OrdinalBase;
i = Search_ExportOrdinalTable (ordinal);

rva = ExportAddressTable [ordinal];
name = ExportNameTable [i];

导出名称表

导出名称表包含导出名称指针表指向的实际字符串数据。 此表中的字符串是其他图像可用于导入符号的公共名称。 这些公共导出名称不一定与符号在其自己的映像文件和源代码中具有的专用符号名称相同,尽管可以。

每个导出的符号都有一个序号值,该值只是导出地址表中的索引。 但是,使用导出名称是可选的。 某些、全部或未导出的符号可以具有导出名称。 对于具有导出名称的导出符号,导出名称指针表中的相应条目和导出序号表协同工作,以便将每个名称与序号相关联。

导出名称表的结构是一系列以 null 结尾的 ASCII 字符串,长度可变。

.idata 节

导入符号的所有图像文件(包括几乎所有可执行 (EXE) 文件)都具有 .idata 节。 导入信息的典型文件布局如下:

  • 目录表

    Null 目录条目

  • DLL1 导入查阅表格

    Null

  • DLL2 导入查找表

    Null

  • DLL3 导入查找表

    Null

  • Hint-Name表

导入目录表

导入信息以导入目录表开头,其中描述了导入信息的其余部分。 导入目录表包含用于解析对 DLL 映像中入口点的修复引用的地址信息。 导入目录表由一组导入目录条目组成,每个映像引用的 DLL 都有一个条目。 最后一个目录条目为空 (用 null 值填充) ,指示目录表的末尾。

每个导入目录条目采用以下格式:

Offset 大小 字段 说明
0
4
导入查阅表 RVA (特征)
导入查找表的 RVA。 此表包含每个导入的名称或序号。 (Winnt.h 中使用名称“特征”,但不再描述此字段。)
4
4
Time/Date Stamp
在绑定映像之前设置为零的标记。 绑定映像后,此字段将设置为 DLL 的时间/数据戳。
8
4
转发器链
第一个转发器引用的索引。
12
4
名称 RVA
包含 DLL 名称的 ASCII 字符串的地址。 此地址相对于映像基。
16
4
导入地址表 RVA (Thunk 表)
导入地址表的 RVA。 此表的内容与导入查找表的内容相同,直到绑定图像。

 

导入查阅表格

导入查找表是 PE32 的 32 位数字数组,或 PE32+ 的 64 位数字数组。 每个条目都使用下表中所述的位字段格式。 在此格式中,位 31 是 PE32 的最大有效位,位 63 是 PE32+ 的最大有效位。 这些条目的集合描述了从给定 DLL 导入的所有导入。 最后一个条目设置为零 (NULL) 以指示表末尾。

大小 位域 说明
31/63
1
序号/名称标志
如果设置了此位,则按序号导入。 否则,按名称导入。 将位屏蔽为 PE32 的 0x80000000,0x8000000000000000 PE32+。
15-0
16
序号编号
16 位序号。 仅当序号/名称标志位字段为 1 (按序号) 导入时,才使用此字段。 位 30-15 或 62-15 必须是 0。
30-0
31
提示/名称表 RVA
提示/名称表项的 31 位 RVA。 仅当序号/名称标志位字段为 0 (按名称) 导入时,才使用此字段。 对于 PE32+ 位,62-31 必须为零。

 

提示/名称表

整个导入节的一个提示/名称表足以满足要求。 提示/名称表中的每个条目具有以下格式:

Offset 大小 字段 说明
0
2
提示
导出名称指针表的索引。 首先尝试使用此值进行匹配。 如果失败,则会对 DLL 的导出名称指针表执行二进制搜索。
2
可变
名称
包含要导入的名称的 ASCII 字符串。 这是必须与 DLL 中的公共名称匹配的字符串。 此字符串区分大小写,并由 null 字节终止。
*
0 或 1
Pad
尾随零垫字节出现在尾随 null 字节之后(如有必要)以在偶数边界上对齐下一个条目。

 

导入地址表

导入地址表的结构和内容与导入查找表的结构和内容相同,直到文件绑定为止。 在绑定期间,导入地址表中的条目被 PE32) 的 32 位 (覆盖,对于要导入的符号的符号) 地址,将覆盖 64 位 (。 这些地址是符号的实际内存地址,尽管从技术上讲,它们仍称为“虚拟地址”。加载程序通常处理绑定。

.pdata 节

.pdata 节包含用于异常处理的函数表项数组。 它由图像数据目录中的异常表项指向。 必须根据函数地址对条目进行排序, (每个结构中的第一个字段) ,然后再发出到最终图像中。 目标平台确定使用以下三个函数表条目格式变体中的哪一种。

对于 32 位 MIPS 图像,函数表条目的格式如下:

Offset 大小 字段 说明
0
4
开始地址
相应函数的 VA。
4
4
结束地址
函数末尾的 VA。
8
4
异常处理程序
指向要执行的异常处理程序的指针。
12
4
处理程序数据
指向要传递给处理程序的其他信息的指针。
16
4
Prolog结束地址
函数 prolog 末尾的 VA。

 

对于 ARM、PowerPC、SH3 和 SH4 Windows CE 平台,函数表条目的格式如下:

Offset 大小 字段 说明
0
4
开始地址
相应函数的 VA。
4
8 位
Prolog长度
函数 prolog 中的指令数。
4
22 位
函数长度
函数中的指令数。
4
1 位
32 位标志
如果设置,则函数包含 32 位指令。 如果明确,该函数包含 16 位指令。
4
1 位
异常标志
如果已设置,则函数存在异常处理程序。 否则,不存在异常处理程序。

 

对于 x64 和 Itanium 平台,函数表条目的格式如下:

Offset 大小 字段 说明
0
4
开始地址
相应函数的 RVA。
4
4
结束地址
函数末尾的 RVA。
8
4
展开信息
展开信息的 RVA。

 

仅) .reloc Section (映像

基本重定位表包含映像中所有基本重定位的条目。 可选标头数据目录中的“基本重定位表”字段提供基重定位表中的字节数。 有关详细信息,请参阅 仅) (映像的可选标头数据目录 。 基本重定位表分为块。 每个块表示 4K 页的基本重定位。 每个块必须在 32 位边界上启动。

加载程序不需要处理链接器解析的基本重定位,除非无法在 PE 标头中指定的映像基础加载映像。

基本重定位块

每个基本重定位块都以以下结构开头:

Offset 大小 字段 说明
0
4
页面 RVA
图像基础加上页面 RVA 将添加到每个偏移量,以创建必须应用基础重定位的 VA。
4
4
块大小
基重定位块中的字节总数,包括“页 RVA”和“块大小”字段以及后面的“类型/偏移量”字段。

 

然后,“块大小”字段后跟任意数量的“类型”或“偏移量”字段条目。 每个条目是 WORD (2 个字节) ,具有以下结构:

Offset 大小 字段 说明
0
4 位
类型
存储在 WORD 的高 4 位中,该值指示要应用的基重定位类型。 有关详细信息,请参阅 基本重定位类型
0
12 位
Offset
存储在 WORD 的剩余 12 位中,与块的 Page RVA 字段中指定的起始地址偏移量。 此偏移量指定应用基重定位的位置。

 

若要应用基本重定位,将计算首选基址与实际加载图像的基址之间的差异。 如果图像加载在其首选基础处,则差异为零,因此无需应用基本重定位。

基本重定位类型

返回的常量 Value 说明
IMAGE_REL_BASED_ABSOLUTE
0
跳过基本重定位。 此类型可用于填充块。
IMAGE_REL_BASED_HIGH
1
基重定位将高 16 位的差值添加到偏移量为 16 位字段。 16 位字段表示 32 位单词的高值。
IMAGE_REL_BASED_LOW
2
基本重定位将差的低 16 位添加到偏移量的 16 位字段。 16 位字段表示 32 位单词的低一半。
IMAGE_REL_BASED_HIGHLOW
3
基本重定位将所有 32 位差异应用于偏移量的 32 位字段。
IMAGE_REL_BASED_HIGHADJ
4
基重定位将高 16 位的差值添加到偏移量为 16 位字段。 16 位字段表示 32 位单词的高值。 32 位值的低 16 位存储在此基础重定位后的 16 位单词中。 这意味着此基础搬迁占用两个槽。
IMAGE_REL_BASED_MIPS_JMPADDR
5
重定位解释依赖于计算机类型。
当计算机类型为 MIPS 时,基础重定位适用于 MIPS 跳转指令。
IMAGE_REL_BASED_ARM_MOV32
5
仅当计算机类型为 ARM 或 Thumb 时,此重定位才有意义。 基本重定位在连续 MOVW/MOVT 指令配对中应用符号的 32 位地址。
IMAGE_REL_BASED_RISCV_HIGH20
5
仅当计算机类型为 RISC-V 时,此重定位才有意义。 基本重定位适用于 32 位绝对地址的高 20 位。
6
保留,必须为零。
IMAGE_REL_BASED_THUMB_MOV32
7
仅当计算机类型为 Thumb 时,此重定位才有意义。 基本重定位将符号的 32 位地址应用于连续 MOVW/MOVT 指令配对。
IMAGE_REL_BASED_RISCV_LOW12I
7
仅当计算机类型为 RISC-V 时,此重定位才有意义。 基本重定位适用于以 RISC-V I-type 指令格式构成的 32 位绝对地址的低 12 位。
IMAGE_REL_BASED_RISCV_LOW12S
8
仅当计算机类型为 RISC-V 时,此重定位才有意义。 基本重定位适用于以 RISC-V S 类型指令格式构成的 32 位绝对地址的低 12 位。
IMAGE_REL_BASED_LOONGARCH32_MARK_LA
8
仅当计算机类型为 LoongArch 32 位时,此重定位才有意义。 基本重定位适用于由两个连续指令构成的 32 位绝对地址。
IMAGE_REL_BASED_LOONGARCH64_MARK_LA
8
仅当计算机类型为 LoongArch 64 位时,此重定位才有意义。 基本重定位适用于由四个连续指令构成的 64 位绝对地址。
IMAGE_REL_BASED_MIPS_JMPADDR16
9
仅当计算机类型为 MIPS 时,重定位才有意义。 基本重定位适用于 MIPS16 跳转指令。
IMAGE_REL_BASED_DIR64
10
基本重定位将差应用于偏移量为 64 位字段。

 

.tls 节

.tls 部分为静态线程本地存储提供直接 PE 和 COFF 支持, (TLS) 。 TLS 是一种特殊的存储类,Windows支持数据对象不是自动 (堆栈) 变量,但对于运行代码的每个线程都是本地的。 因此,每个线程都可以为使用 TLS 声明的变量维护不同的值。

请注意,可以使用 API 调用 TlsAlloc、TlsFree、TlsSetValue 和 TlsGetValue 来支持任意数量的 TLS 数据。 PE 或 COFF 实现是使用 API 的替代方法,具有从高级语言程序员的角度来看更简单的优势。 此实现使 TLS 数据能够定义和初始化,类似于程序中的普通静态变量。 例如,在 Visual C++ 中,静态 TLS 变量可以定义如下,而无需使用 Windows API:

__declspec (thread) int tlsFlag = 1;

为了支持此编程构造,PE 和 COFF .tls 节指定以下信息:初始化数据、每个线程初始化和终止的回调例程以及以下讨论中介绍的 TLS 索引。

备注

静态声明的 TLS 数据对象只能在静态加载的图像文件中使用。 这一事实使得在 DLL 中使用静态 TLS 数据是不可靠的,除非你知道 DLL 或与之静态链接的任何内容永远不会使用 LoadLibrary API 函数动态加载。

 

可执行代码通过以下步骤访问静态 TLS 数据对象:

  1. 在链接时,链接器将设置 TLS 目录的“索引地址”字段。 此字段指向程序预期接收 TLS 索引的位置。

    Microsoft 运行时库通过定义 TLS 目录的内存映像,并为它提供特殊名称“__tls_used” (Intel x86 平台) 或“_tls_used” (其他平台) 来促进此过程。 链接器查找此内存映像,并使用那里的数据创建 TLS 目录。 支持 TLS 并使用 Microsoft 链接器的其他编译器必须使用相同的技术。

  2. 创建线程时,加载程序通过将线程环境块的地址放置在 FS 寄存器 (TEB) 来传达线程的 TLS 数组的地址。 指向 TLS 数组的指针位于 TEB 开头0x2C偏移量。 此行为特定于 Intel x86。

  3. 加载程序将 TLS 索引的值分配给索引地址字段指示的位置。

  4. 可执行代码检索 TLS 索引以及 TLS 数组的位置。

  5. 该代码使用 TLS 索引和 TLS 数组位置 (将索引乘以 4,并将其用作数组的偏移量,) 获取给定程序和模块的 TLS 数据区域的地址。 每个线程都有自己的 TLS 数据区域,但这对程序是透明的,这不需要知道如何为单个线程分配数据。

  6. 作为 TLS 数据区域的一些固定偏移量访问单个 TLS 数据对象。

TLS 数组是系统为每个线程维护的地址数组。 此数组中的每个地址都为给定模块提供 TLS 数据的位置, (EXE 或 DLL) 程序中。 TLS 索引指示要使用的数组成员。 索引是一个对标识模块的系统) 有意义的数字 (。

TLS 目录

TLS 目录具有以下格式:

偏移量 (PE32/PE32+) 大小 (PE32/PE32+) 字段 说明
0
4/8
原始数据"开始"菜单 VA
TLS 模板的起始地址。 模板是用于初始化 TLS 数据的数据块。 系统每次创建线程时都会复制所有这些数据,因此它不得损坏。 请注意,此地址不是 RVA;它是 .reloc 节中应有基本重定位的地址。
4/8
4/8
原始数据结束 VA
TLS 最后一个字节的地址,零填充除外。 与原始数据"开始"菜单 VA 字段一样,这是一个 VA,而不是 RVA。
8/16
4/8
索引地址
接收加载程序分配的 TLS 索引的位置。 此位置位于普通数据部分中,因此可为程序提供可访问的符号名称。
12/24
4/8
回调地址
指向 TLS 回调函数数组的指针。 数组以 null 结尾,因此,如果没有支持回调函数,则此字段指向设置为零的 4 个字节。 有关这些函数的原型的信息,请参阅 TLS 回调函数
16/32
4
零填充的大小
模板的大小(以字节为单位)超出了原始数据"开始"菜单 VA 和原始数据结束 VA 字段分隔的初始化数据。 模板总大小应与映像文件中 TLS 数据的总大小相同。 零填充是初始化的非零数据之后的数据量。
20/36
4
特征
四位 [23:20] 描述对齐信息。 可能的值是定义为IMAGE_SCN_ALIGN_*的值,这些值还用于描述对象文件中节的对齐方式。 其他 28 位保留供将来使用。

 

TLS 回调函数

程序可以提供一个或多个 TLS 回调函数,以支持 TLS 数据对象的其他初始化和终止。 此类回调函数的典型用途是调用对象的构造函数和析构函数。

尽管通常没有多个回调函数,但回调作为数组实现,以便根据需要添加其他回调函数。 如果有多个回调函数,则会按数组中显示其地址的顺序调用每个函数。 null 指针终止数组。 如果空列表 () 不支持回调,那么回调数组正好具有一个成员一个 null 指针,这一点非常有效。

回调函数的原型 (由类型PIMAGE_TLS_CALLBACK) 指针指向的原型与 DLL 入口点函数具有相同的参数:

C++
typedef VOID
(NTAPI *PIMAGE_TLS_CALLBACK) (
    PVOID DllHandle,
    DWORD Reason,
    PVOID Reserved
    );

保留参数应设置为零。 Reason 参数可以采用以下值:

设置 说明
DLL_PROCESS_ATTACH
1
已开始新进程,包括第一个线程。
DLL_THREAD_ATTACH
2
已创建一个新线程。 此通知针对第一个线程发送的所有其他通知。
DLL_THREAD_DETACH
3
线程即将终止。 此通知针对第一个线程发送的所有其他通知。
DLL_PROCESS_DETACH
0
进程即将终止,包括原始线程。

 

仅) 加载配置结构 (映像

(IMAGE_LOAD_CONFIG_DIRECTORY) 负载配置结构以前用于Windows NT操作系统本身中非常有限的情况下,用于描述各种功能太困难或太大,无法描述映像的文件头或可选标头。 Microsoft 链接器的最新版本和Windows XP 及更高版本的Windows将此结构的新版本用于包含保留 SEH 技术的基于 32 位 x86 的系统。 这提供了操作系统在异常调度期间使用的安全结构化异常处理程序的列表。 如果处理程序地址驻留在图像的 VA 范围内,并且标记为保留 SEH 感知 ((即,IMAGE_DLLCHARACTERISTICS_NO_SEH在可选标头的 DllCharacteristics 字段中清晰显示),如前面) 所述,则处理程序必须位于该映像的已知安全处理程序列表中。 否则,操作系统将终止应用程序。 这有助于防止过去用于控制操作系统的“x86 异常处理程序劫持”攻击。

Microsoft 链接器自动提供默认的负载配置结构,以包含保留的 SEH 数据。 如果用户代码已提供负载配置结构,则必须包含新的保留 SEH 字段。 否则,链接器不能包含保留的 SEH 数据,并且图像未标记为包含保留 SEH。

加载配置目录

预留 SEH 负载配置结构的数据目录条目必须指定负载配置结构的特定大小,因为操作系统加载程序始终期望它是特定值。 在这方面,大小实际上只是版本检查。 若要与 Windows XP 和早期版本的 Windows 兼容,对于 x86 映像,大小必须为 64。

加载配置布局

负载配置结构针对 32 位和 64 位 PE 文件具有以下布局:

Offset 大小 字段 说明
0
4
特征
指示文件的属性(当前未使用)的标志。
4
4
TimeDateStamp
日期和时间戳值。 根据系统时钟,该值以自午夜 (00:00:00) 、1970 年 1 月 1 日以来经过的秒数表示。 可以使用 C 运行时 (CRT) 时间函数打印时间戳。
8
2
MajorVersion
主版本号。
10
2
MinorVersion
次版本号。
12
4
GlobalFlagsClear
当加载程序启动进程时,要清除此进程的全局加载程序标志。
16
4
GlobalFlagsSet
当加载程序启动进程时,要为此进程设置的全局加载程序标志。
20
4
CriticalSectionDefaultTimeout
要用于此过程的关键部分的默认超时值。
24
4/8
DeCommitFreeBlockThreshold
在内存返回到系统之前必须释放的内存(以字节为单位)。
28/32
4/8
DeCommitTotalFreeThreshold
可用内存总量(以字节为单位)。
32/40
4/8
LockPrefixTable
[仅 x86]使用 LOCK 前缀的地址列表的 VA,以便可以在单个处理器计算机上将其替换为 NOP。
36/48
4/8
MaximumAllocationSize
最大分配大小(以字节为单位)。
40/56
4/8
VirtualMemoryThreshold
最大虚拟内存大小(以字节为单位)。
44/64
4/8
ProcessAffinityMask
将此字段设置为非零值等效于仅在进程启动过程中使用此值调用 SetProcessAffinityMask (.exe)
48/72
4
ProcessHeapFlags
与 HeapCreate 函数的第一个参数对应的进程堆标志。 这些标志适用于进程启动期间创建的进程堆。
52/76
2
CSDVersion
Service Pack 版本标识符。
54/78
2
保留
必须为零。
56/80
4/8
EditList
保留供系统使用。
60/88
4/8
SecurityCookie
指向 Visual C++ 或 GS 实现使用的 Cookie 的指针。
64/96
4/8
SEHandlerTable
[仅 x86]图像中每个有效唯一标准版处理程序的 RVA 排序表的 VA。
68/104
4/8
SEHandlerCount
[仅 x86]表中唯一处理程序的计数。
72/112
4/8
GuardCFCheckFunctionPointer
存储 Control Flow Guard 检查函数指针的 VA。
76/120
4/8
GuardCFDispatchFunctionPointer
存储 Control Flow Guard 调度函数指针的 VA。
80/128
4/8
GuardCFFunctionTable
图像中每个 Control Flow Guard 函数的 RVA 排序表的 VA。
84/136
4/8
GuardCFFunctionCount
上表中唯一的 RVA 计数。
88/144
4
GuardFlags
控制Flow防护相关标志。
92/148
12
CodeIntegrity
代码完整性信息。
104/160
4/8
GuardAddressTakenIatEntryTable
存储控制Flow防护地址的 VA。
108/168
4/8
GuardAddressTakenIatEntryCount
上表中唯一的 RVA 计数。
112/176
4/8
GuardLongJumpTargetTable
存储 Control Flow Guard 长跳目标表的 VA。
116/184
4/8
GuardLongJumpTargetCount
上表中唯一的 RVA 计数。

 

GuardFlags 字段包含以下一个或多个标志和子字段的组合:

  • 模块使用系统提供的支持执行控制流完整性检查。

    #define IMAGE_GUARD_CF_INSTRUMENTED 0x00000100

  • 模块执行控制流和写入完整性检查。

    #define IMAGE_GUARD_CFW_INSTRUMENTED 0x00000200

  • 模块包含有效的控制流目标元数据。

    #define IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT 0x00000400

  • 模块不使用 /GS 安全 Cookie。

    #define IMAGE_GUARD_SECURITY_COOKIE_UNUSED 0x00000800

  • 模块支持只读延迟加载 IAT。

    #define IMAGE_GUARD_PROTECT_DELAYLOAD_IAT 0x00001000

  • 延迟加载导入表在其自己的 .didat 节 (,其中没有任何其他内容) 可以自由重新保护。

    #define IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION 0x00002000

  • 模块包含禁止导出信息。 这也推断加载配置中也存在采用 IAT 表的地址。

    #define IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT 0x00004000

  • 模块支持禁止导出。

    #define IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION 0x00008000

  • 模块包含 longjmp 目标信息。

    #define IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT 0x00010000

  • 包含 Control Flow Guard 函数表项的步长 (的子字段掩码,即每个表项的附加字节数) 。

    #define IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK 0xF0000000

此外,Windows SDK winnt.h 标头定义此宏,以便将 GuardFlags 值右移的位数调整为控制Flow Guard 函数表的步幅:

#define IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT 28

.rsrc 节

资源由多级二进制排序树结构编制索引。 常规设计可以包含 2**31 个级别。 但是,按照约定,Windows使用三个级别:

类型名称语言

一系列资源目录表按以下方式关联所有级别:每个目录表后跟一系列目录条目,这些条目为该级别提供名称或标识符 (ID) (类型、名称或语言级别) 以及数据说明或其他目录表的地址。 如果地址指向数据说明,则数据是树中的叶。 如果地址指向另一个目录表,则该表会在下一级别列出目录条目。

叶的类型、名称和语言 ID 取决于通过目录表访问叶的路径。 第一个表确定类型 ID,第二个表 (由第一个表中的目录项指向) 确定名称 ID,第三个表确定语言 ID。

.rsrc 部分的一般结构为:

数据 说明
资源目录表 (和资源目录条目)
一系列表,树中的每个节点组的一个表。 第一个表中列出了所有顶级 (类型) 节点。 此表中的条目指向二级表。 每个二级树具有相同的类型 ID,但名称 ID 不同。 第三级树具有相同的类型和名称 ID,但不同的语言 ID。
每个单独的表紧接着是目录条目,其中每个条目都有一个名称或数字标识符,以及指向下一个较低级别的数据说明或表的指针。
资源目录字符串
双字节对齐的 Unicode 字符串,用作目录条目指向的字符串数据。
资源数据说明
一个由表指向的记录数组,用于描述资源数据的实际大小和位置。 这些记录是资源说明树中的叶子。
资源数据
资源部分的原始数据。 “资源数据说明”字段中的大小和位置信息分隔资源数据的各个区域。

 

资源目录表

每个资源目录表的格式如下。 此数据结构应被视为表的标题,因为该表实际上由第 6.9.2 节“资源目录条目”) 和此结构中所述 (目录条目组成:

Offset 大小 字段 说明
0
4
特征
资源标志。 保留此字段供将来使用。 它当前设置为零。
4
4
Time/Date Stamp
资源数据由资源编译器创建的时间。
8
2
主要版本
用户设置的主要版本号。
10
2
次要版本
用户设置的次要版本号。
12
2
名称条目数
表后面的目录项数,这些条目使用字符串来标识类型、名称或语言条目 (,具体取决于表) 级别。
14
2
ID 条目数
使用类型、名称或语言条目的数字 ID 的“名称”条目紧接着的目录条目数。

 

资源目录条目

目录条目构成表的行。 每个资源目录条目采用以下格式。 条目是否由资源目录表指示名称或 ID 条目,这指示其后面的名称和 ID 条目数 (请记住,表的所有 ID 条目都位于表的所有 ID 条目之前) 。 表的所有条目按升序排序:名称项按区分大小写的字符串和按数字值的 ID 条目进行排序。 偏移量相对于 IMAGE_DIRECTORY_ENTRY_RESOURCE DataDirectory 中的地址。 有关详细信息 ,请参阅 PE 内部对等互连:Win32 可移植可执行文件格式教程

Offset 大小 字段 说明
0
4
名称偏移量
给定类型、名称或语言 ID 条目的字符串的偏移量,具体取决于表级别。
0
4
整数标识符
标识类型、名称或语言 ID 条目的 32 位整数。
4
4
数据输入偏移量
高位 0。 资源数据条目的地址 (叶) 。
4
4
子目录偏移量
高位 1。 较低的 31 位是另一个资源目录表的地址, (下一个级别) 。

 

资源目录字符串

资源目录字符串区域由 Unicode 字符串组成,这些字符串是单词对齐的。 这些字符串存储在最后一个资源目录条目之后和第一个资源数据条目之前。 这会最大程度地减少这些可变长度字符串对固定大小目录条目对齐的影响。 每个资源目录字符串的格式如下:

Offset 大小 字段 说明
0
2
长度
字符串的大小,不包括长度字段本身。
2
可变
Unicode 字符串
可变长度 Unicode 字符串数据,单词对齐。

 

资源数据条目

每个资源数据条目描述资源数据区域中原始数据的实际单位。 资源数据条目的格式如下:

Offset 大小 字段 说明
0
4
数据 RVA
资源数据区域中资源数据的单位的地址。
4
4
大小
数据 RVA 字段指向的资源数据的大小(以字节为单位)。
8
4
codepage
用于解码资源数据中的代码点值的代码页。 通常,代码页将是 Unicode 代码页。
12
4
保留,必须为 0。

 

仅限 .cormeta Section (对象)

CLR 元数据存储在本节中。 它用于指示对象文件包含托管代码。 元数据的格式未记录,但可以交给 CLR 接口来处理元数据。

.sxdata 节

该对象的 .sxdata 节中列出了对象的有效异常处理程序。 该部分标记为IMAGE_SCN_LNK_INFO。 它包含每个有效处理程序的 COFF 符号索引,每个索引使用 4 个字节。

此外,编译器通过发出绝对符号“@feat.00”并将值字段的 LSB 设置为 1,将 COFF 对象标记为已注册 SEH。 没有已注册 SEH 处理程序的 COFF 对象将具有“@feat.00”符号,但没有 .sxdata 节。

存档 (库) 文件格式

COFF 存档格式提供用于存储对象文件集合的标准机制。 这些集合通常称为编程文档中的库。

存档的前 8 个字节由文件签名组成。 其余存档由一系列存档成员组成,如下所示:

  • 第一个和第二个成员是“链接器成员”。每个成员都有自己的格式,如 “导入名称类型”部分所述。 通常,链接器会将信息放入这些存档成员中。 链接器成员包含存档的目录。

  • 第三个成员是“longnames”成员。 此可选成员由一系列以 null 结尾的 ASCII 字符串组成,其中每个字符串都是另一个存档成员的名称。

  • 存档的其余部分由标准 (对象文件) 成员组成。 其中每个成员都包含一个对象文件的内容。

存档成员标头位于每个成员之前。 以下列表显示了存档的一般结构:

签名 :“!<arch>\n”
标题
第一个链接器成员
标题
第二个链接器成员
标题
Longnames 成员
标题
OBJ 文件 1 的内容
(COFF 格式)
标题
OBJ 文件 2 的内容
(COFF 格式)

...

标题
OBJ 文件 N 的内容
(COFF 格式)

存档文件签名

存档文件签名标识文件类型。 例如,任何实用工具 (,将存档文件作为输入的链接器) 可以通过读取此签名来检查文件类型。 签名由以下 ASCII 字符组成,下面每个字符按字面表示,但换行符 (\n) 字符除外:

!<arch>\n

存档成员标头

每个成员 (链接器、longname 或对象文件成员) 前面都有一个标头。 存档成员标头采用以下格式,其中每个字段都是 ASCII 文本字符串,该字符串保持对齐,并用空格填充到字段末尾。 这些字段中没有任何终止 null 字符。

每个成员标头在上一个存档成员末尾之后的第一个偶数地址开始。

Offset 大小 字段 说明
0
16
名称
存档成员的名称,附加了斜杠 (/) 以终止名称。 如果第一个字符是斜杠,则名称具有特殊解释,如下表所述。
16
12
日期
创建存档成员的日期和时间:这是自 1970 年 1 月 1 日 UCT 以来秒数的 ASCII 小数表示形式。
28
6
用户 ID
用户 ID 的 ASCII 十进制表示形式。 此字段不包含Windows平台上有意义的值,因为 Microsoft 工具会发出所有空白。
34
6
组 ID
组 ID 的 ASCII 十进制表示形式。 此字段不包含Windows平台上有意义的值,因为 Microsoft 工具会发出所有空白。
40
8
模型
成员的文件模式的 ASCII 八进制表示形式。 这是 C 运行时函数_wstat中的ST_MODE值。
48
10
大小
存档成员的总大小的 ASCII 十进制表示形式,不包括标头的大小。
58
2
标头结尾
C 字符串“̃\n” (0x60 0x0A) 中的两个字节。

 

“名称”字段具有下表中显示的格式之一。 如前所述,这些字符串中每个字符串都保持对齐,并在 16 个字节的字段中用尾随空格填充:

“名称”字段的内容 说明
name/
存档成员的名称。
/
存档成员是两个链接器成员之一。 这两个链接器成员都有此名称。
//
存档成员是 longnames 成员,由一系列以 null 结尾的 ASCII 字符串组成。 longnames 成员是第三个存档成员,是可选的。
/n
存档成员的名称位于 longnames 成员中的偏移 n 处。 数字 n 是偏移量的十进制表示形式。 例如:“/26”表示存档成员的名称位于 longnames 成员内容的开头以外的 26 个字节。

 

第一个链接器成员

第一个链接器成员的名称为“/”。 包含第一个链接器成员以实现向后兼容性。 当前链接器不使用它,但其格式必须正确。 此链接器成员提供符号名称目录,与第二个链接器成员一样。 对于每个符号,信息指示在何处查找包含符号的存档成员。

第一个链接器成员具有以下格式。 此信息显示在标头后面:

Offset 大小 字段 说明
0
4
符号数
包含索引符号数的无符号长。 此数字以大端格式存储。 每个对象文件成员通常定义一个或多个外部符号。
4
4 * n
偏移量
文件偏移量数组,用于存档成员标头,其中 n 等于“符号数”字段。 数组中的每个数字都是以大端格式存储的无符号长。 对于字符串表中命名的每个符号,偏移数组中的相应元素提供包含符号的存档成员的位置。
*
*
字符串表
一系列以 null 结尾的字符串,用于命名目录中的所有符号。 每个字符串在上一个字符串中的 null 字符之后立即开始。 字符串数必须等于“符号数”字段的值。

 

偏移数组中的元素必须按升序排列。 此事实意味着字符串表中的符号必须按照存档成员的顺序排列。 例如,第一个对象文件成员中的所有符号都必须在第二个对象文件中的符号之前列出。

第二个链接器成员

第二个链接器成员的名称为“/”,与第一个链接器成员一样。 尽管这两个链接器成员都提供包含符号和存档成员的目录,但第二个链接器成员优先于所有当前链接器的第一个。 第二个链接器成员按词法顺序包括符号名称,这样就可以更快地按名称进行搜索。

第二个成员具有以下格式。 此信息显示在标头后面:

Offset 大小 字段 说明
0
4
成员数
包含存档成员数的无符号长。
4
4 * m
偏移量
存档成员标头的文件偏移量数组,按升序排列。 每个偏移量都是一个无符号长 。 数字 m 等于“成员数”字段的值。
*
4
符号数
包含索引符号数的无符号长。 每个对象文件成员通常定义一个或多个外部符号。
*
2 * n
指标
基于 1 的索引数组 (无符号短) ,用于将符号名称映射到存档成员偏移量。 数字 n 等于“符号数”字段。 对于字符串表中命名的每个符号,Indexes 数组中的相应元素为偏移量数组提供索引。 偏移数组反过来会提供包含符号的存档成员的位置。
*
*
字符串表
一系列以 null 结尾的字符串,用于命名目录中的所有符号。 每个字符串在上一个字符串中的 null 字节之后立即开始。 字符串数必须等于“符号数”字段的值。 此表按升序列出所有符号名称。

 

Longnames 成员

longnames 成员的名称为“//”。 longnames 成员是存档成员名称的一系列字符串。 仅当“名称”字段中的空间不足时,才会在此处显示一个名称, (16 字节) 。 longnames 成员是可选的。 它可以是空的,只有一个标头,或者它完全不存在,甚至没有标头。

字符串以 null 结尾。 每个字符串在上一个字符串中的 null 字节之后立即开始。

导入库格式

传统的导入库,即描述从一个映像导出供另一个映像使用的库,通常遵循第 7 节“ 存档 (库”) 文件格式中所述的布局。 主要区别在于,导入库成员包含伪对象文件而不是实际文件,其中每个成员都包含生成第 6.4 节中所述的导入表所需的节贡献, .idata 节链接 器在生成导出应用程序时生成此存档。

可以从一组信息推断导入的部分贡献。 链接器可以在库创建时为每个成员生成完整的详细信息,也可以只将规范信息写入库,让以后使用它生成所需的数据的应用程序。

在格式较长的导入库中,单个成员包含以下信息:

  • 存档成员标头
  • 文件标头
  • 节标头
  • 对应于每个节标头的数据
  • COFF 符号表
  • 字符串

相比之下,将编写一个简短的导入库,如下所示:

  • 存档成员标头
  • 导入标头
  • 以 Null 结尾的导入名称字符串
  • 以 Null 结尾的 DLL 名称字符串

这是足够的信息,以便在其使用时准确重新构造成员的全部内容。

导入标头

导入标头包含以下字段和偏移量:

Offset 大小 字段 说明
0
2
Sig1
必须IMAGE_FILE_MACHINE_UNKNOWN。 有关详细信息,请参阅 计算机类型
2
2
Sig2
必须是0xFFFF。
4
2
Version
结构版本。
6
2
计算机
标识目标计算机类型的数字。 有关详细信息,请参阅 计算机类型
8
4
Time-Date邮票
创建文件的时间和日期。
12
4
数据大小
标头后面的字符串的大小。
16
2
序号/提示
导入的序号或提示,由“名称类型”字段中的值确定。
18
2 位
类型
导入类型。 有关特定值和说明,请参阅 导入类型
3 位
名称类型
导入名称类型。 有关详细信息,请参阅 导入名称类型
11 位
保留
保留,必须为 0。

 

此结构后跟两个以 null 结尾的字符串,这些字符串描述导入符号的名称和传入的 DLL。

导入类型

为导入标头中的 Type 字段定义了以下值:

返回的常量 Value 说明
IMPORT_CODE
0
可执行代码。
IMPORT_DATA
1
数据。
IMPORT_CONST
2
在 .def 文件中指定为 CONST。

这些值用于确定哪些节贡献必须由使用库的工具生成(如果该工具必须访问该数据)。

导入名称类型

以 null 结尾的导入符号名称紧跟其关联的导入标头。 为导入标头中的“名称类型”字段定义了以下值。 它们指示如何使用名称生成表示导入的正确符号:

返回的常量 Value 说明
IMPORT_ORDINAL 0 导入按序号进行。 这表示导入标头的序号/提示字段中的值是导入的序号。 如果未指定此常量,则应始终将序号/提示字段解释为导入的提示。
IMPORT_NAME 1 导入名称与公共符号名称相同。
IMPORT_NAME_NOPREFIX 2 导入名称是公共符号名称,但跳过前导 ?、@或可选 _。
IMPORT_NAME_UNDECORATE 3 导入名称是公共符号名称,但跳过前导 ?、@或可选 _,并在第一个 @处截断。

附录 A:计算验证码 PE 图像哈希

应使用多个属性证书来验证映像的完整性。 但是,最常见的是 Authenticode 签名。 验证码签名可用于验证 PE 映像文件的相关部分是否从未从文件的原始表单中以任何方式更改。 若要完成此任务,验证码签名包含一些称为 PE 图像哈希的内容

什么是验证码 PE 图像哈希?

验证码 PE 图像哈希或短文件哈希类似于文件校验和,因为它会生成与文件完整性相关的小值。 校验和由简单的算法生成,主要用于检测内存故障。 也就是说,它用于检测磁盘上的内存块是否已损坏,存储的值已损坏。 文件哈希类似于校验和,因为它还会检测文件损坏。 但是,与大多数校验和算法不同,很难修改文件,使其具有与其原始 (未修改) 形式相同的文件哈希。 也就是说,校验和旨在检测导致损坏的简单内存故障,但文件哈希可用于检测对文件的有意甚至细微的修改,例如病毒、黑客或特洛伊木马程序引入的文件。

在 Authenticode 签名中,文件哈希通过使用仅对文件的签名者已知的私钥进行数字签名。 软件使用者可以通过计算文件的哈希值并将其与 Authenticode 数字签名中包含的已签名哈希值进行比较来验证文件的完整性。 如果文件哈希不匹配,则已修改 PE 映像哈希涵盖的一部分文件。

验证码 PE 图像哈希中涵盖的内容是什么?

在 PE 图像哈希的计算中包括所有图像文件数据是不可能的,也不需要这样做。 有时,它只是 (呈现不受欢迎的特征,例如,无法从公开发布的文件中删除调试信息) ;有时这简直是不可能的。 例如,无法在 Authenticode 签名中包含图像文件中的所有信息,然后将包含 PE 图像哈希的 Authenticode 签名插入 PE 映像,然后能够再次在计算中包含所有图像文件数据来生成相同的 PE 图像哈希,因为该文件现在包含最初不存在的 Authenticode 签名。

生成验证码 PE 映像哈希的过程

本部分介绍如何计算 PE 映像哈希,以及如何修改 PE 映像的各个部分,而不会使 Authenticode 签名失效。

备注

特定文件的 PE 映像哈希可以包含在单独的目录文件中,而无需在哈希文件中包括属性证书。 这很相关,因为通过修改实际不包含 Authenticode 签名签名的 PE 映像的 PE 映像,使验证码签名文件中的 PE 图像哈希失效。

除以下排除范围外,分区表中指定的 PE 映像各节中的所有数据均按其全部哈希处理:

  • 可选标头Windows特定字段的文件 CheckSum 字段。 此校验和包括整个文件 (包括文件) 中的任何属性证书。 在插入 Authenticode 签名后,校验和很可能不同于原始值。

  • 与属性证书相关的信息。 与 Authenticode 签名相关的 PE 映像区域不包括在 PE 图像哈希的计算中,因为可以在图像中添加或删除 Authenticode 签名,而不会影响图像的整体完整性。 这不是问题,因为存在依赖于重新签名 PE 映像或添加时间戳的用户方案。 验证码从哈希计算中排除以下信息:

    • 可选标头数据目录的“证书表”字段。

    • 证书表和由上面列出的“证书表”字段指向的相应证书。

    若要计算 PE 图像哈希,Authenticode 按地址范围对节表中指定的节进行排序,然后对生成的字节序列进行哈希处理,并传递超过排除范围。

  • 上一部分末尾的信息。 ) 最高偏移量定义的上一节 (区域未进行哈希处理。 此区域通常包含调试信息。 调试信息通常可被视为调试器咨询;它不会影响可执行程序的实际完整性。 在交付产品并不会影响程序的功能后,可以完全从映像中删除调试信息。 事实上,这有时作为磁盘节省措施完成。 值得注意的是,如果验证码签名无效,则无法删除 PE 映像的指定部分中包含的调试信息。

可以使用Windows 平台 SDK中提供的 makecert 和 signtool 工具来尝试创建和验证 Authenticode 签名。 有关详细信息,请参阅下面的参考。

参考

用于Windows (的下载和工具包括 Windows SDK)

创建、查看和管理证书

内核模式代码签名演练 (.doc)

SignTool

Windows Authenticode 可移植可执行签名格式 (.docx)

ImageHlp 函数

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多