This is the third part of the DLL Injection series. For your convenience you can find other parts in the table of contents in Part 1 – Registry
We already know how to inject a DLL into process using registry or hooks. With the former approach we cannot control the moment of injection (because it usually happens when the process is starting) whilst with the latter approach we cannot choose the target process (since our library is called to every process). Today we will see yet another method. This time we will be able to precisely choose the target process and the time of injection.
Loading library
In order to load library during runtime we can call LoadLibrary
or LoadLibraryEx
method and pass it a path to the library. What if we could do the same but in another process? Fortunately, Windows gives us an option to execute code in other process using CreateRemoteThread function. Basically, we are able to start a thread in other process.
Every thread requires a thread function: a piece of code which newly spawned thread will execute. This method can accept at most one parameter. How can we use this to inject DLL? Well, we can create remote thread and point its thread function to LoadLibrary
in target process. However, we need to be able to pass a path to the library as a parameter. We cannot simply allocate memory using malloc
because this method allocates memory in our process.
There is a method which is able to allocate memory in other process: VirtualAllocEx. We can use it to allocate some bytes in target address space and write there a DLL path using WriteProcessMemory function. This should work good.
So the plan looks as follows: we allocate memory in the target process and fill it with the path to the DLL. Next, we get the address of LoadLibrary
function and use it as a thread function for thread created in remote process. Let’s see some code.
Code
The code should be pretty obvious:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
BOOL ok = FALSE; // Assume that the function fails HANDLE process = NULL, thread = NULL; PWSTR memory = NULL; __try { // Get a handle for the target process process = OpenProcess( PROCESS_QUERY_INFORMATION | // Required by Alpha PROCESS_CREATE_THREAD | // For CreateRemoteThread PROCESS_VM_OPERATION | // For VirtualAllocEx PROCESS_VM_WRITE, // For WriteProcessMemory FALSE, targetProcessId); if (!process) __leave; // Calculate the number of bytes required to store DLL path int numberOfCharacters = 1 + lstrlenW("Path to DLL"); int numberOfBytes = numberOfCharacters * sizeof(wchar_t); // Allocate memory memory = (PWSTR) VirtualAllocEx(process, NULL, numberOfBytes, MEM_COMMIT, PAGE_READWRITE); if (!memory) __leave; // Write path to DLL if (!WriteProcessMemory(process, memory, (PVOID) pszLibFile, numberOfBytes, NULL)) __leave; // Get the address of LoadLibraryW in Kernell32.dll PTHREAD_START_ROUTINE loadLibraryFunction = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW"); if (!loadLibraryFunction) __leave; // Create a remote thread thread = CreateRemoteThread(process, NULL, 0, loadLibraryFunction, memory, 0, NULL); if (!thread) __leave; // Wait for the remote thread to terminate WaitForSingleObject(thread, INFINITE); // We are done ok = true; } __finally { // Cleanup if (memory) VirtualFreeEx(process, memory, 0, MEM_RELEASE); if (thread) CloseHandle(thread); if (process) CloseHandle(process); } return ok; |
Summary
We are now able to inject DLL into the process we choose and when we decide. Next time we will use this method to inject managed (.NET) DLL.