Detouring SendPacket

03/23/2013 15:06 Salim*#1
Hello there.
I'm posting about my problem since I haven't found any thread about this topic :(
I am trying to make an 'outgoing packet listener' for this game, by detour-ing its SendPacket function. Obviously it only listens for packets being sent.
I want to avoid using MS Detours, so I'm using a detour function I found on the internet.
Needless to say I am injecting a dll into the client in order to do that.
The problem is the game crashes, most likely when jumping from my custom SendPacket function to the real SendPacket function (according to OllyDbg).

Here's the code (C++) for the dll I'm injecting into the client:
Code:
#define WIN32_LEAN_AND_MEAN
#include <iostream>
#include <cstdio>
#include <windows.h>
#include <process.h>
#include <io.h>
#include <fcntl.h>

typedef int (__stdcall *pSendPacket) (void *Src, size_t Size);

unsigned int __stdcall mainThread(void* pArguments);
bool OpenConsole();
void SetConsoleTitle(wchar_t *title);
void *SetDetour(BYTE *source, const BYTE *destination, const unsigned int length);

pSendPacket o_SendPacket = NULL;
int _stdcall h_SendPacket(void *Src, size_t Size);

extern "C" __declspec(dllexport) BOOL APIENTRY DllMain(HANDLE hModule, DWORD lpReason, LPVOID lpReserved)
{
	switch (lpReason)
	{
		case DLL_PROCESS_ATTACH:
			// attach to process
			HANDLE hThread;
			unsigned int threadId;
			hThread = (HANDLE)_beginthreadex(NULL, 0x1000, &mainThread, (void *)hModule, 0, &threadId);
			break;
		case DLL_PROCESS_DETACH:
			// detach from process
			break;
		case DLL_THREAD_ATTACH:
			// attach to thread
			break;
		case DLL_THREAD_DETACH:
			// detach from thread
			break;
	}
	return TRUE; // succesful
}

unsigned int __stdcall mainThread(void* pArguments)
{
	OpenConsole();
	SetConsoleTitle(L"Packet Listener");
	DWORD addr = 0x63AA80; // sendpacket's address (old version of the game, I play on a private server, 1.4.4 I believe)
	o_SendPacket = (pSendPacket)SetDetour((BYTE *)addr, (BYTE *)h_SendPacket, 7); // detour SendPacket, it should now jump to h_SendPacket
	wprintf(L"SendPacket's address:   %x\nh_SendPacket's address: %x\no_SendPacket's address: %x\n", (BYTE *)addr, h_SendPacket, o_SendPacket); // check the functions' addresses, to make it easier to debug
	return S_OK; // exit current thread
}

bool OpenConsole()
{
	FILE *out;
	FILE *in;
	FILE *err;
	if (AllocConsole() == FALSE)
		return false;
	out = _fdopen(_open_osfhandle(PtrToUlong(GetStdHandle(STD_OUTPUT_HANDLE)), _O_TEXT), "w");
	in = _fdopen(_open_osfhandle(PtrToUlong(GetStdHandle(STD_INPUT_HANDLE)), _O_TEXT), "r");
	err = _fdopen(_open_osfhandle(PtrToUlong(GetStdHandle(STD_ERROR_HANDLE)), _O_TEXT), "w");
	*stdout = *out;
	*stdin = *in;
	*stderr = *err;
	setvbuf(stdout, NULL, _IONBF, 0);
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stderr, NULL, _IONBF, 0);
	return true;
}

void SetConsoleTitle(wchar_t *title)
{
	SetConsoleTitleW(title);
}

void *SetDetour(BYTE *source, const BYTE *destination, const unsigned int length) // this wasn't written by myself
{
		BYTE *jmp = (BYTE *)malloc(length + 5);
		DWORD dwBack;
		VirtualProtect(source, length, PAGE_EXECUTE_READWRITE, &dwBack);
		memcpy(jmp, source, length);
		jmp += length;
		jmp[0] = 0xE9;
		*(DWORD *)(jmp + 1) = (DWORD)(source + length - jmp) - 5;
		source[0] = 0xE9;
		*(DWORD*)(source + 1) = (DWORD)(destination - source) - 5;
		for(int i = 5; i < length; i++)
		{
			source[i] = 0x90;
		}
		VirtualProtect(source, length, dwBack, &dwBack);
		return jmp - length + 1;
}

int __stdcall h_SendPacket(void *Src, size_t Size)
{
	wprintf(L"SendPacket triggered! opcode: %x (%d bytes long)\n", ((BYTE *)Src)[0], (int)Size); // check the packet's opcode, to make sure it works
	return o_SendPacket(Src, Size);
}
It compiles fine. I inject it into the client with a simple dll injector, and here's what happen:
1) A console gets attached to the client.
2) I move/sit/attack/whatever.. in game to trigger SendPacket.
3) SendPacket gets called, it jumps to my custom SendPacket (h_SendPacket in my code) which prints text on the console.
4) Crash.

I assume that it crashes when jumping back to the real SendPacket, maybe it screws up the registers?
This method used to work for me on other games, and I'm still a noob at Olly/ASM, so I'm kinda clueless for now :(
03/23/2013 15:45 Sᴡoosh#2
Why do you not want to use ms detours? They work well.

I'd not rely on some detour method you found, but instead create it manually.

Start of sendpacket :

006B4220 /$ 6A FF PUSH -1
006B4222 |. 68 68A3A600 PUSH elementc.00A6A368
006B4227 |. 64:A1 00000000 MOV EAX, DWORD PTR FS:[0]

You need 5 byte for a jump, and the first two instructions are 7 bytes long. So, pushad, NOP 7 byte, place your jmp (so you have 2 byte nop behind it), and in your function, before you return, you push what the sendpacket function needs. Then popad.

Something along these lines. I see your detour function is basically doing this already, but if it crashes, i'd try doing it manually and see if the crash is fixed then. Then you at least know it's the detour doing funny stuff, and not something else.

Cheers
03/23/2013 21:17 Salim*#3
Okay, got it working now :)
There were 2 mistakes in my code.
The first one is I did not push the parameters needed for SendPacket.
Second one is my code was giving a wrong offset to one of the JMPs.

It is now working properly, I'll post a sample later on, if anyone wants to give a look at it.
Next step will be doing the same thing for incomming packets (hooking the function that decrypts those will be enough hopefully :p).

__EDIT__

Here's the final source code:
Code:
#define WIN32_LEAN_AND_MEAN
#include <iostream>
#include <cstdio>
#include <windows.h>
#include <process.h>
#include <io.h>
#include <fcntl.h>

typedef int (__stdcall *pSendPacket) (void *Src, size_t Size);

unsigned int __stdcall mainThread(void* pArguments);
bool OpenConsole();
void SetConsoleTitle(wchar_t *title);
void *SetDetour(BYTE *source, const BYTE *destination, const unsigned int length);

pSendPacket o_SendPacket = NULL;
int _stdcall h_SendPacket(void *Src, size_t Size);

DWORD SendPacketAddress(0x63AA80);
DWORD BaseAddress(0xA521C0);

extern "C" __declspec(dllexport) BOOL APIENTRY DllMain(HANDLE hModule, DWORD lpReason, LPVOID lpReserved)
{
	switch (lpReason)
	{
		case DLL_PROCESS_ATTACH:
			// attach to process
			HANDLE hThread;
			unsigned int threadId;
			hThread = (HANDLE)_beginthreadex(NULL, 0x1000, &mainThread, (void *)hModule, 0, &threadId);
			break;
		case DLL_PROCESS_DETACH:
			// detach from process
			break;
		case DLL_THREAD_ATTACH:
			// attach to thread
			break;
		case DLL_THREAD_DETACH:
			// detach from thread
			break;
	}
	return TRUE; // succesful
}

unsigned int __stdcall mainThread(void* pArguments)
{
	OpenConsole();
	SetConsoleTitle(L"Packet Listener");
	o_SendPacket = (pSendPacket)SetDetour((BYTE *)SendPacketAddress, (BYTE *)h_SendPacket, 7); // detour SendPacket, it should now jump to h_SendPacket

	return S_OK; // exit current thread
}

void *SetDetour(BYTE *source, const BYTE *destination, unsigned int length)
{
	unsigned int const jmpLength(5);
	unsigned int const nopOpcode(0x90);
	unsigned int const jmpOpcode(0xE9);

	if (length < jmpLength) length = jmpLength; // Make sure the patch's length is long enough to hold a 32bit JMP.
	unsigned int tunnelLength = length + jmpLength;
	BYTE *tunnel = new BYTE[tunnelLength]; // Create a body for the "tunnel" function.
	FillMemory(tunnel, 12, 0);
	DWORD oldProtection(NULL); // Old page protection.
	VirtualProtect(source, length, PAGE_EXECUTE_READWRITE, &oldProtection);
	memcpy(tunnel, source, length);
	for (unsigned int i(jmpLength); i < length; i++)
		source[i] = nopOpcode;
	source[0] = jmpOpcode;
	tunnel[tunnelLength - jmpLength] = jmpOpcode;
	*(DWORD*)(source + 1) = (DWORD)(destination - source) - jmpLength; // JMP Offset 1
	*(DWORD*)(tunnel + 1 + tunnelLength - jmpLength) = (DWORD)((source + length) - (tunnel + tunnelLength - jmpLength) - jmpLength); // JMP Offset 2
	VirtualProtect(source, length, oldProtection, &oldProtection);
	return tunnel;
}

bool OpenConsole()
{
	FILE *out;
	FILE *in;
	FILE *err;
	if (AllocConsole() == FALSE)
		return false;
	out = _fdopen(_open_osfhandle(PtrToUlong(GetStdHandle(STD_OUTPUT_HANDLE)), _O_TEXT), "w");
	in = _fdopen(_open_osfhandle(PtrToUlong(GetStdHandle(STD_INPUT_HANDLE)), _O_TEXT), "r");
	err = _fdopen(_open_osfhandle(PtrToUlong(GetStdHandle(STD_ERROR_HANDLE)), _O_TEXT), "w");
	*stdout = *out;
	*stdin = *in;
	*stderr = *err;
	setvbuf(stdout, NULL, _IONBF, 0);
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stderr, NULL, _IONBF, 0);
	return true;
}

void SetConsoleTitle(wchar_t *title)
{
	SetConsoleTitleW(title);
}

int __stdcall h_SendPacket(void *packet, size_t len)
{
	wprintf(L"Packet type: %02X\n", ((BYTE *)packet)[0]); // Display the packet's opcode (first byte).
	DWORD ecx_val = *(DWORD *)(*(DWORD *)BaseAddress + 0x20);
	__asm
	{
		push ecx
		push len
		push packet
		mov ecx,ecx_val
		call o_SendPacket
		pop ecx
	}
}
It displays the first byte of the packets being sent in the console (too lazy to make it output the whole packet :p).
03/24/2013 11:52 AlainProvist#4
Thanks for sharing your code :).

What method do you use for injecting your dll inside the client ?
03/24/2013 15:00 Salim*#5
Any injector should work, you can find plenty of them on the internet.
I have not try to code one myself yet, however I think you can do it by calling LoadLibraryA into the client via WriteProcessMemory and CreateRemoteThread.
If I get my head around it and get some code working I post it here.
03/24/2013 16:15 AlainProvist#6
I wrote my own injector :

Code:
#include <iostream>
#include <windows.h>
#include <WindowsX.h>
#include <tchar.h>
#include <malloc.h>
#include <direct.h>
#include <TlHelp32.h>

BOOL InjectLibrary(DWORD processID, const char *fnDll)
{
	HANDLE hProcess = OpenProcess(
		PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION  | PROCESS_VM_WRITE,
		FALSE, processID);
	if (!hProcess)
	{
		return false;
	}

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

	CloseHandle(hProcess);

	return success;
}

BOOL EjectLibrary(DWORD processID, const char *fnDll)
{
	HANDLE hProcess = OpenProcess(
		PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION  | PROCESS_VM_WRITE,
		FALSE, processID);
	if (!hProcess)
	{
		return false;
	}

	BOOL success = FALSE;
	HANDLE hSnapshot = NULL;
	HANDLE hThread = NULL;
	FARPROC procFreeLibrary = NULL;

	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID);

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

	CloseHandle(hProcess);

	return success;
}

int _tmain(int argc, _TCHAR* argv[])
{
	char srcDll[512]; //dll in the same directory as the injection.exe, its code is specified below.
	_getcwd(srcDll, 512);
	strcat(srcDll, "\\Detouring.dll");

	DWORD processID; 
	std::cout << "Enter any PID : ";
	std::cin >> processID;
	if (InjectLibrary(processID, srcDll))
	{
		std::cout << "Injection successful" << std::endl;
	}
	system("PAUSE");

	EjectLibrary(processID, srcDll);

	return 0;
}
For some strange reason the EjectLibrary() make the client crash when FreeLibrary is called. I don't even get any DLL_PROCESS_DETACH from my dll before the crash. Any idea of why it crashes the client ?
(I would like to make it detachable for debug purpose when I want to modify the dll without restarting the client.)
03/24/2013 16:17 Arkendo#7
Most common injection techniques I can think of are proxy DLLs (often used for dx hooks) and, as you said, calling LoadLibrary in the target process to load your DLL.
LoadLibrary is probably the easiest way of doing it, but you will need to know the client's ID in order to use all the APIs needed. You can retrieve it at runtime by using a snapshot of the running processes (nice tutorial here: [Only registered and activated users can see links. Click Here To Register...]).
Alternatively, you can run the client from your injector by using CreateProcess, which will give you the process' ID right away :)

Here's an old code I've got that uses the 2nd method (CreateProcess).
The error checking is pretty bad, but as long as it works.. xD
Code:
#include <iostream>
#include <Windows.h>
using namespace std;

typedef HINSTANCE (*fpLoadLibrary)(char*);
bool inject(DWORD pId);

wchar_t processPath[] = L"elementclient.exe";
wchar_t dllPath[] = L"myDll.exe"; //Replace myDll.exe by the name of your DLL.

int main()
{
	STARTUPINFOW startupInfo = {0};
	PROCESS_INFORMATION processInfo = {0}; //This struct will hold the process' ID.
	if (!CreateProcessW(NULL, processPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInfo)) //Try to create the process (elementclient) and 'pause' it.
	{ //Creation failed.
		wcout << L"Error: Process creation failed.\n";
	}
	else //Process succesfully created.
	{
		if (inject(processInfo.dwProcessId)) //Try to inject the DLL.
			ResumeThread(processInfo.hThread); //Succesful, resume the process.
		else
		{ //Failed. Too bad.
			wcout << L"Error: Dll injection failed.\n";
			TerminateThread(processInfo.hThread, 0); //Close the process.
		}
	}
}

bool inject(DWORD pId)
{
	bool memWritten;
	HINSTANCE hDll = LoadLibrary(L"KERNEL32");
	fpLoadLibrary LoadLibraryAddr = (fpLoadLibrary)GetProcAddress(hDll, "LoadLibraryW"); //Get LoadLibraryW's address.
	HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, false, pId); //Get the process' handle.
	if (!hProc) //Ret if OpenProcess failed.
		return false;
	LPVOID paramAddr = VirtualAllocEx(hProc, 0, wcslen(dllPath) + 1, MEM_COMMIT, PAGE_READWRITE); //Allocate some bytes to copy our dll's name.
	memWritten = WriteProcessMemory(hProc, paramAddr, dllPath, wcslen(dllPath) + 1, NULL); //Write the dll's name into the bytes.
	if (memWritten)
		CreateRemoteThread(hProc, 0, 0, (LPTHREAD_START_ROUTINE)LoadLibraryAddr, paramAddr, 0, 0); //Run LoadLibraryW in elementclient and feed it our dll.
	CloseHandle(hProc); //We're done.
	return memWritten; //memWritten = TRUE if WriteProcessMemory was succesful.
}
For runtime injection, just replace the CreateProcess part with a process snapshot, and check the processes one by one until you find elementclient.exe.

1rst Edit: Wow, ninja'd, you beat me at it Alain xD

2nd Edit:
Since Salim's DLL detours sendpacket, if you unload the dll, the client will try to call an unexisting function, resulting in a crash. You should restore the original sendpacket before unloading :p.
Also make sure to change the following lines in his code if you're running it on the latest client - those addresses are fairly outdated:
DWORD SendPacketAddress(0x63AA80);
DWORD BaseAddress(0xA521C0);
03/24/2013 17:15 AlainProvist#8
Yes for sure but i was supposing my dll would have call the dllmain with DLL_PROCESS_DETACH reason param to let me undetour the sendpacket address, when trying to unload the dll with freeLibrary. But apparently no oO

edit : My fault... got this code from a tuto without double checking the for loop
Code:
for(; isMoreMods && !isFound; isMoreMods = Module32Next(hSnapshot, &me))
			isFound = (_strcmpi(me.szModule, fnDll) == 0 || _strcmpi(me.szExePath, fnDll) == 0);
The noob that wrote this tuto aparently never tested his code cause this makes unload the next dll intead of our dll... :rtfm:

edit 2: Finally got it : my dll can hook the send packet at the start of my injector app and unhook at the end of the app, removing the console and making all back to normal. Was a good practice for me, thanks again for the code and the idea ;).
03/24/2013 23:16 Salim*#9
Your welcome thank you both for the tips about injection.
Currently trying to find a way to get incoming packets, in their decrypted form of course :)
03/25/2013 22:27 zowex#10
So this is for perfect world, right?
03/27/2013 20:57 deltadagger#11
what this does?