An Asynchronous Procedure Call is basically a function/code that is set to execute (asynchronously) within the context of a specified thread. Said functions (callbacks) are added to the APC Queue of a particular thread - which will then be executed in First in First Out order once the thread enters an alertable state. Every running thread has its own APC Queue, APCs can be added to this queue via the QueueUserAPC() WinAPI call. Get additional info from the experts here: https://docs.microsoft.com/en-us/windows/desktop/sync/asynchronous-procedure-calls
Malware authors can abuse APCs to get code to execute evasively. One particular APC injection technique is 'Early Bird Injection'. This technique involves creating the target process in a suspended state, injecting code into the suspended process, adding an APC (pointing to the injected code) to the target process, and finally resuming the suspended thread - allowing the malicious APC to execute. This technique allows our code to execute early in the process creation routine, specifically when ntdll.dll is loaded and performing some housekeeping. My guess is that AV will be less likely to pay attention to code executed in this phase of process creation. This kind of injection also does the job without a remote thread, which is an anomaly that many AVs and EDRs rely on to catch code injection. I created an innocuous piece of malware that utilizes this technique to inject a piece of messagebox shellcode into calc.exe to better understand the technique.
First, we prepare a couple data structures for CreateProcess(), define our shellcode (char array), and declare our APC callback.
Next, we spawn our target in a suspended state (CreateProcess), allocate memory for our shellcode (VirtualAllocEx), and inject it into the newly allocated memory within calc.exe (WriteProcessMemory).
Finally, we add our shellcode to the APC Queue of calc.exe (QueueUserAPC), and resume the suspended thread (ResumeThread), allowing our APC to be executed.
Now lets reverse this subroutine in x32dbg to better understand this technique.
Whenever we see a call to CreateProcess, two important parameters we want to pay attention to are the first (executable to be invoked), and sixth (process creation flags). The creation flag of value 0x4 is the numeric representation of the symbolic constant for CREATE_SUSPENDED. Now to the call to VirtualAllocEx - first, there is a very important difference between VirtualAlloc and VirtualAllocEx. The former will allocate memory in the calling process, the latter will allocate memory in a remote process. So if we see malware call VirtualAllocEx, there more than likely will be some kind of cross process activity about to commence. The fifth parameter passed to VirtualAllocEx is the Memory Protection for the newly allocated memory region. A numeric constant of 0x40 represents PAGE_EXECUTE_READWRITE - meaning that this memory is readable, writable, and executable (anomalous!).
If successful, VirtualAllocEx will return the address of the newly allocated memory region (return values will be stored in EAX), if it fails it will return 0. This address will be move from EAX into a local variable (EBP - *) and used again throughout this subroutine. As expected, the new region of memory is tagged with RWX for PAGE_EXECUTE_READWRITE, and we can see in the hex dump that the memory is properly allocated/initialized.
Next, we have a call to WriteProcessMemory, which is performing the injection of our shellcode.
Let's take a look at the arguments passed to WriteProcessMemory. The first argument is a handle to the process to be injected into.
If we pop open Process Hacker, we can take a look at the handles window and see that 0xCC corresponds to calc.exe.
The second argument is the base address of where to inject the code within calc.exe. Notice that this is the address returned from VirtualAllocEx. The third argument is a pointer to the buffer containing the code to be injected. If we follow that address in the dump window, we can see our shellcode.
The fourth argument is simply the size of the buffer to be injected, or our variable sc_len. Here is the new memory region following the injection.
Next, we add our shellcode/callback function to the APC Queue of calc.exe. Here is the stack/arguments passed to QueueUserAPC.
The first argument is a pointer to the APC function, which is where our shellcode is sitting. The second argument is a handle to the thread for which the APC function to be added. Process Hacker can show us that this is the handle to calc.exe's main thread (0xC8).
Finally, ResumeThread is called - which takes a single argument, a handle to the thread to be resumed. And the handle (0xC8) corresponds to the same thread in calc.exe.
If we take allow this instruction to execute, we will get our message box.
As far as detecting this attack, the lack of a remote thread makes this technique a bit more evasive. However, the memory protections associated with code injection are leveraged, so that is one anomaly that may be used as a detection. What I have not explored, is the possibility of using processes spawned SUSPENDED as a detection. Or even more specifically, a non-native process spawning a native executable in a suspended state. Or a non-native process spawning an instance of itself in a suspended state (typical of Process Hollowing/Injection). If a DLL is injected and added to the APC Queue, having a DLL in memory not mapped to a file on disk is another anomaly. I hope this post spreads awareness to the blue teamers of this interesting technique, and adds a weapon to the red teamers arsenal to push the blue team for better security! Happy hacking/hunting!
Early Bird Demo source code: https://raw.githubusercontent.com/rnranalysis/payloads/master/EarlyBirdDemo.cpp
Malware authors can abuse APCs to get code to execute evasively. One particular APC injection technique is 'Early Bird Injection'. This technique involves creating the target process in a suspended state, injecting code into the suspended process, adding an APC (pointing to the injected code) to the target process, and finally resuming the suspended thread - allowing the malicious APC to execute. This technique allows our code to execute early in the process creation routine, specifically when ntdll.dll is loaded and performing some housekeeping. My guess is that AV will be less likely to pay attention to code executed in this phase of process creation. This kind of injection also does the job without a remote thread, which is an anomaly that many AVs and EDRs rely on to catch code injection. I created an innocuous piece of malware that utilizes this technique to inject a piece of messagebox shellcode into calc.exe to better understand the technique.
First, we prepare a couple data structures for CreateProcess(), define our shellcode (char array), and declare our APC callback.
Next, we spawn our target in a suspended state (CreateProcess), allocate memory for our shellcode (VirtualAllocEx), and inject it into the newly allocated memory within calc.exe (WriteProcessMemory).
Finally, we add our shellcode to the APC Queue of calc.exe (QueueUserAPC), and resume the suspended thread (ResumeThread), allowing our APC to be executed.
Now lets reverse this subroutine in x32dbg to better understand this technique.
Whenever we see a call to CreateProcess, two important parameters we want to pay attention to are the first (executable to be invoked), and sixth (process creation flags). The creation flag of value 0x4 is the numeric representation of the symbolic constant for CREATE_SUSPENDED. Now to the call to VirtualAllocEx - first, there is a very important difference between VirtualAlloc and VirtualAllocEx. The former will allocate memory in the calling process, the latter will allocate memory in a remote process. So if we see malware call VirtualAllocEx, there more than likely will be some kind of cross process activity about to commence. The fifth parameter passed to VirtualAllocEx is the Memory Protection for the newly allocated memory region. A numeric constant of 0x40 represents PAGE_EXECUTE_READWRITE - meaning that this memory is readable, writable, and executable (anomalous!).
If successful, VirtualAllocEx will return the address of the newly allocated memory region (return values will be stored in EAX), if it fails it will return 0. This address will be move from EAX into a local variable (EBP - *) and used again throughout this subroutine. As expected, the new region of memory is tagged with RWX for PAGE_EXECUTE_READWRITE, and we can see in the hex dump that the memory is properly allocated/initialized.
Next, we have a call to WriteProcessMemory, which is performing the injection of our shellcode.
Let's take a look at the arguments passed to WriteProcessMemory. The first argument is a handle to the process to be injected into.
If we pop open Process Hacker, we can take a look at the handles window and see that 0xCC corresponds to calc.exe.
The second argument is the base address of where to inject the code within calc.exe. Notice that this is the address returned from VirtualAllocEx. The third argument is a pointer to the buffer containing the code to be injected. If we follow that address in the dump window, we can see our shellcode.
The fourth argument is simply the size of the buffer to be injected, or our variable sc_len. Here is the new memory region following the injection.
Next, we add our shellcode/callback function to the APC Queue of calc.exe. Here is the stack/arguments passed to QueueUserAPC.
The first argument is a pointer to the APC function, which is where our shellcode is sitting. The second argument is a handle to the thread for which the APC function to be added. Process Hacker can show us that this is the handle to calc.exe's main thread (0xC8).
Finally, ResumeThread is called - which takes a single argument, a handle to the thread to be resumed. And the handle (0xC8) corresponds to the same thread in calc.exe.
If we take allow this instruction to execute, we will get our message box.
As far as detecting this attack, the lack of a remote thread makes this technique a bit more evasive. However, the memory protections associated with code injection are leveraged, so that is one anomaly that may be used as a detection. What I have not explored, is the possibility of using processes spawned SUSPENDED as a detection. Or even more specifically, a non-native process spawning a native executable in a suspended state. Or a non-native process spawning an instance of itself in a suspended state (typical of Process Hollowing/Injection). If a DLL is injected and added to the APC Queue, having a DLL in memory not mapped to a file on disk is another anomaly. I hope this post spreads awareness to the blue teamers of this interesting technique, and adds a weapon to the red teamers arsenal to push the blue team for better security! Happy hacking/hunting!
Early Bird Demo source code: https://raw.githubusercontent.com/rnranalysis/payloads/master/EarlyBirdDemo.cpp
James wow! What a different way to see code injection. Thanks for sharing.
ReplyDeleteWhen I was preparing from Pass4sure Checkpoint dumps, my thorough focus was on my study and I followed all the guidelines given by experts. I have passed my exam now and I did it at the first attempt. I say thanks to Dumpspass4sure for putting their efforts for the creation of Checkpoint PDF questions and answers.
ReplyDelete
ReplyDeleteWelcome to the world of develop for MSK clinic.Best services provider in clinic. We provide genuine likes, followers and views to your Ultrasound guided injection.
Regards
Ultrasound guided injection
I am reading your post from the beginning, it was so interesting to read & I feel thanks to you for posting such a good blog, keep updates regularly.
ReplyDeleteRegards,
Ultrasound guided Injection in Maidstone