KernelCallbackTable Injection
Hijacking a GUI process by patching an entry in its PEB KernelCallbackTable to point at injected shellcode, then triggering execution with a window message such as WM_COPYDATA.
KernelCallbackTable injection targets GUI processes (classically explorer.exe)
by abusing a dispatch table that user32.dll uses to call back into user mode
when the kernel delivers window messages. Each GUI process's PEB holds a
KernelCallbackTable pointer; the kernel indexes into it (via
KeUserModeCallback) for callbacks like fnCOPYDATA. By patching one entry to
point at injected shellcode and then sending the right window message, the
attacker gets the OS itself to invoke the payload.
How it works
The injector writes shellcode into the target, reads the original
KernelCallbackTable from the target PEB, makes a copy with one entry (e.g.
fnCOPYDATA) repointed at the shellcode, writes the modified table back, and
updates PEB->KernelCallbackTable to reference it. Sending a WM_COPYDATA
message to the target's window then causes user32 to dispatch through the
patched entry and execute the shellcode in the target's context.
WriteProcessMemory(hProc, shellAddr, shellcode, len)
ReadProcessMemory(hProc, &peb->KernelCallbackTable, &origTable, ...)
ReadProcessMemory(hProc, origTable, &localCopy, sizeof(localCopy))
localCopy.__fnCOPYDATA = shellAddr // patch one callback entry
newTable = VirtualAllocEx(hProc, ...); WriteProcessMemory(hProc, newTable, &localCopy, ...)
WriteProcessMemory(hProc, &peb->KernelCallbackTable, &newTable, sizeof(PVOID))
SendMessage(hWnd, WM_COPYDATA, ...) // kernel dispatches the patched entryDetection & bypass
- Static — The injector imports
WriteProcessMemory/ReadProcessMemoryplus window/message APIs (FindWindow,SendMessage/SendNotifyMessagewithWM_COPYDATA). References to the PEB (NtQueryInformationProcessto find it) combined with messaging APIs and a GUI target name are a distinctive pairing. - Dynamic — The high-fidelity trigger is a cross-process write to the remote
PEB->KernelCallbackTablefield (or to the table it references), followed by aWM_COPYDATA/window message that drives execution. Watch forWriteProcessMemorywhose destination is the PEB offset ofKernelCallbackTablein a GUI process. ETW thread/stack traces will show a callback (fnCOPYDATA) returning into non-user32memory. - Memory forensics — Inspect the target's
KernelCallbackTable: a healthy table's entries all resolve insideuser32.dll; a stomped entry points into private/unbacked memory or another module. The shellcode region itself is typically private executable memory that pe-sieve/Moneta flag, and the patched table pointer can be compared against a cleanuser32baseline. - Tools — Process Hacker / WinDbg (
dt nt!_PEB KernelCallbackTable, dump and validate entries), pe-sieve and Moneta (private executable shellcode region), API Monitor / Sysmon ETW (remote PEB writes, window messages), and Volatility (peb/VAD inspection plus callback-table validation).