如何通过模块枚举和特征比对识别程序非法注入DLL行为?

2026-04-30 19:461阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计943个文字,预计阅读时间需要4分钟。

如何通过模块枚举和特征比对识别程序非法注入DLL行为?

直接提出结论,不涉及图形解释、冗余和超过100字:

用 EnumProcessModules 获取当前进程所有已加载模块

Windows 不保证 EnumProcessModules 返回顺序,且可能跳过某些特殊映射(如反射式注入的内存 DLL)。必须配合 GetModuleFileNameExGetModuleInformation 获取基址、大小、路径三要素。

  • GetModuleFileNameEx 返回空字符串或路径为 "\??\C:\Windows\System32\ntdll.dll" 类似格式,需进一步查证——某些注入会伪造路径但实际未映射磁盘文件
  • 64 位进程调用时,HMODULE 是 8 字节指针,传给 GetModuleInformationlpmodinfo 必须指向 MODULEINFO 结构体,不能误用 32 位偏移计算
  • 常见错误:未检查 EnumProcessModules 返回值是否为 0,导致后续读取越界或崩溃

识别无文件模块(fileless module)的关键判断点

合法系统 DLL 几乎都映射自磁盘,而反射式注入、直接内存加载的 DLL 往往不关联真实路径。但注意:GetModuleFileNameEx 失败 ≠ 一定非法——.NET Core 的原生 AOT 模块也可能无路径。

  • 优先检查 GetMappedFileName:它能返回内核视角下的映射来源,比 GetModuleFileNameEx 更底层、更难伪造
  • GetMappedFileName 返回 "DeviceHarddiskVolumeX..." 以外的路径(如 "DevicePhysicalMemory" 或空),高度可疑
  • 不要只看扩展名:攻击者常把恶意 DLL 命名为 winhttp.dllmsvcp140.dll,必须校验数字签名与导出函数数量

导出表 + 签名双重验证防绕过

只验证签名会被无签名但结构合规的恶意 DLL 绕过;只看导出表又会漏掉以 DllMain 为唯一导出的精简注入体。二者必须结合。

立即学习“C++免费学习笔记(深入)”;

  • ImageNtHeader 定位 PE 头后,检查 OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress 是否非零且落在节区内
  • 再读取 IMAGE_EXPORT_DIRECTORY,确认 NumberOfFunctions > 0 —— 有些壳会保留导出表头但清空函数地址数组,此时 AddressOfFunctions 指向全零内存
  • 调用 WinVerifyTrust 验证签名时,传入 WINTRUST_ACTION_GENERIC_VERIFY_V2,而非旧版 WINTRUST_ACTION_TRUSTPROVIDER_TEST,否则无法识别 EV 证书

为什么不能只依赖 LoadLibraryA 地址扫描?

远程线程注入常用 LoadLibraryA,但现代绕过手段(如 APC 注入、线程劫持)完全不调用它。扫描该函数地址只能捕获一部分注入行为,且易被 inline hook 或 IAT 替换干扰。

  • APC 注入中,恶意代码通过 QueueUserAPC 注入到目标线程 APC 队列,执行时不经过 LoadLibraryA 入口
  • 线程劫持直接覆写目标线程上下文中的 RIP,跳转到内存中 shellcode,全程无 API 调用痕迹
  • 真正可靠的依据永远是:模块是否在内存中、是否有合法来源、是否具备 DLL 基本结构特征——而不是它“怎么进来”的

最易被忽略的一点:32/64 位混合场景下,EnumProcessModules 在 Wow64 进程中可能只返回 32 位模块视图,需显式调用 EnumProcessModulesEx 并传 LIST_MODULES_32BIT | LIST_MODULES_64BIT 标志,否则会彻底漏掉跨架构注入的模块。

标签:C

本文共计943个文字,预计阅读时间需要4分钟。

如何通过模块枚举和特征比对识别程序非法注入DLL行为?

直接提出结论,不涉及图形解释、冗余和超过100字:

用 EnumProcessModules 获取当前进程所有已加载模块

Windows 不保证 EnumProcessModules 返回顺序,且可能跳过某些特殊映射(如反射式注入的内存 DLL)。必须配合 GetModuleFileNameExGetModuleInformation 获取基址、大小、路径三要素。

  • GetModuleFileNameEx 返回空字符串或路径为 "\??\C:\Windows\System32\ntdll.dll" 类似格式,需进一步查证——某些注入会伪造路径但实际未映射磁盘文件
  • 64 位进程调用时,HMODULE 是 8 字节指针,传给 GetModuleInformationlpmodinfo 必须指向 MODULEINFO 结构体,不能误用 32 位偏移计算
  • 常见错误:未检查 EnumProcessModules 返回值是否为 0,导致后续读取越界或崩溃

识别无文件模块(fileless module)的关键判断点

合法系统 DLL 几乎都映射自磁盘,而反射式注入、直接内存加载的 DLL 往往不关联真实路径。但注意:GetModuleFileNameEx 失败 ≠ 一定非法——.NET Core 的原生 AOT 模块也可能无路径。

  • 优先检查 GetMappedFileName:它能返回内核视角下的映射来源,比 GetModuleFileNameEx 更底层、更难伪造
  • GetMappedFileName 返回 "DeviceHarddiskVolumeX..." 以外的路径(如 "DevicePhysicalMemory" 或空),高度可疑
  • 不要只看扩展名:攻击者常把恶意 DLL 命名为 winhttp.dllmsvcp140.dll,必须校验数字签名与导出函数数量

导出表 + 签名双重验证防绕过

只验证签名会被无签名但结构合规的恶意 DLL 绕过;只看导出表又会漏掉以 DllMain 为唯一导出的精简注入体。二者必须结合。

立即学习“C++免费学习笔记(深入)”;

  • ImageNtHeader 定位 PE 头后,检查 OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress 是否非零且落在节区内
  • 再读取 IMAGE_EXPORT_DIRECTORY,确认 NumberOfFunctions > 0 —— 有些壳会保留导出表头但清空函数地址数组,此时 AddressOfFunctions 指向全零内存
  • 调用 WinVerifyTrust 验证签名时,传入 WINTRUST_ACTION_GENERIC_VERIFY_V2,而非旧版 WINTRUST_ACTION_TRUSTPROVIDER_TEST,否则无法识别 EV 证书

为什么不能只依赖 LoadLibraryA 地址扫描?

远程线程注入常用 LoadLibraryA,但现代绕过手段(如 APC 注入、线程劫持)完全不调用它。扫描该函数地址只能捕获一部分注入行为,且易被 inline hook 或 IAT 替换干扰。

  • APC 注入中,恶意代码通过 QueueUserAPC 注入到目标线程 APC 队列,执行时不经过 LoadLibraryA 入口
  • 线程劫持直接覆写目标线程上下文中的 RIP,跳转到内存中 shellcode,全程无 API 调用痕迹
  • 真正可靠的依据永远是:模块是否在内存中、是否有合法来源、是否具备 DLL 基本结构特征——而不是它“怎么进来”的

最易被忽略的一点:32/64 位混合场景下,EnumProcessModules 在 Wow64 进程中可能只返回 32 位模块视图,需显式调用 EnumProcessModulesEx 并传 LIST_MODULES_32BIT | LIST_MODULES_64BIT 标志,否则会彻底漏掉跨架构注入的模块。

标签:C