0%
毅种循环

模块 55 - IAT 隐藏与混淆 - API 哈希 (API Hashing)

恶意软件开发课程 - IAT 隐藏与混淆 - API 哈希 (API Hashing)


模块 55 - IAT 隐藏与混淆 - API 哈希 (API Hashing)#

[!IMPORTANT] 本知识库声明

本知识库由本人整理自互联网 MalDev Academy 泄露资源,并由本人手动翻译为中文,过程中增加了大量关键技术提示实践心得

  1. 内容完整性:并未修改任何核心代码与技术逻辑,仅做汉化与注释加强。
  2. 版权归属:原始知识产权归原作者/官方所有。
  3. 支持正版:本仓库仅供内部学习交流,如果您有经济能力,请务必支持正版课程
  4. 权利申诉:如相关内容侵犯了您的权益,请联系我,我将立即核实并删除。

IAT 隐藏与混淆 - API 哈希#

简介#

在涉及前两个模块中,我们创建了两个自定义函数 GetProcAddressReplacementGetModuleHandleReplacement 来替换原生的 GetProcAddressGetModuleHandle。这足以实现 运行时动态链接 (Run-Time Dynamic Linking),从而在 IAT 中隐藏导入函数。然而,代码中使用的字符串仍然会暴露正在使用的函数。例如,下面的代码行使用了这些函数来检索 VirtualAllocEx

GetProcAddressReplacement(GetModuleHandleReplacement("kernel32.dll"), "VirtualAllocEx")
c

安全解决方案可以轻而易举地从编译后的二进制文件中提取字符串,并识别出程序正在使用 VirtualAllocEx。为了解决这个问题,我们将对 GetProcAddressReplacementGetModuleHandleReplacement 应用字符串哈希算法。这些函数将不再通过字符串比较来获取指定的模块基址或函数地址,而是通过 哈希值 进行工作。

实现 API 哈希#

在本模块中,我们将上述两个函数重命名为 GetProcAddressHGetModuleHandleH。这些更新后的函数使用 Jenkins One At A Time 字符串哈希算法,将函数名和模块名替换为代表它们的哈希值。请回想一下,该算法是在 字符串哈希 (String Hashing) 模块中通过 JenkinsOneAtATime32Bit 函数引入的。

获取哈希值#

为了使用本模块所示的函数,必须先获取模块名(例如 User32.dll)和函数名(例如 MessageBoxA)的哈希值。这可以通过先将哈希后的值打印到控制台来实现。请确保哈希算法使用相同的种子(Seed)。

// ...
int main(){
    // 打印模块名(建议全大写以匹配后续逻辑)和函数名的哈希值
	printf("[i] Hash Of \"%s\" Is : 0x%0.8X \n", "USER32.DLL", HASHA("USER32.DLL")); 
	printf("[i] Hash Of \"%s\" Is : 0x%0.8X \n", "MessageBoxA", HASHA("MessageBoxA"));
	
  	return 0;
}
c

上述 main 函数将输出:

[i] Hash Of "USER32.DLL" Is : 0x81E3778E
[i] Hash Of "MessageBoxA" Is : 0xF10E27CA
text

这些哈希值现在可以用于下文的函数中。

使用方法#

除了传递哈希值而不是字符串值之外,函数的使用方式与之前相同。

// 0x81E3778E 是 USER32.DLL 的哈希值
// 0xF10E27CA 是 MessageBoxA 的哈希值
fnMessageBoxA pMessageBoxA = (fnMessageBoxA)GetProcAddressH(GetModuleHandleH(0x81E3778E), 0xF10E27CA); 
c

GetProcAddressH 函数#

GetProcAddressH 与之前的自定义函数类似,主要区别在于它使用 JenkinsOneAtATime32Bit 算法将导出函数名进行哈希,并与输入的哈希值进行比较。

为了保持代码简洁,我们使用宏来调用哈希函数:

  • HASHA:调用 ASCII 版本的哈希。
  • HASHW:调用宽字符版本的哈希。
#define HASHA(API) (HashStringJenkinsOneAtATime32BitA((PCHAR) API))
#define HASHW(API) (HashStringJenkinsOneAtATime32BitW((PWCHAR) API))
c

GetProcAddressH 实现如下:

GetModuleHandleH 函数#

GetModuleHandleH 同样是之前的增强版。由于模块名大小写不一,为了统一,我们在哈希对比前先将枚举到的 DLL 名称转换为 全大写。因此,输入的 dwModuleNameHash 参数也必须是全大写模块名的哈希值(如 USER32.DLL)。

演示#

这个演示使用哈希值通过 GetModuleHandleHGetProcAddressH 调用 MessageBoxA

搜索 MessageBox 字符串特征#

使用 Sysinternals 的 Strings.exe 工具搜索字符串 “MessageBox”:

图片
图片

可以观察到,二进制文件中 完全没有 对应的明文字符串。我们成功调用了 MessageBoxA,而它既没有出现在 IAT 中,也没有作为明文字符串暴露。这种方法在 32 位和 64 位系统中均有效。


🎯 总结#

在本模块中,我们学习了:

  1. 特征消除:过哈希技术彻底清除了二进制文件中的敏感 API 字符串。
  2. Jenkins 哈希集成:将模块 51 的算法真正应用到了 IAT 隐藏逻辑中。
  3. 双重隐匿:结合了“运行时动态链接”和“字符串混淆”,让防御者即便进行静态分析也难以弄清程序意图。

💡 关键要点

  • 哈希冲突预防:虽然理论上存在碰撞可能,但对于 Windows API 这类有限集合,Jenkins 算法的表现非常完美。
  • 自动化前奏:当前的哈希值是手动计算并填入的,后续我们将学习如何更高效地管理这些哈希值。

📚 下一步学习

下一个模块将介绍 IAT Hiding & Obfuscation - Custom Pseudo Handles。我们将探讨如何进一步优化 API 的获取流程,使其更加隐蔽和高效。