_IMAGE_DOS_HEADER = packed record { DOS .EXE header } e_magic: Word; { Magic number } e_cblp: Word; { Bytes on last page of file } e_cp: Word; { Pages in file } e_crlc: Word; { Relocations } e_cparhdr: Word; { Size of header in paragraphs } e_minalloc: Word; { Minimum extra paragraphs needed } e_maxalloc: Word; { Maximum extra paragraphs needed } e_ss: Word; { Initial (relative) SS value } e_sp: Word; { Initial SP value } e_csum: Word; { Checksum } e_ip: Word; { Initial IP value } e_cs: Word; { Initial (relative) CS value } e_lfarlc: Word; { File address of relocation table } e_ovno: Word; { Overlay number } e_res: array [0..3] of Word; { Reserved words } e_oemid: Word; { OEM identifier (for e_oeminfo) } e_oeminfo: Word; { OEM information; e_oemid specific} e_res2: array [0..9] of Word; { Reserved words } _lfanew: LongInt; { File address of new exe header } end;
Magic number :用于標識可執行文件的類型,如下常量表示不同的類型
cDOSMagic = $5A4D; // magic number for a DOS executable
cNEMagic = $454E; // magic number for a NE executable (Win 16)
cPEMagic = $4550; // magic nunber for a PE executable (Win 32)
cLEMagic = $454C; // magic number for a Virtual Device Driver
Bytes on last page of file : 文件在最后一個頁面的字節數
Pages in file : 文件所占用的頁面數
Relocations : 需要重定位的segment數目
size of header in paragraphs : dos文件頭占用大小
Minimum extra paragraphs needed : 需要額外的最少段數目
Maximum extra paragraphs needed : 需要額外的最大段數目
Initial (relative) SS value : 堆棧段寄存器的初始值
Initial (relative) sp value :堆棧寄存器的初始值
Checksum : 校驗和
A checksum or hash sum 是固定的值,由任意的一塊數字計算而來。這塊數字用于檢測意外的錯誤。任何時候可以重新計算checksum,然后比對是否一致。更多詳細參考:http://en.wikipedia.org/wiki/Checksum
Initial IP value : 初始的指令寄存器值
File address of relocation table: 重定位表,在早期的16位系統中,為使得尋址能力達到20位,以滿足可訪問更多的空間,搭配使用了段(segment)。這里記錄了指向代碼段(code)、數據段(data)的地址,但這些地址并不是絕對、固定的。因為代碼段和數據段等,只有在被加載到內存時,才能他們的實際地址。當程序被加載到內存後,exe loader會通過重定位表,再修正segment中的相對值(偏移量),以正確定位到代碼段及數據段的真實地址。
隨著32系統的到來,windows exe被設計為遵循PE的格式,這時的重定位表就不再被使用了。這是因為虛擬地址的出現——在虛擬空間里,Exe可以被加載到任意的地址,程序總是被以虛擬空間的基址加載,所以也就不要調整重定位表了。然而,在dll文件里,重定位表,仍是需要的,因為會被加載到已存在進程地址空間中,不再已既定的基址加載,所以需要矯正表中的偏移值。
Overlay number :交叠数
///以下收集
一、简介 PE文件最前面是一个DOS可执行文件(STUB),这使PE文件成为一个合法的MS-DOS可执行 文件。 DOS文件头后面是一个32位的PE文件标志0X00004550(IMAGE_NT_SIGNATURE)。 接着就是PE的文件头了,包含的信息有该程序运行平台、有多少段(sections)、文件 链接的时间、它是一个可执行文件(EXE)还是一个动态链接库(DLL)或是其他。 后面紧接着有一个“可选”头部(这个部分总是存在,但是因为COFF在库(Libraries) 中用了这个词,在一可执行模块中并没有用这个词,但是仍被叫做可选的)。这可部分包含程 序加载的更多的信息:开始地址、保留堆栈数量、数据段大小等等。 可选头中还有一个重要的域是一叫做“数据目录表”(data directories)的数组;表 中的每一项是一个指向某一个段的指针。例如:如果某程序有一个输出目录表(export dire ctory ),那你就会在数据目录表中找到一个为IMAGE_DIRECTORY_ENTRY_EXPORT的指针,并且 它将指向某一个段。 可选头的下面就是“段”(sections)了,通过一个叫做“段头”(section headers) 的结构索引。实际上,段的内容才是你要真正执行的程序,上面介绍的所有的文件头及目录表 等信息就是为了能正确的找到它。 每一个段都有一些有关的标志,例如它包含什么数据(“初始化数据”或其他),它能 否被共享等,及它数据本身的特征。大多数情况下(并不是全部),每个段会被一个或多个目 录表指向,目录表可通过可选头的“数据目录表”的入口找到,就象输出函数表或基址重定位 表。也有没有目录表指向的段,如可执行代码或初始化数据。 整个文件结构如下: +-------------------+ | DOS-stub | +-------------------+ | file-header | +-------------------+ | optional header | |- - - - - - - - - -| | | | data directories | | | +-------------------+ | | | section headers | | | +-------------------+ | | | section 1 | | | +-------------------+ | | | section 2 | | | +-------------------+ | | | ... | | | +-------------------+ | | | section n | | | +-------------------+ 下面介绍一下相关虚拟地址(Relative Virtual Addresses) PE格式文件中经常用到RVA,即相关虚拟地址,用在不知道基地址的情况下表示一个内存 地址。它需要加上基地址才能得到线性地址(Linear address)。 例如:假设一个可执行程序调入内存0x400000处并且程序从RVA 0x1560处开始执行。那 么正确的开始地址是0x401560。如果可执行程序调入0x100000处,则开始地址为0x101560。 因为PE文件的每一个段不必按同样的边界对齐方式调入,因此RVA地址的计算变得比较复 杂。例如,在文件中每一个段往往按512个字节的方式对齐,而在内存中可能以4096字节的方 式对齐。这方面的介绍可见下面的“SectionAlignment”、“FileAlignment”。举个例子, 假设你知道一个程序从RVA 0x1560开始执行,你想从那儿反汇编它。你发现内存中的段对齐方 式为4096并且.code段开始于内存RVA 0x1560并且有16384字节长;那么你可以知道RVA 0x156 0在这个段的0x560处。你又发现这个段在文件中以512字节方式对齐并且.code开始于文件0x8 00处,那现在你知道了可执行程序开始于0x800+0x560 = 0xd60处。 二、DOS头(DOS-stub ) 众所周知DOS头的概念是从16位的WINDOWS可执行程序(NE格式)中来的,这个部分主要 用在OS/2可执行程序、自解压文档及其他应用程序。在PE格式文件中,大多数程序的这个部分 中只有大约100个字节的代码,只输出一个诸如“this program needs windows NT ”之类的 信息。 你可以通过一个叫做IMAGE_DOS_HEADER的结构来识别一个合法的DOS头。这个结构的头两 个字节一定是“MZ”(#define IMAGE_DOS_SIGNATURE "MZ")。怎么才能找到PE开始的标志呢 ?你可以通过该结构的一个叫做“e_lfanew”(offset 60,32bits) 的成员来找到它。在O S/2及16位WINDOWS程序中这个标志是一个16位的字;在PE程序中,它是一个32位的双字,值为 0x00004550(#define IMAGE_NT_SIGNATURE 0x00004550)。 typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // Magic number WORD e_cblp; // Bytes on last page of file WORD e_cp; // Pages in file WORD e_crlc; // Relocations WORD e_cparhdr; // Size of header in paragraphs WORD e_minalloc; // Minimum extra paragraphs needed WORD e_maxalloc; // Maximum extra paragraphs needed WORD e_ss; // Initial (relative) SS value WORD e_sp; // Initial SP value WORD e_csum; // Checksum WORD e_ip; // Initial IP value WORD e_cs; // Initial (relative) CS value WORD e_lfarlc; // File address of relocation table WORD e_ovno; // Overlay number WORD e_res[4]; // Reserved words WORD e_oemid; // OEM identifier (for e_oeminfo) WORD e_oeminfo; // OEM information; e_oemid specific WORD e_res2[10]; // Reserved words LONG e_lfanew; // File address of new exe header } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 三、文件头(File Header) 通过DOS头,你可以找到一个叫做IMAGE_FILE_HEADER的结构,如下;下面我分别介绍一 下。 typedef struct _IMAGE_FILE_HEADER { WORD Machine; //0x04 WORD NumberOfSections; //0x06 DWORD TimeDateStamp; //0x08 DWORD PointerToSymbolTable; //0x0c DWORD NumberOfSymbols; //0x10 WORD SizeOfOptionalHeader; //0x14 WORD Characteristics; //0x16 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; Machine:表示该程序要执行的环境及平台,现在已知的值如下: IMAGE_FILE_MACHINE_I386(0x14c) Intel 80386 处理器以上 0x014d Intel 80486 处理器以上 0x014e Intel Pentium 处理器以上 0x0160 R3000(MIPS)处理器,高位在前 IMAGE_FILE_MACHINE_R3000(0x162) R3000(MIPS)处理器,低位在前 IMAGE_FILE_MACHINE_R4000(0x166) R4000(MIPS)处理器,低位在前 IMAGE_FILE_MACHINE_R10000(0x168) R10000(MIPS)处理器,低位在前 IMAGE_FILE_MACHINE_ALPHA(0x184) DEC Alpha AXP处理器 IMAGE_FILE_MACHINE_POWERPC(0x1f0) IBM Power PC,低位在前 NumberOfSections:段的个数,段的概念我们将在下面介绍。 TimeDateStamp:文件建立的时间。你可用这个值来区分同一个文件的不同的版本,即使 它们的商业版本号相同。这个值的格式并没有明确的规定,但是很显然的大多数的C编译器都 把它定为从1970.1.1 00:00:00以来的秒数(time_t )。这个值有时也被用做绑定输入目录表 ,这将在下面介绍。 注意:一些编译器将忽略这个值。 PointerToSymbolTable 及 NumberOfSymbols:用在调试信息中,我不太清楚它们的用途 ,不过发现它们总为0。 SizeOfOptionalHeader:可选头的长度(sizeof IMAGE_OPTIONAL_HEADER)你可以用它 来检验PE文件的正确性。 Characteristics:是一个标志的集合,其中大部分的位用在目标文件(OBJ)或库文件 (LIB)中: Bit 0 (IMAGE_FILE_RELOCS_STRIPPED):置1表示文件中没有重定向信息。每个段都 有它们自己的重定向信息。这个标志在可执行文件中没有使用,在可执行文件中是用一个叫做 基址重定向目录表来表示重定向信息的,这将在下面介绍。 Bit 1 (IMAGE_FILE_EXECUTABLE_IMAGE):置1表示该文件是可执行文件(也就是说 不是一个目标文件或库文件)。 Bit 2 (IMAGE_FILE_LINE_NUMS_STRIPPED):置1表示没有行数信息;在可执行文件 中没有使用。 Bit 3 (IMAGE_FILE_LOCAL_SYMS_STRIPPED):置1表示没有局部符号信息;在可执行 文件中没有使用。 Bit 4 (IMAGE_FILE_AGGRESIVE_WS_TRIM): Bit 7 (IMAGE_FILE_BYTES_REVERSED_LO) Bit 15 (IMAGE_FILE_BYTES_REVERSED_HI):表示文件的字节顺序如果不是机器所期 望的,那么在读出之前要进行交换。在可执行文件中它们是不可信的(操作系统期望按正确的 字节顺序执行程序)。 Bit 8 (IMAGE_FILE_32BIT_MACHINE):表示希望机器为32位机。这个值永远为1。 Bit 9 (IMAGE_FILE_DEBUG_STRIPPED):表示没有调试信息,在可执行文件中没有使 用。 Bit 10 (IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP):置1表示该程序不能运行于可移 动介质中(如软驱或CD-ROM)。在这种情况下,OS必须把文件拷贝到交换文件中执行。 Bit 11 (IMAGE_FILE_NET_RUN_FROM_SWAP):置1表示程序不能在网上运行。在这种 情况下,OS必须把文件拷贝到交换文件中执行。 Bit 12 (IMAGE_FILE_SYSTEM):置1表示文件是一个系统文件例如驱动程序。在可执 行文件中没有使用。 Bit 13 (IMAGE_FILE_DLL):置1表示文件是一个动态链接库(DLL)。 Bit 14 (IMAGE_FILE_UP_SYSTEM_ONLY):表示文件被设计成不能运行于多处理器系 统中。 四、可选头(Optional Header) 文件头下面就是可选头,这是一个叫做IMAGE_OPTIONAL_HEADER的结构。它包含很多关于 PE文件定位的信息。下面分别介绍: typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // WORD Magic; //0x18 BYTE MajorLinkerVersion; //0x1a BYTE MinorLinkerVersion; //0x1b DWORD SizeOfCode; //0x1c DWORD SizeOfInitializedData; //0x20 DWORD SizeOfUninitializedData; //0x24 DWORD AddressOfEntryPoint; //0x28 DWORD BaseOfCode; //0x2c DWORD BaseOfData; //0x30 // // NT additional fields. // DWORD ImageBase; //0x34 DWORD SectionAlignment; //0x38 DWORD FileAlignment; //0x3c WORD MajorOperatingSystemVersion; //0x3e WORD MinorOperatingSystemVersion; //0x40 WORD MajorImageVersion; //0x42 WORD MinorImageVersion; //0x44 WORD MajorSubsystemVersion; //0x46 WORD MinorSubsystemVersion; //0x48 DWORD Win32VersionValue; //0x4c DWORD SizeOfImage; //0x50 DWORD SizeOfHeaders; //0x54 DWORD CheckSum; //0x58 WORD Subsystem; //0x5c WORD DllCharacteristics; //0x5e DWORD SizeOfStackReserve; //0x60 DWORD SizeOfStackCommit; //0x64 DWORD SizeOfHeapReserve; //0x68 DWORD SizeOfHeapCommit; //0x6c DWORD LoaderFlags; //0x70 DWORD NumberOfRvaAndSizes; //0x74 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER; Magic:这个值好象总是0x010b。 MajorLinkerVersion及MinorLinkerVersion:链接器的版本号,这个值不太可靠。 SizeOfCode:可执行代码的长度。 SizeOfInitializedData:初始化数据的长度(数据段)。 SizeOfUninitializedData:未初始化数据的长度(bss段)。 AddressOfEntryPoint:代码的入口RVA地址,程序从这儿开始执行。 BaseOfCode:可执行代码起始位置,意义不大。 BaseOfData:初始化数据起始位置,意义不大。 ImageBase:载入程序首选的RVA地址。这个在址可被Loader改变。 SectionAlignment:段加载后在内存中的对齐方式。 FileAlignment:段在文件中的对齐方式。 MajorOperatingSystemVersion及MinorOperatingSystemVersion:操作系统版本,Load er并没有用它。 MajorImageVersion及MinorImageVersion:程序版本。 MajorSubsystemVersion及MinorSubsystemVersion:子系统版本号,这个域系统支持; 例如:如果程序运行于NT下,子系统版本号如果不是4.0的话,对话框不能显示3D风格。 Win32VersionValue:这个值好象总是为0。 SizeOfImage:程序调入后占用内存大小(字节),等于所有段的长度之和。 SizeOfHeaders:所有文件头的长度之和,它等于从文件开始到第一个段的原始数据之间 的大小。 CheckSum:校验和。它仅用在驱动程序中,在可执行文件中可能为0。它的计算方法Mic rosoft不公开,在imagehelp.dll中的CheckSumMappedFile()函数可以计算它。 Subsystem:NT子系统,可能是以下的值: IMAGE_SUBSYSTEM_NATIVE (1) 不需要子系统。用在驱动程序中。 IMAGE_SUBSYSTEM_WINDOWS_GUI(2) WIN32 graphical程序(它可用AllocConsole()来打开一个控制台,但是不能在 一开始自动得到)。 IMAGE_SUBSYSTEM_WINDOWS_CUI(3) WIN32 console程序(它可以一开始自动建立)。 IMAGE_SUBSYSTEM_OS2_CUI(5) OS/2 console程序(因为程序是OS/2格式,所以它很少用在PE)。 IMAGE_SUBSYSTEM_POSIX_CUI(7) POSIX console程序。 Windows95程序总是用WIN32子系统,所以只有2和3是合法的值。 DllCharacteristics:Dll状态。 SizeOfStackReserve:保留堆栈大小。 SizeOfStackCommit:启动后实际申请的堆栈数,可随实际情况变大。 SizeOfHeapReserve:保留堆大小。 SizeOfHeapCommit:实际堆大小。 LoaderFlags:好象没有用。 NumberOfRvaAndSizes:下面的目录表入口个数,这个值也不可靠,你可用常数IMAGE_N UMBEROF_DIRECTORY_ENTRIES来代替它,值好象总等于16。 DataDirectory:是一个IMAGE_DATA_DIRECTORY数组,数组元素个数为IMAGE_NUMBEROF_ DIRECTORY_ENTRIES,结构如下: typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; VirtualAddress:起始RVA地址。 Size:长度。 每一个目录表代表以下的值: IMAGE_DIRECTORY_ENTRY_EXPORT (0) IMAGE_DIRECTORY_ENTRY_IMPORT (1) IMAGE_DIRECTORY_ENTRY_RESOURCE (2) IMAGE_DIRECTORY_ENTRY_EXCEPTION (3) IMAGE_DIRECTORY_ENTRY_SECURITY (4) IMAGE_DIRECTORY_ENTRY_BASERELOC (5) IMAGE_DIRECTORY_ENTRY_DEBUG (6) IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7) IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8) IMAGE_DIRECTORY_ENTRY_TLS (9) IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10) IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11) IMAGE_DIRECTORY_ENTRY_IAT (12)