加壳与脱壳(四):内存Dump、Import Table重建、IAT修复
好,咱们继续聊脱壳。前面几章我们把壳的原理、入口点定位、OEP 查找都过了一遍。今天要聊的,是脱壳实战中最核心、也最容易翻车的一步——内存 Dump、Import Table 重建和 IAT 修复。
说实话,这三步是连在一起的。你想想看,壳把原始程序的导入表藏起来了,甚至把 IAT 给加密了。你光把内存 dump 出来,拿到的是一个“残废”的 PE。没有正确的导入表,程序跑不起来。所以,这三步必须一起搞定。
1. 内存Dump:把“活的”程序抓下来
脱壳的第一步,是把已经解码到内存中的原始程序“拍个快照”。这个过程就叫内存 Dump。
什么时候 dump?
我个人习惯,是在程序已经运行到 OEP,并且所有区段都已经被壳解码完毕之后。如果你在壳的代码还没跑完就 dump,那 dump 出来的东西还是加密的,等于白干。
怎么 dump?
常用的工具有 OllyDump、LordPE、Scylla 等。以 OllyDbg 为例:
- 停在 OEP 处(比如 push ebp)。
- 打开 OllyDump 插件,选择“Dump debugged process”。
- 设置起始地址和大小(一般默认就行)。
- 点击“Dump”,保存为新的 exe 文件。
dump 出来的文件,现在只是一个“空壳子”。它虽然有代码段,但导入表是空的,或者指向了错误的地方。接下来,我们要重建导入表。
2. Import Table重建:找回程序的“依赖清单”
导入表(Import Table)是 PE 文件里的一张清单,记录了程序需要从哪些 DLL 里导入哪些函数。壳通常会把这个清单给抹掉,或者加密后藏到某个地方。
重建的思路是什么?
说白了,就是让程序在运行时,把真正用到的 DLL 和函数名,重新写回到 PE 文件的导入表里。这个过程,我们通常借助工具来自动完成。
常用的工具:
- ImportREC:老牌工具,功能强大,但操作稍复杂。
- Scylla:我个人更推荐,界面友好,而且能自动修复 IAT。
用 Scylla 重建导入表的步骤:
- 打开 Scylla,附加到正在调试的进程(就是刚才停在 OEP 的那个进程)。
- 在“OEP”输入框中,填入你找到的 OEP 地址(比如 0x00401000)。
- 点击“IAT Autosearch”,Scylla 会自动扫描内存中的 IAT 表。
- 如果扫描成功,你会看到一列 DLL 和函数名。点击“Get Imports”,获取导入信息。
- 最后点击“Fix Dump”,选择刚才 dump 出来的文件,Scylla 会帮你重建导入表。
3. IAT修复:让函数调用“对号入座”
IAT(Import Address Table)是导入表的“运行时版本”。程序在调用 MessageBoxA 时,实际上是通过 IAT 中的地址来跳转的。壳会把 IAT 中的地址加密,或者重定向到壳自己的代码中。
IAT 修复的本质是什么?
就是把 IAT 中那些被篡改的地址,恢复成正确的、指向系统 DLL 函数的地址。说白了,就是让程序知道:“你调用的 MessageBoxA,应该去 user32.dll 里找,而不是去壳的代码里找。”
修复方法:
如果你用了 Scylla 的“Fix Dump”功能,它已经自动帮你修复了 IAT。但有时候,自动修复会失败。这时候就需要手动修复。
手动修复的常见场景:
- IAT 被加密:壳把 IAT 中的地址异或了某个值。你需要找到解密算法,或者直接跟踪壳的代码,在它解密 IAT 之后、调用函数之前,把正确的地址记录下来。
- IAT 被重定向:壳把 IAT 中的地址改成了壳自己的函数。这些函数会先执行一些检查,然后再跳转到真正的系统函数。你需要把这些“跳板”函数替换成真正的系统函数地址。
我的经验: 遇到 IAT 被加密的情况,我一般会写一个简单的 IDAPython 脚本,在壳解密 IAT 的代码处下断点,然后批量记录解密后的地址。这样比手动一个一个复制快多了。
4. 实战流程总结
嗯,说了这么多,咱们把整个流程串起来。下面这张图,是我自己总结的脱壳核心流程,你照着走,基本不会跑偏。
你看,整个流程其实不复杂。但每一步都有坑。我刚开始学脱壳的时候,经常在 IAT 修复这一步卡住。后来发现,大部分问题都是因为 dump 的时机不对,或者 OEP 找错了。
5. 避坑指南
最后,分享几个我踩过的坑,希望能帮你省点时间:
- 坑一:dump 出来的文件无法运行。原因通常是 OEP 找错了,或者 dump 时程序还没完全解码。解决办法:重新定位 OEP,确保所有区段都已解码。
- 坑二:导入表重建后,程序报“无法定位程序输入点”。这通常是因为 IAT 扫描不完整,漏掉了一些函数。解决办法:手动检查 IAT 区域,看看有没有被遗漏的指针。
- 坑三:程序运行后崩溃,但没有任何错误提示。这可能是 IAT 修复不完整,或者某些函数被重定向了。解决办法:用调试器加载脱壳后的程序,看它崩溃在哪里,然后回溯修复。
好了,这一章的内容就到这里。内存 Dump、Import Table 重建、IAT 修复,这三步是脱壳的“硬功夫”。多练几次,你就能找到感觉了。
公众号:蓝海资料掘金营,微信deep3321