WHCSRL 技术网

逆向工程之PE文件格式(一)

一.介绍

  • PE文件是Windows操作系统下使用的可执行文件文件格式。

  • PE文件是指32位的可执行文件,也叫PE32。64位的可执行文件称为PE+或者PE32+,是PE文件的一种扩展形式(不是PE64)。

二.PE文件格式

PE文件种类

以上的文件类型中,OBJ(对象)文件是不可以执行的,其他都是可以执行的,DLL,SYS文件不能直接在shell上执行,但是在调试器或服务上是可执行的。(学过汇编语言的同学都知道OBJ是编译结果文件,也是PE文件)

下面以记事本(notepad.exe)程序进行简单说明,首先使用Hex Editor打丁开记事本程序。
在这里插入图片描述

图是notepad.exe文件的起始部分,也是PE文件的头部分(PE header)。notepad.exe文件运行需要的所有信息就存储在这个PE头中。

在学习PE文件时,最重要的就是学习PE头中的结构体。

书中将以 Windows XP SP3 的notepad.exe 为例进行说明,与其他版本Windows 下的notepad.exe 文件结构类似,但是地址不同。

1. 基本结构

1.1 基本结构

notepad.exe具有普通PE文件的基本结构。
从DOS头(DOS header)到节区头(Sectionheader)是PE头部分,其下的节区合称PE体。文件中使用偏移(o1fset),内存中使用VA (Virtual Address,虚拟地址)来表示位置。文件加载到内存时,情况就会发生变化(节区的大小、位置等)。文件的内容一般可分为代码(.text)、数据data)、资源(.rsrc)节,分别保存。

提示
根据所用的不同开发工具(VB/VC++/Delphiletc)与编译选项,节区的名称、大小个数、存储的内容等都是不同的。
最重要的是它们按照不同的用途分类保存到不同的节中。
  • 1
  • 2
  • 3

在这里插入图片描述

各节区头定义了各节区在文件或内存中的大小、位置、属性等。
在这里插入图片描述

1.2VA&RVA

RVA+Imagebase=VA
  • 1

RVA是相对虚拟地址(相对地址)
VA是进程虚拟内存的绝对地址
Imagebase是基址
32位系统中VA的范围是00000000~FFFFFFFF

1.3PE头

DOS头

一个PE文件起始的部分开始就是DOS头,这和GIF,BMP,JPG,RAR等等都一样,只是在文件起始部位标识这个文件属于什么文件类型。这里是DOS头的结构体:

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

IMAGE_DOS_HEADER结构体的大小为64字节。而在结构体中必须知道的两个重要成员:

e_magic:DOS签名(MZ->4D5A)
e_ifanew:指示NT头的偏移(可变值)

DOS存根
  DOS存根在DOS头下方,是由代码和数据混合组成的。
  ![在这里插入图片描述](https://img-blog.csdnimg.cn/c36c929b41304ea09f70d7df9701e608.png)
  • 1
  • 2

DOS存根是一段简单的DOS程序,主要用来输出类似“This program cannot be run in DOS mode.”的提示语句,由于DOS存根里的内容无关紧要,所以可以把这部分用来存储其他数据而不改变程序原本的功能。

1.4 NT头

IMAGE_NT_HEADERS结构体:
在这里插入图片描述

在这里插入图片描述

  • 这个PE头标志,也就是标准PE头
  • NT头这个结构体的大小为F8

1.5 NT头:文件头

在这里插入图片描述
在这里插入图片描述

20个字节,对应的结构体名字:IMAGE_FILE_HEADER STRUCT
从结构的定义可以看到第二个字段是当前PE文件的节的数目。
Machine:指出该文件运行所需的CPU。
NumberOfSections:文件的节数目。
Characteristics文件属性:区分文件是exe还是dll等。

1.6 可选头定义

在这里插入图片描述
对应的结构体名字:IMAGE_OPTIONAL_HEADER32 STRUCT
由于可选头字段数目多达32个,这里没有对全部字段的定义进行列举,只列举了几个相对重要的一些字段(12个)。
可选头中定义了如下重要信息:

  • 所有含代码的节的总大小
  • 所有含已初始化数据的节的总大小
  • 所有含未初始化数据的节的大小
  • 程序执行入口RVA
  • 代码的节的起始RVA
  • 数据的节的起始RVA
  • 程序的建议装载地址
  • 内存中的节的对齐粒度
  • 文件中的节的对齐粒度
  • 内存中整个PE映像尺寸
  • 所有头+节表的大小
  • 导出表 导入表
  • 资源
  • 重定位表
  • 调试信息
  • 版权信息
  • 导入函数地址表

1.7 节表

在这里插入图片描述
节表:PE代码和数据的结构数据,指示装载系统代码段在哪里,数据段在哪里等。
PE文件中所有节的属性都被定义在节表(段表)中,节表是一个由IMAGE_SECTION_HEADER结构组成的数组,每个结构用来描述一个节。
结构的排列顺序和它们描述的节在文件中的排列顺序是一致的。全部有效结构的最后以一个空的IMAGE_SECTION_HEADER结构作为结束,所以节表中总的IMAGE_SECTION_HEADER结构数量等于节的数量加一。节表总是被存放在紧接在PE文件头的地方,也就是从PE文件头开始的偏移为00f8h的地方。

(注意:不是文件本身的头部,由于程序的dos块的大小并不是固定的,所以导致PE头相对于mz头的偏移变化的。所以我们在计算偏移的时候,对于PE头后面的数据都习惯用相对于PE头的偏移,PE头大小是固定的的。)

在文件头中已经定义了文件的节的数目:0ah个,那么该文件的节表共有11个IMAGE_SECTION_HEADER结构。

1.8 节数据

常见的节数据:

  • text:代码段,是在编译或汇编结束时产生的一种块,它的内容全部是指令代码。也有的编译器将该段命名为.code
  • .data:初始化的数据块,是初始化的数据块,包含那些编译时被初始化的变量、字符串
  • .idata:输入表,包含其他外来dll的函数和数据信息,也就是输入表,也有人称之为导入表。
  • .rsrc:资源数据块,包含模块的全部资源数据,如图标、菜单、位图等。
  • .reloc:重定位表,用于保存基址的重定位表。即当装在程序不能按照连接器所指定的地址装载文件是,需要对指令或已经初始化的变量进行调整,该块中也包含了调整过程中所需要的一些数据,如果装载能够正常装在则忽略此段中的数据。
  • .edata:导出表,是pe文件的输出表,以供其他模块使用,并不是每个pe文件都有此数据段,因为有的文件并不需要输出一些函数,该数据段常见于动态连接库文件中。
  • .radata:存放调试目录、说明字符串,该数据块并不常见主要是用于存放一些调试信息。

后续更新PE文件的其他学习内容
参考教材《逆向工程核心原理》

推荐阅读