正如我们在初识PE文件一节中看到的,PE文件头中包含几个重要的结构,DOS头、DOS块(DOS Stub)和NT头。NT头就是PE特征码+文件头(COFF 文件标头)+扩展头(可选标头),合称为NT头。这一节我们将详细讲解这几个重要的结构。我们将DOS头和DOS 块合称为MS-DOS 存根。COFF 对象文件(obj)标头由 COFF 文件标头和可选标头组成。
本节必须掌握的知识点:
DOS头
DOS块
NT头
3.2.1 DOS头
DOS头(DOS Header)是可执行文件中的一个数据结构,它是用于支持早期的DOS操作系统的标准格式。DOS头位于可执行文件的开头,包含了一些关于文件的基本信息和可执行程序的入口点。
MS-DOS 存根是在 MS-DOS 下运行的有效应用程序。 它放置在 EXE 映像的前面。 链接器在此处放置默认存根,当映像在 MS-DOS 中运行时,此存根会输出消息“此程序不能在 DOS 模式下运行”。 用户可以使用 /STUB 链接器选项指定不同的存根。
在位置 0x3c,存根具有 PE 签名(PE特征码“PE\0\0”)文件偏移量。 此信息使 Windows 能够正确执行映像文件,即使此文件具有 MS-DOS 存根也不例外。 链接期间,此文件偏移量放在位置 0x3c。
实验九:在winnt.h头文件中查看DOS头、文件头和扩展头的结构定义
在VS中输入#include "winnt.h" ,点击右键,打开文档。然后搜索 IMAGE_DOS_HEADER 或者在程序里面输入IMAGE_DOS_HEADER 按F12转到定义。
■IMAGE_DOS_HEADER结构
typedef struct _IMAGE_DOS_HEADER {
WORD e_magic; // DOS 魔数
WORD e_cblp; // 文件的最后一页的字节数
WORD e_cp; // 文件中的页数
WORD e_crlc; // 重定位项的数量
WORD e_cparhdr; // 标头的段数
WORD e_minalloc; // 程序所需的最小附加段数
WORD e_maxalloc; // 程序所需的最大附加段数
WORD e_ss; // 初始堆栈段的相对偏移量
WORD e_sp; // 初始堆栈指针
WORD e_csum; // 文件校验和
WORD e_ip; // 初始指令指针
WORD e_cs; // 初始代码段的相对偏移量
WORD e_lfarlc; // 重定位表的文件偏移量
WORD e_ovno; // 覆盖号
WORD e_res[4]; // 保留字段
WORD e_oemid; // OEM 标识符(用于 e_oeminfo)
WORD e_oeminfo; // OEM 信息;由 e_oemid 指定
WORD e_res2[10]; // 保留字段
LONG e_lfanew; // 新的 PE 头的文件偏移量
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
■下面是DOS头中一些重要字段的说明:
●e_magic:这是一个16位的字段,用于表示可执行文件的标识符。对于标准的可执行文件,该字段应为5A4DH,即"MZ"的ASCII码,取自微软开发人员名字。
●e_lfanew:这是一个32位的字段,它表示PE头的偏移量。PE头(Portable Executable Header)是在DOS头之后的一个数据结构,包含了更详细的可执行文件信息。
●e_cblp和e_cp:这两个字段分别表示文件的最后一个页(512字节)的字节数和文件中的页数。
●e_crlc和e_cparhdr:这两个字段分别表示重定位表项数量和标准头的字节数。
●e_minalloc和e_maxalloc:这两个字段分别表示程序所需的最小和最大内存量。
●e_ss和e_sp:这两个字段分别表示程序的初始堆栈段和堆栈指针。
●e_csum:这是一个16位的字段,用于存储文件的校验和。在早期的DOS操作系统中,可以使用该字段进行简单的文件完整性校验。现在已经没什么用了,可以随便改。
●e_ip和e_cs:这两个字段分别表示初始指令指针和代码段。
<