В прятки с отладчиком Крис Касперски ака мыщъх Хакер, номер #086, стр. 086-120-4 Внимание: имеется вполне осязаемая вероятность вызова API-функции посторонним потоком в момент, когда она будет восстановлена перехватчиком! В этом случае перехватчик упустит вызов, поэтому использовать данный алгоритм в "сторожевых" программах типа брандмауэра ни в коем случае нельзя). Семафоры и interlock'и здесь не спасают, и единственное, что в наших силах, — проектировать программу так, чтобы в каждый момент времени одну и ту же функцию мог вызывать только один поток. Самое простое — создать однопоточное приложение или вызывать API-функции через переходники с блокировками, примеры которых можно найти в любой книге, посвященной программированию многоЦПшных систем. Так что данный способ перехвата все-таки имеет право на существование, поэтому рассмотрим его подробнее, вплотную занявшись программированием. [создаем jump на thunk] Написать перехватчик можно и на чистом Си, но настоящие хакеры так не поступают, да и не интересно это. Мы будем писать на ассемблере! Для упрощения интеграции перехватчика с кодом защищаемой программы используем ассемблерные вставки. Microsoft Visual C++ поддерживает спецификатор naked (голый), запрещающий компилятору самовольничать. "Обнаженные" функции не содержат ни пролога, ни эпилога, компилятор даже не вставляет ret — все это мы должны сделать самостоятельно. Если, конечно, захотим. Лучшего средства для создания защитных механизмов, пожалуй, и не придумаешь! Подробное описание naked-функций можно найти в SDK (см. раздел Naked Function Calls), нам же достаточно знать, что naked-функции объявляются как __declspec(naked) function_name(), придерживаются cdecl-соглашения (аргументы передаются справа налево и удаляются из стека материнской функцией) и не формируют стековый фрейм, то есть смещения локальных переменных и аргументов нам придется рассчитывать самостоятельно. Попробуем в качестве разминки создать процедуру, внедряющуюся в начало некоторой API-функции и передающую управление на thunk. Проще всего это сделать с помощью команды jmp thunk, однако при этом значение thunk придется рассчитывать вручную, поскольку в x86 процессорах команда jmp ожидает не само смещение, а разница между смещением целевого адреса и концом команды jmp. Вычислить-то его несложно, просто отнял/прибавил, и все, но… это же нужно высаживаться на самомодифицрующийся код, что не входит в наши планы. Лучше пойти другим путем, обратившись к конструкции mov reg32, offset thunk/jmp reg32. Это на один байт длиннее и к тому же портит регистр reg32, однако это не так страшно. Все API-функции придерживаются соглашения stdcall, то есть принимают аргументы через стек, а возвращают значение через регистровую пару [edx]:eax, то есть значение eax при входе в функцию не играет никакой роли и может быть безболезненно искажено. |