Сrackme, прячущий код на API-функциях

Классический перехват API-функций


Прежде, чем приступать к кодированию, разберем алгоритм классического перехвата, пример готовой реализации можно найти, например, в wmfhotfix.cpp от Ильфака Гильфанова, исходный код которого можно скачать по адресу: castlecops.com/downloads-file-499-details-WMF_hotfix_source_code.html:

q       перехватчик запоминает несколько первых команд API-функции в своем буфере (buf);

q       дописывает к ним jump на остаток API-функции (after_thunk);

q       в начало API-функции ставит jump на X-код (thunk);

q       X-код выполняет все, что задумано, и прыгает на buf, отдавая управление API-функции;

Эта схема позволяет внедрять X-код перед вызовом API-функции. Внедрение-после-завершения осуществляется чуть-чуть сложнее (call вместо jump с подменой адреса возврата), но… классическая схема имеет кучу проблем и подводных граблей, которые очень сложно обойти (ведь они под водой!).

Переменная длина машинных команд на x86 затрудняет определение их границ и мы не можем просто взять и скопировать несколько байт, ведь при этом легко разрезать последнюю машинную команду напополам и тогда вместо перехвата мы поимеем крах. Приходится либо тащить за собой примитивный дизассемблер (а это очень много ассемблерных строк), либо ориентироваться на стандартный пролог push ebp/mov ebp,esp, занимающий всего три байта (55 8B EC), в то время как jump на thunk требует как минимум пять! За прологом же может идти все, что угодно — и sub esp,xxx и push, и… В общем-то, вариантов не так уж и много, но закладываться на них ни в коем случае нельзя, иначе перехватчик получится не универсальным и не переносимым.

Проблема номер два — точки останова. Допустим, отладчик типа soft-ice или OllyDbg установил программную точку останова на перехваченную функцию, внедрив в ее начало машинную команду INT 03h (CCh) с предварительным сохранением оригинального содержимого в своем теле.
Что должен делать в этом случае наш перехватчик? Даже если он распознает искаженный пролог, копировать начало API-функции в buf ни в коем случае нельзя, ведь тогда будет скопирована и точка останова. Когда она сработает и передаст управление отладчику — отладчик просмотрит свои записи, увидит, что по данному адресу лично он не устанавливал никакой точки останова и не будет ее восстанавливать! Как следствие — возникает необработанное исключение и крах. Как быть, что делать? Можно, конечно, проанализировать первый байт API-функции и если он равен CCh отказаться от перехвата, только с таким предложением лучше сразу идти в зад. Зачем нам нужен перехватчик, который ничего не перехватывает?

К тому же, механизм DEP, поддерживаемый XP SP2 и выше, запрещает выполнение кода в области данных, значит, располагать buf в стеке нельзя. То есть можно, конечно, но перед этим необходимо вызывать VirtualProtect, назначая права доступа на исполнение. Это не проблема, но все-таки напрягает. Исходный код классического перехватчика получается слишком большим и совершенно ненаглядным, так что не будем его здесь приводить, а лучше покурим хорошей травы и подумаем как нам быть.


Содержание раздела