IAT导入函数地址表 | IT导入表

之前对 IAT 的理解一直比较模糊,现在通过一个简单脱壳来梳理一下。

一、场景

用到的两个素材如下,一个没壳,一个有UPX壳。

首先在Crackme中查看MessageBoxA函数的入口地址,不同系统不同版本可能地址不一样,这个程序在我电脑上运行正常,可能在别的电脑上就会出错,为了解决这个兼容问题,IAT(导入函数地址表)就诞生了。

 

二、栗子

在OD中右键查看所有模块间调用,将显示主模块中调用了哪些模块和API,双击第一个MessageBoxA跳转过去

可见在程序的 40135C 处执行了 call 40143A 命令,可见实际上是直接调用了 40143A 处的 jmp.&USER32.MessageBoxA函数。跳转过去看看是什么

40143A 地址处执行了一个间接跳转:jmp [4031AC],其实 4031AC 地址中保存的数值才是MessageBoxA函数的真正入口地址。跳转过去看看,API入口地址为 75C6EA11,这块区域包含了该程序调用的所有API函数的入口地址,这块区域称之为IAT——导入函数地址表。

·

这个栗子也就是为了解决上述的问题而设计的,当程序需要调用某个API函数的时候,都是通过一个间接跳转来调用的,读取某个地址中保存的API函数地址,然后调用。或者说,该程序在不同版本的操作系统上都是调用间接跳转跳转到IAT表中,然后在IAT表中读取到真正的API入口地址,调用,因此只要将所有API真正的入口地址填充到IAT中,就能确保不同版本系统调用的都是正确的API函数。

·

三、那么 4031AC 这个地址所代表的这块IAT 在不同机器上也是不一样的吧 ?

定位到此处,然后右键在可执行文件中查看,0FAC 就是4031AC这个虚拟地址对应于磁盘文件中的文件偏移,3360是FAC偏移处的内容。当程序运行起来时,0FAC这个文件偏移对应的虚拟地址的内容就会被填充为API的入口地址 75C6EA11,此处对应的虚拟地址就是4031AC。系统会将MessageBoxA函数正确的入口地址填充到4031AC中。

文件偏移0FAC处的内容为3360,这个3360实际上是RVA——相对虚拟地址,指向对应的API的名称,查看一下 403360,可以看到MessageBoxA这个字符串:

操作系统就是根据这个指针,获得API的名称,然后调用GetProcAddress函数获取对应的API函数的地址,再将这个地址填充到IAT中,覆盖原来的3360。这样就能调用MessageBoxA了。在程序开始执行前,IAT已经被填入了正确的API的地址。

·

所以,为了确保操作系统将正确的API函数地址填充到IAT中,应该满足以下两点:

① 可执行文件各IAT项所在的文件偏移处必须是一个指针,指向一个字符串

② 这个字符串就是API函数的名称

·

只要上面两项满足,就可以确保程序在启动时,操作系统会将正确的API函数的地址填充到IAT中。

 

四、为啥要修复IAT ?

比方说现在已经到了被加壳程序的OEP处,接下来可以将程序进行dump了,但是在dump之前需要修复IAT,为什么?

难道壳将IAT破坏了了吗?是的,壳根本不需要源程序的IAT。

因为被加壳程序会首先执行解密,读取IAT中所需要的API的名称指针,然后通过GetPro从Address定位到API函数地址,将API的入口地址填入IAT中,此时IAT已经被填入了正确的API函数地址,对应的API函数名称字符串已经不需要了,可以清除掉。

大部分的壳会将API函数名称对应的字符串加密保存到某个地址处,不让Cracker轻易发现。

 

五、回到Creame UPX

发现 MessageBoxA 这个API入口地址的存放处——4031AC 为空

指向API名称字符串的——403360 也为空

下面来到OEP处,再看看上面两处有没有内容,因为程序在运行之前 IAT 必须填充上正确的API地址。

可以看到4031AC处的MessageBoxA的入口地址 75C6EA11 已经被正确填入。

403360 处本该保存API字符串的,还是空

所以就算此时Dump下来,脱壳也会失败,因为dump出来的程序启动所必需的数据并不完整(少了API名称),系统无法填充IAT。用OD打开也会提示同样的错误。

·

实际上此时这个报错程序的IAT已经完成了填充,如下图,但是想要在其他机器上正确运行的话,必须需要保存所有API名称字符串的指针(地址),这样才能保证操作系统可以通过GetProAddress获取到正确的API函数地址并填充到IAT中——不同机器不同操作系统中的API地址是不一样的。因此在此处操作系统找不到指向API名称字符串的指针(比如403360),就报错啦。

·

 

六、正确操作:在Dump之前将IAT修复

切换到CrackMe,来看看未加壳程序的IAT,先来看看一些重要字段。数据窗口定位400000,切换为显示PE结构。

可以看到PE偏移为100,跳转过去看看还真是~呵呵往下看

继续往下看,能够看到 IT——导入表 的 偏移地址0x3000 和 大小0x670,注意IT——导入表和IAT——导入地址表不一样,

我们知道程序启动之前操作系统会将各个API函数的地址填充到IAT中,那么IT又是什么鬼?跳转过去看看。

记住IT导入表起始虚拟地址:403000,结束地址:403000+670=403670

起始图中403000开始选中的20个字节就是一个导入表的描述符结构 IMAGE_IMPORT_DESCRIPOR ,每个结构为20个字节,一个IT导入表对应一个DLL,IMAGE_IMPORT_DESCRIPTOR包含了一个字符串指针 Name,指向了某个DLL的名称字符串。

以下将IMAGE_IMPORT_DESCRITPOR简称为 IID。五个重要字段见上图,重要的是Name、FirstThunk:

Name——指向DLL的名称

FirstThunk——对应DLL的IAT(导入函数地址表)的起始地址

·

因此下面选中的20个字节就是第一个IID结构,可以看到DLL的Name为403290,FirstThunk为403184

跳转到403290看看究竟是哪个DLL,原来是USER32.dll

USER32.dll 的 IAT 的起始地址处

·

上面就是IT导入表中的一个IID(实际上IT导入表中包含了许多个IMAGE_IMPORT_DESCRIPTOR结构。每个IID指明了DLL的名称以及DLL对应的IAT的起始地址,所有IID紧密排列,供操作系统使用)

·

也就是说,IAT 的地址实际上是保存在IT导入表中的IID结构中的。

这句话不完全对,实验证明只要IAT位于程序中任何具有写权限的地方即可,只要程序运行起来后,操作系统能够定位到这些IID,然后根据IAT中标明的API函数名称获取到API的入口地址即可。系统填写IAT具体步骤如下:

① 定位导入表 IT(PE结构的知识)

② 解析第一个IMAGE_IMPORT_DESCRIPTOR,根据第4个字段Name定位DLL的名称

③ 根据IMAGE_IMPORT_DESCRIPTOR的第5个字段FirstThunk定位对应DLL的IAT的起始地址

④ 根据IAT 中的指针定位到对应API函数名称的字符串(这就是文章一开头的栗子)

⑤ 通过GetProcAddress获取API函数的入口地址并填充到IAT

⑥ 当定位的IAT项为0的时候,表示DLL的API入口地址遍历完毕,将继续解析下面的第2个IID,不断重复。

·

七、实际演示一遍

1)定位导入表的起始地址和大小:403000、403670

2)定位到导入表IT的起始地址 403000

3)根据第一个IMAGE_IMPORT_DESCRIPTOR的第4个字段Name,得到DLL名称字符串的指针,这里指向了USER32.dll

4)根据第5个字段FirstThunk定位到USER32.dll的IAT导入函数地址表的起始地址 403184,可以看到已经填充了正确的API的入口地址 75C264F7,这是第一个API的入口地址哟

右键此处查看可执行文件的内容,虚拟地址 403184 对应的磁盘文件的偏移位置为 0F84,保存的内容为32CC(是虚拟偏移RVA),那么 4032CC 保存的内容应该就是USER32.dll中的第一个API的名称。

可以看到第一个API的名称是 KillTimer

在OD中看看,果然是 75C264F7, 这个地址是操作系统调用GetProAddress获取到的,这个地址将会被填充到IAT相应的单元中去覆盖原来的值。

上面就是第一个API的获取填充IAT的过程。

再向后偏移4字节就是第二个API的入口地址,可以查看对应地址的可执行文件中的内容,看看这个API的名称是 GetSystemMetrics

名称指针不为0,说明还是在USER32.dll中。

获取到了第二个API的函数名称为 GetSystemMetrics,然后操作系统就能调用GetProcAddress获取其入口地址,然后填充到IAT中。按照这个过程依次获取USER32.dll中的其他函数的入口地址,直到IAT项为0。一个API入口地址对应4字节,遇到 00000000说明该木块就遍历完了,可以计算该程序导入了USER32.dll中的37个函数。

 

接下来看下一个IMAGE_IMPORT_DESCRIPTOR,即开始遍历下一个DLL

DLL的Name是 40329B 处,为KERNEL32.dll

KERNEL32.dll的IAT的起始地址是 40321C,往上面也能看到 USER32.dll的结束地址

 

IAT填充过程大致如上。

 

 

 

 

 

参考:《使用OD从0开始Cracking》

 

 

 

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值