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.

Part of the steam log file:

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
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:

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
// 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:

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
// 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):

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#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:

Advertisements