一:DOS部分
DOS部分分为DOS MZ文件头和DOS块,其中DOS MZ头实际是一个64位的IMAGE_DOS——HEADER结构体。
DOS MZ头部结构体的内容如下,我们所需要关注的是前面两个字节(e_magic)和后面四个字节(e_lfanew)
typedef struct _IMAGE_DOS_HEADER
{WORD e_magic; // DOS头的标识 "MZ" 4Dh 5Ah(2个字节) #define IMAGE_DOS_SIGNATURE 0x5A4DWORD e_cblp; // 文件最后一页中的字节数WORD e_cp; // 文件中的全部页数WORD e_crlc; // 重定位表中的指针数WORD e_cparhdr; // 头部尺寸,以段落为单位WORD e_minalloc; // 所需的最小附加段 WORD e_maxalloc; // 所需的最大附加段 WORD e_ss; // 初始的SS值(相对偏移量)WORD e_sp; // 初始的SP值WORD e_csum; // 补码校验值WORD e_ip; // 初始的IP值 WORD e_cs; // 初始的CS值WORD e_lfarlc; // 重定位表的字节偏移量 WORD e_ovno; // 覆盖号WORD e_res[4]; // 保留字WORD e_oemid; // OEM 标识符(相对e_oeminfo) WORD e_oeminfo; // OEM 信息WORD e_res2[10]; // 保留字// DosHeader + 0x3C 正好定位到e_lfanewLONG e_lfanew; // NT头相对于文件起始地址的偏移, 4字节, 指示NT头的位置
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
二、PE文件头
PE文件头可以分为三个部分,分别为PE标识、标准PE头、扩展PE头,结构体内容如下所示
typedef struct _IMAGE_NT_HEADERS {DWORD Signature; ``// PE标识--4字节IMAGE_FILE_HEADER FileHeader; ``// 标准PE头--20字节IMAGE_OPTIONAL_HEADER32 OptionalHeader; ``// 扩展PE头--224字节/240字节
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
现在我们详细解释一下标准PE头,标准PE头的结构体如下:
typedef struct _IMAGE_FILE_HEADER
{WORD Machine; //PE文件运行的平台,值为IMAGE_FILE_MACHINE_I386(0x14c)表示是x86处理器,//IMAGE_FILE_MACHINE_AMD64(0x8664)或IMAGE_FILE_MACHINE_IA64(0x200)表示是x64处理器。WORD NumberOfSections; //文件中存在的节的个数,如果想在PE文件中增加或删除节,必须变更此处的值DWORD TimeDateStamp; //创建此文件时的时间戳DWORD PointerToSymbolTable; //COFF符号表的文件偏移,对于映像文件来说,此值为0DWORD NumberOfSymbols; //符号表中元素的数目,对于映像文件来说,此值为0WORD SizeOfOptionalHeader; //可选头的大小,32位下默认为00E0h,64位下默认为00F0hWORD Characteristics; //文件属性标志,exe一般是010fh,dll一般是210eh
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
现在我们得到:
那什么是PE文件属性呢,现在我们知道PE文件属性的十六进制值是00 22(倒序读取),那么可以得到二进制是0001 0110
对应下面的表,也就是第1位、第二位和第四位对应数字为1得到的属性值
对于扩展PE头,大小有标准PE头的SizeOfOptionalHeader决定,就是00F0
三、节表
每个节表的固定大小是40字节,节表不止一个,可能有多个,节表的数量是标准PE头中的NumberOfSections属性决定的,虽然节表有多个,但是每个节表中的结构是相同的。
根据上面的标准头解析,可以看到是六个节表(00 06),每个节表大小是40字节,一共是240字节,如下图
每个节表的最后四个字节是属性,这里是60 00 00 20,可以得到二进制是0110 0000 0000 0000 0000 0000 0010 0000
那么就可以得到:
利用工具打开,可以发现是六个节表
以下是节表的基本结构
typedef struct _IMAGE_SECTION_HEADER {
0x00 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //00 00 00 74 78 65 74 2E 8字节,节表的名字,一般情况下"\0"来结束,内容可以自己定义
union { 0x08 DWORD PhysicalAddress; 0x08 DWORD VirtualSize;
} Misc; //00 01 80 6C 双字,是该节在没有对齐前的真是尺寸,内容可以不准确
0x0c DWORD VirtualAddress; //00 00 10 00 节区在内存中的偏移地址
0x10 DWORD SizeOfRawData; //00 01 82 00 节在文件中对齐后的尺寸
0x14 DWORD PointerToRawData; //00 00 04 00 节区在文件中的偏移
0x18 DWORD PointerToRelocations; //00 00 00 00 在exe文件中无意义
0x1c DWORD PointerToLinenumbers; //00 00 00 00 在exe文件中无意义
0x20 WORD NumberOfRelocations; //00 00 在exe文件中无意义
0x22 WORD NumberOfLinenumbers; //00 00 该节在行号表中的行号数
0x24 DWORD Characteristics; //60 00 00 20 节的属性
};
四、节数据
查看区段:
我们需要的是节区在文件中的偏移,就是在20字节之后的00000400,节区的大小是在16字节之后的,也就是0046FE00
也就是00000400之后的数据:
以上就是最基本的PE文件结构了。