菜单

[PE结构分析] 十.基址重一直

2019年4月14日 - 皇家赌场系统

结构图如下,图片中 0 和 000 都意味着1陆进制数,转换成二进制是  0000 和 0000
0000 0000:

源代码如下:

扩展头

系统 1

image.png

系统 2

image.png

DOS 头

在我们解析PE 的在此之前,还有别的两个头要精晓一下,DOS
头,不得不说,微软事儿照旧挺多的。

微软在创设PE 文件格式时,人们正在附近运用DOS
文件,所以微软为了思虑包容性的题材,所以在PE 头的最终面还添加了2个IMAGE_DOS_HEADE奥迪Q伍 结构体,用来扩充已有的DOS EXE。在WinNTFS.h
里可以看看她的身影。

系统 3

DOS
头结构体的大小是40字节,那里边有三个至关心器重要的分子,须求掌握,贰个是e_magic
又见魔数,一个是e_lfanew,它只是了NT 头的撼动。

对于PE 文件来说,那一个e_magic,也正是DOS 签名都以MZ,听说是3个叫 马克Zbikowski 的开发人士在微软安插了那种ODS 可执行文件,所以…

笔者们以Windows 下的notepad++
的可执行文件为例,在2进制编辑软件中开辟,此类软件比较多,Heditor 打开:

系统 4

始发的多个字节是4D5A,e_lfanew 为00000拾8 注意存款和储蓄顺序,小端。

您以为初步加上了DOS 头就完事了么,就能够随着接PE 头了么。为了同盟DOS
当然不是那般简单了,紧接着DOS 头,跟的是DOS 存根,DOS
stub。这一块就是为DOS 而准备的,对于PE 文件,即便未有它也能够寻常运转。

系统 5

一旁的ASCII 是读不懂的,因为她是机器码,是汇编,为了在DOS
下执行,对于notepad++ 来说,那里是履行了一句,this program cannot be run
in DOS mode 然后脱离。逗我= =,有新的人,能够在DOS
中创设二个程序,做1些小动作。

系统 6

VirtualAddress 为 0x一千,SizeOfBlock 为 0x64。第一个条目为
0x33八C,高4人为 0x三,offset为 0x3八C,即偏移地址为 0x13捌C (由 0x一千 +
0x3八C得来)应用于此地址上1切叁拾3个人。打开C3二Asm反汇编查看:

image.png

IAT

导入地址表的始末与Windows 操作系统的基本进度,内部存储器,DLL
结构有关。他是壹种表格,记录了先后选拔什么库中的哪些函数。

上边,让大家把目光转到DLL 上,Dynamic Linked Library 支撑了壹切 OS。DLL
的补益在于,不必要把库包蕴在先后中,单独构成DLL
文件,须求时调用即可,内部存款和储蓄器映射技术使加载后的DLL
代码,资源在多少个经过中落到实处共享,更新库时候假使替换相关DLL 文件即可。

加载DLL 的主意有三种,一种是显式链接,使用DLL
时候加载,使用完释放内部存款和储蓄器。另一种是隐式链接,程序先河就一块儿加载DLL,程序终止的时候才假释掉内部存款和储蓄器。而IAT
提供的机制与隐式链接相关,最非凡的Kernel3二.dll。

咱俩来看看notepad++ 调用kernel3二.dll 中的CreateFileW, 使用PE
调节和测试工具Ollydbg

系统 7

咱俩看看填入参数之后,call 了35d7ffff 地址的内容,然后大家去dump
窗口,找一下kernel.CreateFileW:

系统 8

咱俩双击汇编窗口,运营编写制定,发现确实是call 的这几个数值:

系统 9

不过难题来了,上面是E8 3五D7FFFF,上边地址却是 00C6217捌。其实那是Win
Visita, Win 7的ASLLacrosse技术,首要正是对准缓冲溢出攻击的一种爱慕技巧,通过随机化布局,让逆向跟踪者,难以找寻地址,就难以不难的展开溢出攻击。可是仍然得以由此跳板的不二法门,找到溢出的格局,那就是后话了。

近期能够规定的是,35D七FFFF 能够认为保存的数值正是 CreateFileW
的地点。而为何不直接使用CALL 7509168B 那种格局向来调用呢?
Kernel3二.dll 本子各不一致,对应的CreateFileW
函数也各不一样,为了合作各样环境,编写翻译器准备了CreateFileW
函数的实际上地址,然后记下DWO途睿欧D PT奇骏 DS:[xxxxxx]
那样的命令,执行文书时候,PE 装载器将CreateFileW 函数地址写到那个职位。

而且,由于重一直的来头存在,所以也不能够直接采纳CALL 750916八B
的方法,比如多个DLL 文件有同1的
ImageBase,装载的时候,二个装载到该职位然后,另3个就不可能装载该地方了,要求换地方。所以大家不能够对实在地址进行硬编码。

IMAGE_IMPORT_DESCRIPTOR

系统 10

系统 11

对此叁个常见程序来说,必要导入多少个库,就会存在多少个这么的结构体,那么些结构体组成数组,然后数组最后是以NULL
结构体甘休。个中有多少个重点的积极分子:

那么PE 是什么样导入函数输出到IAT 的:

  1. 读取NAME 成员,获取扩名称字符串
  2. 装载相应库: LoadLibrary(“kernel3二.dll”)
  3. 读取OriginalFirstThunk成员,获取INT 地址
  4. 读取INT 数组中的值,获取相应的
    IMAGE_IMPORT_BY_NAME地址,是RVA地址
  5. 使用IMAGE_IMPORT_BY_NAME 的Hint 只怕是name
    项,获取相应函数的苗头地点 GetProcAddress(“GetCurrentThreadId”)
  6. 读取FistrThunk 成员,获得IAT 地址。
  7. 将方面得到的函数地址输入相应IAT 数组值。
  8. 重复4-7 到INT 结束。

此间就生出了二个狐疑,OriginalFirstThunk 和 First Thunk
都指向的是函数,为何多此一举呢?

先是,从直观上说,多少个都指向了库中引进函数的数组,鱼C 画的那张图挺直观:

系统 12

OriginalFirstThunk 和 FirstThunk 他们都以多个门类为IMAGE_THUNK_DATA
的数组,它是三个指南针大小的一路(union)类型。
每一个IMAGE_THUNK_DATA
结构定义一个导入函数音信(即指向协会为IMAGE_IMPORT_BY_NAME
的东西,这个家伙稍后再议)。
下一场数组最终以一个内容为0 的 IMAGE_THUNK_DATA 结构作为实现标志。
IMAGE_THUNK_DATA3贰 结构体如下:

系统 13

因为是Union 结构,IMAGE_THUNK_DATA 事实上是三个双字大小。
规定如下:

当 IMAGE_THUNK_DATA 值的最高位为 一时,表示函数以序号方式输入,那时候低
三14位被看作一个函数序号。

当 IMAGE_THUNK_DATA 值的参天位为
0时,表示函数以字符串类型的函数名艺术输入,这时双字的值是1个RAV4VA,指向2个 IMAGE_IMPORT_BY_NAME 结构。

咱俩再看IMAGE_IMPORT_BY_NAME 结构:

系统 14

布局中的 Hint
字段也象征函数的序号,可是那几个字段是可选的,某些编译器总是将它设置为 0。

Name 字段定义了导入函数的名目字符串,那是二个以 0 为最终的字符串。

近来重大来了:

率先个数组(由 OriginalFirstThunk
所指向)是独立的1项,而且无法被改写,大家前边称为 INT。第四个数组(由
FirstThunk 所指向)事实上是由 PE 装载注重写的。

PE 装载器装载顺序正如上边所讲的那样,大家再将它讲详细一点:

PE 装载器首先搜索 OriginalFirstThunk
,找到之后加载程序迭代搜索数组中的每个指针,找到各个IMAGE_IMPORT_BY_NAME
结构所针对的输入函数的地址,然后加载器用函数真正入口地址来顶替由
FirstThunk 数组中的一个输入,因而我们称为输入地址表(IAT).

持续套用鱼C 的图,就能直观的感触到了:

系统 15

于是,在读取一遍OriginalFirstThunk 之后,程序正是重视IAT
提供的函数地址来运作了。

任何中文翻译:

typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
//  WORD    TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

PE中有结构体数组的组织的下结论:

数录节入重!

系统 16

image.png

.text 段:代码段
.data段:数据段
.bss段:表示未开头化的多寡,比如Static变量
.rdata 段:表示只读的数量,比如字符串
……
.relcoc段:存款和储蓄重一向音讯的区段
各变量存放于哪个区:
常量 ——————>.rdata区
静态变量————->.bss区
全局变量————–>.data 区
节表里面包车型客车多少个重点数据:
VirtualAddress:那个区段的争持虚拟地址
SizeofRawData:那一个区段在磁盘中的大小,举办了文本对齐
PointerToRawData:区段的文件偏移,就是那几个区段在磁盘文件中的起先地点
二个根本的公式:
offset(转)=PAJEROVA(要求更换的MuranoVA)-酷威VA(所在区段的PAJEROVA)+offset(正是PointerToRawData)

字符串表

何以会有字符串表呢?其实这些也是在时时刻刻前行更上壹层楼中找到的化解办法,在ELF
文件中,会用到很多的字符串,段名,变量名等等,不过字符串其本人又长度不稳定,借使应用一定结构来表示,就会拉动空间上的劳动。所以,构造五个字符串表,将利用的字符串统1放在这里,然后通过偏移量来引用字符串,岂不美哉。

亟需运用的时候,只须求给2个偏移量,然后就到字符串该岗位找字符串,蒙受\0
就停止。

字符串在ELF 文件中,也是以段的花样保留的,常见的段名 .strtab, .shstrtab
几个字符串分别为字符串表和段表字符串,前者用来保存普通的字符串,后者保存段名。

在大家选取readelf -h 的时候,我们看到最终3个成员,section header string
table index ,实际上她指的正是字符串表的下标,bash
对应的字符串表下标为贰七,在接纳objdump
的时候,实际上忽略了字符串表,我们使用readelf
,就足以看到第贰伍位即字符串表:

系统 17


上边大家回看一下,那一个ELF 构造的精致之处,当1个ELF
文件到来的时候,系统本来的找到他的起来,获得文件头,首先看魔数,识别基本音讯,看是还是不是天经地义的,或然是可识其他文件,然后加载他的为主音信,包蕴CPU
平台,版本号,段表的义务在哪,还可以得到字符串表在哪,以及整个程序的入口地址。那壹多元初阶化音讯得到后来,程序能够透过字符串表定位,找到段名的字符串,通过段表的开头地点,确认各样段的职位,段名,长度等等音信,进而到达入口地址,准备举办。

自然,那只是早先时期始的始末,其后还要思虑链接,Import,Export
等等情节,留待未来完善。

各种元素的轻重缓急都记载在 SizeOfBlock 中,那么些元素是由 3个
_IMAGE_BASE_RELOCATION 结构体和一个TypeOffset 数组组成的。TypeOffset
数组的每种成分占3个字节,在那之中,高3个人是偏移类型(type),低1四个人代表必要重一向的地点(Offset),即,它与
VirtualAddress 相加正是指向 PE 影像中要求修改的十分代码的途锐VA。

别的中文翻译:

系统 18

PE 文件

上边大家去看看更为广阔的PE 文件格式,实际上PE 与 ELF
文件基本相同,也是利用了根据段的格式,同时PE
也同意程序员将变量恐怕函数放在自定义的段中, GCC
*系统,*attribute(section(‘name’))** 扩大属性。

PE 文件的前身是COFF,所以分析PE 文件,先来探望COFF
的文件格式,他保留在WinNT.h 文件中。

COFF 的文件格式和ELF 大约一毛一样:

Image Header
SectionTable Image_SECTION_HEADER
.text
data
.drectve
.debug$S
… other sections
Symbol Table

文本头定义在WinNT.h 中,我们打开来看一下:

系统 19

笔者们可以看看,它那个文件头和ELF
实际上是平等的,也在文书头中定义了段数,符号表的职位,Optional Header
的高低,这么些Optional Header 后面就看出了,他正是PE
可执行文件的文本头的一对,以及段的天性等。

跟在文书头前面包车型地铁是COFF 文件的段表,结构体名称叫 IMAGE_SECTION_HEADER :

系统 20

属性包蕴那一个,和ELF 没差:

找到数据:

系统 21

PE尾部包括了Dos头,一直到节表的终结地点,.text区段伊始以前
![](https://upload-images.jianshu.io/upload_images/5676193-cfbc56c21cd7568f.png)

image.png

段头

PE 的段头直接沿用的COFF 的段头结构,上面也说过了,我们查阅notepad++
的段头,能够拿走各类段名,以及其新闻,那里,大家得以使用部分软件查看,尤其方便人民群众:

系统 22

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图