APC Injection

Earlier in this chapter, you saw that by creating a thread using CreateRemoteThread, you can invoke functionality in a remote process. However, thread creation requires overhead, so it would be more efficient to invoke a function on an existing thread. This capability exists in Windows as the asynchronous procedure call (APC).

APCs can direct a thread to execute some other code prior to executing its regular execution path. Every thread has a queue of APCs attached to it, and these are processed when the thread is in an alertable state, such as when they call functions like WaitForSingleObjectEx, WaitForMultipleObjectsEx, and SleepEx. These functions essentially give the thread a chance to process the waiting APCs.

If an application queues an APC while the thread is alertable but before the thread begins running, the thread begins by calling the APC function. A thread calls the APC functions one by one for all APCs in its APC queue. When the APC queue is complete, the thread continues running along its regular execution path. Malware authors use APCs to preempt threads in an alertable state in order to get immediate execution for their code.

APCs come in two forms:

  • An APC generated for the system or a driver is called a kernel-mode APC.

  • An APC generated for an application is called a user-mode APC.

Malware generates user-mode APCs from both kernel and user space using APC injection. Let’s take a closer look at each of these methods.

APC Injection from User Space

From user space, another thread can queue a function to be invoked in a remote thread, using the API function QueueUserAPC. Because a thread must be in an alertable state in order to run a user-mode APC, malware will look to target threads in processes that are likely to go into that state. Luckily for the malware analyst, WaitForSingleObjectEx is the most common call in the Windows API, and there are usually many threads in the alertable state.

Let’s examine the QueueUserAPC’s parameters: pfnAPC, hThread, and dwData. A call to QueueUserAPC is a request for the thread whose handle is hThread to run the function defined by pfnAPC with the parameter dwData. Example 12-5 shows how malware can use QueueUserAPC to force a DLL to be loaded in the context of another process, although before we arrive at this code, the malware has already picked a target thread.

Note

During analysis, you can find thread-targeting code by looking for API calls such as CreateToolhelp32Snapshot, Process32First, and Process32Next for the malware to find the target process. These API calls will often be followed by calls to Thread32First and Thread32Next, which will be in a loop looking to target a thread contained in the target process. Alternatively, malware can also use Nt/ZwQuerySystemInformation with the SYSTEM_PROCESS_INFORMATION information class to find the target process.

Example 12-5. APC injection from a user-mode application

00401DA9         push    [esp+4+dwThreadId]      ; dwThreadId
00401DAD         push    0                       ; bInheritHandle
00401DAF         push    10h                     ; dwDesiredAccess
00401DB1         call    ds:OpenThread 
00401DB7         mov     esi, eax
00401DB9         test    esi, esi
00401DBB         jz      short loc_401DCE
00401DBD         push    [esp+4+dwData]          ; dwData = dbnet.dll
00401DC1         push    esi                     ; hThread
00401DC2         push    ds:LoadLibraryA       ; pfnAPC
00401DC8         call    ds:QueueUserAPC

Once a target-thread identifier is obtained, the malware uses it to open a handle to the thread, as seen at . In this example, the malware is looking to force the thread to load a DLL in the remote process, so you see a call to QueueUserAPC with the pfnAPC set to LoadLibraryA at . The parameter to be sent to LoadLibraryA will be contained in dwData (in this example, that was set to the DLL dbnet.dll earlier in the code). Once this APC is queued and the thread goes into an alertable state, LoadLibraryA will be called by the remote thread, causing the target process to load dbnet.dll.

In this example, the malware targeted svchost.exe, which is a popular target for APC injection because its threads are often in an alertable state. Malware may APC-inject into every thread of svchost.exe just to ensure that execution occurs quickly.

APC Injection from Kernel Space

Malware drivers and rootkits often wish to execute code in user space, but there is no easy way for them to do it. One method they use is to perform APC injection from kernel space to get their code execution in user space. A malicious driver can build an APC and dispatch a thread to execute it in a user-mode process (most often svchost.exe). APCs of this type often consist of shellcode.

Device drivers leverage two major functions in order to utilize APCs: KeInitializeApc and KeInsertQueueApc. Example 12-6 shows an example of these functions in use in a rootkit.

Example 12-6. User-mode APC injection from kernel space

000119BD         push    ebx
000119BE         push    1 
000119C0         push    [ebp+arg_4] 
000119C3         push    ebx
000119C4         push    offset sub_11964
000119C9         push    2
000119CB         push    [ebp+arg_0] 
000119CE         push    esi
000119CF         call    ds:KeInitializeApc
000119D5         cmp     edi, ebx
000119D7         jz      short loc_119EA
000119D9         push    ebx
000119DA         push    [ebp+arg_C]
000119DD         push    [ebp+arg_8]
000119E0         push    esi
000119E1         call    edi       ;KeInsertQueueApc

The APC first must be initialized with a call to KeInitializeApc. If the sixth parameter (NormalRoutine) is non-zero in combination with the seventh parameter (ApcMode) being set to 1, then we are looking at a user-mode type. Therefore, focusing on these two parameters can tell you if the rootkit is using APC injection to run code in user space.

KeInitializeAPC initializes a KAPC structure, which must be passed to KeInsertQueueApc to place the APC object in the target thread’s corresponding APC queue. In Example 12-6, ESI will contain the KAPC structure. Once KeInsertQueueApc is successful, the APC will be queued to run.

In this example, the malware targeted svchost.exe, but to make that determination, we would need to trace back the second-to-last parameter pushed on the stack to KeInitializeApc. This parameter contains the thread that will be injected. In this case, it is contained in arg_0, as seen at . Therefore, we would need to look back in the code to check how arg_0 was set in order to see that svchost.exe’s threads were targeted.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.137.59