APC Injection

Written by: Iron Hulk Published: October 31, 2025 Reading time: Iron Hulk
Back to Blogs

بسم الله الرحمن الرحيم


Unveiling APC Injection


Welcome back, today, we're peeling back the layers on a classic yet still highly relevant code injection technique used by both malware and legitimate software known as Asynchronous Procedure Call (APC) Injection. If you've ever wondered how a process can be forced to execute code that isn't part of its original binary, you're in the right place. We'll break down everything from the fundamental concepts to the step-by-step mechanics of how APC injection works.

What is an Asynchronous Procedure Call (APC)?

At its core, an Asynchronous Procedure Call (APC) is a function that executes asynchronously in the context of a particular thread. Think of it like a callback or an interrupt, that the OS will invoke on a specific thread when that thread is in an alertable state When a thread is in a state where it can be interrupted, the OS will deliver any pending APCs, causing the thread to execute the APC function before resuming its normal work. User-mode APCs

Quick analogy: an APC is like slipping a note into a worker’s pocket — when the worker pauses for a moment, they read and act on the note.

What is APC Injection?

APC Injection is the technique of abusing the Windows APC mechanism to force a thread within a remote (target) process to execute malicious code. Here's the high-level attack flow:

  • An attacker gains the necessary permissions to manipulate a target process, often via OpenProcess
  • They write their malicious shellcode into the memory of that target process.
  • Instead of creating a remote thread, they identify a thread within the target process that is, or can be put into, an alertable state.
  • They queue a User-Mode APC that points to the injected shellcode.
  • The next time that thread enters an alertable wait, it will automatically execute the attacker's shellcode.

The beauty and danger of this technique is that it doesn't require creating a new thread CreateRemoteThread, which can be a more detectable indicator of compromise (IoC).

When can an APC run? (Alertable State)

This is the most critical concept to grasp for understanding APC injection. A user-mode APC can only be delivered to a thread that is in an "Alertable State". A thread enters an alertable state when it voluntarily makes a call to specific Windows API functions that wait for an event, signal, or I/O operation. These functions include:

  • Suspends the current thread for specified time while allowing APC interruption
  • DWORD SleepEx(
        DWORD  dwMilliseconds,      // Time to sleep in milliseconds
        BOOL   bAlertable           // TRUE = enable APC processing
    );

  • Waits for a single synchronization object to become signaled
  • DWORD WaitForSingleObjectEx(
        HANDLE hHandle,             // Handle to object (mutex, event, process, etc.)
        DWORD  dwMilliseconds,      // Timeout interval
        BOOL   bAlertable           // TRUE = enable APC processing
    
        /*
        Common Wait Objects:
        - hProcess - Wait for process to terminate
        - hThread - Wait for thread to exit
        - hEvent - Wait for event to be signaled
        - hMutex - Wait for mutex ownership
        */
    );

  • Waits for multiple synchronization objects simultaneously
  • DWORD WaitForMultipleObjectsEx(
        DWORD   nCount,             // Number of handles in array
        const HANDLE *lpHandles,    // Array of object handles
        BOOL    bWaitAll,           // Wait for all or any object
        DWORD   dwMilliseconds,     // Timeout interval
        BOOL    bAlertable          // TRUE = enable APC processing
    
        /*
        Wait Modes:
        - bWaitAll = TRUE: Wait until ALL objects are signaled
        - bWaitAll = FALSE: Wait until ANY object is signaled
        */
    );

  • Atomically signals one object and waits on another
  • DWORD SignalObjectAndWait(
        HANDLE hObjectToSignal,     // Object to signal (mutex, event, semaphore)
        HANDLE hObjectToWaitOn,     // Object to wait for
        DWORD  dwMilliseconds,      // Timeout interval
        BOOL   bAlertable           // TRUE = enable APC processing
    );

  • Waits for objects OR specific window messages - crucial for UI threads
  • DWORD MsgWaitForMultipleObjectsEx(
        DWORD   nCount,             // Number of handles
        const HANDLE *pHandles,     // Array of handles
        DWORD   dwMilliseconds,     // Timeout interval
        DWORD   dwWakeMask,         // Types of messages to wait for
        DWORD   dwFlags             // Wait flags (MWMO_ALERTABLE, etc.)
    
        /*
        Key Parameters:
        - dwWakeMask: What messages to wake for (e.g., QS_ALLINPUT for any message)
        - dwFlags: MWMO_ALERTABLE enables APC processing
        */
    );

When a thread calls one of these functions, it effectively says to the OS: "I'm going to sleep for a while. If anyone leaves a note (queues an APC) for me, wake me up so I can handle it immediately."


APC Injection Attack Lifecycle

APC injection follows a systematic sequence of operations to execute unauthorized code within a target process. The complete attack flow comprises five distinct phases:

1) Process and Thread Handle Acquisition

The attacker first gains access to the target environment by Opening a handle to the victim process using OpenProcess() with privileges including PROCESS_VM_OPERATION for memory allocation and PROCESS_VM_WRITE for writing shellcode.

2) Allocate memory in the target address space

Memory is reserved inside the victim’s address space using VirtualAllocEx with PAGE_EXECUTE_READWRITE permissions to host the payload. This creates a writable and executable region for hosting malicious payload and the allocation size matches the shellcode footprint to minimize detection.

3) Write payload into the target

The payload bytes are written into the allocated region using WriteProcessMemory, and the shellcode becomes accessible within the target process's context. Now the payload is ready to run but is still inactive.

4) APC Queueing and Targeting

An APC is queued to a target thread. It will execute only when that thread enters an alertable wait (EX: SleepEx/WaitForSingleObjectEx with alertable enabled).

5) Trigger or await an alertable state and execute

Now, when the target thread enters to an alertable wait for example, via SleepEx, WaitForSingleObjectEx, SignalObjectAndWait, or other alertable mechanisms), the OS dispatches queued APCs and the callback at the staged address executes in the context of that thread.


Why Attackers Abuse APC Injection for Payload Delivery


Attackers use APC-queue techniques because they allow them to run code in another process and they provide a low-noise, high-stealth way. These methods are quiet and hard to notice because they don’t immediately create clear signs like new threads or files on the disk. APCs let adversaries leverage legitimate OS callback mechanisms, like I/O completion routines or timers, to hide their execution inside regular threads. This helps them avoid simple security tools that check for unusual behavior or track the origin of code. APC injection also gives attackers better access and memory permissions, since it runs with the same rights and memory as the target process. There are different ways to use APCs, some of which are more reliable and others that focus more on staying stealth. These advantages explain why attackers often use APC-based methods in advanced malware and targeted attacks.


Stealth/low API noise:

APCs avoid creating a visible remote thread, there is no CreateRemoteThread signature, so the common heuristic “remote thread creation = injection” is bypassed. Instead execution happens inside an existing thread’s context, which is much less conspicuous in many telemetry pipelines.

Living-off-the-land (LotL) and legitimacy blending:

APCs are legitimate OS primitive used by applications for I/O completions and timers such as ReadFileEx, SetWaitableTimer etc. Attackers piggyback on these legitimate mechanisms so behavior can appear normal.

Fileless / memory-resident capabilities (reduced disk artifacts):

Staging payloads into process memory and executing them via APCs minimizes on-disk footprints, there is no new dropped EXE or DLL required, hindering signature-based AV and forensic triage that focuses on files.
Tradeoff: staging leaves memory-write and allocation telemetry that defenders can detect and correlate.

Reduced immediate forensic linkage:

Because the code runs under an existing process and thread stack, initial triage may attribute malicious behavior to the host process rather than an external actor, complicating attribution and response.