Как стать автором
Обновить

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

Время на прочтение3 мин
Количество просмотров5.6K
Так уж случилось, что время от времени я развлекаюсь анализом клиентов ММО-игр на тему различного рода полезностей или получения информации о действии тех или иных игровых аспектов. Основное количество информации приносит разбор клиента в 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) Не нашел аналогов, однако это не значит, что нету более адекватных способов / нельзя оптимизировать текущий. Замечания по данному поводу также были бы кстати.

Благодарю за внимание!
Теги:
Хабы:
Всего голосов 16: ↑14 и ↓2+12
Комментарии16

Публикации

Истории

Работа

Программист C++
103 вакансии
QT разработчик
4 вакансии

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань