一、前言
二、PE整体结构
三、DOS头
四、NT头
4.1.文件头
4.2.扩展头
五、区段头
六、导出表
七、导入表
八、资源表
九、其他表
四、NT头
图4.1
010Editor中,在右侧选中IMAGE_NT_HEADERS(NT头)结构体,左侧对应数据内容即被选中。
起始地址:0x0158H,和上一篇DOS头里提到的e_lfanew完全相符。
NT头结构:在图右侧可以看到,整个NT头包含一个4字节的Signature,以及两个结构体IMAGE_FILE_HEADER(文件头)和IMAGE_OPTIONAL_HEADER(扩展头)
Signature:这也是一个标识位。(还记得DOS头的e_magic吗?值为WZ的PE文件判断标识。)用于判断是否是PE文件的第二个标识符。
起始地址:0x0158H,和上一篇DOS头里提到的e_lfanew完全相符。
NT头结构:在图右侧可以看到,整个NT头包含一个4字节的Signature,以及两个结构体IMAGE_FILE_HEADER(文件头)和IMAGE_OPTIONAL_HEADER(扩展头)
Signature:这也是一个标识位。(还记得DOS头的e_magic吗?值为WZ的PE文件判断标识。)用于判断是否是PE文件的第二个标识符。
// NT头起始地址,Signature地址 DWORD dwNtSign = (DWORD)pFile + ((PIMAGE_DOS_HEADER)pFile)->e_lfanew; // 定义NT头结构体对象 PIMAGE_NT_HEADERS32 pNTHeader = (PIMAGE_NT_HEADERS32)(dwNtSign); // 判断标识 if(pNTHeader->Signature != IMAGE_NT_SIGNATURE) { // 不是PE文件 }
4.1.文件头
typedef struct _IMAGE_FILE_HEADER { WORD Machine; // 文件运行平台 WORD NumberOfSections; // 区段数量 DWORD TimeDateStamp; // 文件创建时间 DWORD PointerToSymbolTable; // 符号表偏移 DWORD NumberOfSymbols; // 符号个数 WORD SizeOfOptionalHeader; //扩展头大小 WORD Characteristics; // PE文件属性 }
Machine
表示文件运行的平台,比如:0x014c代表i386……我是没用到过。
NumberOfSections
区段个数,标识文件的主体被分成了多少部分,一般有text段、data段……
TimeDateStamp
文件创建时间,值需要经过比较复杂的转化,才能显示成正确的时间,就不详细写了
SizeOfOptionalHeader
TimeDateStamp
文件创建时间,值需要经过比较复杂的转化,才能显示成正确的时间,就不详细写了
SizeOfOptionalHeader
扩展头大小,通常32位程序里值为00E0,64位程序里值为00F0
Characteristics
Characteristics
文件属性,里面是按位进行的标识。知道0x0210代表DLL,0x010F代表EXE初期应该就够了
4.2.扩展头
图4.3
很复杂很烦很闹心的一个结构体 IMAGE_OPTIONAL_HEADER32
这里只挑里面重要的字段进行说明
SizeOfCode
代码区段(一般是.text段)文件对齐(200的整数倍对齐,后面会讲到)大小。
该字段可被用于校验文件是否被改动。
图4.4
图4.3可以看到这里的值是0x45D400H
图4.4
图4.3可以看到这里的值是0x45D400H
从图4.4中,可以验证,百度网盘的text段的大小确实是0x45D400H。
AddressOfEntryPoint
简称OEP,程序入口点,程序开始执行的相对虚拟地址,非常重要的字段。
以前学开发时,一直以为程序是从Main函数开始的,其实Main函数只是用户编写程序的开始,在此之前还有很多初始化、预处理的程序需要由系统执行,那么这些程序开始的地址,就是OEP指向的地址。
ImageOfBase
加载基址,非常重要。
PE文件没加载进内存前,在文件内都是从0x0000H开始的,在DOS头部分已经讲过。
PE文件加载进内存后,其起始地址就不确定了。
通常PE文件ImageOfBase的值都是0x400000H,但这只是首选地址,如果被占用,则会加载到其他地址。
DLL的加载基址是0x10000000H。
SectionAlignment、FileAlignment
内存对齐 和 文件对齐。默认值分别是0x1000H和0x200H。
当文件加载到内存后,所有头部在一起所占空间必须是0x1000H的整数倍,每个区段所占空间 必须是0x1000H的整数倍。
文件未加载到内存时,所有头部在一起所占空间必须是0x200H的整数倍,每个区段所占空间 必须是0x200H的整数倍 。
图4.5
图4.5显示的是百度云盘程序未加载到内容时的数据结构,可以看到标红的地方,是每部分数据的起始地址,都是0x200H的整数倍。
切记:文件加载前后,其各部分的地址和大小是会发生变化的,当然具体数值可以互相转化,稍后会介绍。
SizeOfImage
文件加载进内存所需要的对齐(0x1000H)后的内存大小。
SizeOfHeaders
所有头部大小,可用于得到文件主体相对文件起始的偏移。
DllCharacteristics
DLL特性标志。该字段也是按位来进行各种特性的标识。
其中有一个比较重要 IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
这个位标识是否启用动态基址,1启用、0关闭
图4.6
NumberOfRvaAndSizes
数据目录个数,也是下一个要介绍的数组的原始个数。
通常该值都为0x10。也就是有16个数据目录。
DataDirectory
数据目录表。这是一个结构体数组。数组里的每个元素对应一个数据表。通常有16个。
数组每个元素都是一个结构体,结构体如下
图4.6
NumberOfRvaAndSizes
数据目录个数,也是下一个要介绍的数组的原始个数。
通常该值都为0x10。也就是有16个数据目录。
DataDirectory
数据目录表。这是一个结构体数组。数组里的每个元素对应一个数据表。通常有16个。
数组每个元素都是一个结构体,结构体如下
typedef struct _IMAGE_DATA_DIRECTORY
{
DWORD VirtualAddress; // 数据表的起始虚拟地址
DWORD Size; // 数据表大小
}IMAGE_DATA_DIRECTORY,*IMAGE_DATA_DIRECTORY
16个数据表依次如下:
导出表、导入表、资源表、异常处理表、安全表、重定位表、调试表、版权、指针目录、TLS、载入配置、绑定输入目录、导入地址表、延迟载入、COM信息。
上一篇 : 【PE结构】1.PE文件结构、DOS头
导出表、导入表、资源表、异常处理表、安全表、重定位表、调试表、版权、指针目录、TLS、载入配置、绑定输入目录、导入地址表、延迟载入、COM信息。
上一篇 : 【PE结构】1.PE文件结构、DOS头
下一篇 :
【PE结构】3.区段头