ximo 发表于 2008-10-24 10:33

从1个简单的ReverseMe开始谈PE文件结构的学习

PE文件结构的学习,是脱壳以及逆向技术必须要学的一项内容。但是,由于网上的一些PE文件结构的文章比较抽象,因此,一些新手学起来会觉得比较困难。鉴于此,我就用1个简单的ReverseMe开始,来给大家简单的讲解一下PE文件结构,尤其是输入表这块。想学习脱壳技术的,输入表这块内容必须彻底掌握!

这个ReverseMe的任务很简单:
1.手动添加1个区段
2.创建1个对话框

要求:只用OD和16进制编辑器!

关于一些PE格式的基础知识,比如,有DOS MZ head,DOS Stub,有PE头,区块等等,就不在具体说了,本文的重点,是关于输入表(IAT)的一些内容。

OK,按照任务来吧!

一、添加1个区块

其实,我们用Lord PE以及其他的一些PE工具可以很方便的添加1个区块,但是,既然要求不能用工具,也为了巩固一下对PE结构的了解,我们就只用16进制工具来进行添加吧。

首先,我们来了解下区块的结构:

IMAGE_SIZEOF_SHORT_NAME equ 8

IMAGE_SECTION_HEADER STRUCT
Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)
union Misc
PhysicalAddress dd ?
VirtualSize dd ?
ends
VirtualAddress dd ?
SizeOfRawData dd ?
PointerToRawData dd ?
PointerToRelocations dd ?
PointerToLinenumbers dd ?
NumberOfRelocations dw ?
NumberOfLinenumbers dw ?
Characteristics dd ?
IMAGE_SECTION_HEADER ENDS

简单的了解一下区段的结构后,我们来看下原来所拥有的区段的一些信息:



翻转整理一下就得如下的信息:

Name:.text
VirtualSize:0x000E
VirtualAddress:0x1000
SizeOfRawData:0x200
PointerToRawData:0x0400
PointerToRelocations:0
PointerToLinenumbers:0
NumberOfRelocations:0
NumberOfLinenumbers:0
Characteristics:0x60000020
--
Name:.rdata
VirtualSize:0x0054
VirtualAddress:0x2000
SizeOfRawData:0x200
PointerToRawData:0x0600
PointerToRelocations:0
PointerToLinenumbers:0
NumberOfRelocations:0
NumberOfLinenumbers:0
Characteristics:0x40000040
--
Name:.data
VirtualSize:0x00e2
VirtualAddress:0x3000
SizeOfRawData:0x200
PointerToRawData:0x0800
PointerToRelocations:0
PointerToLinenumbers:0
NumberOfRelocations:0
NumberOfLinenumbers:0
Characteristics:0xC0000040

根据上面的一些信息,我们来构造1个新的区段;

Name:.ximo
VirtualSize:0x0100
VirtualAddress:0x4000
SizeOfRawData:0x0200
PointerToRawData:0x0A00
PointerToRelocations:0
PointerToLinenumbers:0
NumberOfRelocations:0
NumberOfLinenumbers:0
Characteristics:0xC0000040


很明显,从0x220处开始,就可以开始写入新的区段的信息了。注意字节以及数值的翻转(如:0040要改写成40 00)

填完后如下:



填完区段信息后,必须要在后面填充相应大小的数据。如,本区段的大小为0x0200,因此在最后面添加0x0200大小的数据,用00填充即可!



最后,把区段数改为4,位置在0xB6处,同时,镜象大小也改大些,改为5000即可,位置在0x100处。

至此,手动添加区段的操作就结束了。运行一下修改后的文件,没有提示错误!
借助PE编辑工具(Lord PE)看下修改后的信息吧:





二、手动添加函数

由于我们的任务是添加对话框,用到的函数自然就是MessageBoxA,其所在的DLL为USER32.DLL。
但是可以发现,原程序中并没有MessageBoxA这个函数,也没有USER32.DLL这个DLL,因此,一切得什么自己来添加。

首先,我们查找下原来程序IAT信息

来到0x3C处,它的值指向PE头,它的值为0xB0,PE头的骗移80处,既是此文件的RVA。



也就是:0xB0+0x80=0x130

定位到0x130处看下:



可以发现,其RVA的值为08200000,翻转下就是00002008,大小为28000000,翻转下就是00000028

用LordPE验证下:




数据都吻合!


继续来看,由于RVA=2008,那么具体的偏移值为多少呢?也就是在16进制工具中所对应的值
可以发现,2008在此PE文件的.rdata段,.rdata段的Voffset=0x2000,Roffset=0x600,
则ΔH=0x2000-0x600=0x1A00 (这个偏移值一会将一直用到)

所以,RVA所指向的地址为:0x2008-0x1A00=0x608

开始分析0x608开始的数据:

这个就是IID(IMAGE_IMPORT_DESCRIPTOR)数组:

IMAGE_IMPORT_DESCRIPTOR STRUCT
union
Characteristics dd ?
OriginalFirstThunk dd ?
ends
TimeDateStamp dd ?
ForwarderChain dd ?
Name1 dd ?
FirstThunk dd ?
IMAGE_IMPORT_DESCRIPTOR ENDS


OriginalFirstThunk:30200000
TimeDateStamp:00000000
ForwarderChain:00000000
Name:46200000
FirstThunk:00200000

翻转1下为:
OriginalFirstThunk:0x2030
TimeDateStamp:0x0000
ForwarderChain:0x0000
Name:0x2046
FirstThunk:0x2000

简单的分析下:

定位到0x608处,可以发现数据为30200000,也就是00002030,换算1下:0x2030-0x1A00=0x630
接着定位到0x630,数据为38200000,翻转下,就是00002038,换算1下:0x2038-0x1A00=0x638
再定位到0x638,可以在旁边的窗口,很明显的看到,此API为ExitProcess。


同理:0x2046所指向的既是KERNEL32.DLL这个DLL

整理1下可得:

OriginalFirstThunk:0x2030(ExitProcess)
TimeDateStamp:0x0000
ForwarderChain:0x0000
Name:0x2046(KERNEL32.DLL)
FirstThunk:0x2000(ExitProcess)
有了上面的基础,我们开始来自己添加1个输入函数:USER32.MessageBoxA

由于不想破坏原来的输入表结构,怕损坏以至无法运行,因此,我们把他移动到我们自己所加的区段里

再来计算下新的偏移量:

ΔH=0x4000-0xA00=0x3600

我们在自己加的区段写那函数吧!

找个地址:0xA00,开始写吧

然后,再找个地址写入输入表信息(0xA21)

根据写入函数的地址,构造个新的IID数据结构如下:

OriginalFirstThunk:0x4019(MessageBoxA)
TimeDateStamp:0x0000
ForwarderChain:0x0000
Name:0x4000(USER32.DLL)
FirstThunk:0x4019(MessageBoxA)


下面可以开始写信息了:

原来所有的函数就不必要自己写了,直接用原来的数据吧:

3020 0000 0000 0000 0000 0000 4620 0000 0020 0000(原来那个函数)

1940 0000 0000 0000 0000 0000 0040 0000 1940 0000(新添加的函数)



添加完数据后,来到0x130处,把RVA的值改为0xA21+0x3600=0x4021,也就是2140 0000

OK,保存一下吧。

再次运行,发现没有问题,说明,添加函数成功了!

再次借助工具验证下:



发现已经添加成功了!

三、写代码,调用对话框

下面的任务就是很简单的了,在OD里写代码,调用对话框吧。

看下MessageBoxA的原型:

int MessageBox(
HWND hWnd,// handle of owner window
LPCTSTR lpText,// address of text in message box
LPCTSTR lpCaption,// address of title of message box
UINT uType // style of message box
);


反汇编后就是:

push 0
push title
push caption
push 0
call MessageBoxA


打开OD,找个新区段的代码处,如:00404080

随便找2个地方写字符串:

如:4040A0写title:www.52pojie.cn
4040C0写caption:Hello!I am ximo!

然后在00404080处写入如下代码:

push 0
push 4040A0
push 4040C0
push 0
call dword ptr

最后别忘了跳回原来的代码处:

jmp 00401000

保存一下,当然,此时还是无法运行的,因为入口代码还是以前的。
下面来修正下入口代码:

打开16进制编辑器;定位到:0xD8处,把原来的数据0010 0000修改为8040 0000

OK,保存一下。运行看看情况:




出现对话框了吧。

工作到此结束吧!

文章内容比较简单,希望本文对大家学习PE文件结构有所帮助!
----ximo

aisht 发表于 2008-10-24 10:57

顶哇.超神...
支持哇.

下雪天 发表于 2008-10-24 11:36


好好看下,很重要哦。

小糊涂虫 发表于 2008-10-24 11:40

超神的东西.顶起.......

wesley 发表于 2008-10-24 12:12

不顶对不起我自己超兄辛苦

wgz001 发表于 2008-10-24 12:31

多谢了多看几遍好好体会下

355047739 发表于 2008-10-24 13:55

冒个泡!~~好教程呀!~迟滞lz

小生我怕怕 发表于 2008-10-24 14:45

支持超兄,弄来做电子书收藏!

niliu 发表于 2008-10-24 15:45

超神啊超神不愧是神级大牛

球球 发表于 2008-10-24 15:55

经验太重要了破解真难啊
页: [1] 2 3 4 5 6 7
查看完整版本: 从1个简单的ReverseMe开始谈PE文件结构的学习