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