PE文件结构学习笔记

PE的意思就是 (可移植的执行体) 。它是Win32环境自身所带的执行体文件格式 。它的一些特性继承自Unix的Coff (file )文件格式 。" "(可移植的执行体)意味着此文件格式是跨win32平台的:即使运行在非Intel的CPU上,任何win32平台的PE装载器都能识别和使用该文件格式 。所有win32执行体(除了VxD和16位的Dll)都使用PE文件格式,所以熟知PE结构有助于对操作系统的深刻理解 。

PE文件结构学习笔记

文章插图
图1 PE文件结构框架
上图是PE文件结构的总体层次分布 。所有的PE文件都要以一个简单的DOS MZ 开始 。有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随MZ 之后的DOS stub 。DOS stub实际上是个有效的EXE,在不支持PE文件格式的操作系统中,它将简单显示一个错误提示 。
DOS stub后面就是PE,PE 是PE相关结构的简称,其中包含了许多PE装载器用到的重要域 。执行体在支持PE文件结构的操作系统中执行时,PE装载器将从DOS MZ 中找到PE 的起始偏移量 。因而跳过了DOS stub直接定位到真正的文件头PE。
PE接下来的数组结构table(节表) 。每个结构包含对应节的属性、文件偏移量、虚拟偏移量等 。PE文件的真正内容划分成块,称之为(节) 。每节是一块拥有共同属性的数据,比如代码/数据、读/写等 。节的划分是基于各组数据的共同属性:而不是逻辑概念 。有了节表,就能定位节 。
对PE的物理结构有了大致了解之后,再大致了解一下装载PE的文件的步骤:
当PE文件被执行,PE装载器检查DOS MZ 里的PE 偏移量 。如果找到,则跳转到PE。PE装载器检查PE 的有效性 。如果有效,就跳转到PE 的尾部 。紧跟PE 的是节表 。PE装载器读取其中的节信息,并采用文件映射方法将这些节映射到内存,同时付上节表里指定的节属性 。PE文件映射入内存后,PE装载器将处理PE文件中类似 table(引入表)逻辑部分 。
1 PE的基本概念
1.1 DOS头
DOS MZ又命名为 . 。其中只有两个域比较重要:包含字符串"MZ",包含PE 在文件中的偏移量 。
PE文件结构学习笔记

文章插图
图2 DOS MZ 结构
1.2 DOS 存根
接下来的DOS stub实际上是个EXE,当当前系统不支持PE文件结构时它能输出一个错误提示“This”
1.3 PE
PE头是一个类型的结构,下面是这个结构在WINNT.H中的定义:
{
DWORD ;
;
R32 ;
} , *;
是一个标志变量,这个就是当我们判断一个文件是否是PE文件时第二步需要判断的,若这个值等于"PE\0\0"时就是一个PE文件 。
第二个成员是一个类型的对象,通常我们叫它映像文件头,这个结构在头文件中的定义是:
{
WORD;
WORD;
DWORD ;
DWORD ;
DWORD ;
WORD;
WORD;
} , *;
这里对比较重要的成员做出说明,这个成员保存了节的数目,这个成员保存了PE头中这个成员的大小,最后一个成员是一个关于文件的标记,即这个文件是EXE还是DLL文件 。
1.
它是一个R32类型的对象,这个结构的定义是:
PE文件结构学习笔记

文章插图
术语--RVA 代表相对虚拟地址() 。
可选头中比价重要的有:

PE装载器准备运行的PE文件的第一个指令的RVA 。若您要改变整个执行的流程,可以将该值指定到新的RVA,这样新RVA处的指令首先被执行 。
PE文件的优先装载地址 。比如,如果该值是,PE装载器将尝试把文件装到虚拟地址空间的处 。字眼"优先"表示若该地
址区域已被其他模块占用,那PE装载器会选用其他空闲地址 。

内存中节对齐的粒度 。例如,如果该值是4096 (1000h),那么每节的起始地址必须是4096的倍数 。若第一节从开始且大小是10个字节,
则下一节必定从开始,即使和之间还有很多空间没被使用 。

文件中节对齐的粒度 。例如,如果该值是(200h),,那么每节的起始地址必须是512的倍数 。若第一节从文件偏移量200h开始且大小是10个字节,
则下一节必定位于偏移量400h: 即使偏移量512和1024之间还有很多空间没被使用/定义 。
n
n :
win32子系统版本 。若PE文件是专门为Win32设计的,该子系统版本必定是4.0否则对话框不会有3维立体感 。

内存中整个PE映像体的尺寸 。它是所有头和节经过节对齐处理后的大小 。

所有头+节表的大小,也就等于文件尺寸减去文件中所有节的尺寸 。可以以此值作为PE文件第一节的文件偏移量 。

NT用来识别PE文件属于哪个子系统 。对于大多数Win32程序,只有两类值:GUI 和CUI (控制台) 。

一 结构数组 。每个结构给出一个重要数据结构的RVA,比如引入地址表等 。非常重要!!
1.5table
节表就相当于书的目录,而书中的各个章节就相当于PE文件结构中的节,通过目录我们能很快找到书中我们感兴趣的内容,同样
通过节表我们很快能找到PE文件中的各个节 。(注意:多个数据只要是具有共同属性我们就能把它放在同一节)
PE文件结构学习笔记

文章插图
【PE文件结构学习笔记】Name:这个成员是一个字节型的数组,这就是节的名字,不过这个数组的上限是8,最多只能保存8个字符,还有就是它不是一个字符串,因为它不是以null结尾的 。

本节的RVA(相对虚拟地址) 。PE装载器将节映射至内存时会读取本值,因此如果域值是1000h,而PE文件装在地址处,那么本节就被载到 。

这是节基于文件的偏移量,PE装载器通过本域值找到节数据在文件中的位置 。

包含标记以指示节属性,比如节是否含有可执行代码、初始化数据、未初始数据,是否可写、可读等 。
1.6 输入表
可执行文件使用来自于其他dll的代码或数据时,称为输入 。当PE文件装入时,加载器的工作之一就是定位所有被输入的函数和数据,并且让正在被装入的
PE文件可以使用那些地址 。这个过程是通过PE文件的输入表( Tab 也称之为导入表)完成的,输入表中保存的是函数名和其驻留的dll名等,动态连接所需输信息,
输入表在软件外壳技术上的地位十分重要,因此在研究外壳的技术时一定要掌握这部分知识 。
->R32->的第二个成员指向输入表,输入表以一个TOR
(简称IDD)开始,(IID) TOR的结构包含如下5个字段:
, , , Name,
(INT)
该字段是指向一32位以00结束的RVA偏移地址串,此地址串中每个地址描述一个输入函数,它在输入表中的顺序是不变的 。
一个32位的时间标志,有特殊的用处 。
输入函数列表的32位索引 。
Name
DLL文件名(一个以00结束的ASCII字符串)的32位RVA地址 。
(IAT)
该字段是指向一32位以00结束的RVA偏移地址串,此地址串中每个地址描述一个输入函数,它在输入表中的顺序是可变的 。
PE文件结构学习笔记

文章插图
1.7 输出表
当PE装载器执行一个程序,它将相关DLLs都装入该进程的地址空间 。然后根据主程序的引入函数信息,查找相关DLLs中的真实函数地址来修正主程
序 。PE装载器搜寻的是DLLs中的引出函数 。
输出表结构如下:
PE文件结构学习笔记

文章插图
输出表的设计是为了方便PE装载器工作 。首先,模块必须保存所有输出函数的地址以供PE装载器查询 。模块将这些信息保存在域指向的数组中,而数组元素数目存放在域中 。因此,如果模块引出40个函数,则指向的数组必定有40个元素,而值为40 。PE装载器在名字数组中找到匹配名字的同时,它也获取了指向地址表中对应元素的索引 。而这些索引保存在由s域指向的另一个数组(最后一个)中 。由于该数组是起了联系名字和地址的作用,所以其元素数目必定和名字数组相同 。
PE文件结构学习笔记

文章插图
1.8 基址重定位
PE文件结构学习笔记

文章插图
在32位代码中,涉及到直接寻址的指令都是需要重定位的 。对操作系统来说,其任务就是在对可执行程序透明的情况下完成重定位操作 。
在现实中,重定位信息是在编译的时候由编译器生成并保留在可执行文件中的,在程序被执行前由操作系统根据重定位表信息修正代码,这样在开发程序的时候就不用考虑重定位问题了 。重定位信息在PE文件中被存放在重定位表中 。在PE文件没有被加载到预期的位置时,PE加载器根据下面的重定位表来确定哪些指令数据是需要修改的 。
重点就是重定位表的结构和使用方法了 。
PE文件结构学习笔记

文章插图

PE文件结构学习笔记

文章插图
指令(数据)的指针=(数据项低12位+)+PE文件实际被映射的基址
重定位结果=需要重定位数据+(PE文件实际被映射的基址-)
1.9 资源
资源一般使用树来保存,通常包含3层,在NT下,最高层是类型,然后是名字,最后是语言 。
一个PE文件是否包含资源文件,通常检测块表( Table)中是否含有'.rsrc',不过这个方法对有些PE文件无效 。
PE文件结构学习笔记

文章插图
1.10 TLC
线程本地存储TLS( Local ) 。TLS的作用是能将数据和执行的特定的线程联系起来 。实现TLS有两种方法:静态TLS和动态TLS 。
第一次写博客,排版什么都比较随意,发现把学过的东西记录下来确实学起来效率高一点 。
由于我也是第一次真正接触PE,所以有些概念理解的不是很准确,虽然现在水平还不是很高,但尽力了就行了,希望大家多多包含,有时间大家可以看看原版 。
最后附上一张简略的PE结构图:
-------------*-------------------------------------------------*
| DOS () | -->64 Byte
DOS头部--------------------------------------------------
| DOS Stub| -->112 Byte
-------------*-------------------------------------------------*
| "PE"00 ()| -->4 Byte
-------------------------------------------------
|| -->20 Byte
PE文件头 --------------------------------------------------
| R32| -->96 Byte
---------------------------------------------------
| 数据目录表| -->128 Byte
-------------*--------------------------------------------------*
|| -->40 Byte
---------------------------------------------------
块表|| -->40 Byte
--------------------------------------------------
|| -->40 Byte
-------------*--------------------------------------------------*
|.text| -->512 Byte
---------------------------------------------------
块|.rdata| -->512 Byte
---------------------------------------------------
|.data| -->512 Byte
-------------*-------------------------------------------------*
| COFF行号| -->NULL
---------------------------------------------------
调试信息 | COFF符号表| -->NULL
---------------------------------------------------
| Code View 调试信息| -->NULL
-------------*--------------------------------------------------*
--------->>>摘自互联网
参考书籍:《加密与解密》、《逆向工程核心原理》