Оберточная библиотека-перехватчик

от автора

Доброго времени суток всем!
Недавно я выкладывал свою реализацию перехватчика(тут).
Получился он, чего уж греха таить, громоздким и коряво работающим. В 1 же из комментариев отписался уважаемый k_d и упомянул о своей обертке над mhook(MHook vs Zuma).
Конечно, меня заинтересовала альтернатива, работающая с перехватом не только __cdecl’а но и, более того, позволяющая ставить перехват практически в любом месте кода.
Решение понравилось и я решил переписать свою библиотеку, которую использую для различного рода перехватов, начисто, с использованием материала от k_d. Текущий вариант умеет перехватывать все, до чего дотянутся загребуще-экспериментальные ручки программиста и парсить аргументы из стека(по крайней мере, потенциально идея это все умеет. Наверное.)
Представляю Вашему вниманию то, что получилось.

Для того, что бы скомпилировать рабочую библиотеку, понадобится MHook, обертка от k_d, средневыпрямленные руки, голова, вытащенная из-под стола и уточка, с которой можно посоветоваться: 1364504067-clip-42kb.

Итак, поехали!

I) Функционал:

1) Вывод логов в файл:

void debug_msg(const char* func_name, const char* txt,...) { 	va_list args; 	FILE *file; 	char file_name[256]="\0"; 	strcpy(file_name,"C:\\VariadicDump\\"); 	strcat(file_name,func_name); 	strcat(file_name,".txt"); 	fopen_s(&file,file_name, "a"); 	va_start(args,txt); 	vfprintf(file,txt,args); 	va_end (args); 	fprintf(file, "\n"); 	fclose(file); } 

Использование:

debug_msg("Advanced", "--%s arg list started--", funcName); 

При funcName = «Hello, world, you read this message so many times» получим файл C:\VariadicDump\Advanced.txt со следующим содержимым:

--Hello, world, you read this message so many times arg list started--

2) Парс lua_State (только для инициализированного и частного для данной функции! Полезен, к примеру, в ММО Runes of Magic для перехвата чата, в других случаях не использовал):

void Parse_LuaState(lua_State *L, const char *func_name) { 	int n = lua_gettop(L);    	std::vector<const char*> names; 	for(int i = 0; i < n; i++) 	{ 		size_t arg2Len = 0; 		names.push_back(luaL_checklstring(L, i, &arg2Len)); 	} 	string str = ""; 	for (unsigned int i=0; i<names.size(); i++) 	{ 		str.append(names[i]); 		str.append("; "); 	} 	debug_named_msg(func_name, str.c_str()); } 

Использование:

void lua_hook(lua_State *L) { Parse_LuaState(L, "Lua"); } 

К примеру, в *L было 2 аргумента — тип чата и текст(1 и «hello, world, are you hate to hear it again?»). Функция запишет в «C:\VariadicDump\Lua.txt» следующее:

1; hello, world, are you hate to hear it again?

3) Парсинг аргументов из стека:

void parseArgs(int *ptr, int size, char* funcName) { 	debug_msg("Advanced", "--%s arg list started--", funcName); 	for(int i=0; i*4<size; i++) 	{ 		debug_msg("Advanced", "  |---Element %d: %d", i, ptr[i]); 	} 	debug_msg("Advanced", "--arg list finished--\n"); } 

Использование:

void con_hook(Context *context) { 	int *ptr = (int*)(void*)(context->ESP+4); 	parseArgs(ptr, con_arg_amount, __FUNCTION__); } 

В результате будут выведены аргументы из расчета esp + index*4 name##_arg_amount/4 раз.
По-хорошему, там правильнее и красивее передавать кол-во аргументов, но это не сложно сделать и самому в случае надобности.

4) Адрес основного модуля перехватываемого приложения:

int GetMainModuleAddress() { 	DWORD dwAddress = NULL; 	HANDLE hthSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, _getpid()); 	if (hthSnapshot)  	{ 		MODULEENTRY32 me = { sizeof(me) }; 		if (Module32First(hthSnapshot, &me)) 		{ 			CloseHandle(hthSnapshot); 			dwAddress = (DWORD)me.modBaseAddr; 		} 	} 	return dwAddress; } 

Ничего особо, нужен, к примеру, в Рифте — там виртуальный адрес рассчитывается следующим образом:

#define RECALC(name)\ 	name##_Detour = (t##name)((int)name##_Detour-0x400000+rift); 

где name##_Detour — перехватываемая функция.

5) Функции attach/detach:

Простая обертка для установки/удаления трамплина. Нужны чисто для того, что бы не лазить по коду в поисках DllMain 🙂

Последняя в списке, но не последняя в наших сердцах и полезностью в функционале — unhook’er:

BOOL UnHookFunction(DWORD addr, unsigned char *lpBackup) { 	DWORD dwAddr = addr; 	if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)dwAddr, lpBackup, 6, 0)) 		return TRUE; 	return FALSE; } 

Просто и со вкусом восстанавливает код, в котором стоял трамплин.

II) Макросы

Макросы — основная фишка данного проекта, которая, собственно, и позволила свернуть рабочую часть до неприлично малых размеров.

1) RECALC:

#define RECALC(name)\ 	name##_Detour = (t##name)((int)name##_Detour-0x400000+rift); 

Упоминался выше, нужен для пересчета адреса.

2) DETACH:

#define DETACH(name1)\ 	UnHookFunction((DWORD)name1##_Detour,name1##_var);\ 

Обертка над восста… освобождением кода от трамплина.

3) ATR/ATR_R:

#define ATR(name)\ 	name##_var = RegHook((long)name##_Detour, (long)(void*)name##_hook)  #define ATR_R(name)\ 	RECALC(name)\ 	name##_var = RegHook((long)name##_Detour, (long)(void*)name##_hook) 

Два брата-близнеца для установки трамплина. RegHook — функция обертки над Mhook, описание можно прочесть в исходниках либо в . В той же статье указано, что есть Context и чем закусывать, что бы не бегать голышом по набережной и не звать ежика из тумана(шучу, этого просто так вам никто не расскажет).

Используется в функции attach();

4) DTR:

#define DTR(name)\ 	DETACH(name) 

Обертка над оберткой.
Спорная надобность, но читается красивее рядом с ATR ^^ (DETACH — реликт эпохи, жаль удалять).

Используется в функции detach();

5) RF_O_UP_FUNC_CONTEXT:

#define RF_O_UP_FUNC_CONTEXT(name1, adres, args)\ 	typedef void (__cdecl * t##name1 ) ();\ 	t##name1 name1##_Detour = ( t##name1 ) ( adres );\ /*перехватываемая функция*/ 	void name1##_hook(Context *context);\ /*вызывается при переходе из предыдущей функции; весь функционал перехватчика- здесь*/ 	BYTE *name1##_var;\  /*здесь хранится код оригинальной функции*/ 	int name1##_arg_amount = args;\ /*суммарный размер аргументов, которые нужно отобразить(см. выше)*/ 

Самый хитровыдернутый макрос в этой деревне. Собственно, он и занимается объявлением перехватчика и перехватываемой функции, объявляет переменную для сохранения исходного кода от трамплина и т.д.

Пример:

RF_O_UP_FUNC_CONTEXT(con, 0x60D710, 0x8); 

Объявляем 2 функции — con_Detour и con_hook, привязываем перехватываемую функцию к адресу 0x60D710 и указываем, что если нам нужно будет проанализировать аргументы, то мы получим 2 штуки в выводе.

III) Пример использования

RF_O_UP_FUNC_CONTEXT(con, 0x60D710, 0x8);  void lua_hook(Context *context) { 	int *ptr = (int*)(void*)(context->ESP+4); //помещаем адрес 1 аргумента из стека в указатель ptr 	parseArgs(ptr, con_arg_amount, __FUNCTION__); // парсим аргументы согласно макросу, 2 штуки - чужих портсигаров нам не надо! 	debug_msg("LuaDump", "%s", ptr[0]); //выводим 1 аргумент отдельно - а вот захотелось.Да. } 

Для работы dll могут понадобиться либы от lua, а также mhook + обертка от k_d.

Здесь можно скачать сорцы библиотеки.

Также еще раз благодарю k_d за отлично проделанную работу и идею!

Всем спасибо, всего доброго! И да восторжествует уточка! Кря!

ссылка на оригинал статьи http://habrahabr.ru/post/174647/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *