每个PE文件是以一个DOS程序开始的,有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随MZ header之后的DOS stub (DOS块)。
平常把 DOS MZ头与 DOS stub 合称为DOS文件头。
PE文件的第一个字节起始于一个传统的MS-DOS头部,被称作 IMAGE_DOS_HEADER 。其IMAGE_DOS_HEADER结构如下所示:
typedef struct _IMAGE_DOS_HEADER { // DOS下的.EXE文件头
USHORT e_magic; // 魔数
USHORT e_cblp; // 文件最后一页的字节数
USHORT e_cp; // 文件的页数
USHORT e_crlc; // 重定位
USHORT e_cparhdr; // 段中头的大小
USHORT e_minalloc; // 需要的最少额外段
USHORT e_maxalloc; // 需要的最多额外段
USHORT e_ss; // 初始的(相对的)SS寄存器值
USHORT e_sp; // 初始的SP寄存器值
USHORT e_csum; // 校验和
USHORT e_ip; // 初始的IP寄存器值
USHORT e_cs; // 初始的(相对的)CS寄存器值
USHORT e_lfarlc; // 重定位表在文件中的地址
USHORT e_ovno; // 交叠数
USHORT e_res[4]; // 保留字
USHORT e_oemid; // OEM识别符(用于e_oeminfo成员)
USHORT e_oeminfo; // OEM信息; e_oemid中指定的
USHORT e_res2[10]; // 保留字
LONG e_lfanew; // 新exe头在文件中的地址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
其中有两个字段比较重要,分别是e_magic和e_lfanew.e_magic字段(一个字大小)需要被设置为值5A3Dh,这个值有个#define,名为IMAGE_DOS_SINGNATURE,ASCII表示法里,它的ASCII值为“MZ”,是MS-DOS的最初创建者之一Mark Zbikowski字母的缩写。e_lfanew字段是真正的PE文件头的相对偏侈(RVA),其指出真正PE头的文件偏移位置,它点用4个字节,位于文件开始偏移3Ch字节中。
用十六进制编辑器(WinHex Hex Workshop等带偏移显示的尤佳)打开一个32位PE程序,定位在文件起始位置处,此处就是MS-DOS头部,如图:
查看PE文件 MS-DOS 头部
文件第一个字符:”MZ”就是e_magic字段。偏移3Ch就是e_lfanew的值,显示为”B0000000″.。由于 Intel CPU属于Little-Endian类(逆序),字符储存时低位在前,高位在后,将次序恢复后,e_lfanew的值为000000B0h,这个值就是真正的PE文件头偏移量。


