Saturday 29 July 2017

Asynchronous Procedure Call Shellcode


1. Introduction
I have recently got into windows security and have been researching on this subject for last few days and during this time number of interesting facts(at least for me) have appeared. I have found many posts on the internet on this topic that includes game hacking forums, code project, etc. but the information is either outdated or the solution given is not clear. I am sure there would be many other and better ways to so - feel free to post your ideas below :)

The POC link is available at the bottom of the post.  

2. Theory

Ok, let's start with some basics. DLL injection has both the good side and the bad side. It is used to run code in the context of another process and as a result, the process executes the code. It can be used to patch the legitimate applications or can be used in games to get unlimited armor. One of the common techniques includes calling CreateRemoteThread to run the code as a separate process. But, this can be detected by Sysmon from Sysinternals. What this tool does is that it provides detailed information about process creations, network connections, and changes to file creation time which can be viewed from the Windows event viewer. 
There is a great post on this topic here. Instead of repeating the whole post I would summarize the points. So if you use CreateRemoteThread for Dll injection then it would raise a flag in sysmon as Event ID 8 which would tell you the process name as well as the base address of the shellcode that was injected into the process. If you haven't read the post, I strongly encourage you to read it first.

3. User-mode Asynchronous Procedure Call
APCs can be seen as POSIX signals in Linux, delivering information to POSIX processes. There are two kinds of APCs queues started with every thread: user mode and kernel mode. User mode APCs require permission from the current thread context to run whereas "special" kernel mode APCs do not require permission. 

Let's have a look at the definition of the QueueUserAPC function.


DWORD WINAPI QueueUserAPC(
_In_ PAPCFUNC pfnAPC,
_In_ HANDLE hThread,
_In_ ULONG_PTR dwData
);
The first parameter is a pointer to the callback function we want the process to execute. The handle to the thread defines the second parameter (which can be obtained by calling OpenThread()) and the third parameter can be set to NULL.

An example of how it would work in practice follows:
  1. Create a process in suspended mode.
  2. Allocate memory in remote process with flag PAGE_EXECUTE_READWRITE
  3. Write the shellcode to the memory allocated
  4. Open the thread with at least THREAD_SET_CONTEXT access rights.
  5. Use QueueUserAPC to point to callback function
  6. And at last call ResumeThread() to start executing the shellcode in the separate thread.  
So, the real work is done here by the QueueUserAPC() function. Let's have a look at the working of the function in a bit more detail. 

MSDN states that every thread has its own APC queue and operating system must use a software interrupt to redirect the execution of the thread to call the APC function. This can be seen just before we execute our calc.exe shellcode. 


 We can see here the next instruction that is going to be executed is sysenter.


calc.exe pops up

 The APC function would not be called unless it is in an alertable state.

4. Alertable State

When the thread enters an alertable state, the following events occur:
  1. The kernel checks the thread's APC queue. If the queue contains callback function pointers, the kernel removes the pointer from the queue and sends it to the thread.
  2. The thread executes the callback function.
  3. Steps 1 and 2 are repeated for each pointer remaining in the queue.
  4. When the queue is empty, the thread returns from the function that placed it in an alertable state.

5. Conclusion

No Event ID 8 log in sysmon :)
   
Proof of Concept code can now be downloaded from here

Thanks.

No comments:

Post a Comment