PE文件结构解析 Part3 NT Headers
文章来源:https://0xrick.github.io/win-internals/pe4/
简介
在前面的文章中,我们看过了DOS Header的结构以及逆向了DOS stub。
这篇文章我们准备讨论一下PE文件结构中NT Header的部分。
在我们进入正题之前,我们需要讲一讲等下我们会用到很多次的一个重要的概念,相对虚拟地址(Relative Virtual Address)或者RVA。 RVA(相对虚拟地址)就是相对于EXE在内存中起始地址的一个偏移(相对于Image Base)。也就是说,将相对虚拟地址Relative Virtual Address转化为绝对虚拟地址,我们需要将RVA的值加上ImageBase的值。接下来我们会看到,PE很多地方都会用到RVA。
NT Headers(IMAGE_NT_HEADERS)
NT Headers是一个定义在winnt.h的结构体IMAGE_NT_HEADERS,观察它的定义,我们可以看到它有三个成员(DWORD类型的签名,IMAGE_FILE_HEADER类型的FileHeader,以及IMAGE_OPTIONAL_HEADER类型的OptionalHeader)。
值得一提的是,这个结构体有两个版本的定义。一个用于32bit,IMAGE_NT_HEADERS 。 一个用于64bit,IMAGE_NT_HEADERS64。
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
签名 Signature
NT Headers结构体的第一个成员是PE签名,它是DWORD类型,意味着它需要4字节的空间。
这个字段的值总是固定为0x50450000,用ASCII码表示为PE\0\0。
下面是来自PE-Bear的截图。

File Header(IMAGE_FILE_HEADER)
也被称作"COFF File Header", File Header这个结构体持有PE文件的一些信息。
它在winnt.h中定义为IAMGE_FILE_HEADER, 下面是它的定义:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
这是一个有着7个成员的结构体:
Machine:用于表示可执行文件的目标机器,类型是WORD。这个字段可以有很多值,但是我们只对其中两个感兴趣,0x8864 for AMD64 and 0x14c for i386. 想要了解全部可能的值,可以访问微软官方文档。NumberOfSections:这个字段存储了sections的个数。(也可以说是section header的个数)TimeDateStamp: 表示文件被创建时的unix时间戳。PointerToSymbolTable和NumberOfSymbols: 这两个字段保存了 到COFF符号表的偏移以及符号表中有几个对象。它们也可能被设置为0,意味着没有符号表,这是因为COFF调试信息被丢弃了。SizeOfOptionalHeader:Optional Header的大小。Characteristics:用于表示文件属性的flag。这些属性可以是文件能否被执行,是否是系统文件,以及很多其他信息。详情可以访问微软官方文档。
下面是一个真实的PE文件的PE Header的截图。

Optional Header (IMAGE_OPTIONAL_HEADER)
Optional Header是NT headers中最重要的,PE加载器会查找这个header中提供的特定的信息来加载以及执行EXE文件。
它被称为可选头部信息是因为有些文件类型 像是obj文件不需要,但是这个header对于镜像文件image file很重要。
Optional Header没有固定的大小,所以会存在 IMAGE_FILE_HEADER.SizeOfOptionalHeader。
Optional Header前8个成员对于COFF文件格式来说是必须实现的标准,header剩余的部分是微软对标准定义的一个扩展,结构体中扩展部分的成员会被用于Windows的PE加载器以及链接器。
正如之前提到的,Optional Header 有两个版本,一个用于32bit的Exe,一个用于64bit。这两个版本有以下两方面的区别:
- 结构体本身的大小(或者说结构体中定义的成员的数量):
IMAGE_OPTIONAL_HEADER32有31个成员,而IMAGE_OPTIONAL_HEADER64只有30个成员, 32bit版本多出的成员为DWORD类型的BaseOfData,存储了data section起始位置的Relative Virtual Address. - 一些成员的数据类型:下面5个成员在32bit版本中是
DWORD,在64bit版本中是ULONGLONG:- ImageBase
- SizeOfStackReserve
- SizeOfStackCommit
- SizeOfHeapReserve
- SizeOfHeapCommit
我们来看一下两个结构体的定义:
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData;
//
// NT additional fields.
//
DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
每个字段的详细解释可以看对应的文档。
让我们看一下实际PE文件中Optional Header的内容。

我们可以讨论一下其中的一些字段,首先是Magic字段,它的值是0x20B意味着这是一个64位的可执行程序。
我们可以看到程序入口点entry point的相对虚拟地址RVA是0x12C4并且代码段Code Section开始于相对虚拟地址0x1000,内存中的段对齐SectionAlignment的大小也是0x1000.
文件中的段对齐File Alignment被设置为0x200,并且我们可以观察任何一个section来验证。

你可以看到,data section的实际内容是从0x2200到0x2229,然而section剩余部分被0填充直到0x23ff来满足FileAlignment对齐的要求。
SizeOfImage镜像被加载到内存中的大小被设置为 7000 并且 SizeOfHeaders被设置为400,两个各自都是SectionAlignment和FileAlignment的倍数。
Subsystem字段被设置为3,表示这是一个Windows控制台程序。
DataDirectory下面会讲。
总结
本篇文章到此结束,至此我们看了NT Headers结构,详细讨论了File Header和Optional Header。
下一篇文章我们会看一下Data Directories, Section Headers, 以及sections。
PE文件结构解析 Part2 DOS Header, DOS Stub and Rich Header
PE文件结构解析 Part4 Data Directories, Sections

浙公网安备 33010602011771号