Vectored Overloading PE Injection

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks

Tip

想查找 Windows 11 LFH heap shaping 和 VMware Workstation PVSCSI (vmware-vmx) escape 技术吗?

{{#ref}} vmware-workstation-pvscsi-lfh-escape.md {{#endref}}

技术概述

Vectored Overloading 是一种将经典的 Module Overloading 与 Vectored Exception Handlers (VEHs) 和 hardware breakpoints 结合起来的 Windows PE 注入原语。与其修补 LoadLibrary 或编写自定义 loader,不如:

  1. 创建一个由合法 DLL(例如 wmp.dll)支撑的 SEC_IMAGE section。
  2. 用一个已完成重定位的恶意 PE 覆盖映射视图,但保留 section 对象指向磁盘上的良性映像。
  3. 注册 VEH 并编程调试寄存器,使每次调用 NtOpenSectionNtMapViewOfSection,以及可选的 NtClose 都触发用户模式断点。
  4. 调用 LoadLibrary("amsi.dll")(或任何其他良性目标)。当 Windows loader 调用这些 syscall 时,VEH 会跳过内核转换并返回已准备好的恶意映像的句柄和基址。

因为 loader 仍然认为它映射了请求的 DLL,只有检查 section backing 文件的工具会看到 wmp.dll,即便内存现在包含攻击者的 payload。与此同时,imports/TLS callbacks 仍由真实 loader 解析,显著减少了攻击者必须维护的自定义 PE 解析逻辑量。

Stage 1 – Build the disguised section

  1. Create and map a section for the decoy DLL
NtCreateSection(&DecoySection, SECTION_ALL_ACCESS, NULL,
0, PAGE_READWRITE, SEC_IMAGE, L"\??\C:\\Windows\\System32\\wmp.dll");
NtMapViewOfSection(DecoySection, GetCurrentProcess(), &DecoyView, 0, 0,
NULL, &DecoySize, ViewShare, 0, PAGE_READWRITE);
  1. Copy the malicious PE into that view section by section, honouring SizeOfRawData/VirtualSize and updating protections afterwards (PAGE_EXECUTE_READ, PAGE_READWRITE, etc.).
  2. Apply relocations and resolve imports exactly as a reflective loader would. Because the view is already mapped as SEC_IMAGE, section alignments and guard pages match what the Windows loader expects later.
  3. Normalize the PE header:
  • If the payload is an EXE, set IMAGE_FILE_HEADER.Characteristics |= IMAGE_FILE_DLL and zero the entry point to keep LdrpCallTlsInitializers from jumping into EXE-specific stubs.
  • DLL payloads can keep their headers unchanged.

此时,进程拥有一个其 backing object 仍为 wmp.dll 的 RWX 能力视图,但内存中的字节已由攻击者控制。

Stage 2 – Hijack the loader with VEHs

  1. Register a VEH and arm hardware breakpoints:将 Dr0(或另一个调试寄存器)编程为指向 ntdll!NtOpenSection 的地址,并设置 DR7,以便每次执行都会引发 STATUS_SINGLE_STEP。随后对 NtMapViewOfSection(以及可选的 NtClose)重复相同操作。
  2. Trigger DLL loading:调用 LoadLibrary("amsi.dll")LdrLoadDll 最终会调用 NtOpenSection 来获取真实的 section handle。
  3. VEH hook for NtOpenSection
  • 找到用于 [out] PHANDLE SectionHandle 参数的栈槽。
  • 将先前创建的 DecoySection handle 写入该槽。
  • RIP/EIP 推进到 ret 指令处,从而避免内核被调用。
  • 重新布置硬件断点以监视下一步的 NtMapViewOfSection
  1. VEH hook for NtMapViewOfSection
  • 覆盖 [out] PVOID *BaseAddress(以及 size/protection 输出)为已映射的恶意视图地址。
  • 像之前一样跳过 syscall 的执行体。
  1. (Optional) VEH hook for NtClose 验证伪造的 section handle 被清理,防止资源泄漏并提供最终的完整性检查。

由于这些 syscall 从未执行,内核回调(如 ETWti、minifilter 等)不会观察到可疑的 NtOpenSection/NtMapViewOfSection 事件,从而大幅降低遥测。从 loader 的角度看,一切都成功,amsi.dll 在内存中因此继续执行 imports/TLS 的解析,使用的却是攻击者的视图内容。

PoC implementation notes (2025)

公开的 PoC 展示了一些在重新实现该技术时容易忽视的实际细节:

  • HWBPs are per-thread。PoC 在调用 LoadLibrary 之前对当前线程 设置 CONTEXT_DEBUG_REGISTERS,因此 VEH 必须在触发 loader 的同一线程上运行。
  • Syscall emulation:VEH 将 RAX = 0 并将 RIP 推进到 ntdll stub 内的 ret(它扫描 0xC3),使内核转换永远不会发生,然后用 NtContinue 恢复执行。
  • Output parameters:对于 NtMapViewOfSection,VEH 会覆盖返回的 BaseAddressViewSizeWin32Protect 输出,让 loader 相信映射成功并继续使用攻击者的视图进行 imports/TLS。

Minimal HWBP setup used by the PoC (x64):

CONTEXT ctx = {0};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
GetThreadContext(GetCurrentThread(), &ctx);
ctx.Dr0 = (DWORD64)NtOpenSection;
ctx.Dr7 = 1;
SetThreadContext(GetCurrentThread(), &ctx);
AddVectoredExceptionHandler(1, VehHandler);

隐蔽变体

最近的 VEH 研究指出,可以通过手动操纵 VEH 列表来注册处理程序,而不是调用 AddVectoredExceptionHandler,这可以减少对可能被监视或挂钩的用户模式 API 的依赖。这对于 Vectored Overloading 并非必需,但可以与其结合以减少可观察到的 API 活动。

Stage 3 – Execute the payload

  • EXE payload: 注入器在重定位完成后直接跳转到原始入口点。当加载器认为会调用 DllMain 时,定制代码会改为执行 EXE 风格的入口。
  • DLL payload / Node.js addon: 解析并调用目标导出(Kidkadi 向 JavaScript 导出一个命名函数)。因为该模块已经在 LdrpModuleBaseAddressIndex 中注册,后续查找会将其视为良性 DLL。

当与 Node.js 本地 addon(.node 文件)结合时,所有 Windows 内部实现的繁重工作都留在 JavaScript 层之外,这有助于威胁行为者用多个不同的混淆 Node 包装器分发相同的 loader。

References

Tip

学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE) 学习和实践 Azure 黑客技术:HackTricks Training Azure Red Team Expert (AzRTE)

支持 HackTricks