PE的结构如下图所示:
1.开始以DOS MZ开头,在0000003CH的位置找到对应的值计算真正地址--PE的头地址
PE文件中的DOS部分由MZ格式的文件头和可执行代码部分组成,可执行代码被称为“DOS块”(DOS stub)。一般来说,DOS部分的执行代码只是简单地显示一个“This program cannot be run in DOS mode.”就退出了,这段简单的代码是编译器自动生成的。
1: typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
2: +0h WORD e_magic; // Magic number
3: +2h WORD e_cblp; // Bytes on last page of file
4: +4h WORD e_cp; // Pages in file
5: +6h WORD e_crlc; // Relocations
6: +8h WORD e_cparhdr; // Size of header in paragraphs
7: +0ah WORD e_minalloc; // Minimum extra paragraphs needed
8: +0ch WORD e_maxalloc; // Maximum extra paragraphs needed
9: +0eh WORD e_ss; // Initial (relative) SS value DOS代码的初始化堆栈SS
10: +10h WORD e_sp; // Initial SP value DOS代码的初始化堆栈指针SP
11: +12h WORD e_csum; // Checksum
12: +14h WORD e_ip; // Initial IP value DOS代码的初始化指令入口[指针IP]
13: +16h WORD e_cs; // Initial (relative) CS value DOS代码的初始堆栈入口
14: +18h WORD e_lfarlc; // File address of relocation table
15: +1ah WORD e_ovno; // Overlay number
16: +1ch WORD e_res[4]; // Reserved words
17: +24h WORD e_oemid; // OEM identifier (for e_oeminfo)
18: +26h WORD e_oeminfo; // OEM information; e_oemid specific
19: +29h WORD e_res2[10]; // Reserved words
20: +3ch LONG e_lfanew; // File address of new exe header 指向PE文件头
21: } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
在PE头地址开始,又是一个结构体
1: typedef struct _IMAGE_NT_HEADERS {
2: +0h DWORD Signature; //签名
3: +4h IMAGE_FILE_HEADER FileHeader; //文件信息见下面展开
4: +18h IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选部分
5: } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
1: typedef struct _IMAGE_FILE_HEADER {
2: +4h WORD Machine; //运行平台
3: +6h WORD NumberOfSections; //文件的区块数目
4: +8h DWORD TimeDateStamp; //文件创建日期和时间
5: +0ch DWORD PointerToSymbolTable; //指向符号表(主要用于调试)
6: +10h DWORD NumberOfSymbols; //符号表中符号个数(同上)
7: +14h WORD SizeOfOptionalHeader; //IMAGE_OPTIONAL_HEADER32结构大小
8: +16h WORD Characteristics; //文件属性
9: } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
1.如何计算节,表
节,表,在OptionalHeader之后,所以先要计算OptionalHeader的地址加上OptionalHeader长度之后就是节表的位置
OptionalHeader是在PE结构偏移18H位置, OptionalHeader=00000118H+18H=00000130H
计算SizeOfOptionalHeaeder
SizeOfOptionalHeaeder =PE+14H(20) =00000118H+14H=0000012CH(地址对应的值,00E0)
所以节表的位置在00000130H+00E0=00000210H
1: #define IMAGE_SIZEOF_SHORT_NAME 8
2:
3: typedef struct _IMAGE_SECTION_HEADER {
4: BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //节表名称,如".text"
5: union {
6: DWORD PhysicalAddress; //物理地址
7: DWORD VirtualSize; //真实长度,这两个值是一个联合结构,可以使用任何一个,一般取后一个
8: } Misc;
9: DWORD VirtualAddress; //节区的RVA地址
10: DWORD SizeOfRawData; //在文件中对齐后的尺寸
11: DWORD PointerToRawData; //在文件中的偏移量
12: DWORD PointerToRelocations; //在OBJ文件中使用,重定位的偏移
13: DWORD PointerToLinenumbers; //行号表的偏移,提供调试使用
14: WORD NumberOfRelocations; //在OBJ文件中使用,重定位项目数目
15: WORD NumberOfLinenumbers; //行号表中行号的数目
16: DWORD Characteristics; //节属性如可读,可写,可执行等
17: } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
每个区块表的结构占40个字节 sizeof(IMAGE_SECTION_HEADER)
Name:区块名:由一个8位的ASCII码名,用来定义区块的名称,多数区块名字以"."作为开头(例如: .text)这个"."不是必须的,如果区块名超过8个字节,则没有最后的终止符"NULL"字节,并且欠扁带有一个"$"的区块名会从连接器哪里得到特殊待遇,前边带有"$"的相同名字的区块在载入的时候会被合并,在合并之后的区块中,他们按照"$"后边的字符字母顺序进行合并.
每个区块名称都是唯一的,不能有同名的两个区块,事实上节名不代表任何意义,他的存在仅仅是为了正规统一编程的时候方便程序员查看方便而设置的一个标记而已.所以将包含代码的区块命名为".Data"或者讲包含数据的区块命名为".Code"都是合法的
RVA 相对虚拟地址(Relative Virtual Address)缩写