1 (PE结构图)
在PE文件中,代码,已初始化的数据,资源和重定位信息等数据都被按照属性分类放到不同的section(节区)中。而每个节区的属性和位置用一个IMAGE_SECTION_HEADER结构来描述,所有的IMAGE_SECTION_HEADER结构组成一个节表(section table),节表数据在PE文件中被存放在所有节数据之前。
由于数据是按照属性在节中放置的,不同用途但是属性想同的数据(如导入表,导出表以及.const段指定的只读数据)可能被放在同一个节中,所以PE文件中还用一系列的数据目录结构IMAGE_DATA_DIRECTORY来分别指明这些数据的位置,数据目录表和其他描述文件属性的数据合在一起称为PE文件头,PE文件头被放置在节和节表的前面,如图1。
PE头部分包含了DOS头和NT头,PE文件中的DOS部分由MZ格式的文件头和可执行代码部分组成,可执行代码被称为"DOS块"(DOS stub).MZ格式的文件头由IMAGE_DOS_HEADER结构定义。
IMAGE_DOS_HEADER STRUCT { WORD e_magic //Magic DOS signature MZ(4Dh 5Ah) DOS可执行文件标记 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 //Minimun extra paragraphs needs WORD e_maxalloc //Maximun extra paragraphs needs WORD e_ss //intial(relative)SS value DOS代码的初始化堆栈SS WORD e_sp //intial SP value DOS代码的初始化堆栈指针SP WORD e_csum //Checksum WORD e_ip // intial IP value DOS代码的初始化指令入口[指针IP] WORD e_cs //intial(relative)CS value DOS代码的初始堆栈入口 hWORD 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 DWORD e_lfanew //Offset to start of PE header 指向PE文件头 } IMAGE_DOS_HEADER ENDS
DOS头文件第一个字段e_magic被定义成字符“MZ”作为标识,e_lfanew字段指向了真正的PE头(NT头),这个位置总是以8字节为单位对齐的。
从DOS文件头的e_lfanew字段(文件头偏移003ch)得到真正的PE头位置,PE头由IMAGE_NT_HEADER结构定义。
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; //PE文件标识 IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER OptionalHeader; } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
那么如何解析DOS头和NT头地址呢?
1. 打开文件
2. 创建一个文件映射对象
3. 文件内存视图映射
4. 获取DOS文件头和NT文件头地址
HANDLE fHandle = CreateFile( "1.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if ( INVALID_HANDLE_VALUE == fHandle) { std::cout << "打开文件失败: " << GetLastError() << std::endl; exit(-1); } // 创建一个文件映射对象 HANDLE mapHandle = CreateFileMapping( fHandle, NULL, PAGE_READONLY, 0, 0, NULL); if( NULL == mapHandle ) { std::cout << "打开文件映射对象失败: " << GetLastError() << std::endl; CloseHandle(mapHandle); } // 文件内存视图映射 LPVOID strContet = MapViewOfFile( mapHandle, FILE_MAP_READ, 0, 0, 0); LPBYTE lpBaseAddress = (LPBYTE)strContet; PIMAGE_DOS_HEADER dosHead = (PIMAGE_DOS_HEADER)lpBaseAddress; PIMAGE_NT_HEADERS ntHead = (PIMAGE_NT_HEADERS)(lpBaseAddress + dosHead->e_lfanew);