首页
论坛
课程
招聘
[原创][原创]脱壳系列2_IAT重定向及其修复
2020-10-19 19:49 5682

[原创][原创]脱壳系列2_IAT重定向及其修复

2020-10-19 19:49
5682

   

  首先,让我们思考一个问题,应用程序是如何找到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编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (6)
雪    币: 12
活跃值: 活跃值 (33)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
edens 活跃值 2020-11-9 10:12
2
0
请问3184跟328C是从哪里找出来的?
雪    币: 95
活跃值: 活跃值 (2623)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
SpyGOD 活跃值 2020-11-9 10:16
3
0
edens 请问3184跟328C是从哪里找出来的?
算的,不是给公式了吗
雪    币: 12
活跃值: 活跃值 (33)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
edens 活跃值 2020-11-9 13:16
4
0
抱歉我刚才没问清楚
我是指以这张图来看 JMP的第一行跟最后一行吗?
IAT的起始地址和结束地址的部份
https://bbs.pediy.com/upload/attach/202010/830337_X2GU5WMRNBGEYBZ.jpg
雪    币: 95
活跃值: 活跃值 (2623)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
SpyGOD 活跃值 2020-11-9 17:32
5
0
edens 抱歉我刚才没问清楚 我是指以这张图来看 JMP的第一行跟最后一行吗? IAT的起始地址和结束地址的部份 https://bbs.pediy.com/upload/attach/202010/83 ...
右键follow in dump,转到数据窗口中
雪    币: 249
活跃值: 活跃值 (83)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
astrisk 活跃值 2020-12-10 13:30
6
0
实验用的程序能发一下吗
雪    币: 249
活跃值: 活跃值 (83)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
astrisk 活跃值 2020-12-10 21:13
7
0
第一个程序加的是什么壳
游客
登录 | 注册 方可回帖
返回