首页 > 代码库 > C读取PE信息
C读取PE信息
PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任何扩展名。相信来看本文的多少对于PE结构有一定的了解,那么今天就直接贴代码,以尽量简短的代码来实现PE的读取,没有使用一些windows.h中的很方便的操作。
首先给出一张PE结构的图片,是之前学习时从别的博客中下载下来的,忘记出处了,另外对一些比较重要的项用红色粗线在下方标注
代码部分首先就是包含文件和一些数据类型,因为结构中多采用BYTE,WORD, DWORD,LONG这样的别名,所以:
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef unsigned char BYTE; //1 typedef unsigned short WORD; //2 typedef unsigned int DWORD; //4 typedef unsigned long LONG; //4
接着就是整个PE文件的开始部分,DOS
//DOS typedef struct _IMAGE_DOS_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;
NT头部分,包含标准PE头和可选PE头:
//NT typedef struct _IMAGE_NT_HEADERS { DWORD Signature; IMAGE_FILE_HEADER FileHeader; IMAGE_OPTIONAL_HEADER32 OptionalHeader; } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
NT头中的IMAGE_FILE_HEADER(标准PE) 和 IMAGE_OPTIONAL_HEADER32(可选PE)的定义在下方:
//标准PE 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; //可选PE typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; 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_NUMBEROF_DIRECTORY_ENTRIES 16 IMAGE_DATA_DIRECTORY DataDirectory[16]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
在可选PE头中的最后一项是数据目录表,16个元素的数组
//数据目录表 typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
最后,在跳过DOS,(NT中包含PE和可选PE)PE和可选PE之后就是区段表(也有叫节表的),定义如下:
#define IMAGE_SIZEOF_SHORTT_NAME 8 typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORTT_NAME]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD MumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
以上部分都是PE的结构,固定的结构,从图中可也看出来,接下来的部分就是读取了,首先打开一个exe程序,以记事本为例,常用的fopen和fread即可:
BYTE* openFile() { int fileLen; BYTE* p; FILE *fp = fopen("C:\\WINDOWS\\system32\\notepad.exe", "rb"); fseek(fp, 0, SEEK_END); fileLen = ftell(fp); p = (BYTE*)malloc(fileLen); memset(p, 0, sizeof(p)); fseek(fp, 0, SEEK_SET); fread(p, fileLen, 1, fp); return p; }
接下来的主函数就是计算篇一部分,尽量简短:
因为可选PE在位数不同的情况下默认值是不同的,并且是可以改的,而区段表在可选PE的后面,因此采用NT的地址加上可选PE在NT中的偏移,再加上可选PE的大小就到了区段表的位置,可选PE的大小有标准PE中的SizeOfOptionalHeader指定。
int main() { //读取到内存 BYTE* pBuf = openFile(); //DOS IMAGE_DOS_HEADER* pDos = (IMAGE_DOS_HEADER *)pBuf; //NT IMAGE_NT_HEADERS32* pNt = (IMAGE_NT_HEADERS32 *)(pBuf + pDos->e_lfanew); //PE 标准PE IMAGE_FILE_HEADER* pFile = (IMAGE_FILE_HEADER *)&pNt->FileHeader; //Option 可选PE IMAGE_OPTIONAL_HEADER32* pOption = (IMAGE_OPTIONAL_HEADER32 *)&pNt->OptionalHeader; //DataDirectory 数据目录表 IMAGE_DATA_DIRECTORY* pDataDir = pOption->DataDirectory; //Section table 区段表 IMAGE_SECTION_HEADER* pSection = (IMAGE_SECTION_HEADER *)( (long)pNt +
(long)&(((IMAGE_NT_HEADERS32 *)0)->OptionalHeader) +
pFile->SizeOfOptionalHeader ); free(pBuf); return 0; }
找到了上面地址之后,就可以读取其中的数据,对照图中结构的项,加一些printf语句即可,这里就不写了,代码已用PE读取工具对照过。
C读取PE信息