Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

articles:exploit-review-0x18 [2017/09/05 02:55] (текущий)
Строка 1: Строка 1:
 +====== exploit-review-0x18 ======
 +<​sub>​{{exploit-review-0x18.odt|Original file}}</​sub>​
 +
 +====== exploits review\\ 18h выпуск ======
 +
 +крис касперски ака мыщъх, a.k.a. nezumi, a.k.a elraton, a.k.a. souriz, no-email
 +
 +**этот необычный во всех отношениях обзор посвящен… ошибкам отладчиков,​ приводящим к утрате контроля над отлаживаемым приложением еще на стадии загрузки ****exe-****файла в память,​ что для исследователей малвари зачастую заканчивается катастрофой — только хотел взглянуть на очередного зверька под отладчиком на своей основной машине как отладчик проскакивает точку входа и живчик поселяется на компьютере,​ ищи его потом!**
 +
 +===== Syser – проскок EP на сетевых и сменных носителях =====
 +
 +**brief**:​работая с отладчиком Syser версии 1.95.1900.0894,​ выпущенной в начале 2008 года,​ мыщъх обратил внимание,​ что при загрузке программ с сетевых дисков и сменных носителей (типа дискет) отладчик проскакивает точку входа в файл (она же Entry Point или, сокращенно,​ EP), передавая подопытной программе бразды правления и утрачивая над ней всякий контроль (впрочем,​ глобальные точки останова на API-функции срабатывают нормально,​ если, конечно,​ не забыть заблаговременно их поставить). мыщъх отослал разработчикам баг-репорт,​ получив подтверждение об ошибке вкупе с обещанием ее исправить,​ но… версии Syser'​а продолжают выходить одна за другой,​ а ошибка как была, так и осталась. почему же она вообще возникает?​ откуда берется и зачем никуда не девается?​ дело в том, что механизм загрузки файлов с жестких дисков и сменных носителей принципиально различен. исполняемые файлы (и динамические библиотеки),​ расположенные на винчестере,​ операционная система просто проецирует в память,​ что (грубо говоря) превращает их в "​файл подкачки,​ доступный только на чтение"​. подкачка страниц с диска в оперативную память происходит только по мере обращения к ним, а при недостатке памяти немодифицированные страницы не вытесняются в настоящий файл подкачке,​ а просто высвобождаются под новые данные. действительно,​ какой смысл записывать их в своп? проще вновь обратиться к исполняемому файлу — он же нм куда не денется за это время. ну, это с жесткого диска он не денется (и потому система блокирует к нему доступ на время выполнения),​ а вот с дискеты или сетевого диска… там во-первых,​ скорость _намного_ ниже, чем у винчестера,​ а во-вторых,​ сеть в любой момент может лечь, а диска — быть вынута. разработчики Windows учли такую возможность развития событий и решили проблему путем предварительной загрузки файла в оперативную память,​ за что отвечает совсем другой компонент загрузчика,​ игнорируемый Syser'​ом со всеми отсюда вытекающими…
 +
 +**targets**:​все существующие в данный момент версии Syser'​а;​
 +
 +**exploit**:​не требуется,​ достаточно попробовать загрузить любой исполняемый файл с сетевого диска, дискеты или CD/DVD, как результат все скажет сам за себя;
 +
 +**solution**:​всегда копируйте файлы с сетевых дисков (сменных носителей) на жесткий диск перед их загрузкой в Syser.
 +
 +{{exploit-review-0x18_Image_0.png?​553}}
 +
 +Рисунок 1 попытка загрузка файла с сетевого диска в Syser ведет к "​проскоку"​ точки входа в файл и утрате контроля за отлаживаемым приложением
 +
 +===== Syser: BSOD на файлах определенных типов =====
 +
 +**brief**:​экспериментируя со штатным линкером от Microsoft на предмет создания предельно компактных исполняемых файлов,​ мыщъх получил от одного из бета-тестеров баг-репорт,​ что на его машине мыщъх'​иные файлы при активном Syser'​е (активном — просто запущенном,​ загрузки файла в отладчик не требуется) роняют XP SP2 в BSOD с указанием на драйвер Syser.sys, принадлежащий,​ как и следует из его названия,​ сабжевому отладчику. мыщъх попробовал воспроизвести данный эффект на своей горячо любимой W2K и… получил тот же самый BSOD. вот тебе, бабушка,​ и оптимизация! зато какой шикарный способ борьбы с активными Syser'​ом! на Soft-Ice данный эффект не распространяется,​ однако,​ Soft-Ice на хакерских машинах встречается все реже и реже, особенно на новых системах,​ которые Soft-Ice вообще не поддерживает. разработчикам Syser'​а был отправлен очередной баг-репорт,​ но никакого ответа от них непоследовало и когда они исправят дефект в отладчике — неизвестно. подробнее об этой ошибке можно прочитать на мыщъх'​ном блоге: http://​souriz.wordpress.com/​2008/​05/​09/​syser-causes-bsod/​
 +
 +**targets**:​все существующие версии Syser'​а;​
 +
 +**exploit**:​готовую бинарную сборку файла, вызывающего BSOD (вместе с исходными текстами и командными файлами для сборки в среде MS VC),​мыщъх выложил на свой сервер:​ http://​nezumi.org.ru/​souriz/​TF-bug.zip. впрочем,​ сам исполняемый файл тут не причем (он может быть любым),​ главное — это опции линкера для его сборки,​ которые выглядят следующим образом:​
 +
 +$link.exe %NIK%.obj /FIXED /​ENTRY:​nezumi/​SUBSYSTEM:​CONSOLE
 +
 +**/ALIGN:16 /​MERGE:​.rdata=.text /​STUB:​stub** KERNEL32.LIB
 +
 +Листинг 1 сборка компактного исполняемого файла, высаживающего Syser'​а на полный BSOD
 +
 +здесь: **/​ALIGN:​16** – установить выравнивание секций на файле и в памяти по границе 10h, что намного меньше "​официально"​ разрешенного значения (1000h – для выравнивания секций в памяти,​ что соответствует размеру одной страницы и 200h для выравнивания секций на диске, что соответствует размеру одного сектора),​ однако,​ для драйверов такого ограничения нет, а грузит их один и тот же системный загрузчик,​ поэтому,​ обозначенный трюк вполне законен для всех операционных систем из линейки NT вплоть по Вислу/​Server 2008 включительно.
 +
 +**/​MERGE:​.rdata=.text** – говорит линкеру объединить секцию .rdata (секция данных,​ доступная только на чтение) с секцией .text (содержащей код), в результате чего у нас образуется всего одна секция и мы экономим до 10h байт, которые в противном случае ушли бы на выравнивание второй секции.
 +
 +**/​STUB:​stub** — приказывает линкеру использовать пользовательскую MS-DOS "​затычку",​ за место той, что по умолчанию вставляется в начало всякого PE-файла и выводит на экран известное сообщение,​ что программа жить не может без Windows. В целях оптимизации мыщъх использовал "​голый"​ MS-DOSold-exe заголовок с отрезанным телом файла.
 +
 +в результате всех этих ухищрений размер файла (с полезным кодом, намного более функциональным чем "​hello,​ world"​) составил 624 байта,​ и это при том, что файл написан на языке Си (пускай и не без ассемблерных вставок) и собран штатными средствами! Какой именно из этих параметров привел к падению Syser'​а — мыщъх не знает, а экспериментировать,​ роняя свою систему в BSOD, – этим пуская разработчики Syser'​а занимаются. Кстати,​ если загрузить такой файл в Syser, а не просто запустить его при активном Syser'​е,​ то все будет нормально и BSOD не появится.
 +
 +solution:​выгружать Syser перед запуском потенциально небезопасных файлов (благо,​ Syser, в отличии от Soft-Ice, поддерживает возможность выгрузки из памяти "на лету"​).
 +
 +{{exploit-review-0x18_Image_1.png?​553}}
 +
 +Рисунок 2 BSOD вызываемый Syser'​ом при запуске "​оптимизированного"​ файла
 +
 +===== IDA-Pro: проскок EP на файлах с Image Base равной нулю =====
 +
 +**brief**:​как известно,​ IDA-Pro с некоторых времен не только дизассемблер,​ но еще и отладчик. отладчик не то, чтобы сильно мощный (_намного_ слабее Ольги),​ но все-таки намного более удобный,​ чем постоянное переключение между дизассемблером и внешним отладчиком,​ а потому активно используемый хакерами наряду с исследователями малвари. и все было бы хорошо,​ но если "​скормить"​ дизассемблеру файл с нулевым базовым адресом,​ он нормально загрузит его по этому самому нулевому адресу,​ но вот при попытке запуска отладчика мы получим следующее ругательство:​ "IDA Pro couldn'​t automatically determine if the program should berebased in the database because the database format is too old anddoesn'​t contain enough information.Create a new database if you want automated rebasing to work properly.Notice you can always manually rebase the program by using theEdit, Segments, Rebase program command"​ ("//​IDA-Pro ////не может автоматически определить:​ должна ли программа быть перемещена в базе, поскольку формат базы очень старый и не содержит достаточно информации. Создайте новую базу, если вы хотите автоматизировать перемещение. Примечание:​ вы можете переместить базу и самостоятельно:​ ////Edit -> Segment ////​-////>​ Rebase program//"​),​ после чего нам предлагают нажать на "​ОК",​ чтобы согласиться. но, чтобы мы не нажали — "​ОК"​ или "​Escape",​ IDA-Pro запускает процесс,​ полностью утрачивая контроль и мерзко игнорируя все ранее установленные точки останова. вот такой замечательный анализ малвари! к слову сказать,​ файл с нулевым базовым адресом загрузки при наличии в нем таблицы перемещаемых элементов совершенно законен и операционная система переместит его в памяти автоматически. а вот IDA-Pro – нет. мыщъх послал баг-рапорт Ильфаку и тот сказал,​ что будем посмотреть,​ так что следите за новостями:​ http://​souriz.wordpress.com/​2008/​05/​14/​773-bug-in-ida-pro-fails-to-debug-zero-base-pe/​
 +
 +**target**:​все существующие на данный момент версии IDA-Pro (проверялось на 4.7 и 5.2);
 +
 +**exploit**:​исходный текст программы,​ демонстрирующей уязвимость (вместе с ключами сборки),​ приведен ниже:
 +
 +#include <​stdio.h>​
 +
 +main()
 +
 +{
 +
 +printf("​hello,​ world!\n"​);​
 +
 +}
 +
 +Листинг 2 "​hello-world.c"​ – вполне типичная программа на Си
 +
 +$cl /c hello-world.c
 +
 +$link hello-world.obj /FIXED:NO /BASE:0
 +
 +Листинг 3 сборка программы с нулевым базовым адресом загрузки
 +
 +**solution**:​перед запуском отладчика,​ удостовериться,​ что программа находится по "​правильным"​ адресам,​ в противном случае переместить ее на новое место (например,​ по адресу 400000h) через Edit -> Segment -> Rebase program, или же с помощью утилиты ms editbin.exe (в последнем случае потребуется перезагрузка файла в IDA-Pro с потерей всех предыдущих результатов анализа).
 +
 +{{exploit-review-0x18_Image_2.png?​553}}
 +
 +Рисунок 3 реакция IDA-Pro на попытку отладки файла с нулевым базовым адресом загрузки
 +
 +===== full disclose:\\ универсальный способ выхода из-под отладчика ​ =====
 +
 +**brief**:​в процессе реализации проекта по переносу Soft-Ice на Вислу и Server 2008 (при финансовой поддержке фирмы K7), мыщъх исследовал Soft-Ice вместе с кучей других отладчиков и с удивлением обнаружил,​ что все они спроектированы неправильно и отлаживаемая программа может вырваться из-под контроля еще до начала трассировки — непосредственно в процессе загрузки файла в отладчик. Чтобы выяснить почему так происходит,​ нам необходимо разобраться как вообще отладчики "​стопорят"​ программу,​ а делают они это приблизительно так: сначала процесс загружается в память,​ отладчик отслеживает этот момент и, считывая из PE-заголовка точу входа в файл, устанавливает по этому адресу программную или (реже) аппаратную точку остановка,​ после чего возвращает бразды правления операционной системе,​ которая посредством функции KiUserApcDispatcher создает первичный поток. Прототип функции приведен на рис. 4, откуда видно, что стартовый адрес потока передается как аргумент по NTAPI соглашению,​ то есть через пользовательский стек, после чего система начинает подгружать статически прилинкованные динамические библиотеки,​ вызывая функцию DllMain (если, конечно,​ DLL имеет точку входа) и каждой DLL.
 +
 +{{exploit-review-0x18_Image_3.png?​503}}
 +
 +Рисунок 4 прототип функции KiUserApcDispatcher с которой начинает жизнь любой поток
 +
 +Если DllMain возвращает ноль, система сообщает об ошибке загрузки приложения (см. рис. 5) и завершает процесс. В противном же случае (когда все динамические библиотеки рапортуют,​ что инициализация прошла успешно) управление передается первичному потоку процесса,​ где находится точка останова,​ установленная отладчиком. При попытке ее выполнения процессор генерирует исключение типа breakpoint exception, отлавливаемое операционной системой и передающей отладчику бразды правления.
 +
 +{{exploit-review-0x18_Image_4.png?​470}}
 +
 +Рисунок 5 сообщение операционной системе о неудачной инициализации динамической библиотеки,​ ведущей к завершению процесса
 +
 +Таким образом,​ вырисовывается следующая (не очень-то приятная для отладчиков и исследователей малвари) картина:​
 +
 +  - функции DllMain всех статически прилинкованных библиотек отрабатывают еще до начала "​всплытия"​ отладчика и в них может находиться все что угодно! В принципе,​ исполняемый файл может ограничиться процедурой-пустышкой,​ разместив зловредный код в одной из своих DLL и он будет выполнен еще до "​всплытия"​ отладчика!
 +  - функции DllMain выполняются уже после установки отладчиком точки останова на EntryPoint и, если это программная точка, представленная опкодом CCh, динамическая библиотека может обнаружить,​ что процесс находится под отладкой и либо сделать что-то нехорошее,​ либо же вызывать VirtualProtect,​ присвоить соответствующей странице памяти атрибут на запись,​ снять точку останова,​ восстановив оригинального содержимое первого байта (естественно,​ для этого его нужно где-то хранить или вычислять эвристическим путем, что не представляет никакой проблемы),​ тогда — точки останова уже не будет, процессор не сгенерирует исключение и отладчик не сможет перехватить управление;​
 +  - если же отладчик установил аппаратную точку останова,​ то… можно пойти другим путем, изменив из DllMain аргумент функции KiUserApcDispatcher,​ хранящий стартовый адрес первичного потока,​ тогда, по завершении инициализации всех динамических библиотек,​ поток начнет свое выполнение отнюдь не с точки входа, прописанной в PE-заголовке,​ а оттуда,​ откуда DLL пожелает,​ вырываясь из-под контроля отладчика. Естественно,​ чтобы менять аргумент — его еще необходимо найти, что не так-то просто,​ поскольку,​ в зависимости от версии операционной системы и прочих обстоятельств,​ он меняет свое местоположение в широких пределах,​ поэтому,​ приходится прибегать к различным эвристическим методикам,​ например,​ считывать из PE-заголовка оригинальную точку входа и искать в стеке двойное слово, совпадающее с ней.
 +Конечно,​ теоретики от программирования скажут,​ что мол, в чем проблема-то?​ Операционная система уведомляет отладчик о загрузке всех динамических библиотек еще до начала выполнения DllMain и ничего не стоит исследовать их на вшивость. Многие отладчики даже имеют специальную опцию — останавливаться на DllMain, вот только… ни у одного из них она по умолчанию не включена,​ а анализировать DllMain в отладчике — гиблое дело, особенно если она вызывается из стартового библиотечного кода. Но даже отлаживая DllMain мы можем не заметить (не понять) как она меняет аргумент функции KiUserApcDispatcher или снимает точку останова,​ поскольку,​ для этого необходимо _знать_ что именно у нас находится в памяти и _зачем_ оно там находится. С точки зрения не очень опытного реверсера это выглядит вполне невинно. Ну меняет что-то такое DLL в стеке и завершается,​ но ведь это не страшно,​ правда?​ Мы же ведь все равно вернемся в отладчик при начале выполнение процесса,​ верно? А вот и неверно!!! Уже не вернемся!!! Так что проблема на самом деле очень серьезна. Подробнее ее планируется осветить на мыщъх'​ином блоге (http://​souriz.wordpress.com/​),​ но и в этой статье нарытой информации предоставлено не мало.
 +
 +{{exploit-review-0x18_Image_5.png?​553}}
 +
 +Рисунок 6 блог мыщъх'​а
 +
 +**targets**:​MS VS,​ WinDbg, OllyDbg, ImmDbg, Syser, Soft-Ice, IDA-Pro и многие другие…
 +
 +**exploit**:​демонстрационный пример (вместе с полными исходными текстами,​ правда,​ без комментариев) выложен на OpenRCE в репрозиторий мыщъх'​а:​ https://​www.openrce.org/​repositories/​users/​nezumi/​quux-crackme.zip,​ занимающий в упакованном виде меньше 2х килобайт,​ совместимый с Вислой/​Server 2008 и не конфликтующий с Syser'​ом (в смысле не "​выбивающий"​ из него BSOD), но ускользающий из-под всех вышеперечисленных отладчиков;​
 +
 +{{exploit-review-0x18_Image_6.png?​553}}
 +
 +Рисунок 7 файловый репрозиторий мыщъх'​а на OpenRCE
 +
 +**solution**:​отсутствует. Ну, не то, чтобы _совсем_отсутствует,​ но общих решений нет и помимо DllMain существует туча всего такого,​ исполняющегося _до_ "​всплытия"​ отладчика,​ взять хотя бы те же TLS-callback'​и,​ например,​ а потому загрузка программы в отладчик — равносильна ее запуску и потенциально опасные файлы можно использовать только на специальной (виртуальной) машине,​ на которой нет ничего такого,​ что было бы жалко потерять (примечание:​ Кстати,​ в "Olly Advanced — популярном plug-in'​е для Ольги — есть опция "​Flexible Breakpoints",​ убирающая программную точку останова,​ что предотвращает обнаружение отладчика,​ но не в силах противостоять подмене стартового адреса первичного потока. Аналогичным образом дела обстоят и с другим популярным plug-in'​ом — PhantOm, имеющим опцию "​Remove EP Break",​ назначение которой говорит само за себя).
 +
 +**full disclose:**
 +
 +**комментированные исходные тексты ****quux-crackme**
 +
 +Для облегчения понимая принципов работы отладчиков и способов "​побега"​ из-под них, мыщъх приводит комментированные исходные тексты quux-crackme,​ однако,​ прежде,​ чем их читать,​ рекомендуется попробовать разобраться с crackme самостоятельно,​ благо он очень простой,​ даже start-up убит для облегчения понимая и коду там — всего несколько сотен машинных команд без каких бы то ни было трюков или выкрутасов.
 +
 +// основной исполнимый файл,
 +
 +// подгружающий специальным образом
 +
 +// спроектированную динамическую библиотеку
 +
 +#include <​windows.h>​
 +
 +// импортируем из DLL функцию baz();
 +
 +// просто импортируем! не вызываем!
 +
 +// на самом деле мы вызываем DllMain
 +
 +__declspec(dllimport) baz();
 +
 +// объявляем прототип функции foo()
 +
 +// реализация которой представлена ниже
 +
 +foo();
 +
 +
 +
 +// точка входа в исполняемый файл
 +
 +// чтобы убить библиотечный start-up
 +
 +// пришлось отказаться от main(),
 +
 +// указывая имя точки входа линкеру
 +
 +// в параметре /ENTRY (для ms link)
 +
 +__declspec(naked) nezumi()
 +
 +{
 +
 +__asm{
 +
 +NOP; // сюда отладчик ставит точку останова
 +
 +NOP; // несколько подряд идущих инструкций NOP…
 +
 +NOP; // …не имеют никакого особого смысла,​
 +
 +NOP; // и вставлены лишь для того, чтобы код
 +
 +NOP; // не был уж совсем тривиальным
 +
 +CALL foo; // зовем функцию foo
 +
 +RETN; // завершаем свое выполнение
 +
 +; // функция foo, как легко видеть,​
 +
 +; // сообщает о том, что обнаружен отладчик,​
 +
 +; // и этот код получает управление _только_
 +
 +; // под отладчиком (после того, как вырвется
 +
 +; // из-под его контроля)
 +
 +
 +
 +NOP; // а вот истинная точка входа!!!
 +
 +; // которой передается управление за счет
 +
 +; // подмены стартового адреса первичного потока
 +
 +; // из функции DllMain статически прилинкованной DLL
 +
 +CALL ds:[baz]; // зовем функцию baz из DLL, выдающую мудрость
 +
 +RETN; // завершаем свое выполнение (с чистой совестью!)
 +
 +}
 +
 +}
 +
 +foo()
 +
 +{// эта функция вызывается только из-под отладчика,​ причем ​
 +
 +// не всякого,​ а OllyDbg, в котором есть ошибка - когда DLL
 +
 +// возвращает 0 (а она его возвращает,​ если есть Olly), то
 +
 +// отладчик должен прибить процесс,​ но Olly все-таки позволяет
 +
 +// нажать F9 и продолжить выполнение,​ получив это сообщение:​
 +
 +MessageBox(0,"​\ndebugger is detected!\n\n","​hello,​hacker!",​0);​
 +
 +}
 +
 +Листинг 4 исходный код quux-crackme.c (головной исполняемый файл)
 +
 +#include <​windows.h>​
 +
 +bar();// объявляем функцию bar()
 +
 +// функция foo, вызываемая из экспортируемой
 +
 +// функции baz, представленной далее по тексту
 +
 +__declspec(naked)foo()
 +
 +{
 +
 +__asm
 +
 +{
 +
 +CALL bar
 +
 +RETN
 +
 +}
 +
 +}
 +
 +// выводим пословицу на Малайском с переводом на английский,​
 +
 +// смысл который сводится к тому, что все отладчики - дерьмо
 +
 +// и ломать нужно не отладчиком,​ а лапами,​ хвостом и головой
 +
 +bar() { MessageBox(0,"​\n ​ -> use the correct tool for the correct job <​-\n\n",​
 +
 +" ​ -<* kee chang jahb thak-a-thaen *>​-",​0);​ }
 +
 +// экспортируем функцию baz(),
 +
 +// вызываемую исполняемым файлом
 +
 +// из той его части, что получает
 +
 +// управление,​ благодаря подмене
 +
 +// стартового адреса первичного потока
 +
 +__declspec(dllexport) int baz(){ return foo();}
 +
 +// точка входа в динамическую библиотеку
 +
 +// вызывающая при различных системных событиях,​
 +
 +// например,​ создании потока,​ в том числе и первичного
 +
 +BOOL WINAPI dllmain(HINSTANCE hinstDLL,​DWORD fdwReason,​LPVOID lpvReserved)
 +
 +{
 +
 +// определяем смещения основных полей PE-файла
 +
 +#define PE_off0x3C//​ смещение PE-заголовка в файле
 +
 +#define EP_off0x28//​ смещение поле Entry-Point,​ относительно PE
 +
 +
 +
 +// опкод программной точки останова
 +
 +#define SW_BP0xCC
 +
 +
 +
 +// объявляем переменные
 +
 +BYTE*  base_x;
 +
 +DWORD  pe_off;
 +
 +DWORD  ep_off;
 +
 +BYTE*  ep_adr;
 +
 +DWORD* ​ stackg;
 +
 +BYTE*  BaseAddress;​
 +
 +DWORD  RegionSize;
 +
 +char buf[_MAX_PATH];​
 +
 +MEMORY_BASIC_INFORMATION lpBuffer;
 +
 +
 +
 +// для отладки динамической библиотеки
 +
 +// раскомментирование этой строки приводит
 +
 +// к генерации исключения,​ передаваемому
 +
 +// отладчику,​ который тут же "​всплывает"​
 +
 +//__asm{ int 03}
 +
 +
 +
 +// получаем имя exe-файла на тот случай
 +
 +// если кто-то его захочет переименовать
 +
 +// из quux-crackme.exe во что-то другое
 +
 +GetModuleFileName(0,​buf,​_MAX_PATH);​
 +
 +
 +
 +// определяем базовый адрес exe-файла
 +
 +base_x = (BYTE*) GetModuleHandle(buf);​
 +
 +
 +
 +// определяем смещение PE-заголовка в файле
 +
 +pe_off = *((DWORD*)(base_x + PE_off));
 +
 +
 +
 +// определяем адрес точки входа, преобразуя
 +
 +// ее в указатель на двойное слово, что уже
 +
 +// не будет работать на 64-битных системах,​
 +
 +// но на 64-битных системах по любому все
 +
 +// будет сильно иначе
 +
 +ep_off = *((DWORD*)(base_x + pe_off + EP_off));
 +
 +
 +
 +// вычисляем линейный виртуальный адрес точки входа
 +
 +ep_adr = base_x + ep_off;
 +
 +
 +
 +// проверяем:​ а не установлена ли точка останова на EntryPoint
 +
 +// если установлена - возвращаем системе ноль, сигнализируя
 +
 +// о провале инициализации DLL (или, как вариант,​ здесь мы бы
 +
 +// могли снять точку останова,​ чтобы выйти из-под контроля
 +
 +// отладчика,​ чем, собственно говоря,​ и занимается qux-crackme:​
 +
 +// https://​www.openrce.org/​repositories/​users/​nezumi/​crackme-qux.zip
 +
 +if (*ep_adr == 0xCC) return 0;
 +
 +
 +
 +// передаем функции VirtualQuery адрес аргумента hinstDLL,
 +
 +// переданного процедуре dllmain через стек и, тем самым,
 +
 +// узнаем приблизительное значение регистра ESP без
 +
 +// всяких ассемблерных вставок - красота!
 +
 +VirtualQuery((LPCVOID)&​hinstDLL,​ &​lpBuffer,​ sizeof(lpBuffer));​
 +
 +
 +
 +// VirtualQuery возвращает нам базовый адрес блока,
 +
 +// выделенного под стек, который мы и будем ковырять
 +
 +// на предмет аргумента функции KiUserApcDispatcher
 +
 +BaseAddress = ((BYTE*)lpBuffer.BaseAddress);​
 +
 +
 +
 +// определяем размер блока, чтобы не выйти за его границы
 +
 +// (на тот случай,​ если вдруг в стеке по каким-то причинам
 +
 +// искомого аргумента не окажется)
 +
 +RegionSize = lpBuffer.RegionSize-sizeof(DWORD);​
 +
 +
 +
 +// ищем в стеке двойное слово, совпадающее
 +
 +// с адресом точки входа в исполняемый файл,
 +
 +// начиная поиск со дна стека и продвигаясь наверх
 +
 +// (искомый аргумент лежит почти на дне, но где именно:​
 +
 +// заведомо неизвестно,​ вот и приходится рыскать как лосям)
 +
 +for(RegionSize;​RegionSize>​0;​RegionSize-=sizeof(DWORD))
 +
 +if (((DWORD)ep_adr)==( *(DWORD*)(BaseAddress+RegionSize)))
 +
 +// искомый аргумент (или нечто на него похожее) найден!
 +
 +// увеличиваем его значение на 0Dh, проскакивая не только
 +
 +// бряк, установленный отладчиком,​ на точку входа, но и
 +
 +// первый CALLc RETN, передавая управление сразу на
 +
 +// второй CALL, который IDA-Pro, не найдя ссылок
 +
 +// даже не удосужилась дизассемблировать
 +
 +(*(DWORD*)(BaseAddress+RegionSize)) +=0xD; // change EP
 +
 +// примечание:​ здесь по идее должен стоят break,
 +
 +// типа - раз нашли адрес, то чего шерстить по стеку?​!
 +
 +// а вот мы продолжаем шерстить на тот случай,​ если
 +
 +// вдруг нашли что-то не то и адрес точки входа совпал
 +
 +// с чем-то другим,​ ес-но это может привести к краху
 +
 +// программы и если искомый адрес встречается два и
 +
 +// более раз, следовало бы отказаться от его модификации
 +
 +// именно так и следует поступать в коммерческих защитах
 +
 +
 +
 +return 1;// рапортуем о нормальной инициализации
 +
 +
 +
 +}
 +
 +Листинг 5 исходный текст динамической библиотеки quux-dll.c
 +
 +Разумеется,​ на самом деле это никакой не crackme (в обычном смысле этого слова),​ а настоящий exploit типа proof-of-concept,​ предназначенный для тестирования отладчиков на "​вшивость"​ и написанный так, чтобы предельно облегчить его понимание,​ благодаря чего, сломать его — плевое дело, но вот если наворотить здесь хитрый код, то шансы на его понимание резко снижаются,​ а шансы на "​взлом"​ отладчика,​ соответственно,​ резко возрастают.Однако,​ борьба с отладчиками на данном этапе не входила в задачу мыщх'​а — этому посвящена отдельная колонка в "​Хакере",​ а в этой — мы говорим исключительно о _дырах_ (прорыв сквозь отладчик — в первую очередь дефект проектирования отладчика,​ т. е. дыра, и только потом антиотладочный прием).
 +
 +