Jirairya

PE


PE文件是Windows操作系统下使用的可执行文件。 PE文件是指的是PE32,32位的可执行文件。64位的可执行文件称为PE+或者是PE32+,是PE的另外一种拓展形式。

PE结构.jpg

基本结构

从DOS头到节区头是PE头部分,其下的节区合称PE体。文件中使用偏移(offset),内存中使用VA(虚拟地址)来表示位置。文件加载到内存时,情况就会发生变化(节区的大小、位置等)。文件的内容一般可分为代码(.txt)、数据(.data)、资源(.rsrc)节,分别保存。 各节区头定义了各节区在文件或内存中的大小、位置、属性等。 PE和各节区的尾部存在一个NULL填充的区域。 PE文件结构.png

PE磁盘文件与内存映像结构图.png

PE磁盘文件与内存映像结构图2.png

VA&RVA

RVA+基址=VA VA是指进程虚拟内存的绝对地址,RVA是指从某个基准位置开始的相对位置。

PE头内部信息大多以RVA形式存在。原因在/img/reversecore_assets/PE文件/主要是DLL)加载到进程虚拟内存的特定位置时,该位置可能已经加载了其他PE文件(DLL)。此时必须通过重定位将其加载到其他空白位置,若PE头信息使用的是VA,则无法正常访问。因此,使用RVA来定位信息,即使发生了重定位,只要相对于基准位置的相对地址没有变化,就能正常访问到指定信息,不会出问题。

PE头

  • PE头
  • DOS小程序
    • DOS头
    • DOS存根
  • NT头
    • 签名结构体PE00(50450000h)
    • 文件头
      • Machine
      • NumberOfSection
      • SizeOfOptionalHeader
      • Characteristics
    • 可选头
      • Magic
      • AddressOfEntryPoint(程序开始执行的地方)
      • ImageBase
      • SectionAlignment,FileAlignment
      • SizeOfImage
      • SizeOfHeaders
      • Subsystem
      • NumberOfRvaAndSizes
      • DataDirectory

文件头

Machine

每个CPU都拥有唯一的Machine码

NumberOfSection

PE文件把代码、数据资源等一句属性分类到各个节区保存 。NumberOfSection用来指出文件中存在的节区数量。该值一定要大于0,且当定义的节区数与实际节区不同时,将发生运行错误。

SizeOfOptionalHeader

IMAGE_NT_HEADERS结构体的最后一个成员为IMAGE_OPTIONAL_HEADER32结构体。SizeOfOPtionalHeader成员用来指出IMAGE_OPTIONAL_HEADER32结构体的长度。IMAGE_OPTIONAL_HEADER32结构体由C编写而成,故其大小已经确定。但是Windows的PE装载器需要查看IMAGE_FILE_HEADER的SizeOfOptionalHeader值,从而识别出IMAGE_OPTIONAL_HEADER32结构体大小。 PE32+格式的文件中使用的是IMAGE_OPTIONAL_HEADER64结构体,而不是IMAGE_OPTIONAL_HEADER32结构体。2各结构体不同,所以需要在SizeOfOptionalHeader成员中明确指出结构体的大小。

** 借助IMAGE_DOS_HEADER的e_lfanew成员与IMAGE_FILE_HEADER的SizeOfOptionalHeader成员,可以创建一种脱离常规的PE文件(PE Patch),也有人称为“麻/img/reversecore_assets/PE文件/*

Characteristics

Characteristics该字段用于标识文件的属性,文件是否是可运行的状态、是否为DLL文件等信息,以bit OR形式组合起来。

NT头 : 可选头

在IMAGE_DOS_HEADER32结构体中需要关注以下成员,这些值文件是必须运行的,设置错误将导致文件无法正常运行.

Magic

为IMAGE_DOS_HEADER32结构体时,Magic码为10B;为IMAGE_DOS_HEADER64结构体时,Magic码为20B;

AddressOfEntryPoint

AddressOfEntryPoint持有EP的RVA值,该值指出程序最先执行的代码起始地址,相当重要.

ImageBase

进程虚拟内存的范围是 0~FFFFFFFF(32位系统).PE文件被加载到如此大的内存中时,ImageBase指出文件的优先载入地址.

EXE,DLL文件被装载到用户内存的0~7FFFFFFF中,SYS文件被载入内核内存的80000000~FFFFFFFF中.

SectionAlignment,FileAlignment

PE文件的Body部分划分为若干节区,这些节存储着不同类别的数据.FileAlignment指定了节区在磁盘文件中的最小单位,而SectionAlignment则指定了节区在内存中最小单位.磁盘文件或内存的节区大小必定为FileAlignment或SectionAlignment值的整数倍.

SizeOfImage

加载到PE文件到内存时,SizeOfImage指定了PE Image在虚拟内存中所占空间的大小.

SizeOfHeader

SizeOfHeader用来指定整个PE头的大小,该值也必须是FileAlignment的整数倍.

SubSystem

该SubSystem值用来区分系统驱动文件(.sys)与普通的可执行文件(.exe,*.dll).Subsystem成员可拥有的值如表.

含义 备注
1 Driver文件 系统驱动(如:ntfs.sys)
2 GUI文件 窗口应用程序(如:notepad.exe)
3 CUI文件 控制台应用程序(如:cmd.exe)

NumberOfRvaAndSize

NumberOfRvaAndSize用来指定DataDirectory(IMAGE_OPTIONAL_HEADER32结构体的最后一个成员)数组的个数.

DataDirectory

DataDirectory是由IMAGE_DATA_DIRECTORY结构体组成的数组,数组的每项都要被定义的值.

节区头

把具有相拟属性的数据统一保存在一个被称为”节区”的地方,然后需要把各节区属性记录在节区头中.

需要为每个code/data/resource分别设置不用的特性,访问权限等.

类别 访问权限
code 执行,读取权限
data 非执行,读取权限
resource 非执行,读取权限

IMAGE_SECTION_HEADER

节区头是由IMAGE_SECTION_HEADER结构体组成的数组,每个结构体对应一个节区

RVA to RAW

PE文件加载到内存时,每个节区都要能够准确完成内存地址与文件偏移间的映射。这种映射叫做RVA to RAW 方法是:

  • 查找RVA所在节区
  • 使用简单的公式计算文件偏移(RAW):
    • RAW-PointerToRawData=RVA-VirtualAddress
    • RAW=RVA-VirtualAddress+PointerToRawData(磁盘文件中节区起始位置) notepad.exe文件与内存间的映射.png

例子

Q1:RVA=5000时,File Offset=?

  • 首先查找RVA值所在节区:RVA5000位于第一个节区(.text)(假设基址为1000000)
  • 使用公式:RAW=5000(RVA)-1000(VirtualAddress)+400(PointerToRawData)=4400

Q2:RVA=13314,FileOffset=?

  • 首先查找RVA的值所在节区:RVA=13314位于第三个节区(.rsrc)
  • 使用公式:RAW=RVA-VirtualAddress+PointerToRawData=13314-B000+8400=10714

Q3:RVA=ABA8时,FileOffset=?

  • 首先查找RVA=ABA8在第二个节区
  • 使用公式RAW=RVA-VirtualAddress+PointerToRawData=ABA8-9000-7C00=97A8
  • RAW=97A8在第三个节区,RVA在第二个节区,表明无法定义与RVA(ABA8)相对应的RAW。出现该原因,可能是第二个节区的VirtualSize值要比SizeOfRawData值大。

Windows装载器在装载DOS部分、PE文件头部分和节表(区块表)部分是不进行任何特殊处理的,而在装载节(区块)的时候则会自动按节(区块)的属性做不同的处理 一般情况下,它会处理以下几个方面的内容:

  • 内存页的属性
  • 节的偏移地址
  • 节的尺寸
  • 不进行映射的节

上一篇 PE之IAT

Comments

Content