Vectored Overloading PE Injection

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Tip

Suchen Sie nach Windows 11 LFH heap shaping und VMware Workstation PVSCSI (vmware-vmx) escape techniques?

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

Technikübersicht

Vectored Overloading ist ein Windows PE injection primitive, das klassisches Module Overloading mit Vectored Exception Handlers (VEHs) und hardware breakpoints verschmilzt. Anstatt LoadLibrary zu patchen oder einen eigenen Loader zu schreiben, geht der Angreifer wie folgt vor:

  1. Erstellt eine SEC_IMAGE Section, die von einer legitimen DLL gestützt wird (z. B. wmp.dll).
  2. Überschreibt die gemappte View mit einem vollständig relocierten bösartigen PE, behält aber das Section-Objekt, das auf das harmlose Image auf der Festplatte zeigt.
  3. Registriert einen VEH und programmiert die Debug-Register, sodass jeder Aufruf von NtOpenSection, NtMapViewOfSection und optional NtClose einen User-Mode-Breakpoint auslöst.
  4. Ruft LoadLibrary("amsi.dll") (oder ein anderes harmloses Ziel) auf. Wenn der Windows-Loader diese Syscalls ausführt, überspringt der VEH den Kernel-Übergang und liefert die Handles und Basisadressen des vorbereiteten bösartigen Images zurück.

Weil der Loader weiterhin glaubt, die angeforderte DLL gemappt zu haben, sehen Tools, die nur auf das backing file der Section schauen, wmp.dll, obwohl der Speicher nun den Payload des Angreifers enthält. Import- und TLS-Callbacks werden weiterhin vom echten Loader aufgelöst, was die Menge an eigener PE-Parsing-Logik, die der Angreifer pflegen muss, deutlich reduziert.

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.

An diesem Punkt besitzt der Prozess eine RWX-fähige View, deren backing object weiterhin wmp.dll ist, obwohl die Bytes im Speicher vom Angreifer kontrolliert werden.

Stage 2 – Hijack the loader with VEHs

  1. Register a VEH and arm hardware breakpoints: programmiere Dr0 (oder ein anderes Debug-Register) mit der Adresse von ntdll!NtOpenSection und setze DR7, sodass bei jeder Ausführung STATUS_SINGLE_STEP ausgelöst wird. Später dasselbe für NtMapViewOfSection und optional NtClose wiederholen.
  2. Trigger DLL loading mit LoadLibrary("amsi.dll"). LdrLoadDll wird schließlich NtOpenSection aufrufen, um das echte Section-Handle zu erhalten.
  3. VEH hook for NtOpenSection:
  • Lokalisieren Sie den Stack-Slot für das [out] PHANDLE SectionHandle Argument.
  • Schreiben Sie das zuvor erstellte DecoySection-Handle in diesen Slot.
  • Setzen Sie RIP/EIP auf die ret-Instruktion, sodass der Kernel nie aufgerufen wird.
  • Schalten Sie das Hardware-Breakpoint neu, um als Nächstes NtMapViewOfSection zu überwachen.
  1. VEH hook for NtMapViewOfSection:
  • Überschreiben Sie das [out] PVOID *BaseAddress (und die Size/Protection-Ausgaben) mit der Adresse der bereits gemappten bösartigen View.
  • Überspringen Sie den Syscall-Body wie zuvor.
  1. (Optional) VEH hook for NtClose verifiziert, dass das gefälschte Section-Handle bereinigt wird, verhindert resource leaks und liefert eine abschließende Sanity-Prüfung.

Weil die Syscalls nie ausgeführt werden, sehen Kernel-Callbacks (ETWti, minifilter, etc.) die verdächtigen NtOpenSection/NtMapViewOfSection-Ereignisse nicht, wodurch die Telemetrie drastisch reduziert wird. Aus Sicht des Loaders ist alles erfolgreich verlaufen und amsi.dll befindet sich im Speicher, sodass er mit der Import-/TLS-Auflösung gegen die Bytes des Angreifers fortfährt.

PoC implementation notes (2025)

Das öffentliche PoC zeigt einige praktische Details, die beim erneuten Implementieren der Technik leicht übersehen werden können:

  • HWBPs are per-thread. Das PoC setzt CONTEXT_DEBUG_REGISTERS im current thread bevor LoadLibrary aufgerufen wird, daher muss der VEH im selben Thread laufen, der den Loader auslöst.
  • Syscall emulation: der VEH setzt RAX = 0 und rückt RIP auf das ret im ntdll-Stub vor (er scannt nach 0xC3), sodass der Kernel-Übergang nie stattfindet, und setzt dann mit NtContinue fort.
  • Output parameters: für NtMapViewOfSection überschreibt der VEH die zurückgegebenen BaseAddress, ViewSize und Win32Protect-Outputs, sodass der Loader glaubt, das Mapping sei erfolgreich gewesen, und mit Imports/TLS gegen die View des Angreifers fortfährt.

Minimale HWBP-Konfiguration, die vom PoC verwendet wird (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);

Stealth variation

Jüngste VEH-Forschung hebt hervor, dass Handler durch manuelles Manipulieren der VEH-Liste registriert werden können, anstatt AddVectoredExceptionHandler aufzurufen, was die Abhängigkeit von user-mode APIs verringert, die überwacht oder gehookt sein könnten. Dies ist für Vectored Overloading nicht erforderlich, kann aber damit kombiniert werden, um beobachtbare API-Aktivität zu reduzieren.

Phase 3 – Die Payload ausführen

  • EXE payload: Der Injector springt einfach zum originalen entry point, sobald die relocations abgeschlossen sind. Wenn der Loader glaubt, DllMain aufzurufen, führt der benutzerdefinierte Code stattdessen den EXE-typischen Entry aus.
  • DLL payload / Node.js addon: Den beabsichtigten Export auflösen und aufrufen (Kidkadi stellt eine benannte Funktion für JavaScript zur Verfügung). Da das Modul bereits bei LdrpModuleBaseAddressIndex registriert ist, sehen nachfolgende Lookups es als die harmlose DLL.

Wenn dies mit einem Node.js native addon (.node file) kombiniert wird, bleibt die gesamte Windows-internals-Arbeit außerhalb der JavaScript-Ebene, wodurch der Angreifer denselben Loader mit vielen verschiedenen obfuskierten Node-Wrappern ausliefern kann.

Referenzen

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks