Введение в крэкинг с нуля, используя OllyDbg - Глава 42

Дата публикации 15 май 2009

Введение в крэкинг с нуля, используя 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):
  1.  
  2. --------------------------------------------------------------------------------
  3. <p>
  4. var aux
  5. inicio:
  6.  
  7. bphws 4271b5, "x"
  8.  
  9. trabajo:
  10.  
  11. eob pirulo
  12. run
  13.  
  14. pirulo:
  15. log eip
  16. cmp eip, 7c91eaec
  17. je quitar
  18. cmp eip, 7c91eb03
  19. je restaurar
  20. cmp eip,aux
  21. je restaurar2
  22. jmp final
  23.  
  24. quitar:
  25. bphwc 4271b5
  26. jmp trabajo
  27.  
  28. restaurar:
  29. mov aux,esp
  30. mov aux,[aux]
  31. add aux,0b8
  32. mov aux,[aux]
  33. log aux
  34. bp aux
  35. jmp inicio
  36.  
  37. restaurar2:
  38. bc aux
  39. jmp inicio
  40.  
  41. final:
  42. MSGYN "Continuar?"
  43. cmp $RESULT,1
  44. je inicio
  45. ret
  46.  
  47. --------------------------------------------------------------------------------

В начале изменяемого нами скрипта находится

Код (Text):
  1.  
  2. var aux

var – это команда для объявления переменной. Объявляем переменную aux, которую я использую для хранения и расчёта адрес возврата в программу после исключения.

Код (Text):
  1.  
  2. restaurar:
  3. mov aux,esp
  4. mov aux,[aux]
  5. add aux,0b8
  6. mov aux,[aux]
  7. log aux
  8. bp aux
  9. jmp inicio

Здесь видим часть, где восстанавливаются HBPы, потом в aux помещается значение ESP.

Код (Text):
  1.  
  2. mov aux,[aux]

Вот, что здесь происходит: так как aux указывает на ESP, содержимое которого указывает на начало структуры CONTEXT, то после этой строки в aux будет находиться адрес начала структуры CONTEXT (в моём случае это 12fc8c), затем прибавляется 0b8, в результате чего получается 12fd44, а на ваших машинах это будет значение указатель на адрес возврата. И наконец:

Код (Text):
  1.  
  2. mov aux,[aux]

Перемещаем в aux то, что там должно быть, теперь там, наконец-то, адрес последнего возврата, на следующей строке BP aux, устанавливаем BP на адресе возврата из исключения, а затем снова запускаем программу, и наконец:

Код (Text):
  1.  
  2. pirulo:
  3. log eip
  4. cmp eip, 7c91eaec
  5. je quitar
  6. cmp eip, 7c91eb03
  7. je restaurar
  8. cmp eip,aux
  9. je restaurar2

После расчёта исключений, смотрим вызывается ли по адресу возврату какое-нибудь из них, о котором мы знаем, что оно находится в точке возврата.

Код (Text):
  1.  
  2. restaurar2:
  3. bc aux
  4. 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):
  1.  
  2.  00471090    40              INC EAX

Теперь, раз уже прошли исключение, устанавливаем BP на OEP, так как распаковка уже произошла. BP послужит мне для того, чтобы остановится, когда завершим трассировку.

И с 471090 начинаем трассировку и видим, что она идёт довольно медленно, так как не совершается никаких переходов, и проходим инструкции одна за одной.

Здесь, наконец, останавливаемся на OEP, так что можем заглянуть в листинг txt.

Код (Text):
  1.  
  2. 00485EAE Main     JL SHORT 00485ECA
  3. 00485ECA Main     POPAD                                     ; EAX=00000000, ECX=0012FFB0, EDX=7C91EB94, EBX=7FFDF000, ESI=FFFFFFFF
  4. 00485ECB Main     JMP SHORT 00485ECE
  5. 00485ECE Main     JMP DWORD PTR DS:[485ED4]
  6.     Breakpoint at UnPackMe.004271B5

Это последние строки txt, и поищем отсюда наверх PUSH EBP, который обычно является первой инструкцией программы.

Код (Text):
  1.  
  2. 00485AF3 Main     PUSH EBP
  3. 00485AF4 Main     MOV EBP,ESP                               ; EBP=0012FFC0
  4. 00485AF6 Main     PUSH -1
  5. 00485AF8 Main     NOP
  6. 00485AF9 Main     PUSHAD
  7. 00485AFA Main     PUSHAD
  8. 00485AFB Main     CALL 00485B00
  9. 00485B00 Main     POP ESI                                   ; ESI=00485B00
  10. 00485B01 Main     SUB ESI,6                                 ; ESI=00485AFA
  11. 00485B04 Main     MOV ECX,35                                ; ECX=00000035
  12. 00485B09 Main     SUB ESI,ECX                               ; ESI=00485AC5
  13. 00485B0B Main     MOV EDX,E3D6D5FD                          ; EDX=E3D6D5FD
  14. 00485B10 Main     SHR ECX,2                                 ; ECX=0000000D
  15. 00485B13 Main     SUB ECX,2                                 ; ECX=0000000B
  16. 00485B16 Main     CMP ECX,0

Здесь видим украденные байты, а за ними начинается какая-то ерунда, но до этого правильное значение регистров сохраняется с помощью PUSHAD, которые восстанавливаются с помощью POPAD прямо перед прыжком на фальшивый OEP.

Код (Text):
  1.  
  2. 00485ECA Main     POPAD                                     ; EAX=00000000, ECX=0012FFB0, EDX=7C91EB94, EBX=7FFDF000, ESI=FFFFFFFF
  3. 00485ECB Main     JMP SHORT 00485ECE
  4. 00485ECE Main     JMP DWORD PTR DS:[485ED4]
  5.     Breakpoint at UnPackMe.004271B5

Конечно, видим, что украденные байты совпадают со значениями, которые остались в стеке.

Кроме того другой регистр, который вначале может иметь случайное значение, это EBP, чьё содержимое изменяется с помощью MOV EBP, ESP.

Код (Text):
  1.  
  2. 00485AF3 Main     PUSH EBP
  3. 00485AF4 Main     MOV EBP,ESP                               ; EBP=0012FFC0
  4. 00485AF6 Main     PUSH -1

Листинг секции txt показывает нам, что EBP содержит значение 12FFC0, которое равно тому же значению, которое есть, когда выполнение доходит до фальшивого OEP. Всё, что дальше – просто трюке, чтобы сбить со следа. Видим, что после данного перехода EBP снова содержит значение 12FFC0, и это доказывает, что данные украденные байты являются правильными, а всё, что посередине, просто шелуха.

Ок, мы получили знаменитые украденные байты, изменили скрипт, и думаю, что для этой главы достаточно, поэтому продолжим в 43-ей.

Файлы к статье © Рикардо Нарваха, пер. Aquila


1 3.596
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532

Комментарии


      1. virtuha 23 май 2017
        Оглавление
        Введение в крэкинг с нуля, используя OllyDbg - Глава 1
        Введение в крэкинг с нуля, используя OllyDbg - Глава 2
        Введение в крэкинг с нуля, используя OllyDbg - Глава 3
        Введение в крэкинг с нуля, используя OllyDbg - Глава 4
        Введение в крэкинг с нуля, используя OllyDbg - Глава 5
        Введение в крэкинг с нуля, используя OllyDbg - Глава 6
        Введение в крэкинг с нуля, используя OllyDbg - Глава 7
        Введение в крэкинг с нуля, используя OllyDbg - Глава 8
        Введение в крэкинг с нуля, используя OllyDbg - Глава 9
        Введение в крэкинг с нуля, используя OllyDbg - Глава 10
        Введение в крэкинг с нуля, используя OllyDbg - Глава 11
        Введение в крэкинг с нуля, используя OllyDbg - Глава 12
        Введение в крэкинг с нуля, используя OllyDbg - Глава 13
        Введение в крэкинг с нуля, используя OllyDbg - Глава 14
        Введение в крэкинг с нуля, используя OllyDbg - Глава 15
        Введение в крэкинг с нуля, используя OllyDbg - Глава 16
        Введение в крэкинг с нуля, используя OllyDbg - Глава 17
        Введение в крэкинг с нуля, используя OllyDbg - Глава 18
        Введение в крэкинг с нуля, используя OllyDbg - Глава 19
        Введение в крэкинг с нуля, используя OllyDbg - Глава 20
        Введение в крэкинг с нуля, используя OllyDbg - Глава 21
        Введение в крэкинг с нуля, используя OllyDbg - Глава 22
        Введение в крэкинг с нуля, используя OllyDbg - Глава 23
        Введение в крэкинг с нуля, используя OllyDbg - Глава 24
        Введение в крэкинг с нуля, используя OllyDbg - Глава 25
        Введение в крэкинг с нуля, используя OllyDbg - Глава 26
        Введение в крэкинг с нуля, используя OllyDbg - Глава 27
        Введение в крэкинг с нуля, используя OllyDbg - Глава 28
        Введение в крэкинг с нуля, используя OllyDbg - Глава 29
        Введение в крэкинг с нуля, используя OllyDbg - Глава 30
        Введение в крэкинг с нуля, используя OllyDbg - Глава 31
        Введение в крэкинг с нуля, используя OllyDbg - Глава 32
        Введение в крэкинг с нуля, используя OllyDbg - Глава 33
        Введение в крэкинг с нуля, используя OllyDbg - Глава 34
        Введение в крэкинг с нуля, используя OllyDbg - Глава 35
        Введение в крэкинг с нуля, используя OllyDbg - Глава 36
        Введение в крэкинг с нуля, используя OllyDbg - Глава 37
        Введение в крэкинг с нуля, используя OllyDbg - Глава 38
        Введение в крэкинг с нуля, используя OllyDbg - Глава 39
        Введение в крэкинг с нуля, используя OllyDbg - Глава 40
        Введение в крэкинг с нуля, используя OllyDbg - Глава 41
        Введение в крэкинг с нуля, используя OllyDbg - Глава 42
        Введение в крэкинг с нуля, используя OllyDbg - Глава 43
        Введение в крэкинг с нуля, используя OllyDbg - Глава 44
        Введение в крэкинг с нуля, используя OllyDbg - Глава 45
        Дополнение к 45-ой главе «Введения в крэкинг, используя OllyDbg»
        Введение в крэкинг с нуля, используя OllyDbg - Глава 46
        Введение в крэкинг с нуля, используя OllyDbg - Глава 47
        Введение в крэкинг с нуля, используя OllyDbg - Глава 48
        Введение в крэкинг с нуля, используя OllyDbg - Глава 49
        Введение в крэкинг с нуля, используя OllyDbg - Глава 50
        Введение в крэкинг с нуля, используя OllyDbg - Глава 51
        Введение в крэкинг с нуля, используя OllyDbg - Глава 52
        Введение в крэкинг с нуля, используя OllyDbg - Глава 53