
文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载当我们在 OllyDbg 中借助 ESP 定律等技巧找到程序 OEP 并完成内存转储Dump后往往会发现脱壳产物无法正常运行——绝大多数情况下问题的根源是导入地址表IAT尚未被正确重建。本文以 ctf-wiki 逆向方向的manually_fix_iat示例为核心完整演示手动查找 IAT 地址范围 使用 ImportREC 重建导入表的实操流程并结合仓库中 PE 导入表原理、保护壳简介、ESP 定律法、DLL 文件脱壳 等文档做原理级深化。读完本文你将掌握一条不依赖 ImportREC 自动搜索、完全手工定位 IAT 并完成修复的通用脱壳路线。为什么需要手动查找 IAT壳的运行过程决定了 IAT 必然被破坏在动手操作之前先理解为什么脱壳后必须重建 IAT。根据 保护壳简介 的描述壳本质上是一段先于程序运行、拿到控制权的保护程序其加载过程大致分为四步保存入口参数加壳程序初始化时保存各寄存器值典型如pushad/popad、pushfd/popfd指令对外壳执行完毕后再恢复现场并跳回原程序获取所需函数 API一般壳的输入表中只有GetProcAddress、GetModuleHandle、LoadLibrary等少数 API其他函数通过LoadLibraryA(W)加载 DLL 后再用GetModuleHandleA(W)拿模块句柄、GetProcAddress取函数地址解密各区块数据按区块加密、按区块解密把解密数据放回合适的内存位置跳转回原程序入口点跳转前重新构造输入表对 DLL 引入的所有函数重新获取地址并填写到 IAT 中。也就是说壳在运行时自己构造了一份输入表。当我们把内存中的程序 dump 成文件时这份运行态的 IAT 与 PE 文件结构中应有的导入表描述往往对不上导致转储后的程序无法运行。这一点在 DUMP 及 IAT 重建 中也有印证如果 OEP 找得正确、IDA 反编译结果良好而程序仍无法运行那么第一反应就应该是IAT 出了问题需要重建。AutoSearch 与手动查找的取舍我们常用的ImportREC脱壳走的是软件自带的IAT auto searchAutoSearch流程填入 OEP 后点击 AutoSearch工具自动在内存中扫描并提示发现可能是原 IAT 地址再点Get Imports即可重建。这在绝大多数场景下高效可靠。但手动查找 IAT 的价值在于当AutoSearch 失败或扫描结果可疑时可以手工确认 IAT 的真实位置对于DLL 脱壳等特殊情况DLL 文件脱壳 明确要求照着上篇所说那样手动找到 IAT 表并用 ImportREC 进行重建手动定位能让你真正理解 IAT 在内存中的形态逆向分析能力而非只依赖工具。下文就以示例程序来自 ctf-wiki 的 ctf-challenges 仓库位于reverse/unpack/manually_fix_iat.zip逐步展开。前置用 ESP 定律快速定位 OEP手动查找 IAT 的前提是程序已经停在了正确的 OEP。示例程序使用 ESP 定律可以很快跳到OEP: 00401110。根据 ESP 定律法 的原理壳在自解密 / 自解压过程中往往先pushad将当前寄存器状态压栈解压结束后再popad出栈。因此在寄存器出栈、程序代码恢复原貌的那一刻设置硬件断点再稍作单步即可到达 OEP。标准操作是程序刚载入时执行pushad/pushfd将全部寄存器压栈后对ESP寄存器设硬件断点HW break[ESP]运行程序触发断点删除硬件断点开始分析。本例中按下 F9 运行并在断点处停下后程序已脱壳完成EIP停在真正的入口00401110push ebp如下图所示。OllyDbg 中通过 ESP 定律停在 OEP 00401110记下两个关键值镜像基址 00400000、OEP 地址 00401110。后文 ImportREC 中要填写的 OEP 是相对镜像基址的 RVA即111000401110 - 00400000。在 OllyDbg 中手动追踪 IAT 的完整步骤到达 OEP 后程序代码已经恢复清晰此时 IAT 中存放的是各导入函数在内存中的真实地址。我们通过从程序调用点反查 IAT 表项的方式手工把整张 IAT 的起止范围圈出来。第 1 步列出所有模块间的调用在 OEP 处的反汇编代码上右键点击选择菜单查找 - 所有模块间的调用Find - All intermodular calls。OllyDbg 会弹出一个列表列出当前模块对所有外部模块系统 DLL函数的全部调用点。第 2 步双击程序自身的函数调用列表中会混合显示程序模块调用系统函数与系统函数调用系统函数两类条目。注意这里要双击的应该是程序的函数而不是系统函数——即在列表中找到由我们正在调试的程序模块如UnPackMe发起的调用条目并双击OllyDbg 会把反汇编窗口定位到该条调用指令处。第 3 步跟随进入函数此时我们停在了形如CALL kernel32.xxx的调用指令上。在调用指令处右键点击跟随Follow光标会进入目标函数内部。第 4 步数据窗口中跟随内存地址进入函数后在反汇编窗口内再次右键选择数据窗口中跟随 - 内存地址Follow in Dump - Memory address。此时 OllyDbg 下方的数据窗口会定位到该函数在 IAT 中对应的表项。第 5 步切换为长型-地址显示函数名默认情况下数据窗口以十六进制数值显示表项内容不直观。在数据窗口点击右键选择长型 - 地址Long - Address数据窗口就会把每个 DWORD 按内存地址解释直接显示出对应的函数名方便核对这条 IAT 表项到底指向哪个 API。第 6 步确定 IAT 的起止范围与块大小这是整个手动定位过程中最关键的一步向上翻到 IAT 表的起始位置可以看到整张表最开始的函数是地址004050D8处的kernel.AddAtomA向下翻到最后一个函数即user32.MessageBoxA在 OD 最下方状态栏会显示 IAT 所在块的统计信息起始: 004050D8 结束: 00405153 块大小: 0x7C因此整张 IAT 块的尺寸就是0x7C约 124 字节0x7C / 4 31个 DWORD 表项。OllyDbg 数据窗口中 IAT 块范围起始 004050D8块大小 0x7C至此我们手工获得了重建导入表所需的全部三个参数参数含义本例取值OEP程序真正入口点相对镜像基址的 RVA1110RVAIAT 起始地址相对镜像基址的偏移50D8004050D8 - 00400000SIZEIAT 块的总大小字节7C使用 ImportREC 重建导入表填写 OEP、RVA、SIZE 并获取输入表打开ImportRECImport REConstructor选择我们正在调试的这个进程即 OllyDbg 中停在 OEP 处的UnPackMe.exe注意dump 之前不要关闭这个进程然后在右侧中间的参数区分别输入OEP1110RVA50D8SIZE7CImportREC 中填写 OEP1110、RVA50D8、SIZE7C点击获取输入表Get Imports后左侧输入表函数信息窗口会列出识别出的导入模块。本例中可以看到kernel32.dll、msvcrt.dll、user32.dll三个有效模块且各函数均被正确解析说明我们手工圈定的 IAT 范围准确无误。相比之下DUMP 及 IAT 重建 中 AutoSearch 的成功标志同样是左侧显示各导入函数地址且全部有效。高级命令选择代码块并完整转储在输入表窗口内右键选择高级命令 - 选择代码块Advanced - Select block随后弹出的窗口中选择完整转储Full dump把当前进程的整个内存镜像保存为dump.exe文件。转储到文件修复 IATdump 完成后点击主界面的转储到文件Fix Dump按钮选择刚刚 dump 出的dump.exe。ImportREC 会把内存中的 IAT 真实地址与文件导入表描述做对齐修复输出修复后的文件dump_.exe。此时dump_.exe即为导入表已重建、可正常运行的脱壳产物整个脱壳流程至此完成。原理深化IAT 与 INT 的双桥结构手动重建之所以可行根源在于 PE 导入表双桥结构的设计。根据仓库 导入表 一节DataDirectory[1]指向IMAGE_IMPORT_DESCRIPTOR结构体数组其核心成员如下typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; // 导入名称表 INT 的 RVA }; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; // 库名称字符串 RVA DWORD FirstThunk; // 导入地址表 IAT 的 RVA } IMAGE_IMPORT_DESCRIPTOR;OriginalFirstThunk指向INTImport Name TableINT 数组中的每个指针指向IMAGE_IMPORT_BY_NAME结构体记录Hint函数编号与Name函数名FirstThunk指向IATImport Address Table文件态中 INT 与 IAT 保存的内容完全相同都指向同一批IMAGE_IMPORT_BY_NAME二者形成双桥结构。程序装载进内存时Windows 装载器通过如下过程把函数真实地址写入 IAT读取IMAGE_IMPORT_DESCRIPTOR.Name获得库名如KERNEL32.dll调用LoadLibrary装载对应 DLL读取OriginalFirstThunk获得 INT 地址遍历 INT 数组取得各IMAGE_IMPORT_BY_NAME读取Hint/Name后调用GetProcAddress解析函数地址读取FirstThunk获得 IAT 地址把解析出的地址逐项写入 IAT直到遇到 NULL 结束。这也解释了脱壳实操中的两个现象内存中 IAT 存的是真实地址所以我们在 OD 数据窗口里看到的是7C817077之类的内核地址值切换长型-地址后才能显示kernel.AddAtomA、user32.MessageBoxA这样的函数名壳需要自己重建 IAT加壳时外壳构造了自己的输入表跳回 OEP 前必须为每个 DLL 引入的函数重新获取地址并填写 IAT见 保护壳简介 的跳转回原程序入口点一节dump 出的文件若不含正确的导入表描述自然无法运行。实战延伸DLL 文件脱壳中的 IAT 重建手动定位 IAT 并非只适用于 EXE。在 DLL 文件脱壳 中DLL 脱壳的 IAT 重建步骤就必须照本文所述手动完成流程如下修改 DLL 标志用LordPE打开UnpackMe.dll在特征值Characteristics处点击...取消勾选DLL标志并保存系统就会将该文件视作一个可执行文件改后缀载入调试把UnpackMe.dll后缀改为UnpackMe.exe用 OD 载入入口点一般只做简单的cmp注意本例中jnz会直接跳到 unpacking 过程末尾需要修改寄存器Z标志使跳转失效同时在 unpacking 过程末尾设下断点避免脱壳完成后程序直接运行按本文流程脱壳并重建 IATDLL 脱壳的基本步骤与 EXE 一致同样用 ESP 定律到 OEP、手动圈出 IAT 范围、用 ImportREC 修复恢复 DLL 身份脱壳完 dump 后记得用 LordPE 把DLL标志恢复过来并将文件后缀名改回.dll。常见问题与注意事项OEP 填错是重建失败的首因ImportREC 的 OEP 填写的是相对镜像基址的 RVA。OD 中看到00401110镜像基址00400000则应填1110不要直接填绝对地址双击所有模块间的调用时选错对象应双击程序模块自身发起的调用而非系统 DLL 内部的调用否则跟随进入的是系统代码区而非 IAT 表项IAT 块大小不精确OD 底部显示的块大小来自调试器对相邻表项的合并估算实际操作中建议以起始函数kernel.AddAtomA到末尾函数user32.MessageBoxA之间连续、无空洞的 DWORD 区段为准必要时可多留少量余量保留调试进程与 DUMP 及 IAT 重建 中的提醒一致用 OllyDump / ImportREC 修复时不要关闭 OD 中正在调试的进程否则内存中的 IAT 数据无法读取获取输入表后校验有效性重建成功的标志是左侧每个模块的每个函数都被正确解析显示有效若有大量无效项说明 RVA / SIZE 圈定有误应回到 OD 数据窗口重新核对。掌握手动定位 IAT 的能力后再遇到 AutoSearch 失效的壳或 DLL 类样本你就能脱离盲点自动搜索用确定性的三个参数OEP / RVA / SIZE完成一次扎实的脱壳与导入表重建。赞分享文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载相关推荐ctf-wiki Windows 脱壳实战手动定位 IAT 地址并使用 ImportREC 重建导入表ctf wiki Windows 脱壳实战手动定位 IAT 地址并使用 ImportREC 重建导入表 导读 当加壳程序如 UPX 壳在内存中被解开后程文档网络安全教程ctf-wiki 逆向实战Windows 加壳程序脱壳后的 DUMP 与 IAT 重建OllyDump ImportREC 全流程ctf wiki 逆向实战Windows 加壳程序脱壳后的 DUMP 与 IAT 重建OllyDump ImportREC 全流程 本指南完整讲解 W文档网络安全教程CTF Wiki 逆向脱壳实战OllyDbg SFX 法自动定位 OEPCTF Wiki 逆向脱壳实战OllyDbg SFX 法自动定位 OEP 导读 SFX 法是 Windows 平台逆向脱壳中一种高效、省力的技巧它直接利用文档网络安全教程上一篇KLayout版图设计终极指南从零开始掌握免费开源EDA工具下一篇ComfyUI-VideoHelperSuite视频加载失败5分钟终极修复指南与性能优化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考