Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

[C++] Detours

Ginger by design.
Loyal Member
Joined
Feb 15, 2007
Messages
2,340
Reaction score
653
So most everyone who does hook coding or hacking of games (or just api hijacking in general) has probably heard of detours. I got tired of re-writing a basic detouring engine and being limited by the ones I made because of this simplicity. So I wrote a massively over-complicated one that's highly generic and should fit just about any situation. Previously I'd create a detour as so:

Code:
detour_s* detour_create(void* addr,void* newfunc,u8 bytes){
//	log("detouring function at %p to %p ...\n",addr,newfunc);

	if(bytes < 5)return 0;

	// Change the permissions on memory
	unsigned long oldprot=0;
	VirtualProtect(addr,bytes,PAGE_EXECUTE_READWRITE,&oldprot);

	// Create our detour object
	detour_s* det=new detour_s(bytes,addr);
	memcpy(det->rawdata,addr,bytes);
	memset(addr,0x90,bytes);

	// Write the jump patch in
	char* ptr=(char*)addr;
	*ptr++ = (char)0xE9;
	unsigned long jmp_to_new = (unsigned long)newfunc - ((unsigned long)addr + 5);
	*((unsigned long*)ptr) = jmp_to_new;

	// Finish off our trampoline
	ptr = &det->rawdata[bytes];
	*ptr++ = (char)0xE9;
	unsigned long jmp_to_orig = ((unsigned long)addr + bytes) - ((unsigned long)ptr + 4);
	*((unsigned long*)ptr) = jmp_to_orig;

	det->orig_func_trampoline = det->rawdata;

	// Re-protect the memory
	VirtualProtect(addr,bytes,oldprot,NULL);

	//log("detour complete.  returning new detour object at %p!\n",det);

	// Return the new trampoline!
	return det;
}

This is fairly simple, removing it is trivial (copy original bytes, delete buffers). Now, using this meant you needed a naked gateway to properly set up the call to your C++ function:

Code:
typedef hostent* (__stdcall*ghpfn)(const char* name);
ghpfn ogethostbyname;
detour_s* gethost_det=0;
bool resolved=false;
hostent* __stdcall mygethostbyname(const char* name){
	//log("gethostbyname called with name=\"%s\"!\n",name);

	if(g_cfg.host_addr != 0){
		if(strstr(name,"gwgt")){
	//		log("gethostbyname was called with \"gwgt*\" as the param, substituting our address instead!\n");
			in_addr sin;
			sin.S_un.S_addr=g_cfg.host_addr;
			hostent* pt=gethostbyaddr((char*)&host,4,AF_INET);
	//		log("we got a ptr: %p, returning!\n",pt);
			resolved=true;
			return pt;
		}
	}
	return ogethostbyname(name);
}

// Ugly inefficient wrapper :o
void __declspec(naked) gethostbyname_(void){
	__asm {
		pushad;
		push [esp+0x24];
		call mygethostbyname;
		push eax;
		add esp,4;
		popad;
		mov eax,[esp-0x24];
		retn 4;
	}
}

Problems:
1. You need to make an ugly naked wrapper like this for each function to properly copy the arguments and preserve registers to prevent crashes from optimized code.

2. You need to store the pfn of your trampoline (to run the first N copied bytes then perform a stateless jump to the original function + N bytes). This would permit you to call the function. But most people don't secure this call with register preserving code, because it requires yet another ASM wrapper to call the orig function, and it requires you to store the original state somewhere known (your C++ function may manipulate the stack, your code, unless it is also a C++ compiler, isn't going to guess correctly where on the stack it is without you storing a pointer to the original pushad).

3. The caller needs to store the internal detour data themselves. This is easily fixed even in this situation, but this is nevertheless a problem with this setup.


So the goals become clear. We want to fit a wider situational capability, we want to eliminate the above problems, and we want to make it clean. Preferably something that looks like:

Code:
hostent* __stdcall mygethostbyname(const char* name){
	//log("gethostbyname called with name=\"%s\"!\n",name);

	if(g_cfg.host_addr != 0){
		if(strstr(name,"gwgt")){
	//		log("gethostbyname was called with \"gwgt*\" as the param, substituting our address instead!\n");
			in_addr sin;
			sin.S_un.S_addr=g_cfg.host_addr;
			hostent* pt=gethostbyaddr((char*)&host,4,AF_INET);
	//		log("we got a ptr: %p, returning!\n",pt);
			resolved=true;
			return pt;
		}
	}
	return invoke_orig(); // This magically calls the original hooked function for us
}

// Later on.. in some other function..
..
detour_options_s opts = {0};
opts.foo = ...;
...
opts.bar = ...;
detour_create((void*)&gethostbyname,opts);

And that's it. We have our hook function, and we set up a detour. Nothing else. All of the "bleh" stuff is handled invisibly, and we have a handful of very simple API with a structure to hold options.

My solution is rather straightforward yet ugly, it's C-like in construction (3 exported API and the options struct). In addition, to make that "magic" orig invoke happen, and to give the hook function a LOT more information about the actual call that's in progress, I decided to construct this 'call context' structure which stores the register states, return address, and various other pieces of information, and offered a few methods to manipulate the current call, including that magic invoke. This context would be per-call, making it thread safe, and it would permit us to make this "magical" context sensitive invocation.

Here's the interface I came up with:

Code:
/* 
 * Copyright (c) 2010 Justin "jMerliN" Summerlin
 *
 * This program is proprietary software and is solely owned by the
 * copyright holder(s).  Unauthorized use and/or redistribution is strictly
 * prohibited.
 *
 */

#ifndef _DETOURS_H_
#define _DETOURS_H_

// Detour engine options..
// Comment this out to enable X64 -- NOT CURRENTLY SUPPORTED
#define DETOURS_ENGINE_X86 1

// ============================================
// = Detour options structure
// ============================================
struct detour_options_s {
	// Managed is a special detouring mode, pass_args indicates that the hook wants the arguments to the invocation
	bool managed, pass_args;

	// The general call of a hooked function is like this: [CALLER] call <func> -> [HOOK] <my code> ... call <orig_func_trampoline>
	// -> [ORIG CODE] <original code> return -> [HOOK] <more of my code> return -> [CALLER]
	// In this case, the 'managed' mode comes into play.  If this detour is MANAGED, the detour system will intercept the intended call,
	// it will then call the pre_handler (if any) before invoking the original function (NOTE: IN MANAGED, YOU CANNOT AD-HOC INVOKE THE ORIGINAL, 
	// however you may perform a cold invocation, more on that later).  Once the original function returns, the system will then invoke the post handler.
	// In managed mode, one of these MUST be a valid handler.  In unmanaged mode, the system will intercept the orig call and simply call pre_handler,
	// then it is up to the handler to invoke the original at its leisure (if at all).
	void* pre_handler;
	void* post_handler;

	unsigned char bytes;	// How many bytes should we patch over (MAY: Use a disassembler to compute this automatically?)
	unsigned char argc;		// How many arguments does this function have?

	// More fine-tuned options..
	// WARNING: We *ONLY* support stdcall and cdecl, ensure that your function does NOT get
	// optimized to an inline, etc.  Thiscall and fastcall should be supported by default,
	// args passed in registers can be captured (see opts below).
	bool local_fn_stdcall;
	bool hooked_fn_stdcall;
};

// ============================================
// = Detour context structure
// ============================================
struct detour_context_s {
	// Processor state
	unsigned int eflags;
	unsigned int eax, ecx, edx, ebx, esp, ebp, esi, edi;

	// This will pass a value to the post handler from the pre handler...
	// if force_retval below is true, this will be used as the return value
	// of the call to the [CALLER].
	unsigned int data;

	// DEFAULT = FALSE, this will force the return value (eax before final retn) to the value of data if true..
	bool force_retval;

	// Return address..
	unsigned int retaddr;

	// ID value for this breakpoint..
	const int bpid;

public:
	// Helper funcs
	bool			invoke_orig_manual	(unsigned int argc, ...);
	bool			invoke_orig			();
	void			set_retval			(unsigned int retval);
	unsigned int	get_argc			();
	unsigned char	get_bytes_patched	();
	bool			is_managed			();
	unsigned int	get_orig_retaddr	();
	bool			set_arg				(unsigned int id,unsigned int val);
	unsigned int	get_arg				(unsigned int id);
private:
	detour_context_s(){}
	explicit detour_context_s(const detour_context_s& o){}
};

// ==================================
// == Actual DETOURS api ==
// ==================================
int detour_create(void* target, detour_options_s* opts);
bool detour_remove(int idx);
int detour_clear_all();

// And the one "weirdo" detour_invoke.  This will perform a cold-invocation of the hooked function BYPASSING the indicated detour.  As mentioned above,
// a detour handler can call detour_invoke (the context holds the id here), which will permit it to bypass itself to call the original function.  This is
// in the case that you're hooking an API but you also wish to call it, for instance MessageBoxA.  You intercept the call,
// and manipulate some data, but you may also want to alert yourself as a debug-style of an error while in this detour, or anywhere
// in your code, this will allow you to make this invocation bypassing the detour.
int detour_invoke(int idx, int argc, ...);

#endif // #ifndef _HOOK_H_

Note, this is just the "working copy." I haven't completed this yet nor debugged. I will update this post with the implementation once it's done and at least decently tested. It's almost done, but the impl. is a bit tricky and complicated so documenting it for you guys will take some work as well. :cool:
 
HAARP
Joined
Dec 3, 2006
Messages
632
Reaction score
109
Wtf someone posts a decent advanced tutorial and they give him a lengthy ban 2 days later..

All I can say is thanks for your contributions here jMerliN, some of us do appreciate your work and help in this section. Well, at least I do as I can't talk for everyone else.
 
Mythic Archon
Joined
Apr 8, 2007
Messages
759
Reaction score
21
Someone should change JMerliN status to programming god and give him immunity. Probably the finest (only real?) programmer Ragezone has ever seen. Would hate to see him leave.

inb4whothefuckisthisguy
 
Banned
Banned
Joined
Jul 19, 2010
Messages
22
Reaction score
3
Wtf someone posts a decent advanced tutorial and they give him a lengthy ban 2 days later..

All I can say is thanks for your contributions here jMerliN, some of us do appreciate your work and help in this section. Well, at least I do as I can't talk for everyone else.

I've got the entire thing hammered out. Tested and it works great. I'll release the entire thing here at some point, and I'm putting it under LGPL. You should all tell MentaL he's a moron for letting El ban me for no reason (literally, the ban includes no reason, and in the account appeal section they won't give me a reason).

Abusive mods here = Ducking gay. I'll probably stop helping anyone here because the reward for paying for sub and being a contributing member of this forum = getting banned for absolutely no reason. That's excellent. It's easy to see why anyone with a botnet would kill this forum. It's a pile of poop.
 
Legendary Battlemage
Loyal Member
Joined
Apr 7, 2009
Messages
647
Reaction score
25
So most everyone who does hook coding or hacking of games (or just api hijacking in general) has probably heard of detours. I got tired of re-writing a basic detouring engine and being limited by the ones I made because of this simplicity. So I wrote a massively over-complicated one that's highly generic and should fit just about any situation. Previously I'd create a detour as so:

Code:
detour_s* detour_create(void* addr,void* newfunc,u8 bytes){
//	log("detouring function at %p to %p ...\n",addr,newfunc);

	if(bytes < 5)return 0;

	// Change the permissions on memory
	unsigned long oldprot=0;
	VirtualProtect(addr,bytes,PAGE_EXECUTE_READWRITE,&oldprot);

	// Create our detour object
	detour_s* det=new detour_s(bytes,addr);
	memcpy(det->rawdata,addr,bytes);
	memset(addr,0x90,bytes);

	// Write the jump patch in
	char* ptr=(char*)addr;
	*ptr++ = (char)0xE9;
	unsigned long jmp_to_new = (unsigned long)newfunc - ((unsigned long)addr + 5);
	*((unsigned long*)ptr) = jmp_to_new;

	// Finish off our trampoline
	ptr = &det->rawdata[bytes];
	*ptr++ = (char)0xE9;
	unsigned long jmp_to_orig = ((unsigned long)addr + bytes) - ((unsigned long)ptr + 4);
	*((unsigned long*)ptr) = jmp_to_orig;

	det->orig_func_trampoline = det->rawdata;

	// Re-protect the memory
	VirtualProtect(addr,bytes,oldprot,NULL);

	//log("detour complete.  returning new detour object at %p!\n",det);

	// Return the new trampoline!
	return det;
}

This is fairly simple, removing it is trivial (copy original bytes, delete buffers). Now, using this meant you needed a naked gateway to properly set up the call to your C++ function:

Code:
typedef hostent* (__stdcall*ghpfn)(const char* name);
ghpfn ogethostbyname;
detour_s* gethost_det=0;
bool resolved=false;
hostent* __stdcall mygethostbyname(const char* name){
	//log("gethostbyname called with name=\"%s\"!\n",name);

	if(g_cfg.host_addr != 0){
		if(strstr(name,"gwgt")){
	//		log("gethostbyname was called with \"gwgt*\" as the param, substituting our address instead!\n");
			in_addr sin;
			sin.S_un.S_addr=g_cfg.host_addr;
			hostent* pt=gethostbyaddr((char*)&host,4,AF_INET);
	//		log("we got a ptr: %p, returning!\n",pt);
			resolved=true;
			return pt;
		}
	}
	return ogethostbyname(name);
}

// Ugly inefficient wrapper :o
void __declspec(naked) gethostbyname_(void){
	__asm {
		pushad;
		push [esp+0x24];
		call mygethostbyname;
		push eax;
		add esp,4;
		popad;
		mov eax,[esp-0x24];
		retn 4;
	}
}

Problems:
1. You need to make an ugly naked wrapper like this for each function to properly copy the arguments and preserve registers to prevent crashes from optimized code.

2. You need to store the pfn of your trampoline (to run the first N copied bytes then perform a stateless jump to the original function + N bytes). This would permit you to call the function. But most people don't secure this call with register preserving code, because it requires yet another ASM wrapper to call the orig function, and it requires you to store the original state somewhere known (your C++ function may manipulate the stack, your code, unless it is also a C++ compiler, isn't going to guess correctly where on the stack it is without you storing a pointer to the original pushad).

3. The caller needs to store the internal detour data themselves. This is easily fixed even in this situation, but this is nevertheless a problem with this setup.


So the goals become clear. We want to fit a wider situational capability, we want to eliminate the above problems, and we want to make it clean. Preferably something that looks like:

Code:
hostent* __stdcall mygethostbyname(const char* name){
	//log("gethostbyname called with name=\"%s\"!\n",name);

	if(g_cfg.host_addr != 0){
		if(strstr(name,"gwgt")){
	//		log("gethostbyname was called with \"gwgt*\" as the param, substituting our address instead!\n");
			in_addr sin;
			sin.S_un.S_addr=g_cfg.host_addr;
			hostent* pt=gethostbyaddr((char*)&host,4,AF_INET);
	//		log("we got a ptr: %p, returning!\n",pt);
			resolved=true;
			return pt;
		}
	}
	return invoke_orig(); // This magically calls the original hooked function for us
}

// Later on.. in some other function..
..
detour_options_s opts = {0};
opts.foo = ...;
...
opts.bar = ...;
detour_create((void*)&gethostbyname,opts);

And that's it. We have our hook function, and we set up a detour. Nothing else. All of the "bleh" stuff is handled invisibly, and we have a handful of very simple API with a structure to hold options.

My solution is rather straightforward yet ugly, it's C-like in construction (3 exported API and the options struct). In addition, to make that "magic" orig invoke happen, and to give the hook function a LOT more information about the actual call that's in progress, I decided to construct this 'call context' structure which stores the register states, return address, and various other pieces of information, and offered a few methods to manipulate the current call, including that magic invoke. This context would be per-call, making it thread safe, and it would permit us to make this "magical" context sensitive invocation.

Here's the interface I came up with:

Code:
/* 
 * Copyright (c) 2010 Justin "jMerliN" Summerlin
 *
 * This program is proprietary software and is solely owned by the
 * copyright holder(s).  Unauthorized use and/or redistribution is strictly
 * prohibited.
 *
 */

#ifndef _DETOURS_H_
#define _DETOURS_H_

// Detour engine options..
// Comment this out to enable X64 -- NOT CURRENTLY SUPPORTED
#define DETOURS_ENGINE_X86 1

// ============================================
// = Detour options structure
// ============================================
struct detour_options_s {
	// Managed is a special detouring mode, pass_args indicates that the hook wants the arguments to the invocation
	bool managed, pass_args;

	// The general call of a hooked function is like this: [CALLER] call <func> -> [HOOK] <my code> ... call <orig_func_trampoline>
	// -> [ORIG CODE] <original code> return -> [HOOK] <more of my code> return -> [CALLER]
	// In this case, the 'managed' mode comes into play.  If this detour is MANAGED, the detour system will intercept the intended call,
	// it will then call the pre_handler (if any) before invoking the original function (NOTE: IN MANAGED, YOU CANNOT AD-HOC INVOKE THE ORIGINAL, 
	// however you may perform a cold invocation, more on that later).  Once the original function returns, the system will then invoke the post handler.
	// In managed mode, one of these MUST be a valid handler.  In unmanaged mode, the system will intercept the orig call and simply call pre_handler,
	// then it is up to the handler to invoke the original at its leisure (if at all).
	void* pre_handler;
	void* post_handler;

	unsigned char bytes;	// How many bytes should we patch over (MAY: Use a disassembler to compute this automatically?)
	unsigned char argc;		// How many arguments does this function have?

	// More fine-tuned options..
	// WARNING: We *ONLY* support stdcall and cdecl, ensure that your function does NOT get
	// optimized to an inline, etc.  Thiscall and fastcall should be supported by default,
	// args passed in registers can be captured (see opts below).
	bool local_fn_stdcall;
	bool hooked_fn_stdcall;
};

// ============================================
// = Detour context structure
// ============================================
struct detour_context_s {
	// Processor state
	unsigned int eflags;
	unsigned int eax, ecx, edx, ebx, esp, ebp, esi, edi;

	// This will pass a value to the post handler from the pre handler...
	// if force_retval below is true, this will be used as the return value
	// of the call to the [CALLER].
	unsigned int data;

	// DEFAULT = FALSE, this will force the return value (eax before final retn) to the value of data if true..
	bool force_retval;

	// Return address..
	unsigned int retaddr;

	// ID value for this breakpoint..
	const int bpid;

public:
	// Helper funcs
	bool			invoke_orig_manual	(unsigned int argc, ...);
	bool			invoke_orig			();
	void			set_retval			(unsigned int retval);
	unsigned int	get_argc			();
	unsigned char	get_bytes_patched	();
	bool			is_managed			();
	unsigned int	get_orig_retaddr	();
	bool			set_arg				(unsigned int id,unsigned int val);
	unsigned int	get_arg				(unsigned int id);
private:
	detour_context_s(){}
	explicit detour_context_s(const detour_context_s& o){}
};

// ==================================
// == Actual DETOURS api ==
// ==================================
int detour_create(void* target, detour_options_s* opts);
bool detour_remove(int idx);
int detour_clear_all();

// And the one "weirdo" detour_invoke.  This will perform a cold-invocation of the hooked function BYPASSING the indicated detour.  As mentioned above,
// a detour handler can call detour_invoke (the context holds the id here), which will permit it to bypass itself to call the original function.  This is
// in the case that you're hooking an API but you also wish to call it, for instance MessageBoxA.  You intercept the call,
// and manipulate some data, but you may also want to alert yourself as a debug-style of an error while in this detour, or anywhere
// in your code, this will allow you to make this invocation bypassing the detour.
int detour_invoke(int idx, int argc, ...);

#endif // #ifndef _HOOK_H_

Note, this is just the "working copy." I haven't completed this yet nor debugged. I will update this post with the implementation once it's done and at least decently tested. It's almost done, but the impl. is a bit tricky and complicated so documenting it for you guys will take some work as well. :cool:

Pro.
Simply...pro.
 
Back
Top