DLL injection

DLL injection refers to the insertion of DLL files into other processes by forcing other processes to call the LoadLibrary()API. The difference between DLL injection and normal DLL loading is whether the target is another process or its own process.

When a DLL is loaded into a process, it automatically calls DllMain(), which can be done by writing code to the DllMain() function.

There are three methods of DLL injection

  1. Windows message check

  2. Remote thread injection

  3. Using the Registry

Here’s a look at the principles, implementation, and debugging of these three injection methods.

Windows message check

The principle of Windows message hook

Windows is a message-driven system, which can realize different functions by sending messages and processing messages. The process of sending messages can be roughly divided into the following steps.

Graph TD trigger event --> Encapsulate Message --> System message Queue --> Application message Queue --> Application handler function

Hooks can be used to peek at or intercept messages, and hooks are used to set an intermediate point in the flow of a message, where information passes first.

Windows message hook is set on the system message queue -> application message queue, and the message hook gets the message before the application. For a particular message, the DLL where the hook handler function resides is injected into the corresponding process, and the hook handler function is called to process the message.

Graph TD system message queue --> Hook 1 --> Hook 2 -->... --> Application message queue

A chain consisting of more than one hook is called a trench chain. A call to the SetWindowsHookEx()API sets up the desired hook.

Windows message hook implementation

DLL library

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
{
	switch (dwReason)
	{
	case DLL_PROCESS_ATTACH:
		g_hInstance = hinstDLL;
		break;
	
	case DLL_PROCESS_DETACH:
		break;
	}

	return TRUE;
}
Copy the code

When the DLL is loaded into the process, DllMain() is called first, and dwReason is DLL_PROCESS_ATTACH, assigning the DLL’s handle to the global variable g_hInstance according to the switch branch statement.

	__declspec(dllexport) void HookStart(a)
	{
		g_hHook = SetWindowsHookEx(WH_KEYBOARD,// Keyboard hook
                KeybordProc,// Handle the function address
                g_hInstance, / / handle to the DLL
                0);// The target is all processes
	}

Copy the code

The SetWindowsHookEx()API is called here in the export function HookStart(), mainly for convenience.

LRESULT CALLBACK KeybordProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	CHAR szPath[MAX_PATH] = { 0 };// File path
	CHAR* p = nullptr;

	if (nCode >= 0)
	{
		if(! (lParam &0x80000000))// Release the keyboard keys
		{
			GetModuleFileNameA(nullptr, szPath, MAX_PATH);
                        // Get the full path to the file where the current process loaded the module
			p = strrchr(szPath, '\ \');// Get the process name
			// Compare process names
			if (!strcmp(DEF_PROCESS_NAME, p+1))
			{
				return 1;// Do not pass messages to the application}}}// If it is not notepad.exe or nCode<0, the message is passed to the next hook or application
	return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
Copy the code

KeyBoardProc is a callback function that is automatically called when a message is checked.

HookMain

// The name of the definition
#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"

// The type defined
typedef void (*PFN_HOOKSTART)(a);
typedef void (*PFN_HOOKSTOP)(a);

int main(a)
{
    HMODULE hDll = nullptr;/ / handle to the DLL
    PFN_HOOKSTART HookStart = nullptr;// start the check function
    PFN_HOOKSTOP HookStop = nullptr;// stop the check function

    / / load the DLL
    hDll = LoadLibraryA(DEF_DLL_NAME);

    // Get the address of the exported function
    HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
    HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);

    // Start checking
    HookStart();

    // The wait is over
    printf("press 'q' to quit\n");
    while(getchar() ! ='q');

    // stop fetching
    HookStop();

    return 0;

}

Copy the code

Windows message hook debugging

  • Open OD, load notepad. Exe and run it.
  • Open hookmain.exe and set the hook.
  • Click Debugging Options in Options and select Break On New Module(DLL) in Events, that is, pause when loading a New DLL.

  • Use the keyboard to enter in an open Notepad
  • Keyhook. DLL is injected into the notepad.exe process
  • Select Executable Modules in the View option to see the DLL being injected
  • Note that when loading keyhook. DLL, other DLLS may be loaded first, just press F9

Remote thread injection

How remote thread injection works

Remote thread injection simply calls CreateRemoteThread(), creates a thread in another process, LoadLibrary(), and loads a specific DLL into the middle of the process.

Implementation of remote thread injection

int main(int argc,char* argv[])
{
	if(argc ! =3)
		return 1;

	if (InjectDll((DWORD)atol(argv[1]),argv[2]))
		printf("Success!! \n");
	else
		printf("Failed!! \n");

	return 0;
	
}
Copy the code

Get the PID of the process and the DLL path to be injected from the command line.

	// Get the target process handle
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
	if (hProcess == nullptr)
	{
		return FALSE;
	}
Copy the code

With the passed PID, call the OpenProcess()API to get a handle to the process that can be used to manipulate the process later.


	// Allocate space in the target process
	dwBufSize = (strlen(szDllPath)+1);
	lpRemoteBuf = VirtualAllocEx(hProcess, nullptr, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
        
	// Write the DLL path to the allocated space
	WriteProcessMemory(hProcess, lpRemoteBuf, (LPCVOID)szDllPath, dwBufSize, nullptr);
Copy the code

Allocates space in the target process and writes the DLL path. Note that you are operating on the target process.

        // Get the LoadLibrary address
	hMod = GetModuleHandleA("kernel32.dll");
	lpThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA");
Copy the code

Because kernel32.dll addresses are the same in all processes, so is the LoadLibrary()API address, which is obtained in the original process equal to the address of the target process.

// Create a remote thread
CreateRemoteThread(
hProcess, 
nullptr.0,
lpThreadProc,// Thread entry address
lpRemoteBuf, / / parameters
0.nullptr
);
Copy the code

Here’s a look at LoadLibrary() and ThreadProc() definitions:

HMODULE LoadLibrary( LPCSTR lpLibFileName );
// Returns a DLL module handle with a value of 4 bytes
// The argument is of pointer type

DWORD WINAPI ThreadProc( LPVOID lpParameter );
// Returns an exit code with a value of 4 bytes
// The argument is of pointer type
Copy the code

Thus,LoadLibrary() and ThreadProc() are almost identical in structure, so you can pass the LoadLibrary()API as a thread-handling function, passing it as the fourth argument to CreateRemoteThread(). The fifth parameter also changes to the path of the DLL.

Debugging of remote thread injection

The debugging method is the same as the debugging method of Windows message check. You can view the previously written content.

Registry injection

The implementation and principle of registry injection

Open the registry editor regedit.exe and go to the following path

Write AppInit_DLLs to the DLL path to be injected and set the value of LoadAppInit_DLLs to 1. After the restart, the specified DLL will be injected into the process.

The reason is that when the process loads user32.dll, AppInit_DLLs and LoadAppInit_DLLs are read, and LoadLibrary() is called to load the specified DLL if there is any value.


This is from reading core Principles of Reverse Engineering chapters 21 and 23, and writing this blog post to sort out the content and practice code in the book.