Vectored Overloading PE Injection

Tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Technique overview

Vectored Overloading is a Windows PE injection primitive that fuses classic Module Overloading with Vectored Exception Handlers (VEHs) and hardware breakpoints. Instead of patching LoadLibrary or writing its own loader, the adversary:

  1. Creates a SEC_IMAGE section backed by a legitimate DLL (e.g., wmp.dll).
  2. Overwrites the mapped view with a fully relocated malicious PE but keeps the section object pointing to the benign image on disk.
  3. Registers a VEH and programs debug registers so every call to NtOpenSection, NtMapViewOfSection, and optionally NtClose raises a user-mode breakpoint.
  4. Calls LoadLibrary("amsi.dll") (or any other benign target). When the Windows loader invokes those syscalls, the VEH skips the kernel transition and returns the handles and base addresses of the prepared malicious image.

Because the loader still believes it mapped the requested DLL, tooling that only looks at section backing files sees wmp.dll even though memory now contains the attacker’s payload. Meanwhile, imports/TLS callbacks are still resolved by the genuine loader, significantly reducing the amount of custom PE-parsing logic the adversary must maintain.

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);
    
  2. Copy the malicious PE into that view section by section, honouring SizeOfRawData/VirtualSize and updating protections afterwards (PAGE_EXECUTE_READ, PAGE_READWRITE, etc.).
  3. 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.
  4. 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.

At this point the process owns a RWX-capable view whose backing object is still wmp.dll, yet the bytes in memory are attacker-controlled.

Stage 2 – Hijack the loader with VEHs

  1. Register a VEH and arm hardware breakpoints: program Dr0 (or another debug register) with the address of ntdll!NtOpenSection and set DR7 so every execution raises STATUS_SINGLE_STEP. Repeat later for NtMapViewOfSection and optionally NtClose.
  2. Trigger DLL loading with LoadLibrary("amsi.dll"). LdrLoadDll will eventually call NtOpenSection to obtain the real section handle.
  3. VEH hook for NtOpenSection:
    • Locate the stack slot for the [out] PHANDLE SectionHandle argument.
    • Write the previously created DecoySection handle into that slot.
    • Advance RIP/EIP to the ret instruction so the kernel is never called.
    • Re-arm the hardware breakpoint to watch NtMapViewOfSection next.
  4. VEH hook for NtMapViewOfSection:
    • Overwrite the [out] PVOID *BaseAddress (and size/protection outputs) with the address of the already mapped malicious view.
    • Skip the syscall body just like before.
  5. (Optional) VEH hook for NtClose verifies that the fake section handle is cleaned up, preventing resource leaks and providing a final sanity check.

Because the syscalls are never executed, kernel callbacks (ETWti, minifilter, etc.) do not observe the suspicious NtOpenSection/NtMapViewOfSection events, drastically lowering telemetry. From the loader’s point of view everything succeeded and amsi.dll is in memory, so it proceeds with import/TLS resolution against the attacker’s bytes.

Stage 3 – Execute the payload

  • EXE payload: The injector simply jumps to the original entry point once relocations are done. When the loader thinks it would call DllMain, the custom code instead executes the EXE-style entry.
  • DLL payload / Node.js addon: Resolve and call the intended export (Kidkadi exposes a named function to JavaScript). Because the module is already registered with LdrpModuleBaseAddressIndex, subsequent lookups see it as the benign DLL.

When combined with a Node.js native addon (.node file), all of the Windows-internals heavy lifting stays outside the JavaScript layer, helping the threat actor ship the same loader with many different obfuscated Node wrappers.

References

Tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks