下面将总结一下装载一PE文件的主要步骤:
1 当PE文件被执行,PE装载器检查 DOS MZ header 里的 PE header 偏移量。如果找到,则跳转到 PE header。
2 PE装载器检查 PE header 的有效性。如果有效,就跳转到PE header的尾部。
3 紧跟 PE header 的是节表。PE装载器读取其中的节信息,并采用文件映射方法将这些节映射到内存,同时付上节表里指定的节属性。
4 PE文件映射入内存后,PE装载器将处理PE文件中类似 import table(引入表)逻辑部分。
DOS MZ header:
它是一个IMAGE_DOS_HEADER结构。
所有 PE文件(甚至32位的 DLLs) 必须以一个简单的 DOS MZ header 开始。我们通常对此结构没有太大兴趣。有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随 MZ header 之后的 DOS stub。DOS stub实际上是个有效的 EXE,在不支持 PE文件格式的操作系统中,它将简单显示一个错误提示,类似于字符串 "This program requires Windows" 或者程序员可根据自己的意图实现完整的 DOS代码。通常我们也不对 DOS stub 太感兴趣: 因为大多数情况下它是由汇编器/编译器自动生成。通常,它简单调用中断21h服务9来显示字符串"This program cannot run in DOS mode"。
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;
这个结构占用64个bytes.
第一个字段e_magic,叫所谓的Magic number,幻数.它的值是 0x4D5A, 即为MZ。
最后一个字段e_lfanew,是PE头在文件中的偏移量。
E头(PE header):它是一个IMAGE_NT_HEADERS 结构。
PE header 是PE相关结构 IMAGE_NT_HEADERS 的简称,其中包含了许多PE装载器用到的重要域。
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
Members
Signature
A 4-byte signature identifying the file as a PE image. The bytes are "PE/0/0".
FileHeader
An IMAGE_FILE_HEADER structure that specifies the file header.
OptionalHeader
An IMAGE_OPTIONAL_HEADER structure that specifies the optional file header.
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;// The architecture type of the computer,CUP体系
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;// 文件特征值
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
Members.
NumberOfSections
The number of sections. This indicates the size of the section table, which immediately follows the headers. Note that the Windows loader limits the number of sections to 96.
TimeDateStamp
The low 32 bits of the time stamp of the image. This represents the date and time the image was created by the linker. The value is represented in the number of seconds elapsed since midnight (00:00:00), January 1, 1970, Universal Coordinated Time, according to the system clock.
PointerToSymbolTable
The offset of the symbol table, in bytes, or zero if no COFF symbol table exists.
NumberOfSymbols
The number of symbols in the symbol table.
SizeOfOptionalHeader
The size of the optional header, in bytes. This value should be 0 for object files.
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; //The state of the image file
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
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;
IMAGE_OPTIONAL_HEADER32这个结构大小为224 bytes.虽然名字中有optional一词,但是它却是PE文件中不可或缺的。这个结构中包含很多有意义的信息,初始堆栈大小,程序入门位置,首选基地址,操作系统版本,段对齐信息等。如下是一些重要的字段:
AddressOfEntryPoint
A pointer to the entry point function, relative to the image base address. For executable files, this is the starting address. For device drivers, this is the address of the initialization function. The entry point function is optional for DLLs. When no entry point is present, this member is zero.
BaseOfCode
A pointer to the beginning of the code section, relative to the image base.
BaseOfData
A pointer to the beginning of the data section, relative to the image base.
ImageBase
The preferred address of the first byte of the image when it is loaded in memory. This value is a multiple of 64K bytes. The default value for DLLs is 0x10000000. The default value for applications is 0x00400000, except on Windows CE where it is 0x00010000.
SizeOfStackReserve
The number of bytes to reserve for the stack. Only the memory specified by the SizeOfStackCommit member is committed at load time; the rest is made available one page at a time until this reserve size is reached.
SizeOfStackCommit
The number of bytes to commit for the stack.
SizeOfHeapReserve
The number of bytes to reserve for the local heap. Only the memory specified by the SizeOfHeapCommit member is committed at load time; the rest is made available one page at a time until this reserve size is reached.
SizeOfHeapCommit
The number of bytes to commit for the local heap.
DWORD SectionAlignment
被映射到内存时,每个节都被保证开始于这个值的整数倍的虚拟地址。为了便于分页缺省的SectionAlignment是0x1000。
DWORD FileAlignment
在PE文件中,组成每个节的原始数据都被保证开始于这个值的整数倍。缺省值是0x200 字节,也是是为了确保每个节总是位于一个磁盘扇区的开头(磁盘扇区的长度也是0x200字节)。
这就说明RVA在数值上不等以文件偏移量!需要转化!