Ring3 Circus
December 5th, 2007, 16:45
This isn’t exactly news, but I thought I’d briefly run through the now standard method of injecting a DLL of your choice into an arbitrary process under 32-bit Windows. It will serve as a foundation for the upcoming post on function hooking via DLL injection.
So you have analysed a target program, know how it works and what you’d like to make it do, but there isn’t enough room for an inline patch. Or maybe you need to hook an existing function in order to spy on or modify data being passed back and forth. Perhaps add some functionality or a status window? If any of this sounds familiar then you should consider DLL-based code-injection. It is the cleanest method of injection (if there ever were such a thing) and it offers a lot of scalability and maintainability that a quick-and-dirty inline/diversion patch lacks.
The idea is simple, but very clever. We abuse CreateRemoteThread to call LoadLibrary on a string that was injected by means of WriteProcessMemory. The method hinges on the fact that LoadLibrary takes a single parameter. In eight steps:
http://www.ring3circus.com/rce/dll-injection-via-createremotethread/
So you have analysed a target program, know how it works and what you’d like to make it do, but there isn’t enough room for an inline patch. Or maybe you need to hook an existing function in order to spy on or modify data being passed back and forth. Perhaps add some functionality or a status window? If any of this sounds familiar then you should consider DLL-based code-injection. It is the cleanest method of injection (if there ever were such a thing) and it offers a lot of scalability and maintainability that a quick-and-dirty inline/diversion patch lacks.
The idea is simple, but very clever. We abuse CreateRemoteThread to call LoadLibrary on a string that was injected by means of WriteProcessMemory. The method hinges on the fact that LoadLibrary takes a single parameter. In eight steps:
This seems like quite a lot of work for such a common operation, so it’s a good idea to code it up in a reusable way. Here’s the implementation as pasted from the DLL-hooking interface I’m working on:
Locate the target process in terms of its process-id. Depending on the situation, any of EnumProcesses ("http://msdn2.microsoft.com/en-us/library/ms682629.aspx"), CreateProcess ("http://msdn2.microsoft.com/en-us/library/ms682425.aspx"), FindWindow ("http://msdn2.microsoft.com/en-us/library/ms633499.aspx") and GetWindowThreadProcessId ("http://www.woodmann.com/forum/admincp/GetWindowThreadProcessId") may come in handy.
Acquire a handle to it by calling OpenProcess ("http://msdn2.microsoft.com/en-us/library/ms684320.aspx"). This isn’t necessary in certain cases, such as when the target is spawned using CreateProcess, which returns a handle automatically.
Allocate some space in the target process for a string containing the path of the DLL, using VirtualAllocEx ("http://msdn2.microsoft.com/en-us/library/aa366890.aspx"). Remember to make enough room if using Unicode strings.
Fill the new buffer out with the path of the DLL by means of a call to WriteProcessMemory ("http://msdn2.microsoft.com/en-us/library/ms681674.aspx").
Call GetProcAddress ("http://msdn2.microsoft.com/en-us/library/ms683212.aspx") to get a pointer to LoadLibrary ("http://msdn2.microsoft.com/en-us/library/ms684175.aspx"). While there are no guarantees in the Win32 process specification that this function will be in the same place for every process, a little reverse-engineering and common-sense makes it a very safe bet. If the next XP Hotfix were to change the base of kernel32, user32 or ntdll - none of which are relocatable - then a considerable amount of both Microsoft and non-Microsoft code will break and all hell would break loose.
Call CreateRemoteThread ("http://msdn2.microsoft.com/en-us/library/ms682437.aspx") on the target process, passing the address of LoadLibrary as the LPTHREAD_START_ROUTINE and the address of the newly initialised string buffer as the parameter. Technically, CreateRemoteThread wasn’t designed to deal with this, but in terms of implementation everything is kosher.
When LoadLibrary returns, the thread terminates with the return-value as its exit code. So if we call GetExitCodeThread ("http://msdn2.microsoft.com/en-us/library/ms683190.aspx"), the result will be the base of the DLL in the target process, or NULL if the call failed.VirtualFreeEx ("http://msdn2.microsoft.com/en-us/library/aa366894.aspx") will take care of the now useless string buffer and the process handle should be freed if no more magic needs doing.
Code:
HMODULE DLLInjection::InjectDLL(DWORD process_id) {
// Open Process
process_handle = OpenProcess(PROCESS_ALL_ACCESS,
false,
process_id);
if (process_handle == 0) return NULL;
// Allocate space for string to contain the DLL Path
SIZE_T path_length = dll_path.size() + 1;
void* remote_buffer = VirtualAllocEx(
process_handle,
NULL,
path_length * sizeof(char),
MEM_COMMIT,
PAGE_READWRITE);
bool success = false;
if (remote_buffer != NULL) {
SIZE_T bytes_written = 0;
WriteProcessMemory(process_handle,
remote_buffer,
dll_path.c_str(),
path_length,
&bytes_written);
if (bytes_written == path_length) {
DWORD thread_id = 0;
HMODULE kernel32 = GetModuleHandleA("Kernel32";
LPTHREAD_START_ROUTINE remote_lla =
reinterpret_cast <LPTHREAD_START_ROUTINE> (GetProcAddress(kernel32, “LoadLibraryA”);
if (remote_lla != NULL) {
HANDLE thread = CreateRemoteThread(
process_handle,
NULL,
0,
remote_lla,
reinterpret_cast<void*>
(remote_buffer),
0,
&thread_id);
if (thread != NULL && thread_id != 0) {
WaitForSingleObject(thread, 5000);
DWORD exit_code = 0;
GetExitCodeThread(thread, &exit_code);
if (exit_code != 0) {
success = true;
remote_base = reinterpret_cast<void*> (exit_code);
}
}
}
}
VirtualFreeEx(process_handle,
remote_buffer,
dll_path.size() + 1,
MEM_RELEASE);
}
if (success) return reinterpret_cast<HMODULE> (remote_base);
return NULL; // Fail
}
http://www.ring3circus.com/rce/dll-injection-via-createremotethread/