如何动态地去找导入表

为什么要动态地查找?

静态地查找导入表非常简单,我们只需要解析 PE 文件头中的数据目录(Data Directory),找到导入表的结构体 IMAGE_IMPORT_DESCRIPTOR,然后就可以找到所有的导入函数。但这种方法有几个局限性:

  1. 脱壳(Unpacking): 许多恶意软件会使用加壳技术(packer),将原始的 PE 文件压缩或加密。这种情况下,原始的导入表会被隐藏或破坏,静态分析工具无法找到它。当程序运行时,加壳器会自行解压和修复导入表,因此只有在内存中才能找到真正的导入表
  2. 动态加载(Dynamic Loading): 程序可能会使用 LoadLibraryGetProcAddress 等函数在运行时动态加载 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 段(代码段)设置一个硬件写入断点。当断点触发时,检查写入的地址是否在代码段内部,并分析写入的指令。这可以用来检测自修改代码,是更高级的逆向技术
Copyright © 版权信息 all right reserved,powered by Gitbook该文件修订时间: 2025-09-25 03:13:04

results matching ""

    No results matching ""