在Windows操作系统中,可执行文件通常采用PE(Portable Executable)格式。PE格式不仅支持32位和64位系统,而且在文件结构上具有层次分明、信息丰富的特点。其中,NT头(IMAGE_NT_HEADERS) 是整个PE文件的核心部分,它起到了连接DOS兼容部分和实际执行数据之间的桥梁作用。

NT头的整体结构

NT头一般包括以下几个部分:

  • Signature(签名)
    • 描述:一个DWORD类型的值,通常为"PE\0\0",用于标识文件为合法的PE格式。
    • 作用:通过检测这个魔数,操作系统和工具可以确认文件格式并继续解析后续内容。
  • IMAGE_FILE_HEADER(文件头)
    这一部分包含了描述文件基本属性的重要信息,例如:
    • Machine(机器类型):一个WORD值,用于说明目标CPU平台(如x86、x64等)。
    • NumberOfSections(节区数量):一个WORD值,指出文件中节(section)的个数,每个节代表一个独立的数据区,如代码、数据、资源等。
    • TimeDateStamp(时间戳):一个DWORD值,记录文件生成的时间,可以用来判断文件版本或编译时间。
    • PointerToSymbolTable & NumberOfSymbols(符号表指针及符号数量):虽然在现代PE文件中常常被置为0,但在调试和链接过程中曾有重要作用。
    • SizeOfOptionalHeader(选项头大小):一个WORD值,指明紧随其后的IMAGE_OPTIONAL_HEADER部分的大小。该字段非常重要,因为它决定了后续加载信息数据的边界。
    • Characteristics(属性标志):一个WORD值,描述文件的属性(例如是否为可执行文件 IMAGE_FILE_EXECUTABLE_IMAGE 等)。
  • IMAGE_OPTIONAL_HEADER(选项头)
    尤其在32位程序中为IMAGE_OPTIONAL_HEADER32,包含了加载程序时需要的信息,如:
    • 程序入口点地址
    • 各个段(Section)的加载地址和大小
    • 堆栈信息、堆信息
    • 以及紧跟其后的数据目录(IMAGE_DATA_DIRECTORY数组),这些目录指向导入表、导出表、重定位表、资源表等关键数据结构。

NT头在PE文件中的作用

  • 文件加载与内存映射
    NT头中的信息直接决定了操作系统如何将PE文件映射到内存中。通过解析IMAGE_OPTIONAL_HEADER中的地址和大小信息,系统能够正确加载各个节,并将它们映射到预期的内存地址。
  • 验证文件合法性
    Signature字段的存在使得操作系统和工具能够快速识别出文件是否为合法的PE文件。FileHeader中的属性(Characteristics)也可以帮助判断文件的用途和特性。
  • 支持调试与反向工程
    尽管现代PE文件中符号表信息常常被剥离,但在调试和逆向工程过程中,FILE_HEADER部分的SizeOfOptionalHeaderNumberOfSections等信息仍然为解析文件结构提供了重要线索。
  • 安全与反调试技术
    很多反调试和加解密技术会利用对NT头结构的了解,通过修改或校验NT头中的某些字段来实现保护机制或反分析功能。

代码中的NT头相关说明

在下面这段代码中,主要通过printf函数打印了几个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
33
34
35
36
37
38
39
40
41
#include<stdio.h>
#include<windows.h>

/*
ELF
DOS可执行文件格式(分段)
32位可执行文件格式(分区):PE(Portable Executable)可移植执行格式
PE:
IMAGE_DOS_HEADER e_lfanew(OD)
DOS_STUB[176]
//NT头
IMAGE_NT_HEADERS
DWORD Signature; //magic number
IMAGE_FILE_HEADER FileHeader; //文件头 描述信息
WORD Machine; //机器 重要
WORD NumberOfSections; //节区数量 重要
DWORD TimeDateStamp; //时间
DWORD PointerToSymbolTable; //符号表地址
DWORD NumberOfSymbols; //符号表的数量
WORD SizeOfOptionalHeader; //选项头的大小 重要
WORD Characteristics; //属性(文件类型信息) 重要 IMAGE_FILE_EXECUTABLE_IMAGE
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //选项头 程序加载的信息
//数据目录(表)
IMAGE_DATA_DIRECTORY[]
//节表 描述区的映射
IMAGE_SECTION_HEADER[]
数据
......
附加数据(用户自定义数据)
*/

//反调试,病毒,加密,解密
int main()
{
//兼容16位
printf("IMAGE_DOS_HEADER:%d\n", sizeof(IMAGE_DOS_HEADER));
printf("IMAGE_NT_HEADERS32:%d\n", sizeof(IMAGE_NT_HEADERS32)); // printf("IMAGE_NT_HEADERS64:%d", sizeof(IMAGE_NT_HEADERS64));
printf("IMAGE_SECTION_HEADER:%d\n", sizeof(IMAGE_SECTION_HEADER));
printf("IMAGE_OPTIONAL_HEADER32:%08x\n", sizeof(IMAGE_OPTIONAL_HEADER32));
return 0;
}

代码解析

  • 打印结构大小
    • IMAGE_DOS_HEADER:显示DOS头的大小,该部分虽然在现代系统中主要起到兼容作用,但其存在仍为PE文件打下基础。
    • IMAGE_NT_HEADERS32:重点打印了NT头在32位系统中的大小,从而展示其包含了文件头和选项头的总体空间占用情况。这个结构直接反映了PE文件的核心加载信息。
    • IMAGE_SECTION_HEADER:用于说明单个节区描述结构的大小,帮助理解各节在文件中的映射。
    • IMAGE_OPTIONAL_HEADER32:单独打印选项头的大小,进一步说明加载时各项关键信息所占空间。
  • 强调NT头的重要性
    通过注释详细列出了NT头内部各字段的意义,特别是MachineNumberOfSectionsSizeOfOptionalHeaderCharacteristics,这些字段直接影响到PE文件的解析和加载过程。

运行结果

image-20250208182133533