首页
社区
课程
招聘
[原创]Win PE系列之导入表解析与IAT Hook技术
发表于: 2021-10-11 10:43 13022

[原创]Win PE系列之导入表解析与IAT Hook技术

2021-10-11 10:43
13022

在对PE文件进行解析的过程中,经常需要对这三类地址进行转换。

RVA:相对虚拟内存地址,指的是文件加载到内存中以后相对于起始地址的偏移。

FOA:文件偏移地址,指的是文件保存在磁盘中时候,相对于起始地址的偏移。

VA:虚拟内存地址,文件在内存中的真实地址。

依然是昨天的这张图,可以看到,由于FileAlignment和SectionAlignment的不同,节区中的地址相对于起始地址偏移是不同的。以地址0x401为例,这个地址是在第一个节区中,由于在文件中由于FileAlignment是0x400,所以这个地址在文件中就是0x401。而在内存中,由于SectionAlignment是0x1000,所以前面PE头的数据会被填充到0x1000,所以这个地址就是0x1001。

据此也可以知道RVA和FOA的转换关系,如果地址是在PE头中,那么FOA==RVA。而如果地址是在节区中,就需要判断文件是在哪一个节区,然后根据这个地址和节区的偏移来算出它在文件中的位置FOA。

比如,如果RVA是0x1001,那么在内存中,它就是在第一个节区,它和第一个节区的偏移就是0x1。那么FOA就等于文件中第一个节区的位置加上这个偏移,也就是0x400+1=0x401。在PE的结构中,只要是VirtualAdresss字段名的数据,都是RVA,实际操作的时候都需要转换成FOA。

相应的RVA转FOA的代码如下

由于PE文件加载在内存中的起始地址是由可选头中的ImageBase来指定的,通常对于exe程序它都是0x40000。所以在计算FA,也就是内存中的真实地址的时候,是需要把这个起始地址算进去的,也就是说FA=RVA + ImageBase。

在Windows系统中,可以用两种方式加载DLL。分别称为隐式链接和显示链接,其区别就是隐私链接进来的DLL会随着程序的启动而为函数分配相应的内存,而显示链接则是在程序需要调用相关函数的时候获取相应的地址。但是随着系统版本的不同,加载的DLL数量顺序等等的不同,DLL中的函数地址是不确定的。

为了可以让程序每次都能找到正确的地址,就需要程序用一块地址来专门记录这些隐式链接的DLL中的函数地址,这块专门的地址就是叫导入表。每次启动的时候,操作系统都会获取正确的函数地址填入到导入表中。

比如下面这张图是对MessageBox的调用,可以看到程序不是E8的直接调用,而是E9的间接调用,所调用的是地址0x0042A2AC地址中保存的数据。而0x0042A2AC这个地址其实就是IAT表,后面会详细说这个表。

在内存窗口中查看这个地址中的内容,可以看到调试器已经告诉了我们这是一个MessageBox的函数地址。

要获取导入表的地址,首先就需要到可选头的最后一个成员,也就是IMAGE_DATA_DIRECTORY数组DataDirectory中去找。数组中的第一项保存的就是导入表的信息,在文件中有如下的定义

而IMAGE_DATA_DIRECTORY的定义如下

其中的VirtualAddress就是导入表在内存中的RVA,要在文件中找到就需要转换成相应的FOA。获取到的地址就是导入表的起始地址,所指的就是第一个导入表的地址,保存的是IMAGE_IMPORT_DESCRIPTOR结构体,结构体定义如下

由于程序会需要多个导入表,而每个导入表都有这样的一个结构,它们在内存中顺序存储下去形成数组。所以,为了说明已经加载了全部的导入表,系统会在最后一个导入表后面加入一块IMAGE_IMPORT_DESCRIPTOR结构体大小的0来说明已经结束。

结构中的Name指向的是地址保存了导入的Dll文件的文件名,而OriginalFirstThunk和FirstThunk保存的地址分别是INT和IAT表的地址,这3个地址都是RVA,使用的时候都要转成FOA。

INT和IAT所指地址的内容是一块数组,数组中的每个元素都是IMAGE_THUNK_DATA32结构体的定义如下

结构体中,是一个联合体,而联合体中的每个成员都是4字节的内容,所以我们可以认为INT表和IAT表所指向的是一个整型数组。数组中保存的值的最高位是1的时候,去除最高位,得到的就是函数的导出序号,否则就是一个指向IMAGE_IMPORT_BY_NAME的RVA,结构体定义如下

其中的Name保存的就是导入的函数的名称,该名称是以0作为结束符。

对于判断最高位是否位1,在文档中有如下的定义

据此,便可以找到程序导入的所有的Dll文件名以及导入的相应函数,如下图。

在文件加载到内存中之前,INT和IAT所指的内容是一样的,都是指向保存了函数名地址。而在程序装载进内存的时候,操作系统会根据导入表的名称和导入函数的名称将正确的函数地址填入到IAT表中。

据此可以写出解析导入表的代码如下

程序运行结果如下


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-10-11 10:47 被1900编辑 ,原因:
收藏
免费 5
支持
分享
最新回复 (4)
雪    币: 234
活跃值: (801)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
师傅这个IAT Hook代码好像不能在win7上成功运行
2022-7-19 10:14
0
雪    币: 22411
活跃值: (25361)
能力值: ( LV15,RANK:910 )
在线值:
发帖
回帖
粉丝
3
筱小 师傅这个IAT Hook代码好像不能在win7上成功运行
32位的话应该是可以的
2022-7-19 15:51
0
雪    币: 234
活跃值: (801)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
1900 32位的话应该是可以的
师傅看了我之前给你发的那个链接里面的漏洞没有,那个关于indexsearcher的漏洞
2022-8-3 14:55
0
雪    币: 42
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
*pIAT = (DWORD)HookMessageBoxA;   //将我们的函数地址复制进去  
这里提示没权限的话,用VirtualProtect修改内存的保护属性就可以了。

DWORD oldProtect;
VirtualProtect(pIAT, sizeof(FARPROC), PAGE_READWRITE, &oldProtect);
*pIAT = (DWORD)dwOrgFuncAddr; //将原来的函数恢复回去
VirtualProtect(pIAT, sizeof(FARPROC), oldProtect, &oldProtect);
2024-7-20 13:52
0
游客
登录 | 注册 方可回帖
返回
//