Files
2025-09-25 04:02:19 +08:00

48 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
### 如何动态地去找导入表
**为什么要动态地查找?**
静态地查找导入表非常简单,我们只需要解析 PE 文件头中的数据目录(Data Directory),找到导入表的结构体 `IMAGE_IMPORT_DESCRIPTOR`,然后就可以找到所有的导入函数。但这种方法有几个局限性:
1. **脱壳(Unpacking):** 许多恶意软件会使用加壳技术(packer),将原始的 PE 文件压缩或加密。这种情况下,原始的导入表会被隐藏或破坏,静态分析工具无法找到它。当程序运行时,加壳器会自行解压和修复导入表,因此只有在内存中才能找到真正的导入表
2. **动态加载(Dynamic Loading):** 程序可能会使用 `LoadLibrary``GetProcAddress` 等函数在运行时动态加载 DLL 和获取函数地址。这种方式下,导入的函数根本不会出现在 PE 文件的静态导入表中
3. **防止逆向工程:** 有些程序开发者故意混淆或破坏导入表,以增加逆向工程的难度
因此,动态地查找导入表是进行脱壳、恶意软件分析和深入逆向工程的必备技能
**方法一:利用 `EAT` (导出地址表)**
这是最直接、也是最不寻常的方法。如果一个程序(比如一个 DLL)将自己的导入表中的函数地址作为导出函数暴露出来,你就可以通过解析它的**导出表 (Export Address Table, EAT)** 来找到导入表。但这种情况非常少见,通常只在一些特殊的系统 DLL 或驱动程序中出现。这种方法不具有通用性
**方法二:利用函数调用指令**
这是最常见、最实用的方法。当程序调用一个导入函数时,通常会使用 `CALL` 指令,其目标地址就是导入表中的一个条目
**具体步骤:**
1. **调试器附加:** 使用调试器(如 OllyDbg、x64dbg、IDA Pro Debugger)附加到目标进程
2. **设置断点:** 在程序执行的早期,比如 `main` 函数或 `WinMain` 函数的入口点设置断点
3. **单步调试/跟踪:** 逐步执行(Step Over)程序,并密切关注 `CALL` 指令
4. **识别导入调用:**
- **相对 `CALL`:** 如果你看到 `CALL [地址]` 这样的指令,并且这个地址是一个外部函数的地址,那么这个 `[地址]` 就是一个导入表项。例如,`CALL DWORD PTR [EAX]`
- **直接 `CALL`:** 如果你看到 `CALL MessageBoxA`,这通常是 IDA Pro 这样的反汇编器帮你标记的,它已经识别出这个调用指向了一个导入函数
5. **内存转储和分析:** 当你找到一个导入表项的地址后,你可以从这个地址开始,向前和向后扫描内存,寻找连续的、看起来像函数指针的地址序列。这个序列很可能就是完整的导入表。然后你可以将这一块内存转储出来进行进一步分析
这种方法需要对汇编语言有深入理解,并且需要耐心和细致的调试
**方法三:利用内存断点和内存扫描**
当程序加载时,加载器会向导入表写入外部函数的真实地址。我们可以利用这个特性
**具体步骤:**
1. **调试器附加:** 附加到目标进程
2. **查找 `IMAGE_IMPORT_DESCRIPTOR`** 使用静态分析工具(如 PE Explorer、CFF Explorer)找到 PE 文件中导入表的 `RVA` (Relative Virtual Address)
3. **计算内存地址:**`RVA` 加上基址(ImageBase)得到导入表在内存中的实际地址
4. **设置硬件断点:** 在导入表的第一个条目上设置一个硬件写入断点(Hardware Write Breakpoint
5. **运行程序:** 运行程序。当加载器填充导入表时,断点会被触发。这通常发生在 `LoadLibrary` 函数调用之后,但在 `main` 函数之前
6. **分析内存:** 断点触发后,你就可以检查内存中的导入表,它的内容已经被加载器填充好了。如果程序进行了脱壳,此时的导入表才是真实的
**另一种高级变体是:**
- 在程序执行早期,在整个 `.text` 段(代码段)设置一个硬件写入断点。当断点触发时,检查写入的地址是否在代码段内部,并分析写入的指令。这可以用来检测自修改代码,是更高级的逆向技术