文章来源:https://0xrick.github.io/win-internals/pe4/
- 简介
- NT Headers(IMAGE_NT_HEADERS)
- 签名 Signature
- File Header(IMAGE_FILE_HEADER)
- Optional Header (IMAGE_OPTIONAL_HEADER)
- 总结
简介
在前面的文章中,我们看过了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。