Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #86, ФЕВРАЛЬ 2006 г.

В прятки с отладчиком

Крис Касперски ака мыщъх

Хакер, номер #086, стр. 086-120-7


Вообще-то, можно просто проанализировать значение регистра ESP до и после выполнения API-функции. Дельта и будет тем количеством байт, на которые мы должны увеличить ESP. Но есть и более короткие пути: если перехватываемая функция известна, то достаточно просто создать функцию, имеющую тот же самый прототип. На языке Си это будет выглядеть так (в данном случае перехватывается MessageBoxA, в заголовке которой насильно прописывается строка "hacked", подтверждающая, что перехватчик исправно работает):

__stdcall thunk(HWND h, LPCTSTR lpTxt, LPCTSTR lpCaption, UINT uType)

{

_do(_UNINSTALL_THUNK_); // восстанавливаем оригинальную функцию

MessageBox(h,lpTxt,"hacked",uType); // вызываем оригинальную функцию

_do(_INSTALL_THUNK_); // вновь устаналиваем thunk

}

[перехват произвольной API-функции]

Язык ассемблера выгодно отличается от Си тем, что позволяет реализовать универсальный перехватчик, поддерживающий все функции и не захламляющий стек дублированными аргументами. Все очень просто. Вернемся к схемам нашего стека приведенным выше. Скажи, зачем держать в стеке сразу два адреса возврата, когда можно обойтись и одним? Ведь функции отрабатывают последовательно, передавая эстафету как степени реактивной ракеты. Короче.

В момент передачи управления на thunk на вершине стека находится адрес возврата в прикладную программу. Запоминаем его в глобальной переменной saved и пишем сюда адрес возврата в thunk, на который будет передано управление после выхода из API-функции. Скопировав в стек сохраненный адрес, мы сможем вернуться в прикладную программу по ret, и при этом не придется химичить с аргументами!

А вот как эта красота реализуется:

// ассемблерный код универсального перехватчика, работающего

// через подмену адреса возврата и способный нести на своем

// борту "боевую начинку", выполняемую до или после вызова API-функции

__declspec( naked ) thunk()

{

__asm{

push offset _UNINSTALL_THUNK_ ; снимаем thunk с функции

call _do_asm

add esp,4

pop [saved] ; подменяем ret_addr на post_exit

push offset post_exit

; "боевая нагрузка" до вызова функции

; ========================================================================

; подменяем строку заголовка и меняем тип диалогового окна

pre_run:

mov eax, offset my_string

; mov [esp+12], offset my_string ; ms vc не поддерживает такую команду :(

mov [esp+12], eax

mov [esp+10h], 1

mov eax,[p] ; вызываем перехваченную функцию as is

jmp eax

; "боевая нагрузка" после вызова функции

;=========================================================================

; ничего не делаем (добавьте сюда собственный код, если хотите)

post_exit:

push offset _INSTALL_THUNK_ ; снова устанавливаем thunk на функцию

call _do_asm

add esp,4

push [saved] ; возвращаемся туда, откуда нас вызывали

Назад на стр. 086-120-6  Содержание  Вперед на стр. 086-120-8