Window PE -- 初识

Window PE – 初识

基本概念

PE文件使用的是一个平面地址空间,所有代码和数据都合并在一起,组成了一个很大的结构。文件的内容被分割为不同的区块(Section,又称区段、节等,在本章中不区分“区块”与“块”),区块中包含代码或数据,各个区块按页边界对齐。区块没有大小限制,是一个连续结构。每个块都有它自己在内存中的一套属性,例如这个块是否包含代码、是否只读或可读/写等。

认识到PE文件不是作为单一内存映射文件被载入内存是很重要的。Windows加载器(又称PE装载器)遍历PE文件并决定文件的哪一部分被映射,这种映射方式是将文件较高的偏移位置映射到较高的内存地址中。盘文件一旦被载入内存,磁盘上的数据结构布局和内存中的数据结构布局就是一致的。这样,如果在磁盘的数据结构中寻找一些内容,那么几乎都能在被载人的内存映射文件中找到相同的信息,但数据之间的相对位置可能会改变,某项的偏移地址可能区别于原始的偏移位置。

不管怎样,对所有表现出来的信息,都允许进行从磁盘文件偏移到内存偏移的转换。

基地址

当PE文件通过Windows加载器载入内存后,内存中的版本称为模块(Module)。映射文件的起始地址称为模块句柄(hModule),可以通过模块句柄访问内存中的其他数据结构。这个初始内存地址也称为基地址(ImageBase)。准确地说,对于WindowsCE,这是不成立的,一个模块句辆在WindowsCE下井不等同于安装的起始地址。

1554714173177

内存中的模块代表进程将这个可执行文件所需要的代码、数据、资源、输入表、输出表及其他有用的数据结构所使用的内存都放在一个连续的内存块中,程序员只要知道装载程序文件映像到内存后的基地址即可。PE文件的剩余部分可以被读人,但可能无法被映射。例如,在将调试信息放到文件尾部时,PE的一个字段会告诉系统把文件映射到内存时需要使用多少内存,不能被映射的数据将被放置在文件的尾部。方便起见,WindowNT或Windows95将Modul的基地址作为Module的实例句柄(InstanceHandle,即Hinstance)。

在32位Windows系统中,因为InstanceHandle来源于16位的Windows3.1,其中每执行实例都有自己的数据段并以此来互相区分(这就是InstanceHandle的来历)。在32位Windows系统中,因为不存在共享地址空间,所以应用程序无须加以区别。当然,16位Windows系统和32位Windows系统中的Hinstance还有些联系:在32位Windows系统中可以直接调用GetModuleHandle以取得指向DLL的指针,通过指针访问该DLLModule的内容,示例如下。

1
2


调用该函数时会传递一个可执行文件或DLL文件名字符串,如果系统找到文件,则返回该可执行文件或DLL文件映像所加载的基地址。也可以调用GetModuleHandle来传递NULL参数,此时将返回调用的可执行文件的基地址。

基地址的值是由PE文件本身设定的。按照默认设置,用VisualC++建立的EXE文件的基地址是400000h、DLL文件的基地址是10000000h。可以在创建应用程序的EXE文件时改变这个地址,方法是在链接应用时使用链接程序的/BASE选项,或者在链接后通过REBASE应用程序进行设置。

虚拟地址

在Windows系统中,PE文件被系统加载器映射到内存中。每个程序都有自己的虚拟空间,这个虚拟空间的内存地址称为虚拟地址(VirtualAddress,VA)

相对虚拟地址

在可执行文件中,有许多地方需要指定内存中的地址。例如,引用全局变量时需要指定它的地址。PE文件尽管有一个首选的载入地址(基地址),但是它们可以载入进程空间的任何地方,所以不能依赖PE的载入点。因此必须有一个方法来指定地址(不依赖PE载入点的地址)。

为了避免在E文件中出现绝对内存地址引人了相对虚拟地址(RelativeVirtualAddress,RVA)的概念。RVA只是内存中的一个简单的、相对于PE件载入地址的偏移位置,它是一个“相对”地址(或称偏移量)。例如,假设一个EXE文件从400000h处载人,而且它的代码区块开始于401000h处,代码区块的RVA计算方法如下:

目标地址401000h - 载入地址400000h=RVA 1000h

将一个VA转换成真实的地址只是简单地翻转这个过程,即用实际的载入地址加RVA得到实际的内存地址。它们之间的关系如下:

虚拟地址(VA)=基地址(ImageBase)+相对虚拟地址(RVA)

文件虚拟地址

当PE文件储存在磁盘中时,某个数据的位置相对于文件头的偏移量称为文件偏移地址(FileOffset)或物理地址(RAWOffset)。文件偏移地址从PE文件的第1个字节开始计数,起始值为0。用十六进制工具(例如HexWorkshopWinHex等)打开文件时所显示的地址就是文件偏移地址。

MS-DOS头部

每个PE文件都是以一个DOS程序开始的,有了它,一旦程序在DOS下执行,DOS就能识别出这是一个有效的执行体,然后运行紧随MZ header的DOS stub(DOS块)。DOSstub实际上是一个有效的EXE,在不支持PE文件格式的操作系统中它将单地显示一个错误提示,类似于字符串“This program cannot be rnn in MS-DOS mode”。程序员也可以根据自己的意图实现完整的DOS代码。用户通常对DOSstub不太感兴趣,因为在大多数情况下它是由汇编器/编译器自动生成的。我们通常把DOS MZ头与DOS stub合称为DOS文件头。

PE件的第个字节位于一个传统的MS-DOS头部,称作IMAGE_DOS_HEADER,其结构如下(左边的数字是到文件头的偏移量)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _IMAGE_DOS_HEADER {  // DOS .EXE header
USHORT e_magic; // Magic number
USHORT e_cblp; // Bytes on last page of file
USHORT e_cp; // Pages in file
USHORT e_crlc; // Relocations
USHORT e_cparhdr; // Size of header in paragraphs
USHORT e_minalloc; // Minimum extra paragraphs needed
USHORT e_maxalloc; // Maximum extra paragraphs needed
USHORT e_ss; // Initial (relative) SS value
USHORT e_sp; // Initial SP value
USHORT e_csum; // Checksum
USHORT e_ip; // Initial IP value
USHORT e_cs; // Initial (relative) CS value
USHORT e_lfarlc; // File address of relocation table
USHORT e_ovno; // Overlay number
USHORT e_res[4]; // Reserved words
USHORT e_oemid; // OEM identifier (for e_oeminfo)
USHORT e_oeminfo; // OEM information; e_oemid specific
USHORT e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

其中有两个字段比较重要,分别是e_magic和e_lfanew 。e_magic字段(1个字大小)的值需要被设置为5A4Dh。这个值有一个#define,名为IMAGE_DOS_SIGNATURE,在ASCII表示法里它的ASCII值为“MZ”,是MS-DOS的创建者之一MarkZbikowski名字的缩写。e_lfanew字段是真正的PE文件头的相对偏移(RVA),指出真正的PE头的文件偏移位置,占用4字节,位于从文件开始偏移3Ch字节处。

用十六进制编辑器(WinHex,HexWorkshop等带偏移量显示功能的尤佳)打开随书文件中的示例程序PE.exe定位在文件起始位置,此处就是MS-DOS头部,如图11.3所示。文件的第l个字柯:“MZ”就是e_magic字段;偏移量3Ch就是e_lfanew的值,在这里显示为“B000000。”。为IntelCPU属于Little-Endian类,字符储存时低位在前,高位在后,所以,将次序恢复后,e_lfanew的值为000000b0h,这个值就是真正的PE文件头偏移量。

1554715138941

1571159929511

Author: YuanBi
Link: https://www.basicbit.cn/2018/10/31/2018-10-31-Windows PE 初识/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.