Original text by Malforge Group
This article provides a detailed examination of the EarlyBird APC Injection technique, a sophisticated method for executing arbitrary code within the context of a trusted process. By understanding its mechanics, defenders and security researchers can better analyze and detect this stealthy form of process injection.
https://github.com/Malforge-Maldev-Public-Organization/EarlyBird-APC-Code-Injection
——————————————-

1. Introduction to Process Injection and Asynchronous Procedure Calls (APCs)
Process injection is a broad category of techniques used to run custom code within the address space of another, often legitimate, process. This allows the injected code to inherit the target process’s permissions and evade simple detection mechanisms. One such method leverages the Windows Asynchronous Procedure Call (APC) mechanism.
What is an APC?
An APC is a function that executes asynchronously in the context of a specific thread. When a thread enters an alertable state, the kernel checks its APC queue and executes any pending APCs.
- How Threads Enter Alertable States: A thread becomes alertable by calling specific Windows API functions with the appropriate flags. Common examples include
SleepEx(),WaitForSingleObjectEx(),WaitForMultipleObjectsEx(), andSignalObjectAndWait(). - Kernel-Mode vs. User-Mode APCs: APCs can be queued from kernel-mode (e.g., for I/O completion) or user-mode. The
QueueUserAPC()function is the primary user-mode API for this purpose.
Traditional APC injection targets an existing, running process. An attacker finds a thread in that process and queues an APC to it. The challenge is that the target thread might not enter an alertable state for an unknown or unpredictable amount of time.
2. The EarlyBird Technique: Gaining Control from the Start
The EarlyBird technique elegantly solves this timing problem. Instead of relying on the unpredictable state of an existing thread, it creates a new process with its primary thread in a suspended state. This gives the attacker full control to prepare the injection before execution begins.
The core innovation of EarlyBird is the precise orchestration of process creation and thread state to guarantee execution. By intervening in the window between a thread’s creation and its first opportunity to run, the attacker ensures the malicious APC is the first thing the thread processes when resumed.
3. Step-by-Step Technical Breakdown
Here is a detailed analysis of the five stages of the EarlyBird APC Injection technique, as demonstrated in the provided code example.
Stage 1: Process Creation in a Suspended State
The first step is to create a sacrificial process—a legitimate Windows executable that will host the malicious code.
- API Call:
CreateProcessA() - Key Parameter:
dwCreationFlagsis set toCREATE_SUSPENDED(0x00000004) . - Mechanism: This flag instructs the Windows loader to create the process, load its primary executable and necessary DLLs into memory, and form the primary thread. However, the thread’s execution is halted immediately. The system initializes the thread’s context but does not schedule it for execution on a CPU core. The handle to this suspended thread is returned in the
PROCESS_INFORMATIONstructure (specifically,pi.hThread).
Stage 2: Memory Allocation in the Target Process
With a handle to the suspended process (pi.hProcess), memory must be allocated to store the payload (shellcode).
- API Call:
VirtualAllocEx() - Key Parameters:
hProcess: Handle to the target process (the suspendednotepad.exe).flAllocationType:MEM_COMMIT(0x00001000) to reserve and commit physical storage in memory.flProtect:PAGE_EXECUTE_READ(0x20). This is critical. It allocates memory that is both readable and executable. WhilePAGE_EXECUTE_READWRITEis also possible, it is a stronger indicator of malicious activity.
- Result: A region of memory is set aside within the address space of the target process, with permissions allowing the code to be read and executed.
Stage 3: Writing the Shellcode
The payload is then written into the newly allocated memory region.
- API Call:
WriteProcessMemory() - Mechanism: This function writes data from the current process’s memory (
payload) directly into the address space of the target process at the location specified bypRemoteCode. The target process remains suspended throughout this operation.
Stage 4: Queuing the Asynchronous Procedure Call (APC)
This is the crucial step that links the shellcode to the suspended thread.
- API Call:
QueueUserAPC() - Key Parameters:
pfnAPC: A pointer to the function to be called. Here, thepRemoteCodeaddress (where the shellcode resides) is typecast to aPAPCFUNCfunction pointer. When the APC is executed, the thread will begin executing code from this address.hThread: A handle to the suspended thread (pi.hThread).
- Mechanism: The APC routine (pointing to the shellcode) is placed at the end of the specified thread’s APC queue. Because the thread is suspended, it is not in an alertable state, so the APC remains pending in the kernel.
Stage 5: Resuming the Thread to Trigger Execution
With the shellcode written and the APC queued, the final step is to let the thread run.
- API Call:
ResumeThread() - Mechanism: This function decrements the thread’s suspend count. When the count reaches zero, the thread becomes schedulable. The Windows scheduler will eventually give it CPU time.
- Execution Flow: As part of its initialization and return from the suspended state, the thread checks its APC queue. It finds the queued APC and, because it’s entering a state that can process user-mode APCs, executes it. The thread’s instruction pointer is set to the shellcode’s starting address, and the payload runs within the context of the legitimate
notepad.exeprocess.
4. Code Walkthrough and API Analysis
The provided C code is a minimalist but fully functional implementation of the technique. Let’s analyze it line by line within its context.
int main(void) {
int pid = 0; // Unused variable, can be removed.
HANDLE hProc = NULL; // Unused variable, can be removed.
STARTUPINFO si;
PROCESS_INFORMATION pi;
void *pRemoteCode; // Will hold the address of allocated memory in the target.
// Initialize the STARTUPINFO structure to prevent undefined behavior.
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si); // Must set the structure size.
ZeroMemory(&pi, sizeof(pi));
// 1. Create the target process in a suspended state.
CreateProcessA(0, "notepad.exe", 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi);
// 2. Allocate memory in the remote process for the payload.
// Payload and payload_len are external variables (e.g., generated by msfvenom).
pRemoteCode = VirtualAllocEx(pi.hProcess, NULL, payload_len, MEM_COMMIT, PAGE_EXECUTE_READ);
// 3. Write the shellcode into the allocated memory.
WriteProcessMemory(pi.hProcess, pRemoteCode, (PVOID) payload, (SIZE_T) payload_len, (SIZE_T *) NULL);
// 4. Queue the shellcode as an APC to the suspended thread.
// The shellcode address is cast to a function pointer type.
QueueUserAPC((PAPCFUNC)pRemoteCode, pi.hThread, NULL);
// 5. Resume the thread. The thread will execute the queued APC immediately.
ResumeThread(pi.hThread);
// In a real-world scenario, handles should be closed with CloseHandle().
return 0;
}
Critical Observations for Malware Analysts and Red Teamers:
- Error Handling: Production code must include robust error checking after each API call (e.g.,
if (hProcess == NULL) { ... }). - Cleanup: The code lacks calls to
CloseHandle()forpi.hProcessandpi.hThread, which would cause a resource leak. A well-written implant would include this. - Memory Permissions: Allocating memory with
PAGE_EXECUTE_READis less suspicious thanPAGE_EXECUTE_READWRITE, but the subsequentWriteProcessMemoryinto an executable region is a classic and heavily monitored pattern.
5. Why EarlyBird is Effective and Stealthy
The EarlyBird technique offers several advantages that make it a favored tool for adversaries and a critical subject for defenders.
- Predictable Execution: Guarantees execution as soon as the thread resumes, unlike targeting an existing thread that may never become alertable.
- Bypasses Hooks: By creating a fresh process, the injected code often runs before security products have a chance to install user-mode hooks within that process, allowing it to execute undetected.
- Lives off the Land: The technique primarily uses legitimate, signed Windows APIs (
kernel32.dllfunctions), making it harder to distinguish from normal system operations. - Code Integrity: The host process (e.g.,
notepad.exe) remains on disk unchanged. Only its in-memory instance is modified, which complicates file-based scanning.

