CallWindowProc Shellcode Injection
A process-injection technique that abuses the CallWindowProcW API from user32.dll. Because CallWindowProc accepts a callback address (lpPrevWndFunc) and up to four arguments, it can be repurposed to transfer execution to arbitrary shellcode with controlled arguments.
Pattern Definition
- Allocate buffer: A
DllStructCreate("byte[N]")is created with RWX-compatible memory. - Write shellcode: The shellcode bytes are copied into the struct.
- Get pointer:
DllStructGetPtrreturns the raw memory address. - CallWindowProc: The address is passed as
lpPrevWndFunc; the API jumps directly to shellcode.
Global $PARATIM = DllStructCreate("byte[" & BinaryLen($PBDWLMZ) & "]")
DllStructSetData($PARATIM, 1, $PBDWLMZ)
Global $NUVEFSJM = DllStructGetPtr($PARATIM)
DllCall("user32.dll", "ptr", "CallWindowProc", "ptr", $NUVEFSJM + 9136, ...)
The + 9136 offset skips the shellcode prologue and lands execution at the real entry point. ^[script.au3:1291-1295]
Detection / Fingerprint
CallWindowProcWcalled from processes that do not own windows (e.g., console or hidden AutoIt processes).- The first argument (
lpPrevWndFunc) points to memory outside any loaded module — a strong signal of injection. - Memory allocated by
DllStructCreatewithPAGE_EXECUTE_READWRITEprotections.
ATT&CK Mapping
- T1055 — Process Injection
- T1055.001 — Dynamic-link Library Injection (conceptually similar — arbitrary code execution in process context)
Defensive Countermeasures
- ETW/Sysmon: monitor
CallWindowProcwith a first argument that does not resolve to a known module. - Memory integrity: flag RWX allocations in processes with no legitimate JIT requirement.
Pages Where Observed
- unattributed —
70848598primary sample