吾爱汇编

 找回密码
 立即注册

QQ登录

绑定QQ避免忘记帐号

查看: 3544|回复: 13

[转载技术] 第三十三章-神马是IAT,如何修复

[复制链接]
Shark恒 发表于 2015-1-20 17:12 | 显示全部楼层 |阅读模式

                     第三十三章-神马是IAT,如何修复
在介绍如何修复IAT之前,我们首先来介绍一下IAT的相关基本概念,本章的实验对象依然是Cruehead的CrackMe。首先我们来定位该程序的IAT位于何处,然后再来看看对其加了UPX的壳后,IAT又位于何处。
什么是IAT:
我们知道每个API函数在对应的进程空间中都有其相应的入口地址,例如:我们用OD加载Cruehead的CrackMe,在命令栏中输入MessageBoxA
image002.jpg
大家可以看到在我的机器上,MessageBoxA这个API函数的地址为77D504EA,如果大家在自己的机器上面定位到这个地址的话,可能有部分人的机器上该地址对应的还是MessageBoxA的入口地址,而另外一部分人的机器上该地址对应的就不是MesageBoxA的入口地址了,这取决于大家机器的操作系统版本,以及打补丁的情况。众所周知,操作系统动态库版本的更新,其包含的API函数入口地址通常也会改变。
比如User32.dll
image004.jpg
我们就拿Cruehead的CrackMe中的MessageBoxA这个API函数来说吧,其入口地址为77D504EA,在我的机器上运行的很好,那些跟我操作系统版本以及User32.dll版本相同的童鞋的机器上该程序运行可能也很正常,但是如果在操作系统版本或者User32.dll的版本跟我的不同童鞋的机器上运行,可能就会出错。
为了解决以上兼容问题,操作系统就必须提供一些措施来确保该CrackMe可以在其他版本的Windows操作系统,以及DLL版本下也能正常运行。
这时IAT(Import Address Table:输入函数地址表)就应运而生了。
大家不要觉得其名字很霸气,就会问是不是很难?其实不然。接下来我们一起来探讨一下如何在脱壳过程中定位IAT。
image006.jpg
我们现在通过在反汇编窗口中单击鼠标右键选择-Search for-All intermodular calls来看看主模块中调用了哪些模块以及API函数。
image008.jpg
这里我们可以看到有几处调用了MessageBoxA,我们在第一个MessageBoxA调用处双击鼠标左键。
image010.jpg
反汇编窗口就会马上定位到该调用处,OD提示窗口中显示其实际调用的是40143A处的JMP.&USER32.MessageBoxA,这里用尖括号括起来了,说明这里是直接调用,而非间接调用。
这里其实就是CALL 40143A,显示为CALL<JMP.&USER32.MessageBoxA>大家可能会觉得不太直观。这里我们打开Debugging options菜单项:
image012.jpg
Disasm标签页中的Show sysmbolic addresses选项被勾选上了,如果我们去掉该对勾,将不会显示函数地址。
image014.jpg
我们可以看到右边的注释窗口中同样显示了API函数的参数以及函数名称,比刚刚显示符号地址看起来更直观,一眼就可以看出是一个直接调用。
CALL 40143A
在Search for-All intermodular calls窗口中显示如下:
image016.jpg
我们可以看到这里有三处通过CALL 40143A调用MessageBoxA,我们定位到40143A处看看是什么。
image018.jpg
这里我们可以看到是一个间接跳转。即
JMP [4031AC]
image020.jpg
这里我们再次勾选上显示符号地址的选项,可以更加直观的看出其调用的API函数。
这里有意思的地方就来了,我们看到JMP[4031AC](4031AC这个内存单元中保存的数值才是MessageBoxA真正的入口地址)。我们还可以看到很多类似的间接JMP。
这就是为了解决各操作系统之间的兼容问题而设计的,当程序需要调用某个API函数的时候,都是通过一个间接跳转来调用的,读取某个地址中保存的API函数地址,然后调用之。我们现在在数据窗口中定位到4031AC地址处,看看该内存单元中存放的是什么。
image022.jpg
这里我们可以看到,4031AC中保存的是77D504EA,这一片区域包含了该程序调用的所有API函数的入口地址,这块区域我们称之为IAT(导入函数地址表),这里就是解决不同版本操作系统间调用API兼容问题的关键所在,该程序在不同版本操作系统上都是调用间接跳转到IAT表中,在IAT中读取到真正的API函数入口地址,然后调用之,所以说只需要将不同系统中的API函数地址填充到IAT中,这样就可以确保不同版本系统调用的都是正确的API函数。
有些人可能会问,4031AC这个地址在不同机器上也可能会不同的吧?
呵呵,这个问题提的非常好,我们一起来看看操作系统将正确的API函数入口地址填充到IAT中的具体原理,大家就会明白了。
image024.jpg
这里我们选中4031AC中保存的内容,单击鼠标右键选择-View executable file,就能看到4031AC这个虚拟地址对应于可执行文件中的文件偏移是多少了。
image026.jpg
我们可以看到在可执行文件对应文件偏移处中的内容为60 33 00 00,当程序运行起来的时候,0FAC这个文件偏移对应的虚拟地址处就会被填充为EA 04 D5 77,也就是说该CrackMe进程空间中的4031AC地址处会被填入正确的API函数地址。
有这么神奇?
Windows操作系统当可执行文件被加载到进程所在内存空间中时,会将正确的API函数地址填充到IAT中,这里就是4031AC中被填入了MessageBoxA的入口地址,其他IAT项也会被填入对应的API函数地址。
其实操作系统并没有大家想象得的那么神奇,我们看到0FAC文件偏移处的值3360,该数值其实是RVA(相对虚拟地址),其指向对应的API函数名称。
这里3360加上映像基址即403360,我们定位到403360处,看看是什么。
image028.jpg
这里我们可以看到指向的是MessageBoxA这个字符串,也就是说操作系统可以根据这个指针,定位到相应的API函数名称,然后通过调用GetProcAddress获取对应API函数的地址,然后将该地址填充到IAT中,覆盖原来的3360。这样就能保证在程序执行前,IAT中被填充了正确的API函数地址。如果我们换一台机器,定位到4031AC处,可能会看到里面存放着不同的地址。
JMP [4031AC]
这样就能够调用MessageBoxA了,大家可能会觉得这个过程很复杂,其实填充IAT的过程都是操作系统帮我们完成的,在程序开始执行前,IAT已经被填入了正确的API函数地址。
也就是说,为了确保操作系统将正确的API函数地址填充到IAT中,应该满足一下几点要求:
1:可执行文件各IAT项所在的文件偏移处必须是一个指针,指向一个字符串。
2:该字符串为API函数的名称。
如果这两项满足,就可以确保程序在启动时,操作系统会将正确的API函数地址填充到IAT中(后面会详细介绍操作系统是如何填充IAT的)。
假如,我们当前位于被加壳程序的OEP处,我们接下来可以将程序dump出来,但是在dump之前我们必须修复IAT,为什么要修复IAT呢?难道壳将IAT破坏了吗?对,的确是这样,壳压根不需要原程序的IAT,因为被加壳程序首先会执行解密例程,读取IAT中所需要的API的名称指针,然后定位到API函数地址,将其填入到IAT中,这个时候,IAT中已经被填充了正确的API函数地址,对应的API函数名称的字符串已经不需要了,可以清除掉。
大部分的壳会将API函数名称对应的字符串以密文的形式保存到某个地址处,让Cracker们不能那么容易找到它们。
下面我们来看看CrackMe UPX这个程序,在dump之前我们需要修复IAT。
image030.jpg
我们定位到4031AC处-原程序MessageBoxA入口地址的存放处。
image032.jpg
是空的,那么403360指向的字符串呢?
image034.jpg
也是空的,我们跟到OEP处,再来看看这几个地址处有没有内容,我们知道原程序在运行之前,IAT必须被填充上正确的API函数地址。
JMP [4031AC]
如果此时IAT还是空的话,那么程序运行起来就会出错,我们现在定位到OEP。
image036.jpg
我们在这个JMP OEP指令处设置一个断点,运行起来,接着来看看IAT:
image038.jpg
我们可以看到壳的解密例程已经将正确的API函数地址填充到原程序的IAT中,如果这个时候我们将程序dump出来的话,运行会出错,因为dump出来的程序启动所必须的数据是不完整的。
我们现在来看看各个API函数名称,定位到403360处,会发现是空的。
image040.jpg
现在我们dump出来看看,dump出来的原程序代码肯定是正确的,但是程序仍然无法正常运行,因为缺少数据,操作系统无法填充IAT。
Dump的话我们需要用到一个工具,名字叫做LordPE(PS:大家应该用的很多吧)。
image042.jpg
我们运行LordPE,定位到需要dump的CRACKME UPX所在的进程,当前该进程处于OEP处。
image044.jpg
选中CRACKME UPX所在的进程。
image046.jpg
我们单击鼠标右键选择-active dump engine-IntelliDump-Select!。接着选择dump full。
image048.jpg
我们将dump出来的程序命名为dumped.exe。
image050.jpg
如果我们直接运行dumped.exe的话会发现无法启动,尝试用OD加载dumped.exe,OD会报错,我们来看看日志窗口中的错误信息。
image052.jpg
这里我们机器上提示错误发现在7C929913地址处,我们定位到该地址(大家可以根据自己机器上显示的错误地址自行定位)。
image054.jpg
这里我们可以给这一行设置一个硬件执行断点或者INT 3断点,即当断在这一行时看看错误发生之前是什么状况。
我们运行起来,会发现没有断在这一行,这是因为勾选了忽略异常选项的缘故,这里我们去掉忽略异常选项的对勾,重新运行起来。
image056.jpg
断了下来,我们可以看到该错误是在到达入口点之前产生的,所以dumped.exe无法正常运行,我们现在来看看IAT的情况。
image058.jpg
我们可以看到当前虽然在我的机器上各个API函数的地址被填充到IAT中,但是想要正常运行在其他机器上的话,必须要指向各个API函数名称字符串的指针,这样才是确保操作系统能够通过GetProcAddress获取到正确的API函数地址并填充到IAT中。
这里该dumped.exe缺少这些指向API函数名称字符串的指针,所以运行的时候会发生错误。
这里大家不要尝试先dump出来,然后再恢复各个API函数的名称字符串以及其指针,如果这样手工修复的话,是一件极其困难的工作,你需要将4031AC地址处的内容修改为MessageBoxA这个字符串的指针,IAT中的其他项也要进行相应的处理。
比较明智的做法是,dump出来之前就将IAT修复了。
我们知道dump出来的代码肯定是正确的,我们定位到401000处看一看。
image060.jpg
我们看到API函数的调用处,40135C地址处应该是调用的MessageBoxA。
image062.jpg
我们定位到40143A处,这里依然是通过一个间接跳转。
image064.jpg
这些间接跳转是无法正常运行的,因为在正常情况,操作系统必须知道指向各个API函数名称字符串的指针,然后通过GetProcAddress定位到各个API函数正确的入口地址并填充到IAT中,这样这些间接跳转才能起作用。
下面我们来看看未加壳程序的IAT。
我们用OD加载Cruehead的CrackMe。
image066.jpg
我们来定位该CrackMe PE结构中一些重要字段。首先在数据窗口中定位400000地址处。
image068.jpg
单击鼠标右键选择-Special-PE header切换到PE头的显示模式。
image070.jpg
往下拉。
image072.jpg
我们可以看到PE头的偏移为100。
image074.jpg
即PE头位于400100地址处。
image076.jpg
继续往下拉,我们可以看到IT(导入表)的指针,这里大家不要将其跟IAT搞混淆了。
IT = 导入表
IAT = 输入函数地址表
我们知道当程序启动之前操作系统会将各个API函数的地址填充到IAT中,那么IT(导入表)又是怎么一回事呢?首先我们定位到导入表,该导入表偏移值为3000(即虚拟地址为403000),长度为670(十六进制),即403670为导入表的结尾。我们一起来看一看。
我们将数据窗口的显示模式切换为正常状态。
image078.jpg
这就是导入表了,我们来介绍一下导入表的结构吧。
我们选中的这20个字节是导入表的描述符结构。官方的叫法为IMAGE_IMPORT_DESCRIPTOR。每组为20个字节,IMAGE_IMPORT_DESCRIPTOR包含了一个的字符串指针,该指针指向了某个的动态链接库名称字符串。
我们来看个例子:
image080.jpg
这里我们将IMAGE_IMPORT_DESCRIPTOR简称为IID。这里选中的部分为导入表中的第一个IID。其中5个DWORD字段的含义如下:
OriginalFirstThunk
TimeDateStamp           时间戳
ForwarderChain           链表的前一个结构
Name1                  指向DLL名称的指针
FirstThunk               指向的链表定义了针对Name1这个动态链接库引入的所有导入函数
前三个字段不是很重要,对于我们Cracker来说,我们只对第4,5字段感兴趣。
image082.jpg
正如大家所看到的,第4个字段为指向DLL名称字符串的指针,我们来看看403290处是哪个DLL的名称。
image084.jpg
这里我们可以看到是USER32.DLL,第5个字段指向了USER32.DLL对应IAT项的起始地址,即403184。
image086.jpg
这里就是IAT了,导入表的结束地址为403670。导入表中的每个IID项指明了DLL的名称以及其对应IAT项的起始地址。紧凑的排列在一起,供操作系统使用。
大量实验表明,IAT并不一定位于在导入表中。IAT可以位于程序中任何具有写权限的地方,只要当可执行程序运行起来时,操作系统可以定位到这些IID项,然后根据IAT中标明的API函数名称获取到函数地址即可。下面我们来总结一下操作系统填充IAT的具体步骤:
1:定位导入表
2:解析第一个IID项,根据IID中的第4个字段定位DLL的名称
3:根据IID项的第5个字段DLL对应的IAT项的起始地址
4:根据IAT中的指针定位到相应API函数名称字符串
5:通过GetProcAddress获取API函数的地址并填充到IAT中
6:当定位到的IAT项为零的时候表示该DLL的API函数地址获取完毕了,接着继续解析第二个IID,重复上面的步骤。
下面我们来手工的体验一下这个步骤:
1)定位导入表
   image088.jpg
2)定位到导入表的起始地址
   image090.jpg
3)根据第一个IID项中的第四个字段得到DLL名称字符串的指针,这里指向的是USER32.DLL
   image092.jpg
根据第五个字段的内容定位到IAT项的起始地址,这里是403184,我们定位到该地址处。
image094.jpg
这里我们可以看到已经被填充了正确的API函数的入口地址,跟我们dump出来的结果一样,我们再来看看相应的可执行文件偏移处的内容是什么。
image096.jpg
这里我们可以看到第一个API函数的名称位于4032CC地址处,我们定位到该地址处。
image098.jpg
第一个API函数是KillTimer,我们在OD中看到的KillTimer的入口地址是操作系统调用GetProcAddress获取到的。
image100.jpg
这里我们可以看到KillTimer的入口地址为77D18C42。该地址将被填充到IAT相应单元中去覆盖原来的值。
image102.jpg
这里是IAT中的第一元素。
我们再来看下一个元素,向后偏移4就是,来看一看该API函数名称字符串的指针是多少。
image104.jpg
定位到可执行文件的相应偏移处:
image106.jpg
32D8即4032D8,来看看该API函数的名称是什么,这里由于该指针不为零,说明该API函数还是位于USER32.DLL中的。
image108.jpg
这里我们可以看到第二个API函数是GetSystemMetrics,通过该函数名称可以通过GetProcAddress获取到其函数地址然后填充到IAT中。接下来按照以上步骤依次获取USER32.DLL中的其他的函数地址,直到遇到的IAT项为零为止。我们来看一看可执行文件中结束项位于哪里。
image110.jpg
我们可以看到当IAT中元素为零的时候表明USER32.DLL就搜索完毕了,我们接着来看下一个IID。
image112.jpg
这里我们根据第4,5字段分别可以知道第二个DLL的名称,以及对应IAT项的起始地址。
DLL的名称字符串位于40329B地址处。
image114.jpg
我们可以看到第二个DLL为KERNEL32.DLL,该DLL对应的IAT项起始地址为40321C。
image116.jpg
这里我们可以看到前一个DWORD是零,表示USER32.DLL的API函数的结尾。40321C表示KERNEL32.DLL的API函数地址项的开始。
根据这些指针我们就可以定位到kernel32.dll中的各个API函数名称字符串,进而获取到其函数入口地址,接着填充到对应的IAT项中覆盖原来的内容。
本章这里就结束了,我给大家描述了IAT被填充的整个过程,了解这个过程对大家来说是很有必要的,这部分内容是重建IAT必备的基础知识,大家只有理解了其基本原理,然后配上适当的工具,就可以方便进行IAT的修复工作了。
好了,下一章我们将介绍具体如何修复IAT。

本系列文章汉化版转载看雪论坛

感谢原作者:RicardoNarvaja(西班牙人)
原作者个人主页:http://www.ricardonarvaja.info/

感谢热心翻译的朋友:
1~3章译者:BGCoder
4~58章译者:安于此生

全集配套程序下载地址:
链接: http://pan.baidu.com/s/1eQzTWfo 密码: vytv



评分

参与人数 11HB +13 THX +4 收起 理由
sjtkxy + 1 + 1
Jawon + 2
一路走来不容易 + 1
Soul1999 + 1
zxjzzh + 2 [吾爱汇编论坛52HB.COM]-软件反汇编逆向分析,软件安全必不可少!
大龙科技 + 1
冷亦飞 + 1
消逝的过去 + 2
jaunic + 1
hnymsh + 2
easy + 1 + 1 评分=感恩!简单却充满爱!感谢您的作品!

查看全部评分

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
easy 发表于 2015-7-23 01:05 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
easy 发表于 2015-7-23 01:18 | 显示全部楼层

艾玛,看了半个小时,有字天数啊,有视频最好了.
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
头像被屏蔽
别管我了行 发表于 2022-3-2 03:48 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
zg2600 发表于 2022-6-18 01:49 | 显示全部楼层

[吾爱汇编论坛52HB.COM]-学破解防逆向,知进攻懂防守!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
凌夏随缘 发表于 2022-6-22 17:15 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
曾经沧海 发表于 2022-11-19 17:42 | 显示全部楼层

感觉很牛B 的样子,但我是门外汉 只能看热闹
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
大龙科技 发表于 2022-11-23 18:14 | 显示全部楼层


看看大佬得教程!!!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
一生逍遥 发表于 2022-12-4 10:45 | 显示全部楼层

纯支持一下下,,,
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
曾经沧海 发表于 2023-4-4 11:13 | 显示全部楼层

感谢楼主分享,谢谢分享学习知识
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

警告:本站严惩灌水回复,尊重自己从尊重他人开始!

1层
2层
3层
4层
5层
6层
7层
8层
9层
10层

免责声明

吾爱汇编(www.52hb.com)所讨论的技术及相关工具仅限用于研究学习,皆在提高软件产品的安全性,严禁用于不良动机。任何个人、团体、组织不得将其用于非法目的,否则,一切后果自行承担。吾爱汇编不承担任何因为技术滥用所产生的连带责任。吾爱汇编内容源于网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除。如有侵权请邮件或微信与我们联系处理。

站长邮箱:SharkHeng@sina.com
站长QQ:1140549900


QQ|RSS|手机版|小黑屋|帮助|吾爱汇编 ( 京公网安备11011502005403号 , 京ICP备20003498号-6 )|网站地图

Powered by Discuz!

吾爱汇编 www.52hb.com

快速回复 返回顶部 返回列表