加壳与脱壳(四):内存Dump、Import Table重建、IAT修复

好,咱们继续聊脱壳。前面几章我们把壳的原理、入口点定位、OEP 查找都过了一遍。今天要聊的,是脱壳实战中最核心、也最容易翻车的一步——内存 Dump、Import Table 重建和 IAT 修复。

说实话,这三步是连在一起的。你想想看,壳把原始程序的导入表藏起来了,甚至把 IAT 给加密了。你光把内存 dump 出来,拿到的是一个“残废”的 PE。没有正确的导入表,程序跑不起来。所以,这三步必须一起搞定。

1. 内存Dump:把“活的”程序抓下来

脱壳的第一步,是把已经解码到内存中的原始程序“拍个快照”。这个过程就叫内存 Dump。

什么时候 dump?

我个人习惯,是在程序已经运行到 OEP,并且所有区段都已经被壳解码完毕之后。如果你在壳的代码还没跑完就 dump,那 dump 出来的东西还是加密的,等于白干。

怎么 dump?

常用的工具有 OllyDump、LordPE、Scylla 等。以 OllyDbg 为例:

  1. 停在 OEP 处(比如 push ebp)。
  2. 打开 OllyDump 插件,选择“Dump debugged process”。
  3. 设置起始地址和大小(一般默认就行)。
  4. 点击“Dump”,保存为新的 exe 文件。
注意: 有些壳会修改 PE 头中的 SizeOfImage 字段。如果你发现 dump 出来的文件比实际小很多,记得手动修正 SizeOfImage。我曾经因为这个坑,折腾了一下午才发现是 SizeOfImage 被改小了。

dump 出来的文件,现在只是一个“空壳子”。它虽然有代码段,但导入表是空的,或者指向了错误的地方。接下来,我们要重建导入表。

2. Import Table重建:找回程序的“依赖清单”

导入表(Import Table)是 PE 文件里的一张清单,记录了程序需要从哪些 DLL 里导入哪些函数。壳通常会把这个清单给抹掉,或者加密后藏到某个地方。

重建的思路是什么?

说白了,就是让程序在运行时,把真正用到的 DLL 和函数名,重新写回到 PE 文件的导入表里。这个过程,我们通常借助工具来自动完成。

常用的工具:

  • ImportREC:老牌工具,功能强大,但操作稍复杂。
  • Scylla:我个人更推荐,界面友好,而且能自动修复 IAT。

用 Scylla 重建导入表的步骤:

  1. 打开 Scylla,附加到正在调试的进程(就是刚才停在 OEP 的那个进程)。
  2. 在“OEP”输入框中,填入你找到的 OEP 地址(比如 0x00401000)。
  3. 点击“IAT Autosearch”,Scylla 会自动扫描内存中的 IAT 表。
  4. 如果扫描成功,你会看到一列 DLL 和函数名。点击“Get Imports”,获取导入信息。
  5. 最后点击“Fix Dump”,选择刚才 dump 出来的文件,Scylla 会帮你重建导入表。
小技巧: 如果 IAT Autosearch 找不到,可以手动指定 IAT 的起始地址和大小。怎么找?在内存窗口中,找到连续的、指向 DLL 函数地址的指针数组。这个数组的起始地址就是 IAT 的 RVA。

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. 实战流程总结

嗯,说了这么多,咱们把整个流程串起来。下面这张图,是我自己总结的脱壳核心流程,你照着走,基本不会跑偏。

脱壳核心流程(内存Dump + 导入表重建 + IAT修复) 1. 定位OEP 停在原始入口点 2. 内存Dump 抓取解码后的程序 3. 扫描IAT 找到导入函数表 IAT 正常? 4a. 重建导入表 使用Scylla/ImportREC 4b. 手动修复IAT 跟踪解密/去除重定向 5. 验证运行 测试脱壳后的程序

你看,整个流程其实不复杂。但每一步都有坑。我刚开始学脱壳的时候,经常在 IAT 修复这一步卡住。后来发现,大部分问题都是因为 dump 的时机不对,或者 OEP 找错了。

5. 避坑指南

最后,分享几个我踩过的坑,希望能帮你省点时间:

  • 坑一:dump 出来的文件无法运行。原因通常是 OEP 找错了,或者 dump 时程序还没完全解码。解决办法:重新定位 OEP,确保所有区段都已解码。
  • 坑二:导入表重建后,程序报“无法定位程序输入点”。这通常是因为 IAT 扫描不完整,漏掉了一些函数。解决办法:手动检查 IAT 区域,看看有没有被遗漏的指针。
  • 坑三:程序运行后崩溃,但没有任何错误提示。这可能是 IAT 修复不完整,或者某些函数被重定向了。解决办法:用调试器加载脱壳后的程序,看它崩溃在哪里,然后回溯修复。
我的建议: 如果你刚开始接触脱壳,先从简单的压缩壳(比如 UPX)练手。UPX 的 IAT 是完整的,你只需要 dump 和重建导入表就行。等熟悉了流程,再挑战加密壳(比如 Themida、VMP)。一口吃不成胖子,慢慢来。

好了,这一章的内容就到这里。内存 Dump、Import Table 重建、IAT 修复,这三步是脱壳的“硬功夫”。多练几次,你就能找到感觉了。


公众号:蓝海资料掘金营,微信deep3321