@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. SQLSERVER 获取datetime日期的查询语句

    SELECT varchar(10:57AM SELECT varchar(CONVERT(100), GETDATE(), 2): 11.05.16 SELECT varchar(CONVERT(1 ...

  2. DOM基础知识

    DOM BOMDOM 文档对象模型 document.BOM 浏览器界面上所有内容 broder object.没有括号属性.()方法 DOM写法 document.作用 做特效 找到 摘出元素 增删 ...

  3. Java学习笔记之JNDI(六)

    JNDI 是什么 JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一,不少专家认为,没有透彻理解JNDI的 ...

  4. BZOJ-2127-happiness(最小割)

    2127: happiness(题解) Time Limit: 51 Sec  Memory Limit: 259 MBSubmit: 1806  Solved: 875 Description 高一 ...

  5. NFS服务器配置文档

    Server:192.168.1.206/WindowsClient:192.168.1.208/CentOS一.搭建windows NFS服务:1.安装NFS服务器:打开"服务管理器&qu ...

  6. dom 节点篇

    1,创建元素 document.createElement('要创建的元素名'); 2.插入节点 appendChild 和insertBefore 3.删除节点 removeChild  用法 re ...

  7. php实现文件上传与下载(上)

    php实现文件的上传与下载是一个挺基本的功能,一般网站多多少少都会有这样的需求在内,当然不是说所有的文件都可以被上传,那这网络就太没有安全性可言了.因为接触php时间不长,今天写练练手,随笔也就是公开 ...

  8. IRequiresSessionState接口控制

    刚刚接触.net web端的朋友都会被Session坑过,莫名其妙的不能读取Session数据,后来知道原来有IRequiresSessionState这个接口,不继承的就不能读取Session里面的 ...

  9. Struts2之HelloWorld

    首先既然是开发Struts程序的话,那么自然需要用到Struts2开发包,Struts2是apache旗下的开源框架,所有的开发包和源代码都可以在Apache官网下载. 那么,就来开始编写第一个Str ...

  10. svn强制加注释才能提交

    进入库的hooks目录下 cp pre-commit.tmpl pre-commit 并对pre-commit加入运行权限 修改pre-commit内容如下 REPOS="$1" ...