Vectored Overloading PE Injection

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Tip

¿Buscas Windows 11 LFH heap shaping and VMware Workstation PVSCSI (vmware-vmx) escape techniques?

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

Descripción de la técnica

Vectored Overloading es un Windows PE injection primitive que fusiona el clásico Module Overloading con Vectored Exception Handlers (VEHs) y hardware breakpoints. En lugar de parchear LoadLibrary o escribir su propio loader, el adversario:

  1. Crea una sección SEC_IMAGE respaldada por un DLL legítimo (p. ej., wmp.dll).
  2. Sobrescribe la vista mapeada con un PE malicioso totalmente relocado pero mantiene el objeto de sección apuntando a la imagen benigna en disco.
  3. Registra un VEH y programa los registros de depuración para que cada llamada a NtOpenSection, NtMapViewOfSection, y opcionalmente NtClose provoque un breakpoint en modo usuario.
  4. Llama a LoadLibrary("amsi.dll") (o cualquier otro objetivo benigno). Cuando el loader de Windows invoca esos syscalls, el VEH evita la transición al kernel y devuelve los handles y las direcciones base de la imagen maliciosa preparada.

Porque el loader todavía cree que mapeó el DLL solicitado, las herramientas que solo inspeccionan los archivos de backing de la sección ven wmp.dll aunque la memoria ahora contenga la carga del atacante. Mientras tanto, las imports/TLS callbacks siguen siendo resueltas por el loader genuino, reduciendo significativamente la cantidad de lógica propia de parsing de PE que el adversario debe mantener.

Stage 1 – Construir la sección disfrazada

  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.

En este punto el proceso posee una vista con permisos RWX cuyo objeto backing sigue siendo wmp.dll, sin embargo los bytes en memoria están controlados por el atacante.

Stage 2 – Secuestrar el loader con VEHs

  1. Register a VEH and arm hardware breakpoints: programa Dr0 (o otro registro de depuración) con la dirección de ntdll!NtOpenSection y ajusta DR7 para que cada ejecución lance STATUS_SINGLE_STEP. Repite luego para NtMapViewOfSection y, opcionalmente, NtClose.
  2. Trigger DLL loading con LoadLibrary("amsi.dll"). LdrLoadDll eventualmente llamará a NtOpenSection para obtener el handle de la sección real.
  3. VEH hook for NtOpenSection:
  • Localiza la casilla en la pila para el argumento [out] PHANDLE SectionHandle.
  • Escribe en esa casilla el handle DecoySection previamente creado.
  • Avanza RIP/EIP hasta la instrucción ret para que el kernel nunca sea llamado.
  • Re-arm the hardware breakpoint para vigilar NtMapViewOfSection a continuación.
  1. VEH hook for NtMapViewOfSection:
  • Sobrescribe el [out] PVOID *BaseAddress (y las salidas de tamaño/protección) con la dirección de la vista maliciosa ya mapeada.
  • Omite el cuerpo del syscall tal como antes.
  1. (Opcional) VEH hook for NtClose verifica que el handle de sección falso sea limpiado, evitando resource leaks y proporcionando una comprobación final de sanity.

Como los syscalls nunca se ejecutan, los callbacks del kernel (ETWti, minifilter, etc.) no observan los eventos sospechosos de NtOpenSection/NtMapViewOfSection, reduciendo drásticamente la telemetría. Desde el punto de vista del loader, todo tuvo éxito y amsi.dll está en memoria, por lo que procede con la resolución de imports/TLS usando la vista del atacante.

PoC implementation notes (2025)

El PoC público muestra algunos detalles prácticos que son fáciles de pasar por alto al reimplementar la técnica:

  • HWBPs are per-thread. El PoC establece CONTEXT_DEBUG_REGISTERS en el hilo actual antes de llamar a LoadLibrary, por lo que el VEH debe ejecutarse en el mismo hilo que dispara el loader.
  • Syscall emulation: el VEH establece RAX = 0 y avanza RIP hasta el ret dentro del stub de ntdll (busca 0xC3) para que nunca ocurra la transición al kernel, y luego reanuda con NtContinue.
  • Output parameters: para NtMapViewOfSection, el VEH sobrescribe los outputs BaseAddress, ViewSize y Win32Protect para que el loader crea que el mapeo tuvo éxito y continúe con imports/TLS usando la vista del atacante.

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);

Variación sigilosa

Investigaciones recientes sobre VEH destacan que los handlers pueden registrarse mediante manually manipulating the VEH list en lugar de llamar a AddVectoredExceptionHandler, lo que reduce la dependencia de user-mode APIs que pueden ser monitorizadas o hookeadas. Esto no es necesario para Vectored Overloading pero puede combinarse con él para reducir la actividad de API observable.

Etapa 3 – Ejecutar el payload

  • EXE payload: El injector simplemente salta al original entry point una vez que se completan las relocations. Cuando el loader piensa que llamaría a DllMain, el código personalizado en su lugar ejecuta la entrada al estilo EXE.
  • DLL payload / Node.js addon: Resolver y llamar al export previsto (Kidkadi expone una función nombrada a JavaScript). Dado que el módulo ya está registrado en LdrpModuleBaseAddressIndex, búsquedas posteriores lo ven como la DLL benigna.

Cuando se combina con un Node.js native addon (.node file), todo el trabajo pesado de Windows-internals permanece fuera de la capa de JavaScript, lo que ayuda al threat actor a distribuir el mismo loader con múltiples wrappers de Node ofuscados.

Referencias

Tip

Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprende y practica Hacking en Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks