@date: 2016/11/24

@author: dlive

​ PE (portable executable) ,它是微软在Unix平台的COFF(Common Object File Format 通用对象文集格式)的基础上制成的。

​ PE文件是指32位的可执行文件,也称PE32,64位的可执行文件称为PE+或者PE32+

​ PE文件的种类

种类 主扩展名
可执行系列 exe ,scr
驱动程序系列 sys, vxd
库系列 dll, dcx, cpl, drv
对象文件系列 obj

​ VA 虚拟地址 RVA 相对虚拟地址

​ 32位的Windows OS中,各进程分配有4GB的虚拟内存,因此进程中VA值的范围是00000000-FFFFFFFF

此处还有一个重要的图:p92 图13-2

​ 以下格式推荐使用notepad.exe为例,结合010editor中的EXE Template模板解析结果学习。

0x01 PE头

1.DOS头

​ 微软考虑PE对DOS文件的兼容性,在PE头最前面添加了IMAGE_DOS_HEADER结构体,用来扩展已有的DOS头。

# IMAGE_DOC_HEADER
WORD e_magic; // DOS签名 MZ
...
...
LONG e_lfanew; // NT Header offset

2.DOS存根(Stub)

​ 没有DOS存根,文件也能正常运行,DOS存根由代码和数据混合而成,大小不固定,其中的代码和数据是为了兼容MS-DOS,可以在MS-DOS下运行,输出“This program cannot be run in DOS mode”

3.NT头

# IMAGE_NT_HEADERS
DWORD Signature; //PE签名 50450000 =》 'PE'NULLNULL
IMAGE_FILE_HEADER FileHeader; // 文件头
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选头,PE为IMAGE_OPTIONAL_HEADER32,PE32+ //为IMAGE_OPTIONAL_HEADER64

3.1 IMAGE_FILE_HEADER

# IMAGE_FILE_HEADER
WORD Machine; //CPU架构,Intel x86芯片的Machine码为14C
WORD NumberOfSections; //节区数量
DWORD TimeDataStamp; //记录编译器创建文件的时间,有些开发工具可设置该值,有些不可设置
...
WORD SizeOfOptionalHeader;//optional header大小,PE和PE32+的optional header大小不同
WORD Characteristics; //标识文件是否是可执行,是否为dll等信息

3.2 IMAGE_OPTIONAL_HEADER32

PE32+ 为IMAGE_OPTIONAL_HEADER64

# IMAGE_OPTIONAL_HEADER32
WORD Magic; //IMAGE_OPTIONAL_HEADER32为10B,IMAGE_OPTIONAL_HEADER64为20B
...
DWORD AddressOfEntryPoint; //EP的RVA值
...
DWORD ImageBase; //指示文件装入虚拟内存时的优先装入地址(VA)
//执行PE文件时,PE装载器先创建进程,再将文件载入内存,然后把EIP寄存器的值设置为ImageBase+AddressOfEntryPoint
DWORD SectionAlignment; //节区在内存中的最小单位, alignment(n.准线)
DWORD FileAlignment; //节区在磁盘文件中的最小单位
//磁盘文件或内存的节区大小必定为FileAlignment或SectionAlignment的整数倍
...
DWORD SizeOfImage; //PE Image在虚拟内存中所占空间的大小,一般而言文件大小和加载到内存中的大小是不同的
DWORD SizeOfHeader; //整个PE头的大小,该值必须是FileAlignment的整数倍
WORD Subsystem; //区分系统驱动文件(*.sys)与普通可执行文件(*.exe,*.dll)
DWORD NumbersOfRvaAndSizes; //用来指定DataDirectory数组的个数
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];//后面几章会单独讲解其中重要的几项: ------------------------ DataDirectory[] ------------------------
DataDirectory[0] = EXPORT Directory
DataDirectory[1] = IMPORT Directory
DataDirectory[2] = RESOURCE Directory
...
...
DataDirectory[9] = TLS Directory
...
...

4.节区头

节区头中定义了各节区属性。

PE文件中的code, data, resource等按照属性分类存储在不同节区。

PE文件格式的设计者把具有相似属性的数据统一保存砸一个称为节区的地方,然后需要把各节区属性记录在节区头中(节区属性中有文件/内存的起始位置,大小,访问权限等)

类别 访问权限
code r-x
data rw-
resource r--
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//节区名,如.text,.code,但是PE规范未明确规定节区Name,可以                                    //向其中放入任何值,所以Name的值仅供参考
union {
DWORD PhysicalAddress;
DWORD VirtualSize; //内存中节区所占大小
} Misc;
DOWRD VirtualAddress;//内存中节区起始地址(RVA)
DOWRD SizeOfRawData; //磁盘文件中节区所占大小,和VirtualSize一般具有不同的值,即磁盘中和内存中节区 //大小不同
DOWRD PointerToRawData; //磁盘文件中节区起始位置
...
DWORD Charateristics; //节区属性,是否包含代码,是否包含初始化/未初始化数据,可读/可写/可执行

0x02 RVA to RAW

通过RVA计算RAW

  1. 查找RVA所在节区

  2. 使用公式计算文件偏移(RAW)

    RAW - PointerToRawData = RVA - VirtualAddress

    RAW = RVA - VirtualAddress + PointerToRawData

由于VirtualSize和SizeOfRawData值不同,所以会引起奇怪的事。。。

0x03 IAT(Import Address Table)

IAT(导入地址表)是一种表格,用来记录程序正在使用哪些库中的哪些函数。IAT存储在PE文件的.text段节区

1.DLL(Dynamic Linked Library)

16位DOS时代不存在DLL的概念,当程序用到库函数时编译器会先从C库中读取函数的二进制代码,然后插入到应用程序中。

但是在之后的Windows OS设计中,为了节约内存/磁盘空间,OS的设计者引入DLL概念:

  1. 不要把库包含到程序中,单独组成DLL文件,需要时调用即可
  2. 内存映射技术使加载后的DLL代码,资源在多个进程中实现共享
  3. 更新库时只要替换相关DLL文件即可,简便易行

加载DLL的方式有两种:

  1. 显示链接(Explicit Linking),程序使用DLL时加载,使用完毕后释放内存
  2. 隐式链接(Implicit Linking),程序开始时即一同加载DLL,程序终止时再释放占用内存

IAT提供的机制与隐式链接有关。

notepad.exe调用CreateFileW()时使用call dword ptr ds:[01001104]而非call 7c8107f0:

  1. kernel32.dll版本不同,CreateFileW()函数的位置也不同
  2. DLL重定位

2.IMAGE_IMPORT_DESCRIPTOR

IMAGE_IMPORT_DESCRIPTOR结构体中记录着PE文件要导入哪些库文件

执行一个普通程序时往往需要导入多个库,导入多少库就存在多少IMAGE_IMPORT_DESCRIPTOR结构体,这些结构体组成了数组,且结构体数组最后以NULL结构体结束。

#struct IMAGE_IMPORT_DESCRIPTOR
union {
DWORD Charateristics;
DWORD OriginalFirstThunk; //INT(Import Name Table)的地址(RVA),INT与IAT是DWORD数组,以NULL结束,INT中各元
//素的值为IMAGE_IMPORT_BY_NAME结构体指针
}
...
DWORD Name; //库名称字符串的地址(RVA)
...
DWORD FirstThunk; //IAT的地址(RVA)
#struct IMAGE_IMPORT_BY_NAME
WORD Hint; //序号
BYTE Name[1]; //函数名称字符串

IMAGE_IMPORT_DESCRIPTOR不在PE头,而在PE体中,IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress的值即是IMAGE_IMPORT_DESCRIPTOR结构体数组的起始地址(RVA),如果要在文件中找到对应的地址需要将RVA转换为RAW。

PE装载器把导入函数输入至IAT的顺序: 见书p108

0x04 EAT(Export Address Table)

与IAT一样,PE文件内的特定结构体(IMAGE_EXPORT_DIRECTORY)保存着导出信息,且PE文件中仅有一个用来说明EAT的结构体(IMAGE_EXPORT_DIRECTORY)

IAT的IMAGE_EXPORT_DIRECTORY结构体以数组的形式存在,且拥有多个成员,这是因为PE文件可以同时导入多个库文件

在PE文件头中IMAGE_OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress的值即是IMAGE_EXPORT_DIRECTORY的起始位置(RVA)

#IMAGE_EXPORT_DIRECTORY
...
DWORD Base; //ordinal base
DWORD NumberOfFunctions; //实际Export函数的个数
DWORD NumberOfNames; //Export函数中具名的函数个数
DWORD AddressOfFunctions; //address of functions start address array
DWORD AddressOfNames; // address of funcion name string array
DWORD AddressOfNameOrdinals; //address of ordinal(序号) array

从库中获得函数的地址的API为GetProcAddress()。该API引用EAT来获取指定API的地址。

GetProcAddress()的操作原理:见书p114

0x05 高级PE

IAT/EAT的内容是运行时压缩器(Runtime Packer),反调试,DLL注入,API钩取等多种中高级逆向主题的基础知识。

PE规范只是一个建议性质的书面标准,查看结构体内部会发现,其实有很多成员并未被使用。只要文件符合PE规范就是PE文件,利用这一点可以制作出一些脱离常识的PE文件。

Patched PE指的就是这样的PE文件,这些PE文件仍然符合PE规范,但附带的PE头非常具有创意。

Reverse Core 第二部分 - 13章 - PE文件格式的更多相关文章

  1. Reverse Core 第二部分 - 16&17章 - 基址重定位表&.reloc节区

    第16-17章 - 基址重定位表&.reloc节区 @date: 2016/11/31 @author: dlive 0x00 前言 这几天忙着挖邮箱漏洞,吃火锅,马上要被关禁闭,看书进度比较 ...

  2. Reverse Core 第二部分 - 14&15章 - 运行时压缩&调试UPX压缩的notepad

    @date: 2016/11/29 @author: dlive 0x00 前言 周六周日两天在打HCTF2016线上赛,没时间看书,打完比赛接着看~~ 0x01 运行时压缩 对比upx压缩前后的no ...

  3. PE文件格式详解,第二讲,NT头文件格式,以及文件头格式

    PE文件格式详解,第二讲,NT头文件格式,以及文件头格式 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) PS:本篇博客 ...

  4. 【JavaScript权威指南(第五版)】笔记之第二部分 客户端JavaScript 第13章~第23章

    第十三章 Web浏览器中的javascript ①   eg:下面两行代码实际上执行的是相同的功能 var answer = 42; window.answer = 42;   ③每个window对象 ...

  5. 深入理解 Win32 PE 文件格式

    深入理解 Win32 PE 文件格式 Matt Pietrek 这篇文章假定你熟悉C++和Win32. 概述 理解可移植可执行文件格式(PE)可以更好地了解操作系统.如果你知道DLL和EXE中都有些什 ...

  6. ASM:《X86汇编语言-从实模式到保护模式》第13章:保护模式下内核的加载,程序的动态加载和执行

    ★PART1:32位保护模式下内核简易模型 1. 内核的结构,功能和加载 每个内核的主引导程序都会有所不同,因为内核都会有不同的结构.有时候主引导程序的一些段和内核段是可以共用的(事实上加载完内核以后 ...

  7. Linux就这个范儿 第13章 打通任督二脉

    Linux就这个范儿 第13章 打通任督二脉 0111010110……你有没有想过,数据从看得见或看不见的线缆上飞来飞去,是怎么实现的呢?数据传输业务的未来又在哪里?在前面两章中我们学习了Linux网 ...

  8. PE文件格式详解,第三讲,可选头文件格式,以及节表

    PE文件格式详解,第三讲,可选头文件格式,以及节表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶可选头结构以及作 ...

  9. PE文件格式分析

    PE文件格式分析 PE 的意思是 Portable Executable(可移植的执行体).它是 Win32环境自身所带的执行文件格式.它的一些特性继承自Unix的Coff(common object ...

随机推荐

  1. Error 1606 Could Not Access Network Location %SystemDrive%/inetpub/wwwroot/ 的错误解决方法

    在卸载或者重安装Infragistics NetAdvantage时候提示如标题的错误 win7下 1.打开注册表 Regedit 2.找到HKEY_LOCAL_MACHINE/SOFTWARE/Mi ...

  2. asp.net使用Get请求webservice

    先在Web.config中的System.Web节点下添加如下代码,使其支持Get请求: <webServices> <protocols> <add name=&quo ...

  3. jquery获取url参数及url加参数的方法

    转--http://www.jb51.net/article/73896.htm <script src="js/jquery-1.7.2.min.js" type=&quo ...

  4. 让PDF.NET支持不同版本的SQL Server Compact数据库

    最近项目中需要用到嵌入式数据库,我们选用的数据开发框架是PDF.NET(http://www.pwmis.com/SqlMap/),之前的博文已经总结了让PDF.NET支持最新的SQLite,今天我们 ...

  5. 基于Dubbo框架构建分布式服务(一)

    Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...

  6. log4j的配置

    转载:http://it.oyksoft.com/log4j/ 收藏:http://www.cnblogs.com/ITEagle/archive/2010/04/23/1718365.html 一. ...

  7. 引用js或css后加?v= 版本号的用法

    <span style="font-size:14px;">css和js带参数(形如.css?v=与.js?v= 或 .css?version=与.js?version ...

  8. Node.js API 初解读(一)

    Node.JS API 初解读 Version: NodeJs v6.2.0 一. Assert 1.简介 Assert模块主要用于断言.如果表达式不符合预期,就抛出一个错误. 该模块用于编写程序的单 ...

  9. 关于JSF中immediate属性的总结(二)

    The immediate attribute in JSF is commonly misunderstood. If you don't believe me, check out Stack O ...

  10. iOS优秀博客收录

    原文链接:http://ju.outofmemory.cn/entry/105297 唐巧 王巍 破船之家 NSHipster Limboy 无网不剩 念茜的博客 Xcode Dev Ted's Ho ...