PE头部分的解析可以由工具直接解析,但是新手初次接触PE文件时,可以尝试手动解析进行练习,加深印象。
查看PE头
可以使用PEview打开文件来查看PE文件的头部分,这里以使用PEview打开visio2003为例
在该界面可以看到文件中全部文件指针的数据,我们对照DOS头和NT头的结构,可以将该结构体的每一个成员与对应的数据对应起来。
需要注意的是,PEview是16进制编辑器,因此从左至右依次读取结构体成员对应的数据,但是读取的单个数据是以两个字节为一组,从右至左排列。
举个例子,根据PE头的结构,PE文件头部分的第一部分是DOS头,其第一个成员为e_magic,一共四个字节,则对应上图中的数据可知,e_magic的数据即为5A4D,同理,e_cdlp为0090,e_cp为0003......
将DOS头和NT头全部读取得:
DOS头:
pFile | Data | Descrption |
WORD e_magic | 5A4D | Magic number |
WORD e_cblp | 0090 | Bytes on last page of file |
WORD e_cp | 0003 | Pages in file |
WORD e_crlc | 0000 | Relocations |
WORD e_cparhdr | 0004 | Size of header in paragraphs |
WORD e_minalloc | 0000 | Minimum extra paragraphs needed |
WORD e_maxalloc | FFFF | Maximum extra paragraphs needed |
WORD e_ss | 0000 | Initial SS value |
WORD e_sp | 00B8 | Initial SP value |
WORD e_csum | 0000 | Checksum |
WORD e_ip | 0000 | Initial IP value |
WORD e_cs | 0000 | Initial CS value |
WORD e_lfarlc | 0040 | File address of relocation table |
WORD e_ovno | 0000 | Overlay number |
WORD e_res[4] | 0000 | Reserved words |
WORD e_oemid | 0000 | OEM identifier |
WORD e_oeminfo | 0000 | OEM information |
WORD e_res2[10] | 0000 | Reserved words |
LONG e_lfanew | 000000F8 | File address of new exe header |
标准PE头:
pFile | Data |
WORD Machine | 014C |
WORD NumberOfSections | 0005 |
DWORD TimeDateStamp | 472B98E3 |
DWORD PointerToSymbolTable | 00000000 |
DWORD NumberOfSymbols | 00000000 |
WORD SizeOfOptionalHeader | 00E0 |
WORD Characteristics | 010E |
可选PE头:
pFile | Data |
WORD Magic | 010B |
BYTE MajorLinkerVersion | 07 |
BYTE MinorLinkerVersion | 0A |
DWORD SizeOfCode | 00000600 |
DWORD SizeOfInitializedData | 0002B400 |
DWORD SizeOfUninitializedData | 00000000 |
DWORD AddressOfEntryPoint | 00001114 |
DWORD BaseOfCode | 00001000 |
DWORD BaseOfData | 00002000 |
DWORD ImageBase | 00400000 |
DWORD SectionAlignment | 00001000 |
DWORD FileAlignment | 00002000 |
WORD MajorOperatingSystemVersion | 0004 |
WORD MinorOperatingSystemVersion | 0000 |
WORD MajorImageVersion | 0000 |
WORD MinorImageVersion | 0000 |
WORD MajorSubsystemVersion | 0004 |
WORD MinorSubsystemVersion | 0000 |
DWORD Win32VersionValue | 00000000 |
DWORD SizeOfImage | 00030000 |
DWORD SizeOfHeaders | 00000400 |
DWORD CheckSum | 0003098D |
WORD Subsystem | 0002 |
WORD DllCharacteristics | 0000 |
DWORD SizeOfStackReserve | 00100000 |
DWORD SizeOfStackCommit | 00001000 |
DWORD SizeOfHeapReserve | 00100000 |
DWORD SizeOfHeapCommit | 00001000 |
DWORD LoaderFlags | 00000000 |
DWORD NumberOfRvaAndSizes | 00000010 |
编写读取PE文件的函数
void* ReadPEfile(char* Filename)
{
//定义一个无类型的指针pfilebuffer
void* pfilebuffer;
FILE* pfile=NULL;
//以可读可写的方式打开文件
pfile=fopen(Filename,"rb");
if(pfile=NULL)
{
printf("Failed to open PEfile %s .",Filename);
return 0;
}
//读取文件大小(fseek将文件指针移到文件末尾,ftell返回文件首位之间的长度,再调用fseek将文件指针移动到文件开头)
fseek(pfile,0,SEEK_END);
Filesize=ftell(pfile);//预先定义Filesize为全局变量
fseek(pfile,0,SEEK_SET);
//malloc函数申请内存空间
pfilebuffer=malloc(Filesize);
if(!pfilebuffer)
{
printf("Failed to apply for space .");
fclose(pfile);
return 0;
}
//以一个字节为步长,从pfile所在的位置开始,将Filesize个数据读到pfilebuffer中
int n=fread(pfilebuffer,Filesize,1,pfile);
if(!n)
{
printf("Failed to read data");
free(pfilebuffer);
fclose(pfile);
}
fclose(pfile);
return pfilebuffer;
};