修复IAT表

首先打开目标程序

首先进入程序之后我们可以看到如下界面

我们看起始地址,判断应该是加壳了,这里没有对寄存器环境进行保存,所以没有办法使用esp定律。
我们尝试将程序放到Detect中查看,分析结果是C++6.0

我们知道VC6编译器生成的有一个特点,就是在程序开始的是后会进行平栈,执行的汇编代码是sub esp,0x58,所以我们可以从这个线索出发。
我们在进入到程序中刚刚断下的时候,进行搜索sub esp,0x58,发现找不到该命令,为了查看问题出现在哪里,我们查看程序的代码段

我们看到,代码段的数据都是一些没有规律的命令,根据分析应该是对代码段进行了加密,所以如果我们要搜索到代码段的命令,我们需要将程序运行到正常运行的阶段就是壳程序对代码段解密过之后,我们在搜索这条命令。
壳代码运行完毕之后,主程序运行起来,我们再次转到代码区域可以看到这里的代码已经恢复,在这里我们搜索sub esp,0x58或者搜索二进制83ec58。

根据这个跳转位置,我们可以看出这里是OEP的位置,接下来我们使用OD的插件将程序dump下来

接下来我们修复导入表,我们打开工具加载我们需要修复程序的进程,然后输入我们找到OEP所在的位置,然后自动查找IAT,接着获取输入表

我们发现我们获取的全都是无效的函数,这种情况下我们回到程序中查看一下正在运行程序的导入表的情况。
我们将程序运行到主程序代码区,我们找到CALL指令的OPCODE为FF15(重定位函数跳转)的,跟进去

我们发现本该是函数跳转表的地方变成这些内容,而且还有花指令,我们将花指令修复一下

修复完毕之的结果如上,我们看到这些大部分代码都是无意义的代码,我们尝试将这些指令简化为一个简单的指令。
经我们分析,其这函数的目的是将0x63836F05和0x15151515异或,然后跳转到异或后的地址,所以我们可以直接将这些命令修改为一句jmp 76967A10

修改完毕之后我们发现,这条指令就是跳转到函数ExitProcess的指令,但是被壳给HOOK了,这里先执行了它的一段函数,然后才跳转到函数地址,我们查看一下函数导入表信息

可以看到全部被填充为新的地址,而这些地址中就是执行函数地址异或操作和带有花指令的代码区域,所以我们要知道在壳代码中是哪里访问了这个地址,说明哪里就是对这里的代码进行了修改,所以我们对当前的地址下一个硬件访问断点,然后重新运行程序。

程序断在了这里,我们看到这里也有异或0x15151515的操作,我们猜测这里就是对函数跳转地址加密以及修改命令的地方,我们对这里的代码进行分析一下。
我们先粗略的看一下,OD给我们分析出来的有4个函数,分别是




其中我们还发现有两层循环,第一层循环中套着LoadLibraryA,第二层循环套着GetProcAddress和VirtualAlloc,根据之前的情况,大概可以猜测出:

1.首先修改区段的属性,使代码段可写
2.然后遍历模块
3.根据模块遍历函数
4.得到函数地址后,对跳转到函数的地址进行异或
5.然后申请一块内存,里面存放自己写的处理函数地址的代码段
6.最终在导入表中填写函数的地址为自己编写的带有花指令代码段,用来恢复函数地址
由于加密函数地址一般是在GetProcAddress得到函数地址之后才能进行,所以我们首先在GetProcAddress之后的代码进行分析
7.恢复区段属性

首先进行为VirtualAlloc函数push参数,参数的含义我们可以步入函数中之后在堆栈中看到。
接下来就是这一段代码

首先我们可以看到指令

MOVDQU XMM0,DQWORD PTR SS:[EBP-0x30]
MOV EDX,DWORD PTR SS:[EBP-0x34]
MOVDQU DQWORD PTR DS:[EAX],XMM0
MOVDQU XMM0,DQWORD PTR SS:[EBP-0x20]
MOVDQU DQWORD PTR DS:[EAX+0x10],XMM0
MOV DWORD PTR DS:[EDI],EAX
MOV ECX,DWORD PTR DS:[ESI+0x4]

这段代码中,MOVDQU是移动16个字节的数据从EBP-0x30到xmm0寄存器,在EBP-0x30的地址中存放的是我们之前看到的函数跳转后的恢复函数的代码:

第二条指令中EBP-0x34内存中存放的是

是程序的加载基址,这段代码的意思就是将加载基址赋值到EDX中。
第三条指令中将壳编写的代码写到申请的内存地址中,写入16个字节。
第四条指令是从EBP-20的位置16个字节的内容赋值到XMM0中
第五条指令是将内如赋值给EAX+10的位置,这两次赋值后,得到汇编代码的内容就是我们上买呢图中的内容。
E801000000E958EB01E8B8DC018363EB01153515151515EB01FF50EB02FF15C3
上面的就是我们所有填充的内容,看到这里我们就会发现GetprocAddress上面的代码就是填充这些数据

其中在最下面的红框里,这里的放的是一个变量,就是每次我们申请的地址异或后的内容,然后存放到这里,这样我们就可针对每一个函数都使用相同的模板,仅仅就该这个地址即可。
第六条指令就是将申请的内存空间(我们刚修改过)赋值到EDI中

EDI中存放的是如上内容,这些内容通过观察应该就是函数地址表,里面存放的都是函数的RVA,然后用我们申请的地址覆盖原来的RVA,这样地址表中的地址就失效了。
为了在跳转表中填写正确的内容,所以我们把这段代码中对跳转地址进行修改和后来填充的代码删除,最后留下的代码部分如下图:


最后只留下如上的代码,然后我们将修改另存为,接着OD打开这个修改过的文件,重新进行DUMP。
然后我们对重新dump出来的程序重建导入表,但是在我们获取输入表的是否发现只有两个模块。

如果我们仔细观察会发现,我们在获取输入表的时候,左边有一栏数据,我们仅仅修改了EOP,单是我们对照着程序发现,这个RVA和Size都是不正确的,所以我们也需要手动修改一下,不使用自动查找IAT。
重新获取后,其他的模块也出现了,但是有一些无效函数,我们直接右键剪切指针,然后转储到我们dump的文件中即可。

最后修改:2020 年 08 月 26 日
如果觉得我的文章对你有用,请随意赞赏