分类:
2007-04-19 10:59:36
有关一些PE头文件结构一般都有32位和64位之分,如IMAGE_NT_HEADERS32和IMAGE_NT_HEADERS64等,除了在64位版本中的一些扩展域外,这些结构总是一样的。是采用32位还是64位,需要用#define _WIN64来定义,如果没有这种定义,则采用的是32位的文件结构。编译器将根据此定义选择相应的编译模式。
MS-DOS头部占据了PE文件的头64个字节,描述它内容的结构如下:
l
// 此结构包含于WINNT.H中
//
typedef struct _IMAGE_DOS_HEADER { // DOS的.EXE头部
WORD e_magic; // 魔术数字
WORD e_cblp; // 文件最后页的字节数
WORD e_cp; // 文件页数
WORD e_crlc; // 重定义元素个数
WORD e_cparhdr; // 头部尺寸,以段落为单位
WORD e_minalloc; // 所需的最小附加段
WORD e_maxalloc; // 所需的最大附加段
WORD e_ss; // 初始的SS值(相对偏移量)
WORD e_sp; // 初始的SP值
WORD e_csum; // 校验和
WORD e_ip; // 初始的IP值
WORD e_cs; // 初始的CS值(相对偏移量)
WORD e_lfarlc; // 重分配表文件地址
WORD e_ovno; // 覆盖号
WORD e_res[4]; // 保留字
WORD e_oemid; // OEM标识符(相对e_oeminfo)
WORD e_oeminfo; // OEM信息
WORD e_res2[10]; // 保留字
LONG e_lfanew; // 新exe头部的文件地址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
l
其中第一个域e_magic,被称为魔术数字,它用于表示一个MS-DOS兼容的文件类型。所有MS-DOS兼容的可执行文件都将这个值设为0x5A4D,表示ASCII字符MZ。MS-DOS头部之所以有的时候被称为MZ头部,就是这个缘故。还有许多其他的域对于MS-DOS操作系统来说都有用,但是对于Windows NT来说,这个结构中只有一个有用的域——最后一个域e_lfnew,一个4字节的文件偏移量,PE文件头部就是由它定位的。
PE Header是紧跟在MS-DOS头部和实模式程序残余之后的,描述它内容的结构 如下:
l
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; // PE文件头标志:"PE\0\0"
IMAGE_FILE_HEADER FileHeader; // PE文件物理分布的信息
IMAGE_OPTIONAL_HEADER32 OptionalHeader; // PE文件逻辑分布的信息
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
紧接PE文件头标志之后是PE文件头结构,由20个字节组成,它被定义为:
l
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
#define IMAGE_SIZEOF_FILE_HEADER 20
l
其中请注意这个文件头部的大小已经定义在这个包含文件之中了,这样一来,想要得到这个结构的大小就很方便了。
Machine:表示该程序要执行的环境及平台,现在已知的值如表2.1所示。
应用程序执行的环境及平台代码
IMAGE_FILE_MACHINE_I386(0x14c) |
Intel 80386 处理器以上 |
0x014d |
Intel 80486 处理器以上 |
0x014e |
Intel Pentium 处理器以上 |
0x0160 |
R3000(MIPS)处理器,big endian |
IMAGE_FILE_MACHINE_R3000(0x162) |
R3000(MIPS)处理器,little endian |
IMAGE_FILE_MACHINE_R4000(0x166) |
R4000(MIPS)处理器,little endian |
IMAGE_FILE_MACHINE_R10000(0x168) |
R10000(MIPS)处理器,little endian |
IMAGE_FILE_MACHINE_ALPHA(0x184) |
DEC Alpha AXP处理器 |
IMAGE_FILE_MACHINE_POWERPC(0x1f0) |
IBM Power PC,little endian |
NumberOfSections:段的个数。
TimeDateStamp:文件建立的时间。可用这个值来区分同一个文件的不同的版本,即使它们的商业版本号相同。这个值的格式并没有明确的规定,但是很显然地大多数的C编译器都把它定为从1970.1.1 00:00:00以来的秒数(time_t)。这个值有时也被用做绑定输入目录表。注意:一些编译器将忽略这个值。
PointerToSymbolTable及NumberOfSymbols:用在调试信息中,用途不太明确,不过它们的值总为0。
SizeOfOptionalHeader:可选头的长度(sizeof IMAGE_OPTIONAL_HEADER),可以用它来检验PE文件的正确性。
Characteristics:是一个标志的集合,其大部分位用于OBJ或LIB文件中。
文件头下面就是可选择头,这是一个叫做IMAGE_OPTIONAL_HEADER的结构,由224个字节组成。虽然它的名字是“可选头部”,但是请确信:这个头部并非“可选”,而是“必需”的。可选头部包含了很多关于可执行映像的重要信息。例如,初始的堆栈大小、程序入口点的位置、首选基地址、操作系统版本、段对齐的信息等。IMAGE_ OPTIONAL_HEADER结构如下:
l
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// 标准域
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT附加域
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
l
其中参数含义如下所述。
Magic:这个值好像总是0x010b。
MajorLinkerVersion及MinorLinkerVersion:链接器的版本号,这个值不太可靠。
SizeOfCode:可执行代码的长度。
SizeOfInitializedData:初始化数据的长度(数据段)。
SizeOfUninitializedData:未初始化数据的长度(bss段)。
AddressOfEntryPoint:代码的入口RVA地址,程序从这儿开始执行,常称为程序的原入口点OEP(Original Entry Point)。
BaseOfCode:可执行代码起始位置。
BaseOfData:初始化数据起始位置。
ImageBase:载入程序首选的RVA地址。这个地址可被Loader改变。
SectionAlignment:段加载后在内存中的对齐方式。
FileAlignment:段在文件中的对齐方式。
MajorOperatingSystemVersion及MinorOperatingSystemVersion:操作系统版本。
MajorImageVersion及MinorImageVersion:程序版本。
MajorSubsystemVersion及MinorSubsystemVersion:子系统版本号,这个域系统支持。例如,程序运行于NT下,子系统版本号如果不是4.0,对话框不能显示3D风格。
Win32VersionValue:这个值总是为0。
SizeOfImage:程序调入后占用内存大小(字节),等于所有段的长度之和。
SizeOfHeaders:所有文件头长度之和,它等于从文件开始到第一个段的原始数据之间的大小。
CheckSum:校验和,仅用在驱动程序中,在可执行文件中可能为0。它的计算方法Microsoft不公开,在imagehelp.dll中的CheckSumMappedFile()函数可以计算它。
Subsystem:一个标明可执行文件所期望的子系统的枚举值。
DllCharacteristics:DLL状态。
SizeOfStackReserve:保留堆栈大小。
SizeOfStackCommit:启动后实际申请的堆栈数,可随实际情况变大。
SizeOfHeapReserve:保留堆大小。
SizeOfHeapCommit:实际堆大小。
LoaderFlags:目前没有用。
NumberOfRvaAndSizes:下面的目录表入口个数,这个值也不可靠,可用常数IMAGE_NUMBEROF_DIRECTORY_ENTRIES来代替它,这个值在目前Windows版本中设为16。注意,如果这个值不等于16,那么这个数据结构大小就不能固定下来,也就不能确定其他变量位置。
DataDirectory:是一个IMAGE_DATA_DIRECTORY数组,数组元素个数为IMAGE_NUMBEROF_DIRECTORY_ENTRIES,结构如下:
l
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // 起始RVA地址
DWORD Size; // 长度
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
PE文件格式中,所有的节头部位于可选头部之后。每个节头部为40个字节长,并且没有任何填充信息。节头部被定义为以下的结构:
l
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 节表名称,如".text"
union {
DWORD PhysicalAddress; // 物理地址
DWORD VirtualSize; // 真实长度
} Misc;
DWORD VirtualAddress; // RVA
DWORD SizeOfRawData; // 物理长度
DWORD PointerToRawData; // 节基于文件的偏移量
DWORD PointerToRelocations; // 重定位的偏移
DWORD PointerToLinenumbers; // 行号表的偏移
WORD NumberOfRelocations; // 重定位项数目
WORD NumberOfLinenumbers; // 行号表的数目
DWORD Characteristics; // 节属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
l
其中IMAGE_SIZEOF_SHORT_NAME等于8。注意,如果不是这个值,那么这个数据结构大小就不能固定下来,也就不能确定其他变量位置。