Introduction
If you read a threat report written around 2018, you might be tempted to write off APC injection as a solved problem. The technique was old, a textbook trick, once which most EDRs had learned to flag. By 2023, academic researchers were calling out its “near disappearance” from commodity malware. So why are we writing about it in 2026?
Well, this is because the problem refused to go away and simply mutated, much like a real life virus, it developed two new variants: Early Bird APC and Special User APC and even more like real life viruses, these variants are stealthier. Now both variants have begun showing up in nation-state actor toolkits and fresh new loaders made after 2024.
Process injection sits at the top of the MITRE ATT&CK heap for the second year running. Picus Labs’ Red Report 2025 found T1055 in roughly 31% of the million-plus malware samples they examined.
Recap
An Asynchronous Procedure Call (APC) is a Windows kernel mechanism that allows a function to execute asynchronously within the context of a specific thread. Every thread maintains its own APC queue, and when a thread enters an alertable wait state (by calling functions like SleepEx or WaitForSingleObjectEx with the alertable flag set), Windows drains that queue and executes whatever routines are waiting in it.
Attackers soon realized that hijacking threads was silent compared to CreateRemoteThread and had a lesser chance of getting caught in detections. Hence APC injection was formed as a technique, they'd find a process with a thread in an alertable wait and get a handle to it, call VirtualAllocEx and WriteProcessMemory to plant shellcode and then call QueueUserAPC pointed at the shellcode, the thread would eventually wake up and execute the shellcode. Defenders took time to catch onto these developments and eventually there was sufficient attention towards APC Injection.
What makes APC injection a 2026 problem?
Well, 2 developments and 1 pivot from the attackers.
Development 1: The rise of Special User APCs
The Windows kernel has long allowed a user-mode APC to fire only when a target thread sits in an alertable wait, defenders, quite rightfully, built around that assumption: if a thread wasn’t alertable, your APC wouldn’t run. Then NtQueueApcThreadEx2 arrived (the API was technically present from RS5, but undocumented for years), and with the QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC flag set, the kernel forces the APC to fire whether the target is alertable or not. It does this by piggybacking on a kernel APC; and all of a sudden the detection watching SleepEx no longer is sufficient.
How it works:
A Special User APC is processed by the kernel’s KiInitiateUserApc routine, when the flag is set:
- The kernel queues a kernel APC (not a user APC) to the target thread.
- The kernel APC fires at the next kernel-to-user mode transition, regardless of whether the thread is in an alertable wait.
- The kernel APC’s callback dispatches the attacker’s user-mode APC routine via
KiUserApcDispatcher.
The result is the execution in the target thread’s context, without the thread ever calling SleepEx or any other alertable function. Hence any thread becomes a valid target, broadening the target attack surface.
Development 2: Early Bird APC
Attackers spawn a process suspended, queue an APC into its main thread before the AV/EDR’s user-mode hooks have loaded, then resume. The payload runs before the security product begins. This is now common in multiple modern community loaders like DarkGate, Bumblebee, Kiss Loader etc.
Most common call stacks that can help see this activity look like the following:
ntdll!LdrInitializeThunk ← Windows loader entry
ntdll!LdrpInitializeProcess
ntdll!NtTestAlert ← drains APC queue during loader init
ntdll!KiUserApcDispatcher ← dispatches the attacker's APC routine
<shellcode> ← payload runs here
Pivot: Same Technique, Different Tactic
Finally, the pivot was in shifting APC from the Tactic of Initial Execution to the most important tactic of Evasion. Frameworks like AceLdr, FOLIAGE, Cronos, and Ekko use chains of APCs feeding into NtContinue to obfuscate beacon sleep cycles, AceLdr at release reportedly evaded every open-source memory scanner researchers threw at it: Hunt-Sleeping-Beacons, BeaconHunter, BeaconEye, Patriot, Moneta, PE-sieve, MalMemDetect.
The three frameworks mentioned above follow the same core concept:
- Capture the current execution context (
RtlCaptureContext). - Queue a series of APCs/timer callbacks, each calling
NtContinuewith a crafterCONTEXTstruct that redirects RIP to a specific function (this is ROP without gadget-hunting). - The chain:
VirtualProtect(RW)->RC4 encrypt shellcode-> Sleep ->RC4 decrypt->VirtualProtext(RX)-> restore execution. - The shellcode is encrypted during sleep and marked non-executable, memory scanners are able to find nothing.
AceLdr (presented at DEF CON 30) layered return-address spoofing on top of Ekko-style obfuscation. The spoofed call stack makes it appear as though the beacon is sleeping inside WaitForSingleObject called from ntdll , not from private shellcode. At release it evaded every open-source scanner: Hunt-Sleeping-Beacons, BeaconHunter, BeaconEye, Patriot, Moneta, PE-sieve, and MalMemDetect simultaneously.
Threat Landscape: We’re not the first ones to use it
MuddyWater (2024-25)
- MuddyWater is also tracked as MERCURY, Earth Vewtala and others, and is attributed to Iran’s Ministry of Intelligence and Security (MOIS).
- Starting May 2024 it was noted shifting from exclusively using legitimate Remote Management Tools to deploying a custom backdoor they named BugSleep.
- BugSleep comes with a custom loader that injects encrypted shellcode into running instances of
msedge.exe,chrome.exe,opera.exe,anydesk.exe,onedrive.exeandpowershell.exe. The shellcode is encrypted by subtracting a hardcoded byte value (0x6) from each byte.
Bumblebee Loader (2022-25)
- Bumblebee is an initial access broker with connections to the Conti Ransomware ecosystem, it is considered a successor to Conti/Diavol
- Bumblebee’s injection module (
dijcommand for DLL Injection) dynamically resolvesNtQueueApcThreadat runtime and uses it to inject a payload DLL into a hardcoded list of target process - While the static import of
NtQueueApcThreadis flagged by multiple scanners, a runtimeGetProcAddresslookup on ntdll.dll is invisible to import-table analysis.
DarkGate (2023-25)
- After Operation Duck Hunt dismantled Qakbot in 2023, DarkGate picked up in popularity and became the most dominant phishing payload.
- DarkGate’s loader uses APC injection via
NtTestAlert, which is the function responsible for draining a thread’s APC queue; rather than queuing an APC into a remote process (which requires a handle and triggers cross-process events), DarkGate queues the APC to itself and then manually callsNtTestAlertto drains and execute it immediately. - This is a local self-injection technique, it is stealthy since the entire operation is contained within a single process and there is
OpenProcess,WriteProcessMemoryetc.
PythonRatLoader / XWorm (2024)
- Confense documented a Python-based loader deploying XWorm RAT using APC injection from obfuscated Python code.
- The attack chain used Python’s
ctypeslibrary to call native Windows APIs directly, this removed the need for compiled malware artifacts, the “malware” is Python bytecode that decrypts its payload at runtime, making static analysis and YARA signatures far less effective. Additionally, the attacker can re-obfuscate the Python code trivially between campaigns.
Has anyone done anything?
Microsoft has shipped meaningful mitigations but they haven’t closed the door. The biggest one is Hotpatching in Windows 11 24H2 and Server 2022 24H2, which added NtManageHotPatch and the LdrpQueryCurrentPatch check. This was aimed primarily at process hollowing, but it also breaks Early Bird APC chains because Early Bird also resumes a suspended process, and the loader interruption fires on resume. Defenders can credibly cite this as the single biggest platform-level win against hollowing-style injection.
Yet, this single largest win, platform-wise was bypassed by malware authors within months, Pure Crypter and CastleBot now patch NtManageHotPatch in memory to return STATUS_NOT_SUPPORTED, which restores both classic RunPE and Early Bird reliability on 24H2. Hasherezade published the canonical PoC in January 2025.
What this means is that the mitigation, while effective immediately, is no longer enough, the bypass uses NtManageHotPatch, which when tampered, is rarely noticed by detections.
Microsoft’s kernel ETW Threat-Intelligence provider remains the gold-standard sensor for APC events; the QUEUEUSERAPC_REMOTE event (task ID 4) is a high-fidelity remote-APC signal. But the user-mode APC injection sensor introduced way back in Windows 10 1809 hasn’t had a public update since, and Philip Tsukerman’s 2019 bypass; which decouples the PreviousMode clauses to dodge the sensor’s code path; has not been publicly re-closed as of mid-2026. This makes Microsoft’s ETW a highly case specific and in-depth detection and not as an all in one solution.
What Detection Does Help?
Call-Stack Analysis. Early Bird APC has a near-pathognomonic stack: ntdll!LdrInitializeThunk → ntdll!NtTestAlert → ntdll!KiUserApcDispatcher → <APC>. Get-InjectedThreadEx detection looks for exactly this pattern, and Hunt-Sleeping-Beacons applies the same idea to runtime sleep-mask variants by flagging any thread parked in Wait:UserRequest with KiUserApcDispatcher on its stack heading into a blocking function.
Behavior-chain detection is the other production-ready approach. The pattern to alert on is a CREATE_SUSPENDED process followed by a remote thread or APC operation followed by NtResumeThread, ideally correlated across Sysmon EID 1, 8, 10, and the kernel ETW-TI events. 0xflux’s “Ghost Hunting” approach; correlating QUEUEUSERAPC_REMOTE events with later thread-creation or resume events from the same actor; survives synthetic stack spoofing where pure call-stack analysis can be fooled. It’s a good complementary signal
Two memory-side checks round things out. The MEMORY_WORKING_SET_EX_INFORMATION.SharedOriginal bit (used by Hunt-Sleeping-Beacons) flags module-stomped pages on the call stack which is useful against APC variants that hide shellcode inside legitimately mapped DLLs to defeat naive “private RWX page” checks. And in dead-box forensics, walking the KTHREAD‘s ApcState.ApcListHead[1] in WinDbg or Volatility reveals user APCs whose NormalRoutine points into private commit memory: malicious by definition. Volatility 3’s windows.malfind, windows.threads.Threads, and the community ProcInjectionsFind plugin remain the right starting point.
Where Evasion Will Go Next
While it is true that evasion has already evolved, it is going to get better, Hell’s Hall; an open source Github repository with indirect syscalls that jump to a syscall;ret gadget inside ntdll.dll itself — is now standard in tooling like DarkWidow and Indirect Waffles, combined with Early Bird APC, ACG and BlockDLL mitigations on a sacrificial process, PPID spoofing, and synthetic stack frames. The synthetic frames are specifically designed to make call-stack inspection lie.
Hardware-breakpoint VEH hooking used by Cymulate’s Blindside, CrowdStrike’s VEH², and others sets debug registers to trap on the syscall instruction inside ntdll wrappers, then a vectored exception handler swaps parameters or emits a clean syscall. This bypasses inline hooks without ever patching memory, which means your file-integrity-style detections see nothing. Module stomping pushes further: load a benign signed DLL, overwrite its .text, queue the APC into the stomped module, and your call-stack and SharedOriginal checks both come up clean unless they’re looking at PE header consistency.
And there’s a quietly important new direction in injection itself: execution-only injection, documented by fndsec in May 2025, it skips both the allocate and write phases EDR vendors traditionally key on, triggering execution via APC, fiber, UI callback, UMS, or DCOM marshaling.
Now what to do?
Subscribe to the Microsoft Kernel ETW Threat-Intelligence provider through a PPL-AM driver, Deploy a current Sysmon with rule groups that chain CREATE_SUSPENDED to remote thread or APC activity to NtResumeThread. Run Hunt-Sleeping-Beacons against your high-value workstations weekly, and alert on KiUserApcDispatcher frames into blocking functions.
Build detections. Sigma rules for NtQueueApcThread / Ex / Ex2 sequences. The 0x12Dark Development YARA rule for Special User APC is a solid starting point. Operationalize Get-InjectedThreadEx-style call-stack inspection in scheduled enterprise sweeps via Velociraptor or KAPE. Add an image-tampering rule that flags in-memory patches to NtManageHotPatch itself, since that’s the bypass.
Conclusion
APC Injection shows you how attack techniques can have potential that can be unseen until they are first countered, the original premise of queueing arbitrary code into someone else’s thread is too useful for attackers to give up on easily.
The good part is that the tools to catch the new variants exist and are mostly free, Call-stack analysis, kernel ETW-TI, and behavior-chain correlation will catch most of what’s out there.
References
- Check Point Research (BugSleep, July 2024)
- Sekoia.io DarkGate Internals (March 2025)
- Proofpoint Bumblebee (February 2024)
- IBM X-Force Ramnit-to-Bumblebee
- Cofense PythonRatLoader / XWorm
- Black Hills InfoSec AceLdr (DEF CON 30)
- Binary Defense Sleep Obfuscation
- 0x12Dark Development YARA (December 2025)
- vx-underground Early Cascade (October 2024)
- S12cybersecurity Frankenstein APC (2025)