首先,让我们思考一个问题,应用程序是如何找到API的地址的?
找到一个API调用

enter 跟进

这里是一个间接跳转表,(这里有人可能会有疑问,为什么不直接用API的地址)考虑到操作系统的版本,各个dll的版本,考虑到兼容问题,操作系统必须提供一些措施来确保应用程序可以在其他版本的windows操作系统和dll版本下也能正常运行。
我们看看4031ac处是什么,右键->follow in dump->memory in address

这里保存的是74d9f8b0,这一片地方包含了该程序调用的所有API的地址,我们称它为IAT,因为间接跳转(jmp xxx)所以在不同版本操作系统上仍能找到API的地址,只要将不同系统中的API函数地址填充到IAT中,就能解决不同操作系统兼容的问题
将API的地址填充到IAT中是由操作系统自动完成的,那么操作系统是如何做到的那,我们选中74d9f8b0右键->view executable file,看看4031ac对应的可执行文件中的文件偏移

可执行文件中对应的内容为603300,我们看看该位置处是什么

同时定位到可执行文件中

发现是同样的内容
是MessageBoxA这个字符串
总结一下,操作系统根据这个指针定位到API函数的名称,然后调用GetProcAddress获取地址填充到IAT中.我们来看看GetProcAddress这个函数,这里,MSDN给出了解释

好了,让我们总结一下程序启动时操作系统将正确地址填充到IAT中的充分条件是什么:可执行文件的IAT所对应的文件偏移处必须是一个指针,同时,这个指针指向该API的名称.
我们给该程序加壳,定位到相同地址处

此处竟然是空的,为什么会这样?因为壳把IAT给破坏了,我们运行到OEP

此时IAT已经填上了正确的地址
这期间发生了什么?壳首先会执行解密例程,读取API的名称指针,然后定位到API的地址,将其填入IAT中,此时,API名称字符串已经不需要了.换言之,壳只需要获得API的名字,所以,有的壳会将API的名称加密,以密文的形式保存到某个地址处,以防止被破解.
就算我们在OEP处将程序dump出来,程序依旧无法运行,因为IAT已经被破坏了,壳被拿掉,填充IAT的任务由操作系统完成,但是,这个时候填充IAT的信息是不足的.
下面我们来dump
在OEP处打开LoadPE

右键active dump engine->intellidump->select,然后右键 dump full,这里我把它放到了桌面上,注意,本过程全部在虚拟机中然后双击直接运行

报错.
下面我们来修复IAT,OD定位到OEP处,不要关闭OD
我们在原程序中随便找一处API调用,enter跟进,我们来到跳转表



定位到IAT后将数据窗口上下滚动,如上图
注意:有些壳会在IAT的界限处填上垃圾数据来误导我们,还有的直接将IAT部分填零
好了,现在我们得到了IAT的起始地址和结束地址
OEP:1000 (401000-400000) RVA:3184 (IAT的起始地址,403184-400000) Size:108(40328c-403184)
填入IMPORT REC,然后单机get imports

单击fix dump 选中之前dump出来的文件
双击运行

下面来介绍一下导入表(IT)在PE中的叫法为IMAGE_IMPORT_DESCRIPTOR(这里可以参考<<加密与解密4>>讲的很详细)
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)
DWORD ForwarderChain; // -1 if no forwarders
DWORD Name;
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
IMAGE_IMPORT_DESCRIPTOR简称IID,每组20个字节, name是指向DLL名称的指针,FirstThunk指向该DLL导入的所有API
这里我们了解一下导入表的双桥结构作为延伸,感兴趣的参考<<加密与解密4>>
加载前

加载后

我们来看一个具体的例子
打开PE头模式



好了,这里定位到导入表了,(注意,关于PE的书非常多,感兴趣的自己去看)
好,我们来看下导入表的结构,参考上面IID的结构我们知道,在这个例子中,403290指向DLL的名称,403184指向IAT,导入表以一个空的IID结束

第五个字段的确是指向IAT
操作系统填充IAT的具体步骤
1 定位IT
2 根据IT中第四第五个字段得到IAT地址
3 根据IAT中的指针定位API的名称
4 GetProcAddress获取API的地址并填充到IAT中
****************************
下面来讲一下IAT重定向*****
****************************
看一个加壳的程序,定位到OEP,右键->search for-> all intermoduled calls,看看调用了哪些API
大部分的call都被重定向到了其他区段,只有一小部分是直接调用API,双击一个API直接调用,enter跟进,跟到了跳转表

数据窗口中跟随

往上拉,我们选择4024开头的项(小段存储),右键->found reference

4024开头的项都是一些指针而非调用DLL中的API函数,这些就是IAT中重定向的一些项,壳的解密例程将IAT中原来的一些项覆盖掉,替换到解密历程中(类似于IAT HOOK,关于HOOK以后再谈),我们在OEP下面找一个被壳重定向过的API,enter跟进数据窗口同步

单机OD上方M查看落在了哪个区段,注意,type这项是priv(private,私有),注意,当我们重启OD断在壳的入口点的时候并没有这个区段,这个区段是在壳运行解密例程的过程中创建的.


这里,利用push ret的组合达到间接调用API的目的,壳会将GetVersion的IAT项替换成自己创建的内存单元中的地址,实现重定向
因此我们在定位IAT的边界的时候不仅要判断是否为系统DLL中的地址,还要判断是否是重定向过的地址.
下面我们来定位IAT的开始和结束位置


打开IMPORT REC,填入三个值

单机GetImports

这里,识别出了无效项,(被壳重定向了)
右键->Plugin Tracers->tELock0.98(这里用到了 importREC的插件,importREC的插件有很多,这里在虚拟机中只用了一个
importREC的Plugin目录如下,插件可以自己添加

这种使用插件的修复方式有明显的局限性,只能针对特定的壳,此外,IMPORT REC还有自带的tracers,(tracer level1~3)修复IAT中重定向的项的时候可以尝试,(能用插件的话就用插件,这样就不用手工了,
下面,让我们尝试手工修复IAT,
首先要定位到重定向过的IAT项对应的API函数入口地址
F4运行到第一个API调用处,在下一行下断,这里我们用到OD的跟踪功能

设置自动跟踪停止条件

[esp]==4271dc表明栈顶指针指向了返回地址4271dc
[eip]==0c3表明EIP指向了API函数的返回指令RET
右键trace into
OD断了下来

这里我们位于API函数的返回指令RET处,打开跟踪窗口

这里,我们得到了API函数的名称,GetVersion,这里,我们获取了API的地址

好了,现在打开IMPORT REC

双击,选择dll GetVersion,单机确定,如图

此篇为第二篇,后续想发一些恶意代码分析的文章,毕竟一直发OD觉得烦,
[看雪官方培训] Unicorn Trace还原Ollvm算法!《安卓高级研修班》2021年6月班开始招生!!
最后于 2020-11-9 11:21
被SpyGOD编辑
,原因: