DLL injection in C++

DLL injection in Windows can be used to inject a DLL into a process and to take over/extend functionality. For example, the distribution platform Steam uses DLL injection for the game overlay.

Steam game overlay

Part of the steam log file:

Thu Mar 08 19:59:12 2012 UTC - Module file name: C:\Program Files\Steam\GameOverlayRenderer.dll
Thu Mar 08 19:59:12 2012 UTC - GameID = 8190
Thu Mar 08 19:59:12 2012 UTC - System page size: 4096
Thu Mar 08 19:59:12 2012 UTC - Hooking SetCursorPos, GetCursorPos, ShowCursor, and SetCursor
Thu Mar 08 19:59:12 2012 UTC - Hooking GetRawInputBuffer calls
Thu Mar 08 19:59:12 2012 UTC - Game is using D3D9 or D3D9Ex, preparing to hook.
Thu Mar 08 19:59:12 2012 UTC - Game is using dinput8, preparing to hook.
Thu Mar 08 19:59:12 2012 UTC - Modules at GameOverlayRenderer.dll attach
...
Thu Mar 08 19:59:12 2012 UTC - ----------------------------
Thu Mar 08 19:59:12 2012 UTC - Found GetModuleHandleEx
Thu Mar 08 19:59:12 2012 UTC - Game is using dxgi (dx10/dx11), preparing to hook.
Thu Mar 08 19:59:12 2012 UTC - hookCreateDXGIFactory called
Thu Mar 08 19:59:13 2012 UTC - Hooking vtable for factory
Thu Mar 08 19:59:13 2012 UTC - IWrapDXGIFactory::CreateSwapChain called
Thu Mar 08 19:59:13 2012 UTC - Found GetModuleHandleEx
Thu Mar 08 19:59:13 2012 UTC - Hooking vtable for swap chain
Thu Mar 08 19:59:13 2012 UTC - Hooking vtable for device
Thu Mar 08 19:59:13 2012 UTC - Tracking new device: 2fa5108
Thu Mar 08 19:59:13 2012 UTC - Tracking new swap chain: 33f270 (with device: 2fa5108)
Thu Mar 08 19:59:14 2012 UTC - Trying to setup input hook...
Thu Mar 08 19:59:14 2012 UTC - Set input hook...
Thu Mar 08 19:59:14 2012 UTC - DirectInput8Create hook called, 32c4598
Thu Mar 08 19:59:15 2012 UTC - DirectInput::CreateDevice() mouse instance just created...
Thu Mar 08 19:59:15 2012 UTC - DirectInput::CreateDevice() keyboard instance just created...
Thu Mar 08 19:59:15 2012 UTC - DInput:KB NONEXCLUSIVE
Thu Mar 08 19:59:15 2012 UTC - DInput:KB FOREGROUND
Thu Mar 08 19:59:20 2012 UTC - Created ID3D10Effect from memory ok!
Thu Mar 08 19:59:20 2012 UTC - Game is not using sRGB back buffer
Thu Mar 08 20:00:06 2012 UTC - DInput 32c4598 deleting
Thu Mar 08 20:00:06 2012 UTC - Deleting IWrapMouseDevice8
Thu Mar 08 20:00:06 2012 UTC - Deleting IWrapKeyboardDevice8 32c45f0
Thu Mar 08 20:00:06 2012 UTC - DeleteD3D10RendererForSwapChain called for: 33f270
Thu Mar 08 20:00:06 2012 UTC - Releasing all resources for device: 2fa5108
Thu Mar 08 20:00:07 2012 UTC - DeleteDevice called for: 2fa5108
Thu Mar 08 20:00:07 2012 UTC - No renderer for this device
Thu Mar 08 20:00:07 2012 UTC - GameOverlayRenderer.dll detaching
Thu Mar 08 20:00:07 2012 UTC - Detaching input hook...

The example program below will demonstrate DLL injection by injecting a DLL into the iexplore.exe (Internet Explorer) process and by displaying a MessageBox from within the process.

dllmain.cpp:

// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <Windows.h>
#include <string.h>
#include <stdio.h>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	char* helloStr;
	char buf[250];
	DWORD pid = GetCurrentProcessId();

	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
		helloStr = "Hello from: %d";
		sprintf(buf, helloStr, pid);
		MessageBox(NULL, buf, NULL, NULL);
		break;
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		helloStr = "Goodbye from: %d";
		sprintf(buf, helloStr, pid);
		MessageBox(NULL, buf, NULL, NULL);
		break;
	}
	return TRUE;
}

DLLInjector.cpp:

// DLLInjector.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "Injector.h"

#define DLL_PATH "\\..\\Debug\\exampledll.dll" // The DLL we would like to inject
#define PROC_NAME "iexplore.exe" // Internet Explorer

Injector* injector = new Injector();

int _tmain(int argc, _TCHAR* argv[])
{

	TCHAR currentDir[MAX_PATH];
	TCHAR dllDir[MAX_PATH];
	GetCurrentDirectory(MAX_PATH,currentDir);

	strcpy(dllDir,currentDir);
	strcat(dllDir,DLL_PATH);

	printf("Current directory: %s\n", currentDir);
	printf("DLL path: %s\n", dllDir);
	printf("Process: %s\n", PROC_NAME);

	system("PAUSE");

	if(injector->Inject(PROC_NAME, dllDir)){
		printf("DLL injected!\n");
	} else {
		printf("Failed to inject the dll...\n");
	}

	system("PAUSE");

	return 0;
}

Injector.cpp (Major credits to Matthias Rosche for writing the injector):

#include "Injector.h"
#include <windows.h>
#include <tlhelp32.h>
#include <shlwapi.h>
#include <conio.h>
#include <stdio.h> 

Injector::Injector(void)
{
}

Injector::~Injector(void)
{
} 

#define CREATE_THREAD_ACCESS (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ) 

bool Injector::Inject(char* procName,char* dllName)
{
	DWORD pID = GetTargetThreadIDFromProcName(procName); 

   char DLL_NAME[MAX_PATH] = {0};
   GetFullPathName(dllName, MAX_PATH,DLL_NAME, NULL);
   printf(DLL_NAME);
   printf("\n");

   HANDLE Proc = 0;
   HMODULE hLib = 0;
   char buf[50] = {0};
   LPVOID RemoteString, LoadLibAddy; 

   if(!pID)
      return false; 

   Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);
   if(!Proc)
   {
      sprintf(buf, "OpenProcess() failed: %d", GetLastError());
      //MessageBox(NULL, buf, "Loader", MB_OK);
      printf(buf);
      return false;
   } 

   LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); 

   // Allocate space in the process for our <strong class="highlight">DLL</strong>
   RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, strlen(DLL_NAME), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 

   // Write the string name of our <strong class="highlight">DLL</strong> in the memory allocated
   WriteProcessMemory(Proc, (LPVOID)RemoteString, DLL_NAME, strlen(DLL_NAME), NULL); 

   // Load our <strong class="highlight">DLL</strong>
   CreateRemoteThread(Proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL); 

   CloseHandle(Proc);
   return true;
}

bool Injector::Inject(DWORD pID,char* dllName)
{

   char DLL_NAME[MAX_PATH] = {0};
   GetFullPathName(dllName, MAX_PATH,DLL_NAME, NULL);
   printf(DLL_NAME);
   printf("\n");

   HANDLE Proc = 0;
   HMODULE hLib = 0;
   char buf[50] = {0};
   LPVOID RemoteString, LoadLibAddy; 

   if(!pID)
      return false; 

   Proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);
   if(!Proc)
   {
      sprintf(buf, "OpenProcess() failed: %d", GetLastError());
      MessageBox(NULL, buf, "Loader", MB_OK);
      printf(buf);
      return false;
   } 

   LoadLibAddy = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); 

   // Allocate space in the process for our <strong class="highlight">DLL</strong>
   RemoteString = (LPVOID)VirtualAllocEx(Proc, NULL, strlen(DLL_NAME), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); 

   // Write the string name of our <strong class="highlight">DLL</strong> in the memory allocated
   WriteProcessMemory(Proc, (LPVOID)RemoteString, DLL_NAME, strlen(DLL_NAME), NULL); 

   // Load our <strong class="highlight">DLL</strong>
   CreateRemoteThread(Proc, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibAddy, (LPVOID)RemoteString, NULL, NULL); 

   CloseHandle(Proc);
   return true;
}

DWORD Injector::GetTargetThreadIDFromProcName(const char * ProcName)
{
	PROCESSENTRY32 pe;
	HANDLE thSnapShot;
	BOOL retval, ProcFound = false;

	thSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if(thSnapShot == INVALID_HANDLE_VALUE)
	{
		//MessageBox(NULL, "Error: Unable to create toolhelp snapshot!", "2MLoader", MB_OK);
		printf("Error: Unable to create toolhelp snapshot!");
		return false;
	}

	pe.dwSize = sizeof(PROCESSENTRY32);

	retval = Process32First(thSnapShot, &pe);
	while(retval)
	{
		if(!strcmp(pe.szExeFile, ProcName))
		{
			return pe.th32ProcessID;
		}
		retval = Process32Next(thSnapShot, &pe);
	}
	return 0;
}

Output:

DLL injected into the Internet Explorer process

The archive containing the VC++ source code can be downloaded from here.

This entry was posted in C++ and tagged , , , , , , , , , , , , , . Bookmark the permalink. Trackbacks are closed, but you can post a comment.

5 Comments

  1. nonstopdisco
    Posted March 12, 2012 at 9:37 am | Permalink

    it worked for windows 7. the only problem i saw was that i didnt work for windows xp (virtual pc).

    it failed on: OpenProcess() error: 5

    do you know why?

    • Posted March 13, 2012 at 12:30 pm | Permalink

      Hi,

      It is probably due to PROCESS_ALL_ACCESS within Injector.cpp on line 75 and 35. Quote MSDN:

      “Windows Server 2003 and Windows XP: The size of the PROCESS_ALL_ACCESS flag increased on Windows Server 2008 and Windows Vista. If an application compiled for Windows Server 2008 and Windows Vista is run on Windows Server 2003 or Windows XP, the PROCESS_ALL_ACCESS flag is too large and the function specifying this flag fails with ERROR_ACCESS_DENIED. To avoid this problem, specify the minimum set of access rights required for the operation. If PROCESS_ALL_ACCESS must be used, set _WIN32_WINNT to the minimum operating system targeted by your application (for example,
      #define _WIN32_WINNT _WIN32_WINNT_WINXP
      ). For more information, see Using the Windows Headers.”

      http://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx

  2. Youda
    Posted March 20, 2013 at 5:30 pm | Permalink

    can i draw with this in fullscreened games (directx 11) ? can you show me how? thanks :) Y0uda@seznam.cz ( Y”zero”uda)

  3. Marcus Carey
    Posted September 12, 2013 at 11:56 pm | Permalink

    How do you unload the DLL?

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Why ask?