【PE结构】2.NT头、文件头、扩展头

一、前言
二、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文件的第二个标识符。
// 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
扩展头大小,通常32位程序里值为00E0,64位程序里值为00F0

Characteristics
文件属性,里面是按位进行的标识。知道0x0210代表DLL,0x010F代表EXE初期应该就够了

4.2.扩展头

图4.3

很复杂很烦很闹心的一个结构体 IMAGE_OPTIONAL_HEADER32
这里只挑里面重要的字段进行说明

SizeOfCode
        代码区段(一般是.text段)文件对齐(200的整数倍对齐,后面会讲到)大小。
        该字段可被用于校验文件是否被改动。
        


图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个。
        数组每个元素都是一个结构体,结构体如下
typedef struct _IMAGE_DATA_DIRECTORY
{
    DWORD VirtualAddress;    // 数据表的起始虚拟地址
    DWORD Size;    // 数据表大小
}IMAGE_DATA_DIRECTORY,*IMAGE_DATA_DIRECTORY

        16个数据表依次如下:
        导出表、导入表、资源表、异常处理表、安全表、重定位表、调试表、版权、指针目录、TLS、载入配置、绑定输入目录、导入地址表、延迟载入、COM信息。

上一篇 : 【PE结构】1.PE文件结构、DOS头

猜你喜欢

转载自blog.csdn.net/chy_chenyang/article/details/80768654