Введение в крэкинг с нуля, используя OllyDbg - Глава 42 — Архив WASM.RU
Ок, теперь мы перейдём к более сложным упаковщикам и увидим, что используемые методы могут быть очень разными в зависимости от случая, поэтому у нас должен быть большой запас различных трюков. В этот раз нас будет занимать ACPROTECT 1.09, распакуй-меня со всякого рода защитами.
В этом распакуй-меня есть всего понемножку, поэтому пойдём медленно, анализируя каждую часть защиты.
Распакуй-меня приложен к данной статье, так что здесь нет никакой проблемы: открываем его в пропатченной OllyDbg с установленными плагинами для её скрытия.
Вот он открыт в OllyDbg, и так как все защиты должны работать, проверить, запускается ли в OllyDbg до того, как мы что-нибудь сделаем с этим распакуй-меня.
Ок, в моём parcheado4, который был приложен к предыдущей главе со всеми отмеченными исключениями и используя плагин HideOD 0.12 с конфигурацией, указанной на картинке ниже, нет никакой проблемы и всё прекрасно запускается. Вспомним, что этот упаковщик проверял с помощью API Process32Next, какие процессы открыты, и если обнаруживал, что он был открыт не с помощью двойного щелчка, то есть в отладчике или загрузчике, то не запускался, но плагин HideOD пытается защищаться от этого трюка и аннулирует антиотладку без каких-либо хлопот с нашей стороны.
Так что нам не приходится думать об этом, поэтому можем сконцетрироваться на нахождении OEP.
Из разных методов, которых так много используется для нахождения OEP в OllyDbg, используем встроенный поисковик OEP’ов, так что идём во вкладку SFX и устанавливаем галочку на «TRACE REAL ENTRY BLOCKWISE», нажимаем «OK» и перезапускаем OllyDbg, которая через некоторое время остановится здесь.
Теперь вопрос заключается в том, настоящий ли это OEP, или же это ситуация с украденными байтами.
Посмотрим в этот момент на стек.
Если уберём галочку поиска OEP’а и перезапустим:
Видим, что когда произошла остановка на OEP, стек содержит на два значения больше, чем при запуске, и за исключением редких случаев – это признака украденных байтов, так что готовимся к их поиску.
Но сначала должны запустить скрипт для обработки аппаратных точек останова, который мы рассматривали в прошлых главах, так как из-за небольшого бага нам нужно применять его для этого и для многих других упаковщиков.
Теперь можем попробовать, так как если сейчас мы хотим остановиться на OEP, который нам уже известен, и установить на неё HBP ON EXECUTION, то видим, что не останавливаемся на нём, так как программа его дезактивировала. С использованием скрипта также не останавливается, так как здесь имеется баг.
До это ничего не указывает, где происходит ошибка, поэтому нужно разобраться как ведёт себя скрипт и как его нужно изменить.
Запускаем снова распакуй-меня в OllyDbg и устанавливаем два BP. Столько же в KiUserExceptionDispatcher, сколько в CALL’е, которые ведёт нас к ZwContinue, как это показано на изображении.
Теперь устанавливаем аппаратную точку останова на выполнение по адресу OEP, для того, чтобы попробовать, сотрётся оно или нет. Можем использовать другой адрес, но мы знаем точно, что здесь выполнение произойдёт точно, и случится это после всех манипуляций упаковщика, поэтому и используем именно этот адрес.
Здесь в начале выбираем «GOTO EXPRESSION» и устанавливаем адрес OEP, равный 4271B5.
Здесь устанавливаем HBP ON EXECUTION.
Отмечаем первую строку и устанавливаем HARDWARE BPX ON EXECUTION.
Теперь нажимаем RUN.
Останавливаемся на BP и смотрим стек.
Когда останавливаемся на KiUserExceptionDispatcher, указатель на контекст находится во второй ячейке стека, смотрим контекст через дамп.
Конечно, не буду объясняет все значения структуры CONTEXT, но вот будем использовать на данный момент.
В регистрах меняем режим отображения, чтобы увидеть отладочные, если такой опции нет, значит, мы уже в правильном режиме отображения.
Это знаменитые отладочные регистры от DR0 до DR7. DR0 по DR3 соответствуют аппаратным точкам останова.
Когда программа останавливается на BP, то в этот момент отладочные регистры OllyDbg заполняется значениями HARDWARE BPX, которые они имеют на данный момент. Как видим, остановились на BP, и нам показываются текущие значения HBP, в DR0 находится 4271B5, что является значением установленного нами HBP, DR4 и DR5 не используются, DR6 нас пока не интересуется и DR7 указывает на тип аппаратной точки останова согласно таблице, которую мы рассмотрим позже. Важно то, что DR0 содержит значение нашего HARDWARE BPX. Теперь смотрим CONTEXT.
Здесь видим положение регистров DR0-DR3 в контексте. Жёлтым выделен адрес 4271B5, который соответствует установленному HBP, который сейчас активен. Другие три, выделенные розовым, равны нулю, так как мы установили только один HBP, поэтому они пусты.
То есть когда программа доходит до вышеупомянутого обработчика исключений, эти значения контекста обнуляются. Делаем RUN, чтобы оказаться в втором BP.
Видим, что при проходе через обработчик исключений в DR0 помещается ноль, и моя ошибка заключается в скрипте. Снова восстановим его, так как если помните, когда доходим до 2-го BP, скрипт восстанавливает их, но здесь ошибка, так как затирание DR0 – это только подготовка, установка контекста, истинное использование HBP происходит при выходе из ZwContinue и вход в RING0, до выхода из программы, то есть больше чем снова установить HBP в OLLYDBG не могу из за нуля в CONTEXT’е. При выходе из ZwContinue они будут стёрты (короче, Нарваха превзошёл сам себя в образности мыслей, и что он хотел сказать, я не понял J – прим. пер.).
Тогда есть две возможности. Первая – это когда произойдёт остановка на первом BP сохранить значения, прочитанные из отладочных регистров в CONTEXT’е, а когда дойдём до второго BP, снова скопируем в контекст в Dr0…Dr3 имеющиеся у нас значения, которые будут верно восприняты при возвращении в программу.
Вторая возможность, которая пришла мне в голову, гораздо короче, но сначала надо узнать, где в CONTEXT’е находится адрес возврата в программу?
Если к адресу начала CONTEXT’а прибавим 0B8, то получим указатель на адрес возврата. На моей машине он равен:
12FC8c + 0b8 = 12FD44
То есть 12FD44 на моей машине указывает на адрес возврата в программ. Если в скрипте установим сюда BP, когда остановимся на нём, то значит, уже произошёл возврат в программу после стирания HBP, и можно снова их установить. Смотрим, что остаётся в скрипте.
Код (Text):
-------------------------------------------------------------------------------- <p> var aux inicio: bphws 4271b5, "x" trabajo: eob pirulo run pirulo: log eip cmp eip, 7c91eaec je quitar cmp eip, 7c91eb03 je restaurar cmp eip,aux je restaurar2 jmp final quitar: bphwc 4271b5 jmp trabajo restaurar: mov aux,esp mov aux,[aux] add aux,0b8 mov aux,[aux] log aux bp aux jmp inicio restaurar2: bc aux jmp inicio final: MSGYN "Continuar?" cmp $RESULT,1 je inicio ret --------------------------------------------------------------------------------В начале изменяемого нами скрипта находится
Код (Text):
var auxvar – это команда для объявления переменной. Объявляем переменную aux, которую я использую для хранения и расчёта адрес возврата в программу после исключения.
Код (Text):
restaurar: mov aux,esp mov aux,[aux] add aux,0b8 mov aux,[aux] log aux bp aux jmp inicioЗдесь видим часть, где восстанавливаются HBPы, потом в aux помещается значение ESP.
Код (Text):
mov aux,[aux]Вот, что здесь происходит: так как aux указывает на ESP, содержимое которого указывает на начало структуры CONTEXT, то после этой строки в aux будет находиться адрес начала структуры CONTEXT (в моём случае это 12fc8c), затем прибавляется 0b8, в результате чего получается 12fd44, а на ваших машинах это будет значение указатель на адрес возврата. И наконец:
Код (Text):
mov aux,[aux]Перемещаем в aux то, что там должно быть, теперь там, наконец-то, адрес последнего возврата, на следующей строке BP aux, устанавливаем BP на адресе возврата из исключения, а затем снова запускаем программу, и наконец:
Код (Text):
pirulo: log eip cmp eip, 7c91eaec je quitar cmp eip, 7c91eb03 je restaurar cmp eip,aux je restaurar2После расчёта исключений, смотрим вызывается ли по адресу возврату какое-нибудь из них, о котором мы знаем, что оно находится в точке возврата.
Код (Text):
restaurar2: bc aux jmp inicioПоэтому, совершив возврат в программу, с помощью команды BC стираем точку останова, которую установили в адресе возврата, а затем возвращаемся в начало, где снова устанавливаем стёртые ранее HBP и делаем RUN с ними восстановленными.
Хорошо, как видим, сделанные изменения не слишком велики. Посмотрим, работают ли они, перезапустив программу.
Редактируем скрипт, чтобы установить HBP по адресу OEP.
Здесь редактируем адрес HBP в запускаемом нами скрипте, устанавливаем два BP вручную, что необходимо для его работы, стираем все старые HBP, которые были.
Запускаем скрипт (не забыв установить два BP и установив все галочки в разделе настроек исключений).
Здесь говорится, что сработал наш HBP, так как нам не нужно, чтобы скрипт продолжал работу, то нажимаем NO.
Избегаем стирания установленного нами HBP и останавливаемся на OEP, используя HBP.
Для этих новых упаковщиков очень важно иметь какой-нибудь способ избегать стирания HBP, и этого нам удалось достичь.
Сейчас, так как можем легко управлять HBPами, переходим к украденным байтам.
Смотрим, возможно ли дойти до последнего исключение и трассировать оттуда.
Лог показывает, что это последнее исключение до того, как прибудем в OEP, и как видим, это ACCESS VIOLATION WHEN READING. Снимаем галочку, чтобы остановиться на нём.
Запрещаем эти BP, чтобы использовать скрипт.
Теперь перезапускаем и делаем RUN.
Здесь останавливаемся на последнем исключении, устанавливаем BPM ON ACCESS на секции исполняемого кода, и останавливаемся в обработчике исключений.
Проходим исключение с помощью SHIFT+F9.
Теперь трассируем с помощью F7 строки обработчика исключений, пока не доходим до RET. Нажав на RUN, возвращаемся в программу в:
Код (Text):
00471090 40 INC EAXТеперь, раз уже прошли исключение, устанавливаем BP на OEP, так как распаковка уже произошла. BP послужит мне для того, чтобы остановится, когда завершим трассировку.
И с 471090 начинаем трассировку и видим, что она идёт довольно медленно, так как не совершается никаких переходов, и проходим инструкции одна за одной.
Здесь, наконец, останавливаемся на OEP, так что можем заглянуть в листинг txt.
Код (Text):
00485EAE Main JL SHORT 00485ECA 00485ECA Main POPAD ; EAX=00000000, ECX=0012FFB0, EDX=7C91EB94, EBX=7FFDF000, ESI=FFFFFFFF 00485ECB Main JMP SHORT 00485ECE 00485ECE Main JMP DWORD PTR DS:[485ED4] Breakpoint at UnPackMe.004271B5Это последние строки txt, и поищем отсюда наверх PUSH EBP, который обычно является первой инструкцией программы.
Код (Text):
00485AF3 Main PUSH EBP 00485AF4 Main MOV EBP,ESP ; EBP=0012FFC0 00485AF6 Main PUSH -1 00485AF8 Main NOP 00485AF9 Main PUSHAD 00485AFA Main PUSHAD 00485AFB Main CALL 00485B00 00485B00 Main POP ESI ; ESI=00485B00 00485B01 Main SUB ESI,6 ; ESI=00485AFA 00485B04 Main MOV ECX,35 ; ECX=00000035 00485B09 Main SUB ESI,ECX ; ESI=00485AC5 00485B0B Main MOV EDX,E3D6D5FD ; EDX=E3D6D5FD 00485B10 Main SHR ECX,2 ; ECX=0000000D 00485B13 Main SUB ECX,2 ; ECX=0000000B 00485B16 Main CMP ECX,0Здесь видим украденные байты, а за ними начинается какая-то ерунда, но до этого правильное значение регистров сохраняется с помощью PUSHAD, которые восстанавливаются с помощью POPAD прямо перед прыжком на фальшивый OEP.
Код (Text):
00485ECA Main POPAD ; EAX=00000000, ECX=0012FFB0, EDX=7C91EB94, EBX=7FFDF000, ESI=FFFFFFFF 00485ECB Main JMP SHORT 00485ECE 00485ECE Main JMP DWORD PTR DS:[485ED4] Breakpoint at UnPackMe.004271B5Конечно, видим, что украденные байты совпадают со значениями, которые остались в стеке.
Кроме того другой регистр, который вначале может иметь случайное значение, это EBP, чьё содержимое изменяется с помощью MOV EBP, ESP.
Код (Text):
00485AF3 Main PUSH EBP 00485AF4 Main MOV EBP,ESP ; EBP=0012FFC0 00485AF6 Main PUSH -1
Листинг секции txt показывает нам, что EBP содержит значение 12FFC0, которое равно тому же значению, которое есть, когда выполнение доходит до фальшивого OEP. Всё, что дальше – просто трюке, чтобы сбить со следа. Видим, что после данного перехода EBP снова содержит значение 12FFC0, и это доказывает, что данные украденные байты являются правильными, а всё, что посередине, просто шелуха.
Ок, мы получили знаменитые украденные байты, изменили скрипт, и думаю, что для этой главы достаточно, поэтому продолжим в 43-ей.
Файлы к статье © Рикардо Нарваха, пер. Aquila
Введение в крэкинг с нуля, используя OllyDbg - Глава 42
Дата публикации 15 май 2009