[Development] Function interceptor with unknown arguments

It just so happened that from time to time I have fun analyzing clients of MMO games on the topic of various kinds of utilities or getting information about the action of certain game aspects. Most of the information comes from client parsing in IDA Pro or OllyDbg.

Since I don’t have much experience in this action, I usually take a function in a trite way, write a dll interceptor and analyze the arguments of the function obtained during the interception.

Unfortunately, even an IDA is not ideal - getting function arguments that are true is often problematic (the simplest example is lua functions, lua_state is defined by Ida as int). And, as the client analyzes it, the dll sometimes grows with just an obscene amount of functions, their declarations and descriptions, and also other good. As a result, we get kilometers of code.

But the pursuit of beauty lives in most of us - therefore there is a desire to somehow “tamp” this code, to make it more readable. For example, when writing a dll, I like to use macros, regions, possibly reduce the number of functions of the same type, etc.

And not so long ago, I got the idea to compress the code even more by compressing the declarations and descriptions of functions, variables, and other goodness into 1 macro. Ideally, drop the argument list of the intercepted function into a file. At the moment, I have written a relatively universal interceptor for __cdecl functions (since the level of knowledge in this area is not too high for me, I can assume that it will only work adequately for Windows x86, maybe there are some other limitations). For interception I usually use Detours x86, sometimes a simple analog.

Actually, the code:

#define cdecl_hook(name1)\  /*Macro definition*/void name1##_hook(int a1, ...)\ /*Declare hooker*/
{\
	int check_s = 0;\ 
	__asm{mov check_s, esp}\  /*Save esp state*/int *ptr = &a1;\  /*Get pointer to 1st arg, equialent of va_list*/
	debug_msg("Advanced",true,"--%s arg list started--", __FUNCTION__);\  /*debug_msg() - vfprintf wrapper*/for(int i=0; i*4<name1##_arg_amount; i++)\
	{\
		debug_msg("Advanced",true,"  |---Element %d: %d", i, ptr[i]);\
	}\  /*Arg list -> file(Advanced.txt)*/
	debug_msg("Advanced",true,"--arg list finished--\n");\ 
	__asm{lea ecx, a1}\ /*Move addr of a1 to ecx*/
	__asm{mov eax, name1##_arg_amount}\  /*move size of args in stack(can get from IDA, for ex.) to eax*/
	__asm{label_loop: }\ /*Start loop*/
	__asm{mov ebx, dword ptr[ecx+eax-4]}\ /*Move args from stack to ebx in loop and push ebx*/
	__asm{push ebx}\
	__asm{sub eax,4}\
	__asm{cmp eax,0}\
	__asm{jg label_loop}\
	__asm{call dword ptr[name1##_Detour]}\ /*Call original function*/
	__asm{mov esp, check_s}\ /*Restore stack, same as __asm{add esp, name1##_arg_amount}*/
}\


#define RF_O_UP_FUNC(name1, address, args)\  /*Define needed functions and variables*/typedefvoid(* t##name1 )();\ 
	/*__declspec(dllexport) */t##name1 name1##_Detour = ( t##name1 ) ( address );\ /*Define original function and bind to addr*/void name1##_hook(int a1, ...);\ /*Define hooker*/int name1##_arg_amount = args;\ /*Summary size of args in stack, can get from IDA*/
	cdecl_hook(name1); /*Call hooker definition*/


Well, actually, an example of declaring all this stuff:

RF_O_UP_FUNC(resources, 0x687054, 0x4C); 
RF_O_UP_FUNC(hooker, 0x17E4D18, 7); 
RF_O_UP_FUNC(begin, 0x689BA0, 5); 


So, with 1 line we declare an interceptor function, indicate which function and at what address we want to intercept, and also, when intercepting, we get a list of arguments (in this example, all arguments will be written to the file as int, you can add the format to the macro).

Further, everything is simple - if we use Detours, then the attachment will look something like this:
DetourAttach(&(PVOID&)resources_Detour, resources_hook).

The rest, in principle, does not need explanation.

Example output of arguments:

--resources_hook arg list started--
  |---Element 0: 204181
  |---Element 1: 204181
  |---Element 2: 1277574
  |---Element 3: 1363294854
  |---Element 4: 1
  |---Element 5: 0
  |---Element 6: 0
  |---Element 7: 0
  |---Element 8: 0
  |---Element 9: 0
  |---Element 10: 0
  |---Element 11: 0
  |---Element 12: 0
  |---Element 13: 0
  |---Element 14: 0
  |---Element 15: 0
  |---Element 16: 1
  |---Element 17: 100
  |---Element 18: 1
--arg list finished--


In total, with the help of a couple of small macros, we got the opportunity to get the code well.
Disadvantages:
1) Does not currently work with __stdcall, __thiscall, and other calling conventions. I will not refuse help or advice on this matter.
2) As I already mentioned, experience in this matter is quite small, so there may well be jambs that I did not take into account, so please do not troll much.
3) I did not find analogues, but this does not mean that there are no more adequate ways / it is impossible to optimize the current one. Comments on this subject would also be welcome.

Thank you for attention!

Also popular now: