欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

Windows PE 3.2.3 NT 文件头 - 文件头

最编程 2024-10-02 07:27:11
...

DOS块的后面接着就是NT头了。NT头(NT Header)是PE文件中的一个数据结构,用于描述可执行文件的详细信息和布局。它位于PE文件的DOS头之后,紧接着DOS Stub,如图3-6所示。NT头是用于现代Windows操作系统的可执行文件格式的关键部分。NT头包含了PE文件的各种信息,包括文件的签名、文件头和扩展头。

       ■IMAGE_NT_HEADERS结构

NT头的结构可以通过一个名为IMAGE_NT_HEADERS的结构体来表示。我们在winnt.h头文件中搜索到这个结构体的定义如下:

typedef struct _IMAGE_NT_HEADERS64 {

    DWORD Signature;   //PE文件的签名(PE特征码)

    IMAGE_FILE_HEADER FileHeader;  //文件头

    IMAGE_OPTIONAL_HEADER64 OptionalHeader;   //64位PE文件扩展头

} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

typedef struct _IMAGE_NT_HEADERS {

    DWORD Signature;   //PE文件的签名(PE特征码)

    IMAGE_FILE_HEADER FileHeader;  //文件头

    IMAGE_OPTIONAL_HEADER32 OptionalHeader; //32位PE文件扩展头

} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

我们发现有两个NT头结构的定义,一个是32位PE文件,另一个是64位PE文件。稍后我们将详细讲解它们的区别。

图3-6 PE文件结构

【注】块表为节表的同义词。

PE特征码(签名)

文件的签名由Signature字段表示,它是一个4个字节的固定标识,用于表示该文件是一个PE文件。在32位的PE文件中,Signature的值为0x00004550(或者ASCII字符"PE\0\0"),在64位的PE文件中,Signature的值同样为0x00004550(或者ASCII字符"PE\0\0")。

特征码是一个固定的标识值,用于告知操作系统或其他工具,该文件遵循PE文件格式的规范,并提供了正确的结构和布局信息。操作系统在加载和解析PE文件时,会首先检查特征码,以确认文件是否为有效的PE文件。

特征码的位置在PE文件的NT头中,可以通过IMAGE_NT_HEADERS结构体中的Signature字段来访问。

文件头(COFF文件标头)

在PE文件的开头,或紧接在映像文件签名之后,是以下格式的标准 COFF 文件头20个字节)。 请注意,Windows 加载器将节区数限制为 96。

文件头(FileHeader)字段描述了文件的基本属性和布局,包括文件的机器架构、节表的数量和大小等信息。它是一个IMAGE_FILE_HEADER结构体,包含了各种字段,如Machine、NumberOfSections、SizeOfOptionalHeader等。我们在winnt.h头文件中搜索到文件头结构体的定义如下:

typedef struct _IMAGE_FILE_HEADER {

    WORD  Machine;   // CPU架构类型

    WORD  NumberOfSections; // 节区数量

    DWORD TimeDateStamp; // 文件创建时间戳

    DWORD PointerToSymbolTable; // COFF符号表偏移地址

    DWORD NumberOfSymbols; // COFF符号表条目数量

    WORD  SizeOfOptionalHeader; // 可选头的大小

    WORD  Characteristics;// 文件属性标志

} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

Offset

大小

字段

说明

0

2

设备

标识目标计算机类型的数字。 有关详细信息,请参阅MSDN目标计算机类型

2

2

NumberOfSections

节区数目。 这指示节表的大小,该表紧跟在PE头之后。

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

可选标头(扩展头)的大小,它是可执行文件所必需的,但对象文件(obj)不需要它。 对于对象文件,此值应为零。 有关标头格式的说明,请参阅MSDN可选标头(仅限映像文件)

18

2

特征

指示文件属性的标志。 有关特定标志值,请参阅MSDN特征

以下是对每个字段的注释说明:

●Machine:指定了目标CPU架构的类型,例如x86、x64、ARM等。具体的架构类型可以通过相应的枚举值来表示,如IMAGE_FILE_MACHINE_I386、IMAGE_FILE_MACHINE_AMD64等。014cH代表x86 CPU,8664H表示x64 CPU

我们可以在winnt.h头文件中搜索IMAGE_FILE_MACHINE_I386,查到所有CPU架构类型的枚举值:

常量

Value

说明

IMAGE_FILE_MACHINE_UNKNOWN

0x0

假定此字段的内容适用于任何计算机类型

IMAGE_FILE_MACHINE_ALPHA

0x184

Alpha AXP32 位地址空间

IMAGE_FILE_MACHINE_ALPHA64

0x284

Alpha 6464 位地址空间

IMAGE_FILE_MACHINE_AM33

0x1d3

Matsushita AM33

IMAGE_FILE_MACHINE_AMD64

0x8664

X64

IMAGE_FILE_MACHINE_ARM

0x1c0

ARM little endian

IMAGE_FILE_MACHINE_ARM64

0xaa64

ARM64 little endian

IMAGE_FILE_MACHINE_ARMNT

0x1c4

ARM Thumb-2 little endian

IMAGE_FILE_MACHINE_AXP64

0x284

AXP 64 (与 Alpha 64 相同)

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

Mitsubishi M32R little endian

IMAGE_FILE_MACHINE_MIPS16

0x266

MIPS16

IMAGE_FILE_MACHINE_MIPSFPU

0x366

MIPS FPU 结合使用

IMAGE_FILE_MACHINE_MIPSFPU16

0x466

MIPS16 FPU 结合使用

IMAGE_FILE_MACHINE_POWERPC

0x1f0

Power PC little endian

IMAGE_FILE_MACHINE_POWERPCFP

0x1f1

支持浮点的 Power PC

IMAGE_FILE_MACHINE_R4000

0x166

MIPS little 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 little-endian WCE v2

●NumberOfSections:指定了文件中节区(Section Table)的数量。节表记录了不同段(如代码段、数据段等)的信息,每个段对应一个节表项。

【注意】PE文件中最少要有一个节区,即.text节区(代码段),最多可以有96个节区。

●TimeDateStamp:表示文件的创建时间戳,通常是一个以1970年1月1日以来的秒数表示的时间值。

●PointerToSymbolTable(已弃用):指定了COFF符号表的偏移地址。COFF符号表包含了与文件相关的符号信息,如函数、变量等。

COFF(Common Object File Format)符号表是一种用于存储目标文件中符号信息的数据结构。它通常用于编译后的可执行文件、动态链接库(DLL)和目标文件中,以支持符号的调试和链接。

COFF符号表包含了与文件相关的符号信息,例如函数、变量、类型等。这些符号信息在编译和链接过程中收集和生成,以便在调试和链接阶段使用。

COFF符号表的结构可能因不同的操作系统和编译器而有所差异,但通常包含以下类型的符号信息:

1.符号名称(Symbol Name):指定符号的名称,例如函数名、变量名等。

2.符号值(Symbol Value):表示符号的地址或相对地址,用于定位符号在内存中的位置。

3.符号大小(Symbol Size):表示符号的大小,用于确定符号所占用的内存空间。

4.符号类型(Symbol Type):指定符号的类型,例如函数、变量、类型等。

5.符号绑定(Symbol Binding):表示符号的绑定类型,如全局符号、局部符号等。

6.符号节表索引(Symbol Section Index):指定符号所属的节表索引,用于确定符号所在的段或节。

COFF符号表对于调试器和链接器非常重要。调试器可以使用符号表来解析和显示变量、函数的名称和值,以及调用堆栈信息。链接器可以使用符号表来解析和解决符号引用,以正确连接不同的目标文件和库文件。

【注意】COFF符号表是一种中间表示形式,它不直接在可执行文件中执行。在最终的可执行文件中,符号表通常会被压缩、优化或移除,以减小文件大小和提高执行效率。

●NumberOfSymbols(已弃用):表示COFF符号表中的符号条目数量。

●SizeOfOptionalHeader:指定了扩展头(Optional Header)的大小,即IMAGE_OPTIONAL_HEADER32或IMAGE_OPTIONAL_HEADER64结构体的大小。

实验十三:验证并修改32位和64位PE文件的扩展头大小

●验证扩展头大小

1.32位PE文件

将notepad32.exe拖入WinHex工具,文件偏移地址0xF4地址处的值为0E0H。

在文件偏移地址0xF8~0x1D8之间为该PE文件的扩展头,32位PE文件扩展头大小=0x1D8-0xF8=0E0H(224)字节。

2.64位PE文件

将notepad64.exe拖入WinHex工具,文件偏移地址0x10C地址处的值为0F0H。

在文件偏移地址0x110~0x200之间为该PE文件的扩展头,64位PE文件扩展头大小=0x200-0x110=0F0H(240)字节。

●修改扩展头大小

1.32位PE文件

将notepad32.exe拖入WinHex工具,观察在文件偏移地址0x320~0x400之间为空白区域,大小为0EH(224)字节,这是我们可以扩大扩展头大小的最大字节数。原因读者应该记得,为了不改变下面节区的偏移地址。

接下来我们选定这个区域,按“Delete”键删除该区域。然后在扩展头最后一个字节0x1D7处粘贴224个零字节。

最后一步,在文件偏移地址0xF4地址处将原来扩展头大小的值0E0H增加一倍,修改为01C0H。修改后保存文件,重命名为notepad32_1.exe,运行一切正常。

2.64位PE文件

将notepad64.exe拖入WinHex工具,观察在文件偏移地址0x2F0~0x400之间为空白区域,大小为110H(272)字节,这是我们可以扩大扩展头大小的最大字节数。

接下来我们选定这个区域,按“Delete”键删除该区域。然后在扩展头最后一个字节0x1FF处粘贴272个零字节。

最后一步,在文件偏移地址0x10C地址处将原来扩展头大小的值0F0H修改为0200H。修改后保存文件,重命名为notepad64_1.exe,我们会发现程序无法正常运行。

原因是运行程序时,系统使用LoadString函数加载命令行参数字符串,如果第一个参数为自身的句柄,则会在当前路径或系统路径下寻找资源PE文件(没有代码的资源文件),名称为***.exe.mui。我们在C盘根目录下搜索notepad.exe.mui文件名,找到其所在的文件夹位于“C:\Windows\zh-CN”中,将文件夹“zh-CN”复制到源代码目录“D:\code\winpe”,使记事本程序与资源文件夹位于同一目录,如图3-7所示。再将notepad64_1.exe改回notepad.exe,就可以正常运行了。                                                   

图3-7 添加记事本资源PE文件

练习

       1.请读者以64位记事本程序为例,只修改其文件头中SizeOfOptionalHeader字段的大小为0F1H,其他保持不变,保存修改后观察是否记事本可以正常运行。

       2.将64位记事本程序文件头中SizeOfOptionalHeader字段的大小为0F1H,并在扩展头结尾处添加一个字节,与此同时删除文件偏移地址00000400H之前一个零字节,保存修改后观察是否记事本可以正常运行。

       3.以同样的方法测试32位记事本,将其扩展头的大小改为0F0H,增大16个字节。保存修改后,观察程序是否可以正常运行。

  结论

       1.不论32位还是64位PE程序都可以修改扩展头的大小。但需要注意,不能因此而改变下面节区在PE文件内的偏移地址。

       2.增大扩展头的字节数必须是16的倍数。

●Characteristics:用于描述文件的属性标志,如是否为可执行文件、是否包含调试信息、是否可以作为系统驱动程序等。具体的属性标志可以通过位掩码来表示,如IMAGE_FILE_EXECUTABLE_IMAGE、IMAGE_FILE_DEBUG、IMAGE_FILE_SYSTEM等。

以下是我们在winnt.h头文件中查询到的文件属性标志:

标志

说明

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

Little endian:最低有效位 (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

Big endianMSB 在内存中的 LSB 之前。 此标志已被弃用,应为零。

这些属性和标志位提供了关于PE文件的基本信息,帮助操作系统和工具正确解析和处理可执行文件。

我们在WinHex中观察32位记事本文件头结构的最后一个字段Characteristics的值为010FH,即IMAGE_FILE_32BIT_MACHINE+ IMAGE_FILE_LOCAL_SYMS_STRIPPED+ IMAGE_FILE_LINE_NUMS_STRIPPED+ IMAGE_FILE_EXECUTABLE_IMAGE+ IMAGE_FILE_RELOCS_STRIPPED

意为32位(局部符号信息、行号信息、重定位信息已被剥离)可执行文件。

       64位记事本程序文件头的最后一个字段Characteristics的值为0022H,即IMAGE_FILE_LARGE_ADDRESS_AWARE+ IMAGE_FILE_EXECUTABLE_IMAGE意为应用程序能够处理大于2GB的地址空间的64位可执行程序。