[В разработке] Перехватчик функций с неизвестными аргументами

в 9:21, , рубрики: c++, detours, hook, macro, variadic, Visual Studio, перехват, Песочница, хук, метки: , , , , ,

Так уж случилось, что время от времени я развлекаюсь анализом клиентов ММО-игр на тему различного рода полезностей или получения информации о действии тех или иных игровых аспектов. Основное количество информации приносит разбор клиента в IDA Pro или OllyDbg.

Так как опыта у меня не так уж и много в данном действе, обычно я банально беру какую-либо функцию, пишу dll-перехватчик и анализирую аргументы функции, полученные при перехвате.

К сожалению, даже IDA не идеальна — получить аргументы функции, соответствующие истине, зачастую проблематично(самый простой пример — lua-функции, lua_state определяется Идой как int). Да и, по мере анализа клиента, dll обрастает порой просто неприличным количеством функций, их объявлений и описаний, а также прочего добра. В результате получаем километры кода.

Но стремление к прекрасному живет в большинстве из нас — потому возникает желание как-то этот код «утрамбовать», сделать более читаемым. К примеру, я при написании dll люблю использовать макросы, регионы, по возможности уменьшать количество однотипных функций и т.д.

И вот не так давно я загорелся идеей еще сильнее сжать код путем сжатия объявления и описания функции, переменных и прочего добра в 1 макрос. В идеале — скидывать список аргументов перехваченной функции в файл. На данный момент я написал относительно универсальный перехватчик для __cdecl функций(т.к. уровень знаний в данной области у меня не слишком высок, могу предположить, что адекватно работать будет только для Windows x86, возможно, есть еще какие-то ограничения). Для перехвата использую обычно Detours x86, иногда — простенький аналог.

Собственно, код:

#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*/
	typedef void (* 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*/

Ну и, собственно, пример объявления всего этого добра:

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

Вот так 1 строкой мы объявляем функцию-перехватчик, указываем, какую функцию и по какому адресу мы хотим перехватить, а также при перехвате получаем список аргументов(в данном примере все аргументы будут записаны в файл как int, можно добавить формат в макрос).

Далее все просто — если используем Detours, то аттач будет выглядеть как-то так:
DetourAttach(&(PVOID&)resources_Detour, resources_hook).

Остальное, в принципе, не нуждается в пояснениях.

Пример вывода аргументов:

--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--

Итого, при помощи пары небольших макросов, мы получили возможность неплохо ужать код.
Недостатки:
1) На данный момент не работает с __stdcall'ом, __thiscall'ом и другими соглашениями о вызовах. Не откажусь от помощи или совета по данному поводу.
2) Как я уже упоминал, опыта в данном вопросе достаточно мало, так что вполне могут быть косяки, которых я не учел, так что просьба сильно не тролить.
3) Не нашел аналогов, однако это не значит, что нету более адекватных способов / нельзя оптимизировать текущий. Замечания по данному поводу также были бы кстати.

Благодарю за внимание!

Автор: Demogor

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js