Stealthy injectors
Goals
Execute foreign code in a target process/kernel with minimal artifacts versus EDR/AC.
Usermode technique ladder
- CreateRemoteThread + LoadLibrary — noisy baseline (know it to avoid)
- NtCreateThreadEx + manual map
- Thread hijack (suspend/context/RIP swap/resume)
- QueueUserAPC / special user APC on alertable threads
- Module stomping / module overloading (execute in legit module RX)
- Transacted/ghost / dual-mapping variants
- Process hollowing / doppelgänging / herpaderping (know detection cost)
- SetWindowsHookEx (only if UI-thread delivery fits)
- Instrumentation callback / VEH abuse for redirect
Manual map checklist
- Map sections with correct protections; apply relocs; resolve imports (including API sets)
- TLS callbacks decision; exception directory; cookie
- Erase PE headers optional; fix or avoid module list visibility intentionally
- Prefer RW→RX transitions; avoid long RWX
Kernel injection ladder
- APC to user thread from kernel
- Attach process + write + context
- Thread creation in target via NtCreateThreadEx from kernel
- Shared sections + user trigger
OPSEC
- Call stacks, allocation stubs, RWX, cross-process handle rights, abnormal module ranges
- Clean handles; avoid known bad patterns in public GH gists
- Test under target AC/EDR with telemetry capture
Technique deep mechanics
Thread hijack (the workhorse)
1. OpenThread(target main or worker with known alertable state)
2. SuspendThread -> GetThreadContext
3. Write shellcode stub (loadlib-style or map-call) to executable scratch
4. RIP = stub; stack aligned (0x28 shadow + 16-align per MS x64 ABI)
5. ResumeThread -> wait for completion flag -> restore original RIP/CONTEXT -> resume
- Detection: CONTEXT manipulation flagged by some EDRs via thread-start telemetry; use a legitimately alertable thread and restore RSP exactly.
Special user APC (Win10 20H1+)
NtQueueApcThreadEx(th, …, QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC) — runs regardless of alertable state, on next thread scheduling; kernel-mode counterpart KeInitializeApc with UserApcRoutine inside ntdll's RtlUserApcTrampoline-adjacent region.
- Pair with mapping stub that frees itself; APC routine address must be valid at run time.
Module stomping detail
- Load (or find already-loaded) large legit signed module with RX padding (common: system DLLs with >4KB slack at end of .text)
- memcpy shellcode or tiny PE into slack; optional: use
NtCreateSection dual-map of a data file onto an RX section of a signed module (module overloading proper)
- Execute via hijacked thread; restore original bytes after done if transient
- Wins: allocation is a legit signed image; memory scan sees signed entropy; no RWX.
- Losses: MZ/PE headers of stomped region if sloppy; code-integrity scans of system DLLs catch byte drift.
Manual map CFG + exception correctness
- Register
UNWIND_INFO + functions via RtlAddFunctionTable — or SEH/VEH and C++ exceptions crash inside your image on first throw.
- Control Flow Guard: indirect calls into your mapped image get terminated unless (a)
SetProcessValidCallTargets adds your ranges, (b) you patch CFG bitmap directly (find via NtGetCurrentProcess->ProcessDynamicCodePolicy… actually bitmap via LdrSystemDllInit internals — brittle), or (c) your code only uses direct calls internally and exported thunks via legitimate addresses. Simplest: compile with /guard:cf- and avoid indirect calls out.
- TLS: if image has TLS callbacks, allocate TLS index via
TlsAlloc, fill ThreadLocalStoragePointer, run callbacks with DLL_THREAD_ATTACH on each existing thread you care about.
Kernel-side injection mechanics
KeStackAttachProcess(targetEp, &apcState) -> MmCopyVirtualMemory (pass your driver EPROCESS as from-proc) -> detach. Works for data + stub writes.
- APC from kernel: allocate user-mode stub in target (ZwAllocateVirtualMemory with previous-mode tricks or attach+MmCopy),
KeInitializeApc(apc, thread, OriginalApc, …, UserApcRoutine=stub, UserApcContext=dllpath), KeInsertQueueApc. Choose NormalRoutine-null kernel APC for pure-kernel payloads.
- Create remote thread from kernel:
PsCreateSystemThread then set Thread->CrossThreadFlags/process — brittle across builds; prefer APC or attach+hijack of existing thread via KeSuspendThread-equivalents (undocumented, pin per build — windows-internals).
Detection mapping (what catches what)
| Technique |
Primary detector |
Hardening |
| CreateRemoteThread+LoadLibrary |
everything, since 2010 |
never ship |
| Manual map + NtCreateThreadEx |
ETW-TI thread-create telemetry; start-address heuristics |
indirect syscalls, spoof start address via thread-parameter gadget (call-stack-spoofing) |
| RWX allocations |
memory scanners, ETW map-view |
RW→RX transitions only; consider stomping |
| Module stomping |
integrity scans of signed modules |
restore bytes; pick cold modules |
| Hollowing/doppelgänging |
image-load callbacks mismatch, prefetch artifacts |
transacted-file variants leave USN journal — wipe |
| Kernel APC |
PatchGuard-adjacent, handle-strip telemetry |
hide thread object? no — target legit alertable threads |
Test discipline
- Detonate under target AC with procmon+ETW capture first on a sacrificial account.
- YARA-scan your own artifacts (
yara on E:\Tools) before shipping; public-gist shellcode is signatured within weeks.
- Verify on both 22H2/24H2+ builds: special-APC semantics and CFG defaults differ.
Pair with
kernel-dev, game-hacking, windows-internals, hypervisor-dev.
1---2name: stealth-injectors3description: Stealthy usermode/kernel injection: manual map, thread hijack, APC, module stomping, hollow, mapper design, artifact hygiene.4license: GPL-3.0-or-later5---67# Stealthy injectors89## Goals10Execute foreign code in a target process/kernel with minimal artifacts versus EDR/AC.1112## Usermode technique ladder131. **CreateRemoteThread + LoadLibrary** — noisy baseline (know it to avoid)142. **NtCreateThreadEx** + manual map153. **Thread hijack** (suspend/context/RIP swap/resume)164. **QueueUserAPC / special user APC** on alertable threads175. **Module stomping / module overloading** (execute in legit module RX)186. **Transacted/ghost / dual-mapping** variants197. **Process hollowing / doppelgänging / herpaderping** (know detection cost)208. **SetWindowsHookEx** (only if UI-thread delivery fits)219. **Instrumentation callback / VEH** abuse for redirect2223## Manual map checklist24- Map sections with correct protections; apply relocs; resolve imports (including API sets)25- TLS callbacks decision; exception directory; cookie26- Erase PE headers optional; fix or avoid module list visibility intentionally27- Prefer RW→RX transitions; avoid long RWX2829## Kernel injection ladder30- APC to user thread from kernel31- Attach process + write + context32- Thread creation in target via NtCreateThreadEx from kernel33- Shared sections + user trigger3435## OPSEC36- Call stacks, allocation stubs, RWX, cross-process handle rights, abnormal module ranges37- Clean handles; avoid known bad patterns in public GH gists38- Test under target AC/EDR with telemetry capture3940## Technique deep mechanics4142### Thread hijack (the workhorse)43```441. OpenThread(target main or worker with known alertable state)452. SuspendThread -> GetThreadContext463. Write shellcode stub (loadlib-style or map-call) to executable scratch474. RIP = stub; stack aligned (0x28 shadow + 16-align per MS x64 ABI)485. ResumeThread -> wait for completion flag -> restore original RIP/CONTEXT -> resume49```50- Detection: CONTEXT manipulation flagged by some EDRs via thread-start telemetry; use a legitimately alertable thread and restore RSP exactly.5152### Special user APC (Win10 20H1+)53- `NtQueueApcThreadEx(th, …, QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC)` — runs regardless of alertable state, on next thread scheduling; kernel-mode counterpart `KeInitializeApc` with `UserApcRoutine` inside ntdll's `RtlUserApcTrampoline`-adjacent region.54- Pair with mapping stub that frees itself; APC routine address must be valid at run time.5556### Module stomping detail571. Load (or find already-loaded) large legit signed module with RX padding (common: system DLLs with >4KB slack at end of .text)582. memcpy shellcode or tiny PE into slack; optional: use `NtCreateSection` dual-map of a data file onto an RX section of a signed module (module overloading proper)593. Execute via hijacked thread; restore original bytes after done if transient60- Wins: allocation is a legit signed image; memory scan sees signed entropy; no RWX.61- Losses: MZ/PE headers of stomped region if sloppy; code-integrity scans of system DLLs catch byte drift.6263### Manual map CFG + exception correctness64- Register `UNWIND_INFO` + functions via `RtlAddFunctionTable` — or SEH/VEH and C++ exceptions crash inside your image on first throw.65- Control Flow Guard: indirect calls into your mapped image get terminated unless (a) `SetProcessValidCallTargets` adds your ranges, (b) you patch CFG bitmap directly (find via `NtGetCurrentProcess`->`ProcessDynamicCodePolicy`… actually bitmap via `LdrSystemDllInit` internals — brittle), or (c) your code only uses direct calls internally and exported thunks via legitimate addresses. Simplest: compile with `/guard:cf-` and avoid indirect calls out.66- TLS: if image has TLS callbacks, allocate TLS index via `TlsAlloc`, fill `ThreadLocalStoragePointer`, run callbacks with DLL_THREAD_ATTACH on each existing thread you care about.6768### Kernel-side injection mechanics69- `KeStackAttachProcess(targetEp, &apcState)` -> `MmCopyVirtualMemory` (pass your driver EPROCESS as from-proc) -> detach. Works for data + stub writes.70- APC from kernel: allocate user-mode stub in target (ZwAllocateVirtualMemory with previous-mode tricks or attach+MmCopy), `KeInitializeApc(apc, thread, OriginalApc, …, UserApcRoutine=stub, UserApcContext=dllpath)`, `KeInsertQueueApc`. Choose `NormalRoutine`-null kernel APC for pure-kernel payloads.71- Create remote thread from kernel: `PsCreateSystemThread` then set `Thread->CrossThreadFlags`/process — brittle across builds; prefer APC or attach+hijack of existing thread via `KeSuspendThread`-equivalents (undocumented, pin per build — `windows-internals`).7273## Detection mapping (what catches what)74| Technique | Primary detector | Hardening |75|---|---|---|76| CreateRemoteThread+LoadLibrary | everything, since 2010 | never ship |77| Manual map + NtCreateThreadEx | ETW-TI thread-create telemetry; start-address heuristics | indirect syscalls, spoof start address via thread-parameter gadget (`call-stack-spoofing`) |78| RWX allocations | memory scanners, ETW map-view | RW→RX transitions only; consider stomping |79| Module stomping | integrity scans of signed modules | restore bytes; pick cold modules |80| Hollowing/doppelgänging | image-load callbacks mismatch, prefetch artifacts | transacted-file variants leave USN journal — wipe |81| Kernel APC | PatchGuard-adjacent, handle-strip telemetry | hide thread object? no — target legit alertable threads |8283## Test discipline84- Detonate under target AC with procmon+ETW capture first on a sacrificial account.85- YARA-scan your own artifacts (`yara` on E:\Tools) before shipping; public-gist shellcode is signatured within weeks.86- Verify on both 22H2/24H2+ builds: special-APC semantics and CFG defaults differ.8788## Pair with89`kernel-dev`, `game-hacking`, `windows-internals`, `hypervisor-dev`.