September 23, 2011
DLL Injection via CreateRemoteThread
Well-known in the game cheating scene, DLL injection is used to execute arbitrary code in a remote process by forcing the process to load a DLL (Dynamic Link Library) into its virtual memory address space. Since most anti-game cheating software do not attempt to prevent DLL injection, it has gained a lot of attention over the years and is most commonly done via remotely executing LoadLibrary in the target process by using CreateRemoteThread.
The following is an example DLL injection function I wrote in C:
#include <windows.h>
bool bInjectModule(__in DWORD dwProcessIdentifier, __in TCHAR *szModulePath)
{
// Keep track of whether the function is successful or not.
bool bSuccess = false;
// Get the address of LoadLibraryA in the current process; it is most likely the same address for the remote process.
FARPROC fnLoadLibraryA = GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), TEXT("LoadLibraryA")));
if (fnLoadLibraryA != NULL)
{
// Make sure that the current process has the necessary privileges (SE_DEBUG_NAME).
HANDLE hToken;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
TOKEN_PRIVILEGES TokenPrivileges;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &TokenPrivileges.Privileges[0].Luid);
TokenPrivileges.PrivilegeCount = 1;
TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, 0, &TokenPrivileges, sizeof(TokenPrivileges), NULL, NULL);
}
// Get the handle of the remote process.
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessIdentifier);
if (hProcess != NULL)
{
// Figure out what the size of the DLL's path is, including the null-terminating character.
size_t nModulePathSize = _tcslen(szModulePath) + sizeof(TCHAR);
// Allocate enough memory in the remote process so that it will be able to hold the DLL's path.
HANDLE hRemoteMemoryAddress = VirtualAllocEx(hProcess, NULL, nModulePathSize, MEM_COMMIT, PAGE_READWRITE);
if (hRemoteMemoryAddress != NULL)
{
// Write the DLL's path to the memory address space of the remote process.
if (WriteProcessMemory(hProcess, hRemoteMemoryAddress, szModulePath, nModulePathSize, NULL))
{
// Call LoadLibraryA in the remote process with the recently-written DLL's path as the parameter.
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)fnLoadLibraryA, hRemoteMemoryAddress, 0, NULL);
if (hThread != NULL)
{
// Close the handle to the remote thread.
CloseHandle(hThread);
// Make the claim that the DLL was successfully injected.
bSuccess = true;
}
}
}
// Close the handle to the other process.
CloseHandle(hProcess);
}
}
// If any function up to CreateRemoteThread failed, return false.
return bSuccess;
}