PE(Portable Executeable FileFormat)可移植的执行文体格式,我们平日时常见的exeldll\sys等都是PE文件的一种。PE文件的组成可以被分为PE Head和PE Body,我们先从PE文件头的Dos头部说起。
DOS头部分为两部分:第一部分是DOSMZ头第二部分是DOS Stub(指令令字节码Q)。
以下为DOS MZ头部微软给出的定义,其中我学习中用到的最关键两个字字段就是e_magic和e_lfanew,分别用来确定是否是PE文件和寻址 NT Headers的首地址
typedef struct _IMAGE_DOS_HEADER // DOS .EXE header { WORD e_magic; //魔术字 0x4d5a MZ 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; //DOS头部距离NT头部的偏移 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
Dos Stub大多数时候是由编译器自动生成,其长度是PE文件链接生成时确定的,其长度在不同的PE文件中不一定相同,其紧跟在DOS MZ头部的下面,整个时一个字节块,PE中没有与之相关的结构体。所以以我们不可以直接用Dos Heaher基地址+sizeof(IMAGE DOSHEADER)的方式定位到Nt头部。
接下来附上一点我的测试代码和定位到NT头的代码:
TCHAR* CPEHelper::GetFileMemPointer(TCHAR* FileFullPath,DWORD* m_FileSize) { //打开文件获取句柄 HANDLE FileHandle = CreateFile( FileFullPath, GENERIC_READ, //只读 FALSE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (FileHandle == INVALID_HANDLE_VALUE) { MessageBox(0, _T("文件打开失败\r\n"), 0, 0); return NULL; } //获取文件大小 DWORD FileSize = GetFileSize(FileHandle, NULL); *m_FileSize = FileSize; //文件大小返出值 //申请内存 用来存放PE文件数据 TCHAR* BuferData = new TCHAR[FileSize] {}; //读文件 DWORD ReturnLength; ReadFile(FileHandle, BuferData, FileSize, &ReturnLength, NULL); //关闭文件句柄 if (FileHandle != NULL) { CloseHandle(FileHandle); FileHandle = NULL; } //返回PE文件起始位置(基址) return BuferData; } //.......判断PE文件的一部分代码 PIMAGE_DOS_HEADER ImageDosHeader = (PIMAGE_DOS_HEADER)FileBuffer;//FileBuffer = 上面函数返回的BufferData if (ImageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { //#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ return FALSE; } //定位到Nt头部 PIMAGE_NT_HEADERS ImageNtHeaders = (PIMAGE_NT_HEADERS)(ImageDosHeader->e_lfanew + (ULONG)FileBuffer);