0%
毅种循环

模块 87 - NTDLL 取消挂钩 (Unhooking) - 来自 Web 服务器

恶意软件开发课程 - NTDLL 取消挂钩 (Unhooking) - 来自 Web 服务器


模块 87 - NTDLL 取消挂钩 (Unhooking) - 来自 Web 服务器#

NTDLL 取消挂钩 (Unhooking) - 来自 Web 服务器#

简介#

到目前为止,读者应该已经了解了几种取消 ntdll.dll 挂钩的方法。有人可能会问,为什么不直接在二进制文件中包含一个干净的 NTDLL 版本呢?这种方法的问题在于,为了支持多种 Windows 操作系统版本,你需要包含许多不同版本的 NTDLL。这会极大地增加 Payload 的体积,使其变得不可行。

本模块将演示一种替代方法:从 Web 服务器获取 NTDLL。该实现将首先检查当前机器上的 NTDLL 版本,然后从 Web 服务器获取相应的准确版本。这种方法的难点在于需要在 Web 服务器上托管所有版本的 NTDLL。因此,在本模块中,我们将利用 Winbindex,这是一个包含几乎所有 ntdll.dll 版本的索引库。

💡 初学者提示:为什么要从网络下载?

类比理解: 你的车(操作系统)坏了一个零件(NTDLL 被挂钩)。

  • 方法 1 (磁盘):你从后备箱里找备用件。
  • 方法 2 (KnownDlls):你去家里的车库找备用件。
  • 方法 3 (Web Server):你直接给原厂(Microsoft/Winbindex)打电话,报上你的车辆型号(时间戳和大小),让他们快递一个原装的崭新零件给你。

这种方法的好处是,你不需要随身携带所有型号的零件,只在需要的时候按需下载。

Winbindex#

Winbindex 是一个网站,编录了 Windows 操作系统中发现的许多文件版本。此外,它还包含一个搜索工具来查找所需的文件。下图是搜索 64 位版本 ntdll.dll 的输出结果。

图片
图片

确定 Winbindex 的 URL 格式#

由于必须以编程方式获取 ntdll.dll,因此了解下载链接的格式至关重要。分析以下 3 个 URL:

  1. https://msdl.microsoft.com/download/symbols/ntdll.dll/494079D61ee000/ntdll.dll
  2. https://msdl.microsoft.com/download/symbols/ntdll.dll/2EEE8BDD1ee000/ntdll.dll
  3. https://msdl.microsoft.com/download/symbols/ntdll.dll/F2E8A5AB214000/ntdll.dll

请注意 URL 中只有一部分发生了变化。如下图所示。

图片
图片

链接 1 和 2 的 URL 中都包含 “1ee000”,换算成十进制是 2023424。查看第一个 NTDLL 模块的附加信息并搜索值 “2023424”,会发现它是 NTDLL 的 VirtualSize (虚拟大小)

图片
图片

图片
图片

搜索字符串的第一部分 “494079D6”(十进制为 1228962262),显示这是该文件的 时间戳 (Timestamp)

图片
图片

因此,URL 的第一部分(时间戳)源自 DLL 的 IMAGE_FILE_HEADER.TimeDateStamp 元素。第二部分(虚拟大小)源自 DLL 的 IMAGE_OPTIONAL_HEADER.SizeOfImage 元素。

Winbindex 的下载链接格式如下图所示。

图片
图片

RealNtdllFromServer 函数#

下一步是构建一个函数,为本地机器生成合适的下载 URL。这就是下面 ReadNtdllFromServer 函数的作用。

ReadNtdllFromServer 函数首先调用 FetchLocalNtdllBaseAddress 获取本地 ntdll.dll 映像的基址,以便读取其头部信息来构建下载 URL。这是使用 wsprintfW 完成的,它将固定部分的下载链接 https://msdl.microsoft.com/download/symbols/ntdll.dll/pImgNtHdrs->FileHeader.TimeDateStamppImgNtHdrs->OptionalHeader.SizeOfImage 的值结合起来。

完成后,该函数调用 GetPayloadFromUrl,该函数在 Payload Staging - Web Server 模块中介绍过。该函数负责从 Web 服务器下载 Payload 文件,但在本例中,它被用于从生成的链接下载 ntdll.dll

回顾一下,GetPayloadFromUrl 有三个参数:下载 URL,以及两个分别表示下载文件基址和大小的输出参数。

整合代码#

现在内存中已经有了一个未挂钩的 ntdll.dll 版本,我们可以像之前一样利用 ReplaceNtdllTxtSection 函数来替换被挂钩的 ntdll.dll 的文本节。

📚 知识扩展:网络下载文件的对齐问题

注意,我们是通过把文件作为数据流下载到堆内存中的。这就像是从磁盘读取文件一样,它保持的是 Raw Data (原始数据) 的结构,而不是内存映射结构。

  • 也就是说,它的节对齐通常是 1024 (0x400) 附近的 PointerToRawData
  • 但是,由于我们是把它下载到一个连续的缓冲区中,我们不能直接假设它像 MapViewOfFile 那样被展开了。

下面的代码加入了一个智能判断逻辑:如果发现偏移计算不对(内容不匹配),它会尝试加一个偏移修正值 3072 (4096 - 1024),试图去适配可能的对齐差异。这是一个防御性编程的技巧。

风险提示#

虽然这种 NTDLL 取消挂钩的方法乍一看很不错,但它存在风险,因为使用了 WinINet API。这些 API 用于与 HTTP/S 协议交互,但它们需要加载额外的 DLL 映像,如 wininet.dllwinhttp.dllsechost.dll 以及许多其他导出这些 WinINet API 所需函数的 DLL。加载这些 DLL 是通过 LoadLibraryLdrLoadDll 等很可能已经被挂钩的函数完成的,这会暴露实现的内部设计,增加被检测的风险。

演示#

  1. 从 Winbindex 下载 ntdll.dll 文件。

图片
图片

  1. 待替换的被挂钩 ntdll.dll 文本节。

图片
图片

  1. 错误计算文本节基址(对齐修正前)。

图片
图片

  1. 重新计算并修正基址。

图片
图片

  1. 成功替换文本节。

图片
图片