初识 PE 文件
注:本篇文章为转载,我报错为笔记时没有保存原文章链接。
0x00 前言
PE (Portable Executable),即可移植的执行体。
Linux 平台:ELF(Executable and Linking Format)文件结构。
一般在Windows平台下,所有的可执行文件诸如:exe、dll、sys、ocx、com等均适用PE文件结构。这些使用PE文件结构也被称为PE文件。
0x01 PE 结构
PE 结构是由若干个复杂的结构体组合而成的,不是单单的一个结构体那么简单,它的结构就像文件系统的结构是由多个结构体组成的。
PE 结构包含的结构体有 DOS 头、PE 标识、文件头、可选头、目录结构、节表等。
Windows 下如何判断文件是否是 PE 文件?
- 通过导入文件到 c32asm (IDA、OD 当然也可以) 等工具,观察 MZ 头。
- 通过 lordpe 等工具。
从 数据管理的角度来看,可以把 PE 文件大致分为两部分,DOS 头、PE 头和节表属于 PE 文件的数据管理结构或数据组织结构部分,而节表数据才是 PE 文件真正的数据部分,其中包含着代码、数据、资源等内容。
从PE结构图中可以看出,PE 结构主要分为 4 大部分(DOS头、PE头、节表、节表数据),其中每个部分又进行了细分,存在若干个小的部分。
DOS头
无论是 32 位或 64 位可执行文件,其文件的头部必定是 IMAGE_DOS_HEADER
IMAGE_DOS_HEADER 数据结构定义如下
1 | typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header |
而DOS头又分两部分:
MZ 文件头 和 Dos Stub
MZ文件头: IMAGE_DOS_HEADER 结构体,其大小占 64 个字节,并且该结构中的最后一个 LONG 类型 e_lfanew 成员指向 PE 文件头的位置为中的PE文件头标志的地址。
这里有两个比较有用的成员信息:
1、e_magic,用于判断PE文件的标识。如果不是MZ即不是十六进制值:0x5A4D。计算机存储顺序是低位在前高位在后,所以存储为:0x4D5A。
2、e_lfanew,这里是指pe的偏移量,用于找到pe头的位置。
如下阴影区域:
DOS stub:dos 存根,在 IMAGE_DOS_HEADER 和 IMAGE_NT_HEADERS 之间存在一DOS存根,这其实是一段汇编代码:
PE 文件是运行在 32 位或 64 位操作系统下的。
其功能是当该EXE运行在16位环境下,输出一段文字:”This program cannot be run in DOS mode”,然后并退出该进程。
在 pe 文件利用的时候,我们可以把 payload 写入到当前区域,诸如存放我们的 shellcode,在读取时,获取 dos 头字节数,减去 MZ 头字节数,即为 dos 存根字节大小。然后拿去操作加载 shellcode 等。
PE头
在 MS-DOS 头下 main,就是 PE 头,PE 头是 PE 相关结构 NT 映像头(IMAGE_NT_HEADER)的简称,其中包含许多 PE 装载器用到的重要字段。
IMAGE_NT_HEADER 数据结构定义:
1 | typedef struct _IMAGE_NT_HEADERS { |
参具体含义:
1 | Signature |
将文件标识为 PE 映像的 4 字节签名。字节为 “PE\0\0”。这个字段是 PE 文件的标志字段,通常设置成 00004550h,其 ASCII 码为 PE00,这个字段是 PE 文件头的开始,前面的 DOS_HEADER 结构中的字段 e_lfanew 字段就是指向这里。
1 | FileHeader |
指定文件头的 IMAGE_FILE_HEADER 结构。
IMAGE_FILE_HEADER 结构
这个字段也是包含几个字段结构,它包含了 PE 文件的一些基本信息,最重要的是其中一个域指出了 IMAGE_OPTIONAL_HEADER 的大小。
1 | typedef struct _IMAGE_FILE_HEADER { |
在 PE 文件头的后面,1234567 个框分别对应 _IMAGE_FILE_HEADER 结构的七个参数位置以及各自的值。我们需要判断运行平台,就可以通过第一个参数位置的值来判断。
上述 e_lfanew 中,可以在下图中看到,e_lfanew 的值为 0080,这里可以看到 PE 头就在 0080h。
常见标识如下,比如这里的 014c,就是在 Intel I386 机器上运行。
机器 | 标识 |
---|---|
Intel I386 | 14ch |
MIPS R3000 | 162h |
Alpha AXP | 184h |
Power PC | 1F0h |
MIPS R4000 | 184h |
1)NumberOfSection,标识区块的数目,关于区块后面会详细讲。
2)TimeDateStamp
这个字段没啥好说的,指的就是PE文件创建的事件,这个时间是指从1970年1月1日到创建该文件的所有的秒数。
3)PointerToSymbolTable。这个字段用的比较少,略
4)NumberOfSymbol。这个字段也用得很少,略
5)SizeOfOptionalHeader:紧跟着IMAGE_FILE_HEADER后面的数据大小,这也是一个数据结构,它叫做IMAGE_OPTIONAL_HEADER,其大小依赖于是64位还是32位文件。32位文件值通常是00EOh,对于64位值通常为00F0h。
6)Characteristics:文件属性,普通EXE文件这个字段值为010fh,DLL文件这个字段一般是0210h。
IMAGE_OPTIONAL_HEADER结构
1 | OptionalHeader |
指定可选文件头的 IMAGE_OPTIONAL_HEADER 结构。
这个结构是 IMAGE_FILE_HEADER 结构的补充。这两个结构合起来才能对整个 PE 文件头进行描述。左边的 16 位字符表示相对于文件头的偏移量。
1 | typedef struct _IMAGE_OPTIONAL_HEADER |
这里总共 31 个字段。通常用的就是加 * 字段。
1 | 字段6:AddressOfEntryPoint 表 程序入口RVA,即OEP: |
这里可以知道我们确定的 PE 文件头在 0080h 处,通过偏移计算,我们可以得到 IMAGE_OPTIONAL_HEADER 结构的的首个字段在 80h+18h=98h 的地方。这里直接通过 c32asm 跳转到对应位置。如图所示:
然后比较重要的就是最后一个成员,即数据目录表,大小为 16,每个元素都是一个 IMAGE_DATA_DIRECTORY 结构体,这里看到是一个数组类型。
它的定义如下:
1 | typedef struct _IMAGE_DATA_DIRECTORY { |
在winnt.h中的定义:
1 | #define IMAGE_DIRECTORY_ENTRY_EXPORT //0 导出表 |
在这个数据目录结构体中只有两个成员VirtualAddress和Size,这两个成员的含义比较简单,VirtualAddress指定了数据块的相对虚拟地址(RVA)。Size则指定了该数据块的大小,有时并不是该类型数据的总大小,可能只是该类型数据一个数据项的大小。这两个成员(主要是VirtualAddress)成为了定位各种表的关键,所以一定要知道每个数组元素所指向的数据块类型,以下表格就是它的对应关系:
下面是 DataDirctory[16] 即数据目录表的各个成员
索 引 | 索引值在 Windows.inc 中的预定义值 | 对应的数据块 | 偏移量 |
---|---|---|---|
0 | IMAGE_DIRECTORY_ENTRY_EXPORT | 导出表 | 78h |
1 | IMAGE_DIRECTORY_ENTRY_IMPORT | 导入表 | 80h |
2 | IMAGE_DIRECTORY_ENTRY_RESOURCE | 资源 | 88h |
3 | IMAGE_DIRECTORY_ENTRY_EXCEPTION | 异常(具体资料不详) | 90h |
4 | IMAGE_DIRECTORY_ENTRY_SECURITY | 安全(具体资料不详) | 98h |
5 | IMAGE_DIRECTORY_ENTRY_BASERELOC | 重定位表 | A0h |
6 | IMAGE_DIRECTORY_ENTRY_DEBUG | 调试信息 | A8h |
7 | IMAGE_DIRECTORY_ENTRY_ARCHITECTURE | 版权信息 | B0h |
8 | IMAGE_DIRECTORY_ENTRY_GLOBALPTR | 具体资料不详 | B8h |
9 | IMAGE_DIRECTORY_ENTRY_TLS | Thread Local Storage | C0h |
10 | IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG | 具体资料不详 | C8h |
11 | IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT | 具体资料不详 | D0h |
12 | IMAGE_DIRECTORY_ENTRY_IAT | 导入函数地址表 | D8h |
13 | IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT | 具体资料不详 | E0h |
14 | IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR | 具体资料不详 | E8h |
15 | 未使用保留 |