.dll Injection and Patching

From SkullSecurity
Jump to navigation Jump to search
Assembly Language Tutorial
Please choose a tutorial page:

.dll injection is the easiest and most common way to write a hack.

To perform an injection, a process is told to load an attacker-controlled .dll file into its space. When loaded, the .dll file adds jumps (or "hooks") to certain places in the target program that automatically call functions in the .dll file. This allows the .dll file to capture specific events, such as packets being received, commands being typed, or anything else.

Injection

If you want a program to do it for you without reading any further, feel free to grab the one I wrote. It works well, but can't really be automated.

If you want to learn more about injection, feel free to browse my code here or download the code here. It's my first (and only) Windowsy program, so it might be valuable some day! But seriously, be gentle, and if you think you can improve it I'd welcome the change.

Basically, a program calls CreateRemoteThread() in the foreign process, giving it some code. The code given simply calls LoadLibrary() on your selected .dll file, which loads it into the program's address space.

If you want any more details about how injection works, check out Richter's book or my source code. It's been a long time, and I've always just used that program.

Code

This section is made up mostly of some code from Programming Applications for Microsoft Windows, by Jeffrey Richter. I've modified it greatly, so it's almost entirely my code now, but I always believe in giving credit where credit's due.

Here is the code:

#include <windows.h>
#include <WindowsX.h>
#include <tchar.h>
#include <malloc.h>
#include <TlHelp32.h>
#include "Injection.h"

BOOL InjectLibrary(HANDLE hProcess, char *fnDll)
{
	BOOL success = FALSE;
	HANDLE hThread = NULL;
	char *fnRemote = NULL;
	FARPROC procLoadLibraryA = NULL;

	size_t lenFilename = strlen(fnDll) + 1;

	/* Allocate space in the remote process */
	fnRemote = (char *) VirtualAllocEx(hProcess, NULL, lenFilename, MEM_COMMIT, PAGE_READWRITE);
	
	if(fnRemote)
	{
		/* Write the filename to the remote process. */
		if(WriteProcessMemory(hProcess, fnRemote, fnDll, lenFilename, NULL))
		{
			/* Get the address of the LoadLibraryA function */
			procLoadLibraryA = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");
			hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) procLoadLibraryA, fnRemote, 0, NULL);
			if(hThread)
			{
				WaitForSingleObject(hThread, INFINITE);
				success = TRUE;
			}
		}
		VirtualFreeEx(hProcess, fnRemote, 0, MEM_RELEASE);
	}

	return success;
}

BOOL EjectLibrary(HANDLE hProcess, int dwProcessId, char *fnDll)
{
	BOOL success = FALSE;
	HANDLE hSnapshot = NULL;
	HANDLE hThread = NULL;
	FARPROC procFreeLibrary = NULL;

	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessId);
	
	if(hSnapshot)
	{
		MODULEENTRY32 me = { sizeof(me) };
		BOOL isFound = FALSE;
		BOOL isMoreMods = Module32First(hSnapshot, &me);
		for(; isMoreMods && !isFound; isMoreMods = Module32Next(hSnapshot, &me))
			isFound = (_strcmpi(me.szModule, fnDll) == 0 || _strcmpi(me.szExePath, fnDll) == 0);

		if(isFound)
		{
			/* Get the address of the LoadLibraryA function */
			procFreeLibrary = GetProcAddress(GetModuleHandle("Kernel32"), "FreeLibrary");
			hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) procFreeLibrary, me.modBaseAddr, 0, NULL);
			if(hThread)
			{
				WaitForSingleObject(hThread, INFINITE);
				success = TRUE;
			}
		}

		CloseHandle(hSnapshot);	
	}

	return success;
}

Patching

When loaded, the .dll file, in DLL_PROCESS_ATTACH, typically overwrites sections of the program's code, using WriteProcessMemory(), to point to itself. The overwritten code must also be run before the hack is (or after, but I generally do it before). Otherwise, the commands will never be run, and the program will likely misbehave.

The Patch

The first step to writing the patch is to have the game's code call code controlled by the .dll file. A call is 5 bytes of machine code (E8 + the distance) that alters the stack, so we need one or more instructions that don't touch the stack and that are 5+ characters long. This instruction would make a good candidate:

.text:0040209B 29 90 88 EE 4F 00                 sub     dword_4FEE88[eax], edx

Once the patch is added, the code will look like this:

.text:0040209B E8 xx xx xx xx                    call    [wrapper]
.text:004020A2 90                                nop

The first 5 bytes, 29 90 88 EE 4F, were overwritten with the machine code to make a function call to the .dll-controlled code, the "wrapper". The final byte of the machine code, 00, was overwritten by a nop instruction. Leaving the final instruction intact would likely cause problems, since we don't know what the instruction "00" represents, so it is replaced with a safe "nop".

The Wrapper

The easiest way to ensure that the original code runs is to create a wrapper in the .dll that has those exact bytes, then jumps to the attackers actual function. This process can be referred to as "rebounding," although I prefer just calling it "writing a wrapper". Here is what we want the wrapper string to do:

29 90 88 EE 4F 00   sub  dword_4FEE88[eax], edx   ; This was the code replaced in the patch
E8 xx xx xx xx      jmp  HackFunction             ; This is the ultimate destination of the code

Additionally, it's often a good idea to ensure that all registers are backed up and restored. If you wish to do that, then the wrapper would be:

29 90 88 EE 4F 00   sub  dword_4FEE88[eax], edx   ; This was the code replaced in the patch
60                  pushad                        ; Preserve the state of the registers
E9 xx xx xx xx      call  HackFunction            ; This is the ultimate destination of the code
61                  popad                         ; Restore the registers
C3                  ret                           ; Because this wrapper is doing a "call", we have to return

Which translates to the following machine code:

char *wrapper = "\x29\x90\x88\xee\x4f\x00\x60\xe9\x??\x??\x??\x??\x61\xc3";

Where the four unknown bytes are the distance between them and the HackFunction.

The HackFunction can be any function, but remember that no parameters are passed and no return value can be accepted. By modifying the code in the wrapper, both of those are possible. Just remember that the stack has to be left in the same position as it started in.

Code Execution Summary

Here is a summary of what happens:

  • The process executes normally until reaching the patch
  • At the patch, the process calls the wrapper
    • The over-written instruction(s) run in the wrapper
    • The wrapper saves the registers
    • The wrapper calls the hack function
      • The hack function does whatever the programmer intended
      • The hack function returns back to the wrapper
    • The wrapper restores the variables
    • The wrapper returns back to the ordinary code
  • Program continues executing normally


The actual patch is done with the following C code. Note that this isn't the best way to do it, this could be cleaned up a lot, but this is the most straight forward way:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

void __stdcall HackFunction()
{
}

BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
	/* This is the address in the game where the patch is going */
	int intAddressToPatch = /* Insert address here */;

	/* This creates the wrapper, leaving an "????" where the call distance will be inserted. */
	//char strWrapper[] = "\x29\x90\x88\xee\x4f\x00\x60\xe9????\x61\xc3";
	static char strWrapper[] = "\x89\x90\x58\xee\x4f\x00\x60\xe8????\x61\xc3";

	/* This sets the "????" in the string to equal the distance between HackFunction and from the byte immediately
	 * after the ????, which is 12 bytes from the beginning of the string (that's where the relative distance
	 * begins) */
	*((int*)(strWrapper + 8)) = ((int) &HackFunction) - ((int) strWrapper + 12); 

	/* This is the actual patch */
	char strPatch[] = "\xe8????\x90";

	/* This replaces the ???? with the distance from the patch to the wrapper. 5 is added because that's 
	 * the length of the call instruction (e8 xx xx xx xx xx) and the distance is relative to the byte 
	 * after the call. */
	*((int*)(strPatch + 1)) = ((int) &strWrapper) - (intAddressToPatch + 5);

	/* This is the original buffer, used when the .dll is removed (to restore the program's original 
	 * functionality) */
	char *strUnPatch = "\x29\x90\x88\xee\x4f\x00";

	/* The process handle is required to write */
	HANDLE hProcess = GetCurrentProcess();

	switch(ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		WriteProcessMemory(hProcess, (void*) intAddressToPatch, strPatch, 6, NULL);
		break;

	case DLL_PROCESS_DETACH:
		WriteProcessMemory(hProcess, (void*) intAddressToPatch, strUnPatch, 6, NULL);
		break;
	}
	return TRUE;
}

Questions

Feel free to edit this section and post questions, I'll do my best to answer them. But you may need to contact me to let me know that a question exists.