Table of Contents
- In our previous blog post, we looked into classic process injection, going into the various techniques such as finding target processes to inject to using Windows APIs, as well as injecting into the said processes.
- There are several methods to perform process injection, we will dive into APC Injection, a more advanced technique, that offers more advantages to the standard method.
- This method is harder to detect than the standard process injection. Despite implementing some common APIs used in malware development such as
VirtualAllocEx, WriteProcessMemory and OpenProcess, the major difference is in how shellcode is executed.
- Traditional process injection executes shellcode using
CreateRemoteThread. This API is overtly suspicious and will get flagged by the AV. APC injection uses an API called
QueueUserAPC, which is less suspicious since it is used in normal OS operations such as scheduling work for a thread when it becomes idle. -Let’s dive into what some of these technical terms mean.
Program Execution in Modern Operating Systems #
- When programs are executed in Windows, the operating system allocates necessary resources to the program to start the execution.
- During the execution, multiple threads are usually assigned to a program. A
threadin this case represents a sequence of instructions in the program that can be scheduled by the OS to run. These threads could be performing tasks such as accessing OS resources.
- If a program needs to perform I/O operations such as reading data from files, it uses
synchronous calls, which halts the execution of the thread to allow the I/O operation to take place.
- To address this inefficiency issue, modern Operating Systems will provide support for
asynchronous calls. This allows the thread to continue execution after handing over the I/O operation to the OS.
Asynchronous Procedure Calls #
- When an asynchronous I/O operation is completed, the operating system can
queue an APC associated with that I/O operation.
- The APC can contain some code or a function that is
executed in response to the completion of the I/O event. This requires a thread to be in an
alertable state, which is when a thread is idle and ready to receive Asynchronous Procedure Calls. This allows the OS to deliver the APC to the thread hence executing the code.
- In our case, we will be creating an APC routine that points to our shellcode so that when the APC fires, our shellcode executes!
- This Windows API allows an application to queue an APC to a specified thread. Its implementation is as follows:
pfnAPC: This is a pointer to the function that you want to be executed asynchronously. This function will be invoked when the thread is in a
state where it can process APCs(also known as an
hThread: This is a
handleto the thread to which you want to queue the APC. A handle can be thought of as a unique identifier used to interact with a resource, in our case this will be a specific thread.
dwData: This is the data that you want to
pass to the APC function. It’s a single number (an integer, technically a
ULONG_PTR) that can be used for whatever you want. It is up to you to decide what you want for this value (error code, status code, commands)
Implementing the QueueUserAPC() API #
- I will demonstrate a simple example of how we queue an APC in C++. We will create a thread and execute it. After its execution, we will then queue our APC and see how it gets executed.
- Starting with an empty C++ project. Import the Windows.h header and create a simple thread as shown.
ThreadProcis a callback function that will be executed by CreateThread() as a thread.
- We can use print statements to show that the thread is being executed.
- We use
wprintfto print out our messages. It is similar to
printfbut is used to print wide-character strings. It takes in wide character literals indicated by the prefix
GetLastErroris used to grab the last error code value
CreateThreadis called, it may return before ThreadProc finishes executing. That is why we use the
sleep functioninside the main() function to allow ThreadProc to finish executing.
- We finally queue in our APC using the
QueueUserAPCfunction. Remember that we mentioned we can only queue in threads that have been put in an
alertable state. So how do you do this?
- msdn docs describes this as shown in the screenshot below.
- TLDR; We can use the
SleepExfunction to make our thread
alertable before queueing in the APC. We adjust our code by adding the API and a callback function that will be queued in after the thread has been executed.
- The callback function has a parameter called
Parameter, which contains the data that is passed in the
dwDataparameter in the QueueUserAPC() function, which was
123. We can print it out as well to confirm that as shown in line 22
- It executes as shown:
Implementing QueueUserAPC() in our implant. #
High-Level Overview #
- We now have some understanding of how QueueUserAPC works. A high-level breakdown of how the implementation works is as follows.
- Create the target process in a suspended state.
- Allocate memory using
VirtualAllocExin the suspended process.
- Define the
APC callback routine, it is going to point to our shellcode
- Write shellcode into the allocated memory within the target process using
- Queue the APC to the main thread using
- Once the thread is resumed the shellcode is executed.
- In line 38, we define our APC routine using
PTHREAD_START_ROUTINEwhich declares the APC callback as a pointer to a variable. In this case, our variable would be the
shellcodedefined in line 6.
- In lines 45 & 46 we define 2 structures necessary in implementation of the
CreateProcessAAPI. We use these structures to access information about the process that we are creating. You can see this in line 52 where we obtain the process id and thread id of notepad.
Final demo #
- Using processhacker, we can further verify our process spawned in the context of notepad as shown.
- I hope you enjoyed diving deep into how this technique works. You can play around with the code and see if you can implement some form of encryption for the shellcode. Happy hacking !!
Project Files #
- You can find the project files for process injection here