使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx

上传人:b****2 文档编号:23448603 上传时间:2023-05-17 格式:DOCX 页数:24 大小:548.30KB
下载 相关 举报
使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx_第1页
第1页 / 共24页
使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx_第2页
第2页 / 共24页
使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx_第3页
第3页 / 共24页
使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx_第4页
第4页 / 共24页
使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx_第5页
第5页 / 共24页
点击查看更多>>
下载资源
资源描述

使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx

《使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx》由会员分享,可在线阅读,更多相关《使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx(24页珍藏版)》请在冰豆网上搜索。

使用OllyDbg从零开始Cracking 第三十三章神马是IAT如何修复.docx

使用OllyDbg从零开始Cracking第三十三章神马是IAT如何修复

第三十三章-神马是IAT,如何修复

在介绍如何修复IAT之前,我们首先来介绍一下IAT的相关基本概念,本章的实验对象依然是Cruehead的CrackMe。

首先我们来定位该程序的IAT位于何处,然后再来看看对其加了UPX的壳后,IAT又位于何处。

什么是IAT:

我们知道每个API函数在对应的进程空间中都有其相应的入口地址,例如:

我们用OD加载Cruehead的CrackMe,在命令栏中输入?

MessageBoxA

大家可以看到在我的机器上,MessageBoxA这个API函数的地址为77D504EA,如果大家在自己的机器上面定位到这个地址的话,可能有部分人的机器上该地址对应的还是MessageBoxA的入口地址,而另外一部分人的机器上该地址对应的就不是MesageBoxA的入口地址了,这取决于大家机器的操作系统版本,以及打补丁的情况。

众所周知,操作系统动态库版本的更新,其包含的API函数入口地址通常也会改变。

比如User32.dll

我们就拿Cruehead的CrackMe中的MessageBoxA这个API函数来说吧,其入口地址为77D504EA,在我的机器上运行的很好,那些跟我操作系统版本以及User32.dll版本相同的童鞋的机器上该程序运行可能也很正常,但是如果在操作系统版本或者User32.dll的版本跟我的不同童鞋的机器上运行,可能就会出错。

为了解决以上兼容问题,操作系统就必须提供一些措施来确保该CrackMe可以在其他版本的Windows操作系统,以及DLL版本下也能正常运行。

这时IAT(ImportAddressTable:

输入函数地址表)就应运而生了。

大家不要觉得其名字很霸气,就会问是不是很难?

其实不然。

接下来我们一起来探讨一下如何在脱壳过程中定位IAT。

我们现在通过在反汇编窗口中单击鼠标右键选择-Searchfor-Allintermodularcalls来看看主模块中调用了哪些模块以及API函数。

这里我们可以看到有几处调用了MessageBoxA,我们在第一个MessageBoxA调用处双击鼠标左键。

反汇编窗口就会马上定位到该调用处,OD提示窗口中显示其实际调用的是40143A处的JMP.&USER32.MessageBoxA,这里用尖括号括起来了,说明这里是直接调用,而非间接调用。

这里其实就是CALL40143A,显示为CALL大家可能会觉得不太直观。

这里我们打开Debuggingoptions菜单项:

Disasm标签页中的Showsysmbolicaddresses选项被勾选上了,如果我们去掉该对勾,将不会显示函数地址。

我们可以看到右边的注释窗口中同样显示了API函数的参数以及函数名称,比刚刚显示符号地址看起来更直观,一眼就可以看出是一个直接调用。

CALL40143A

在Searchfor-Allintermodularcalls窗口中显示如下:

我们可以看到这里有三处通过CALL40143A调用MessageBoxA,我们定位到40143A处看看是什么。

这里我们可以看到是一个间接跳转。

JMP[4031AC]

这里我们再次勾选上显示符号地址的选项,可以更加直观的看出其调用的API函数。

这里有意思的地方就来了,我们看到JMP[4031AC](4031AC这个内存单元中保存的数值才是MessageBoxA真正的入口地址)。

我们还可以看到很多类似的间接JMP。

这就是为了解决各操作系统之间的兼容问题而设计的,当程序需要调用某个API函数的时候,都是通过一个间接跳转来调用的,读取某个地址中保存的API函数地址,然后调用之。

我们现在在数据窗口中定位到4031AC地址处,看看该内存单元中存放的是什么。

这里我们可以看到,4031AC中保存的是77D504EA,这一片区域包含了该程序调用的所有API函数的入口地址,这块区域我们称之为IAT(导入函数地址表),这里就是解决不同版本操作系统间调用API兼容问题的关键所在,该程序在不同版本操作系统上都是调用间接跳转到IAT表中,在IAT中读取到真正的API函数入口地址,然后调用之,所以说只需要将不同系统中的API函数地址填充到IAT中,这样就可以确保不同版本系统调用的都是正确的API函数。

有些人可能会问,4031AC这个地址在不同机器上也可能会不同的吧?

呵呵,这个问题提的非常好,我们一起来看看操作系统将正确的API函数入口地址填充到IAT中的具体原理,大家就会明白了。

这里我们选中4031AC中保存的内容,单击鼠标右键选择-Viewexecutablefile,就能看到4031AC这个虚拟地址对应于可执行文件中的文件偏移是多少了。

我们可以看到在可执行文件对应文件偏移处中的内容为60330000,当程序运行起来的时候,0FAC这个文件偏移对应的虚拟地址处就会被填充为EA04D577,也就是说该CrackMe进程空间中的4031AC地址处会被填入正确的API函数地址。

有这么神奇?

Windows操作系统当可执行文件被加载到进程所在内存空间中时,会将正确的API函数地址填充到IAT中,这里就是4031AC中被填入了MessageBoxA的入口地址,其他IAT项也会被填入对应的API函数地址。

其实操作系统并没有大家想象得的那么神奇,我们看到0FAC文件偏移处的值3360,该数值其实是RVA(相对虚拟地址),其指向对应的API函数名称。

这里3360加上映像基址即403360,我们定位到403360处,看看是什么。

这里我们可以看到指向的是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们不能那么容易找到它们。

下面我们来看看CrackMeUPX这个程序,在dump之前我们需要修复IAT。

我们定位到4031AC处-原程序MessageBoxA入口地址的存放处。

是空的,那么403360指向的字符串呢?

也是空的,我们跟到OEP处,再来看看这几个地址处有没有内容,我们知道原程序在运行之前,IAT必须被填充上正确的API函数地址。

JMP[4031AC]

如果此时IAT还是空的话,那么程序运行起来就会出错,我们现在定位到OEP。

我们在这个JMPOEP指令处设置一个断点,运行起来,接着来看看IAT:

我们可以看到壳的解密例程已经将正确的API函数地址填充到原程序的IAT中,如果这个时候我们将程序dump出来的话,运行会出错,因为dump出来的程序启动所必须的数据是不完整的。

我们现在来看看各个API函数名称,定位到403360处,会发现是空的。

现在我们dump出来看看,dump出来的原程序代码肯定是正确的,但是程序仍然无法正常运行,因为缺少数据,操作系统无法填充IAT。

Dump的话我们需要用到一个工具,名字叫做LordPE(PS:

大家应该用的很多吧)。

我们运行LordPE,定位到需要dump的CRACKMEUPX所在的进程,当前该进程处于OEP处。

选中CRACKMEUPX所在的进程。

我们单击鼠标右键选择-activedumpengine-IntelliDump-Select!

接着选择dumpfull。

我们将dump出来的程序命名为dumped.exe。

如果我们直接运行dumped.exe的话会发现无法启动,尝试用OD加载dumped.exe,OD会报错,我们来看看日志窗口中的错误信息。

这里我们机器上提示错误发现在7C929913地址处,我们定位到该地址(大家可以根据自己机器上显示的错误地址自行定位)。

这里我们可以给这一行设置一个硬件执行断点或者INT3断点,即当断在这一行时看看错误发生之前是什么状况。

我们运行起来,会发现没有断在这一行,这是因为勾选了忽略异常选项的缘故,这里我们去掉忽略异常选项的对勾,重新运行起来。

断了下来,我们可以看到该错误是在到达入口点之前产生的,所以dumped.exe无法正常运行,我们现在来看看IAT的情况。

我们可以看到当前虽然在我的机器上各个API函数的地址被填充到IAT中,但是想要正常运行在其他机器上的话,必须要指向各个API函数名称字符串的指针,这样才是确保操作系统能够通过GetProcAddress获取到正确的API函数地址并填充到IAT中。

这里该dumped.exe缺少这些指向API函数名称字符串的指针,所以运行的时候会发生错误。

这里大家不要尝试先dump出来,然后再恢复各个API函数的名称字符串以及其指针,如果这样手工修复的话,是一件极其困难的工作,你需要将4031AC地址处的内容修改为MessageBoxA这个字符串的指针,IAT中的其他项也要进行相应的处理。

比较明智的做法是,dump出来之前就将IAT修复了。

我们知道dump出来的代码肯定是正确的,我们定位到401000处看一看。

我们看到API函数的调用处,40135C地址处应该是调用的MessageBoxA。

我们定位到40143A处,这里依然是通过一个间接跳转。

这些间接跳转是无法正常运行的,因为在正常情况,操作系统必须知道指向各个API函数名称字符串的指针,然后通过GetProcAddress定位到各个API函数正确的入口地址并填充到IAT中,这样这些间接跳转才能起作用。

下面我们来看看未加壳程序的IAT。

我们用OD加载Cruehead的CrackMe。

我们来定位该CrackMePE结构中一些重要字段。

首先在数据窗口中定位400000地址处。

单击鼠标右键选择-Special-PEheader切换到PE头的显示模式。

往下拉。

我们可以看到PE头的偏移为100。

即PE头位于400100地址处。

继续往下拉,我们可以看到IT(导入表)的指针,这里大家不要将其跟IAT搞混淆了。

IT=导入表

IAT=输入函数地址表

我们知道当程序启动之前操作系统会将各个API函数的地址填充到IAT中,那么IT(导入表)又是怎么一回事呢?

首先我们定位到导入表,该导入表偏移值为3000(即虚拟地址为403000),长度为670(十六进制),即403670为导入表的结尾。

我们一起来看一看。

我们将数据窗口的显示模式切换为正常状态。

这就是导入表了,我们来介绍一下导入表的结构吧。

我们选中的这20个字节是导入表的描述符结构。

官方的叫法为IMAGE_IMPORT_DESCRIPTOR。

每组为20个字节,IMAGE_IMPORT_DESCRIPTOR包含了一个的字符串指针,该指针指向了某个的动态链接库名称字符串。

我们来看个例子:

这里我们将IMAGE_IMPORT_DESCRIPTOR简称为IID。

这里选中的部分为导入表中的第一个IID。

其中5个DWORD字段的含义如下:

OriginalFirstThunk

TimeDateStamp时间戳

ForwarderChain链表的前一个结构

Name1指向DLL名称的指针

FirstThunk指向的链表定义了针对Name1这个动态链接库引入的所有导入函数

前三个字段不是很重要,对于我们Cracker来说,我们只对第4,5字段感兴趣。

正如大家所看到的,第4个字段为指向DLL名称字符串的指针,我们来看看403290处是哪个DLL的名称。

这里我们可以看到是USER32.DLL,第5个字段指向了USER32.DLL对应IAT项的起始地址,即403184。

这里就是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)定位导入表

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

3)根据第一个IID项中的第四个字段得到DLL名称字符串的指针,这里指向的是USER32.DLL

根据第五个字段的内容定位到IAT项的起始地址,这里是403184,我们定位到该地址处。

这里我们可以看到已经被填充了正确的API函数的入口地址,跟我们dump出来的结果一样,我们再来看看相应的可执行文件偏移处的内容是什么。

这里我们可以看到第一个API函数的名称位于4032CC地址处,我们定位到该地址处。

第一个API函数是KillTimer,我们在OD中看到的KillTimer的入口地址是操作系统调用GetProcAddress获取到的。

这里我们可以看到KillTimer的入口地址为77D18C42。

该地址将被填充到IAT相应单元中去覆盖原来的值。

这里是IAT中的第一元素。

我们再来看下一个元素,向后偏移4就是,来看一看该API函数名称字符串的指针是多少。

定位到可执行文件的相应偏移处:

32D8即4032D8,来看看该API函数的名称是什么,这里由于该指针不为零,说明该API函数还是位于USER32.DLL中的。

这里我们可以看到第二个API函数是GetSystemMetrics,通过该函数名称可以通过GetProcAddress获取到其函数地址然后填充到IAT中。

接下来按照以上步骤依次获取USER32.DLL中的其他的函数地址,直到遇到的IAT项为零为止。

我们来看一看可执行文件中结束项位于哪里。

我们可以看到当IAT中元素为零的时候表明USER32.DLL就搜索完毕了,我们接着来看下一个IID。

这里我们根据第4,5字段分别可以知道第二个DLL的名称,以及对应IAT项的起始地址。

DLL的名称字符串位于40329B地址处。

我们可以看到第二个DLL为KERNEL32.DLL,该DLL对应的IAT项起始地址为40321C。

这里我们可以看到前一个DWORD是零,表示USER32.DLL的API函数的结尾。

40321C表示KERNEL32.DLL的API函数地址项的开始。

根据这些指针我们就可以定位到kernel32.dll中的各个API函数名称字符串,进而获取到其函数入口地址,接着填充到对应的IAT项中覆盖原来的内容。

本章这里就结束了,我给大家描述了IAT被填充的整个过程,了解这个过程对大家来说是很有必要的,这部分内容是重建IAT必备的基础知识,大家只有理解了其基本原理,然后配上适当的工具,就可以方便进行IAT的修复工作了。

好了,下一章我们将介绍具体如何修复IAT。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 初中教育 > 理化生

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1