PE文件结构

由于一些原因,需要学习安全方面的一些知识,所以学习了PE文件的结构,这篇随笔是学习的一些心得,作为复习。希望对各位有所帮助,如果行文中有什么错误,还请各位指正。


Microsoft Windows Portable Executable是微软可迁移可执行软件的全称,简称PE文件。对PE文件结构的学习虽然并不是程序员必须学的,但是学习对PE文件结构的学习过程中能够提升我们的个人编程能力,从中学到很多东西。而对PE文件的详细学习对我们在安全、病毒检测的工作有很大的帮助。

PE 文件结构

PE文件的结构如下表所示

  +-------------------+
  | DOS MZ header     |
  +-------------------+
  | DOS-stub          |
  +-------------------+
  | file-header       |
  +-------------------+
  | optional header   |
  |- - - - - - - - - -|
  |                   |
  | data directories  |
  |                   |
  +-------------------+
  |                   |
  | section headers   |
  |                   |
  +-------------------+
  |                   |
  | section 1         |
  |                   |
  +-------------------+
  |                   |
  | section 2         |
  |                   |
  +-------------------+
  |                   |
  | ...               |
  |                   |
  +-------------------+
  |                   |
  | section n         |
  |                   |
  +-------------------+

DOS-header & DOS-stub

所有的PE文件都是由一个简单DOS MZ header头开始,后面紧跟着一个DOS-stub。这么做的目的是为了向后兼容性。当全世界在从DOS到Win32转变时
为了DOS的向后兼容性,引入了这两个区域在PE文件的开头。DOS MZ headerDOS-stub是用来兼容DOS运行环境的。当DOS看到这个头时,便知道这是一个可执行文件,接着DOS便会紧接着运行DOS-stub的内容,DOS-stub实际上就是一个EXE文件。在那个时候,有一些程序将DOS版本和Win32版本整合到一个PE文件中,具体的做法就是在DOS-stub中存放DOS版本的程序。现在的DOS-stub中的内容仅仅只是一句话 "This program cannot run in DOS mode",现在DOS-stub这个块的内容已经很少被关注。为并且有一些工作试图将DOS-stub块删除。下图显示的是分析rufus程序的DOS-headerDOS-stub截图

其中在DOS-header的最后一个字给出了PE-header的偏移量(0x00000080)。根据这个就能够找到PE-header。即PE文件中最重要的部分。

PE-header

PE-header是PE文件中最重要的内容,PE-header中包含了PE加载程序需要的许多字段。PE-header实际上是一个名为IMAGE_NT_HEADERS的结构体。结构体的定义如下:

IMAGE_NT_HEADERS STRUCT 
   Signature dd ? 
   FileHeader IMAGE_FILE_HEADER <> 
   OptionalHeader IMAGE_OPTIONAL_HEADER32 <> 
IMAGE_NT_HEADERS ENDS

字段:

  • Signature:PE-header的签名是一个魔数(0x00004550),转换成文本就是"PE"。该成员是PE签名,因此我们将使用它来验证给定文件是否是有效的PE文件。
  • FileHeader:FileHeader包含的是PE的物理布局信息(physical layout)例如Section的数量,PE文件所针对的机器等。
  • OptionalHeader:虽然说这个头部是可选的(Optional),但是在所有的PE文件中都有出现。

微软定义了几个常量用来作为MZ头和PE头的签名,其中IMAGE_DOS_SIGNATURE是DOS头签名,IMAGE_NT_SIGNATURE是PE头签名
IMAGE_DOS_SIGNATURE equ 5A4Dh
IMAGE_OS2_SIGNATURE equ 454Eh
IMAGE_OS2_SIGNATURE_LE equ 454Ch
IMAGE_VXD_SIGNATURE equ 454Ch
IMAGE_NT_SIGNATURE equ 4550h

所以,为了验证一个PE文件是否是有效的,只需要知道PE-header前两个字节是不是等于0x4550就能够知道是不是合法的PE文件。验证一个文件是不是合法的PE文件,步骤如下所示:

  1. 比较一个给定的文件是否有MZ头。
  2. 如果包含了MZ头,使用MZ头的e_lfanew成员,查找PE-header的位置,e_lfanew0x3C地址上。
  3. 将PE-header的第一第二个字节和IMAGE_NT_SIGNATURE进行对比,如果相等,则证明是有效的PE文件。

FileHeader

FileHeader是PE-header 中的一个结构体,虽然我们最感兴趣的部分在于OptionalHeader中,但是FileHeader也包含了一些重要的字段,对FileHeader的定义如下

IMAGE_FILE_HEADER STRUCT 
    Machine WORD ? 
    NumberOfSections WORD ? 
    TimeDateStamp dd ? 
    PointerToSymbolTable dd ? 
    NumberOfSymbols dd ? 
    SizeOfOptionalHeader WORD ? 
    Characteristics WORD ? 
IMAGE_FILE_HEADER ENDS

下表将表示各个字段的意思

字段名 意义
Machine PE文件所针对的CPU平台。
NumberOfSections PE文件中Section的数量
TimeDateStamp PE文件创建的时间,基本没什么用
PointerToSymbolTable DEBUG 用途
NumberOfSymbols DEBUG 用途
SizeOfOptionalHeader OptionalHeader的大小
Characteristics 包含文件的标识,例如这个文件是dll还是exe

Optional Header

虽说Optional Header 名称中含有Optional字样,但是却是PE header中最大、最重要的一个部分。Optional HeaderIMAGE_NT_HEADERS结构体中的最后一个元素,包含了PE文件的逻辑内容。在OptionalHeader中有31个字段,其中某些字段对我们十分有用。在Optional Header中存储的位置,都是RVA。关于RVA的详细内容,参考这篇笔记
下面列举在Optional Header中对我们比较有用的字段:

字段名 意义
AddressOfEntryPoint 这个字段存储的是PE文件加载到内存后第一个执行命令的RVA。如果想要从开始就改变PE文件的执行顺序,就可以修改这一个字段。
ImageBase PE文件加载的首选的RVA。假如这个值是0x400000h那么PE-loader将在该虚拟地址没有被占用的情况下,将映像文件加载到0x400000h地址上。
SizeOfImage PE映像在内存中的大小。
还有其他的字段信息,可以查看这个教程

Section Table

Section Table是一个跟在PE header后的数组。该数组的长度由IMAGE_FILE_HEADER中的NumberOfSection决定。Section Table包含了这些有用的信息

字段 意义
Name1 实际上这个字段应该称为"name",但是由于"name"是MSAM关键字,所以这个字段就成为Name1。这个字段中包含的是Section的名称,并且最大的长度是8个字节。可以使用任何名称,甚至将这个字段留为空。
VirtualAddress Section的RVA,所以如果这个字段的值是1000h,而PE文件中FileHeader中ImageBase字段是400000h的话,这个Section将会加载到内存为401000h的地址上。
SizeOfRawData 见名知意
PointerToRawData 见名知意
Characteristics 包含了一些标志,这些标志说明了Section中是否包含可执行代码/初始化数据/未初始化数据,以及可读性。

Import Table

Import Table内容比较多,重新开一个笔记来记录

posted @ 2018-08-12 10:37  pluviophile  阅读(485)  评论(0编辑  收藏  举报