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

  • На форуме работает ручное одобрение пользователей. Это значит, что, если Ваша причина регистрации не соответствует тематике форума, а также Вы используете временную почту, Ваша учётная запись будет отклонена без возможности повторной регистрации. В дальнейшем - пожизненная блокировка обоих аккаунтов за создание мультиаккаунта.
  • Мы обновили Tor зеркало до v3!
    Для входа используйте следующий url: darkv3nw2...bzad.onion/
  • Мы вновь вернули telegram чат форуму, вступайте, общайтесь, задавайте любые вопросы как администрации, так и пользователям!
    Ссылка: https://t.me/chat_dark_time

AnGel

Администратор
Команда форума

AnGel

Администратор
Команда форума
27 Авг 2015
3,411
2,025
Пожалуйста, Вход или Регистрация для просмотра содержимого URL-адресов!


В предыдущей части мы попытались прояснить концецию OEP, то есть первой строки, выполняемой изначальной программой. Это строка в 99% случаев находится в первой секции (хотя в этой главе будет рассматриваться случай, когда OEP не находится в первой секции, такой я вредный, хе-хе).

Видим, что когда доходим досюда, и смотрим память, её содержимое напоминает то, что было изначально, что даёт нам возможность попытаться сдампить и создать файл, который будет максимально близок к исходному, в целях его последующего реконструирования.

Классический метод следующий:

  1. Находим OEP
  2. Делаем дамп
  3. Чиним IAT
  4. Проверяем, если ли в файле антидамповые и иные проверки, которые могут препятствовать запуску программу, и исправляем эти проблемы
Это классический метод (с небольшими изменениями, зависящими от упаковщика), который обычно используется. В этой главе мы сфокусируемся на методах, которые можно использовать, чтобы добраться до OEP.

Нужно сказать, что во многих случаях необходимо пробовать и ещё раз пробовать. Во многих случаях эти методы работают, в других упаковщики могут препятствовать работоспособности этих методов, поэтому, конечно, нужно приложить немного изобретательности. Идея заключается в том, чтобы найти OEP, так что мы рассмотрим, как можно использовать для этого инструменты, имеющиеся в нашем распоряжении.

Пока для объяснений будем использовать упакованный крэкми Cruehead’а. Потом рассмотрим другие упаковщики, но для общих разъяснений сойдёт и так.

1) Просмотр или поиск опкодов в в листинге до выполнения процедуры распаковки

Это сработает только для невинных пакеров. Мне бы не пришло в голову испробовать этот метод на ASProtect, например, хе-хе, но в некоторых случаях можно поискать опкоды JMP LARGO (0E9) или CALL LARGO (0E8), так как пакеру необходиму совершить переход или длинный вызов на первую секцию, чтобы перейти к OEP (если верны молитвы о том, что тот находится в начале).

В данном случае это было бы так:

9a9314a07158f33f07bf355a65e4572e.png

ff8bec383b8227476612152c6f29905e.png

Когда останавливаемся, смотрим, является ли это переход на первую секцию, и если нет, то нажимаем CTRL+L, чтобы искать следующий E9.

8681277ea30faa53ae95ef8bf51f28f9.png

Нажимаем CTRL+L.

807af741a5fdd7fecf30fa479eb3da68.png

Находим несколько длинных переходов, которые не ведут к первой секции, пока, наконец, не оказываемся здесь:

20d5df8185ab4957caeafef88468c1c8.png

Это переход на первую секцию. Можем поставить BPX, и когда остановимся, нажимаем F7 и оказываемся на OEP.

Также, если есть желание порыться в коде пакера, можно попробовать “CALL EAX”, “CALL EBX”, “JMP EAX”, так как многие упаковщики используют регистры для скрытия перехода на OEP. В данном случае, хорошо то, это полноценные команды, и их можно искать через “SEARCH FOR – ALL COMMANDS” и установить на все BPX. Рассмотрим пример.

c1a64cb8563d60a0dcd7e746e2bc0f03.png

14e36dec464672036c023fe61db8c24a.png

В данном случае не находим никакого результата, но если бы он был, то появился бы список с найденными вхождениями, и можно было бы нажать правой кнопкой мыши на них, чтобы установить BPX. Останавливаясь на каждом из них, можно было бы посмотреть, какое значение находится в EAX, чтобы посмотреть, ведёт ли данный CALL или JMP на первую секцию.

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

2) Использование искателя OEP’ов, встроенного в OLLYDBG

Открываем крэкми UPX и идём в DEBUGGING OPTIONS-SFX:

af80d4243fe37f108472e610103865f0.png

На вкладке SFX видим две опции, чтобы OllyDBG искал OEP’ы. Одна, отмеченная красным, более быстрая, а другая, на которую указывает зелёная стрела, более медленная, но работает временами несколько лучше. Опробуем. Кликаем на опцию, отмеченную красной стрелкой.

Перегрузив крэкми, видим, что в данном случае метод не сработал. Находим объяснение этому.

5c7a8a10003e89d1529840589337b92d.png

В помощи к OllyDbg видим, что эта опция работает только тогда, когда OllyDbg обнаруживает, что точка входа находится в секции кода, как в большинстве упакованных программ, однако в данном случае этого не происходит, так как UPX меняет секцию кода на ту, в которой запускается распаковщик, поэтому EP находит в той же секции кода, и OllyDbg ничего не находит, так как всё запаковано, и в данном случае метод не работает, хотя упаковщики, делающее подобное изменение, довольно редки.

Чтобы продемонстрировать, как работает этот метод, используем другой крэкми, прилагающийся к статье. Он запакован с помощью aspack’а – ещё один простой упаковщик.

Отметим сначала ту опцию, которая была изначально, и увидим, что OllyDbg определяет наличие пакера, посмотрев, находит ли точка входа внутри секции CODE.

Отображает окошко с соответствующим сообщением, и если нажать на кнопку, то оказываемся в точке входа.

e524a205ed5767ba4e949d82c85c6340.png

Теперь делаем активной опцию, отмеченную красной стрелкой. Убеждаемся также, что во вкладке EXCEPTIONS также поставлены галочки, чтобы мы не останавливались на исключениях, после чего перегружаем программу в OllDbg.

d4f918c0b7e7081244f53eedd3544bfb.png

6539134e2258c6971fb73d08afd98950.png

Делаем RUN.

Видим, что останавливаемся в 404000, который помечается как «REAL ENTRY POINT OF SFX CODE», т.е. «настоящая точка входа SFX-кода» (хотя она не находится в первой секции, как уже было сказано, этот крэкми – особый случай, распаковка проиходит в третью секцию, что необычно, но возможно).

9a7ba09d50946e7c940b618d729f6461.png

В данном случае метод сработал, это и есть OEP. Теперь посмотрим, насколько будет медленнее, если отметим опцию, на которую указывает зелёная стрелка.

Перезапускаем, работает не намного медленнее, так как код распаковщика невелик. В обоих случаях метод работает, и OEP был правильно определён.

Всегда нужно помнить, что после использования данного метода, нужно установить изначальную опцию, чтобы OllyDbg не искала всё время OEP потом.

3) Использование пропатченного OllyDbg для поиска OEP’овs

Это тот же OllyDbg, который мы использовали в главах, посвящённых Visual Basic'у. В нём, когда устанавливается BPM ON ACCESS, он срабатывает только на запуск, но не на чтение и запись (ON READ или ON WRITE), что идеально подходит для поиска OEP'ов. Рассмотрим это на примере UPM, идём в M и смотрим секции.

f03099a4d703e806d4c8895ca817919f.png

67db8d863738155c7a817b974eb72640.png

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

e030f8aed9510ba97ac59ae70cb55a03.png

Убеждаемся, что во вкладке EXCEPTIONS отмеченны опции, чтобы не происходило останова во время исключений, делаем RUN, и лично я отправляюсь за чашечкой матэ (те, кто не из Аргентины, могут предпочесть чай или кофе, хе-хе).

Конечно, этот метод довольно медленный, поэтому я и сказал, что можно пойти за чашечкой матэ, в зависимости от упаковщика это может занять от пары секунд до нескольких минут.

Когда возвращаемся с матэ, то видим, что OEP найден:

db3c6e47be90498c84ea20cf5f4b0698.png

Если попробуем на aspack'е:

384a4c90b66f964fdc2f259310a72594.png

Делаем RUN.

791ad066e5a650011578a4b93a64f2d4.png

Видим первую строку, где произошло выполнение, смотрим, что случится, если нажмём F7.

386a777039d52ecf19ad2c96f5180996.png

Возвращаемся из процедуры распаковщика, для чего снова жмём RUN, и видим, что программа запускается без остановки. Почему это происходит?

Если посмотрим внимательно, то увидим, что когда ищем OEP с помощью OllyDbg, то в данном случае распаковщик не распаковывает в первую секцию, поэтому нужно установить BPM ON ACCESS на другую секцию, а не на первую. Для того, чтобы определить на какую именно, нужно запустить крэкми без установки BPM'ов.

32b7a46cb2b98c663bf98231b91f3093.png .

Как того появится вышеукаазанное окно, то мы знаем, что произошла распаковка в память. Теперь, чтобы узнать, в какой секции происходит выполнение, можем установить BPM ON ACCESS в каждой секции по очереди, и если программа продолжает выполняться и не останавливается в OllyDbg, то это значит, что в данной секции выполнения не происходит, и нужно пробовать следующую.

ad3473d4cdebcfb45d1653971b75bca5.png

Устанавливаем BPM ON ACCESS на первую, и ничего не происходит, на вторую – тоже ничего, затем смотрим третью, которая начинается в 404000.

6c651da56f254568b2434195152598b3.png

Видим, что происходит останов в OllyDdbg при попытке посмотреть окно крэкми, это значит, что это и есть секция, где приосходит выполнение, так что повторяем процесс сначала, чтобы найти OEP, но BPM ON ACCESS уже ставим сразу на третью секцию.

dcd9de9abdd37a677361425ce6199538.png

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

4) Метод PUSHAD

Этот метод работает на многих упаковщиках и основывается на предыдущем. Многие упаковщики сначала выполняют инструкцию PUSHAD, чтобы сохранить исходные значения регистров, а после распаковки, но до перехода на OEP, получают значения обратно с помощью инструкции POPAD.

Смотрим в крэкми, запакованном UPX.

91defdab68d873507acdb6841690a26d.png

Видим, что PUSHAD находится в начале, иногда он может находится немного дальше, в иногда упаковщики применяют отдельный PUSH для каждого регистра (PUSH EAX, PUSH EBX и так далее), но в случае, когда сохраняются изначальные значения регистров, они должны быть извлечены обратно до перехода на OEP.

Если выполним PUSHAD с помощью F7:

88588760703c8b412ff6408e93bbce6c.png

Видим, что сюда были сохранены изначальные значения регистров, и если они считываются до перехода на OEP, можем установить HARDWARE BPX ON ACCESS на одно из этих значений, чтобы остановиться, когда оно будет читаться, и окажемся непосредственно перед переходом на OEP.

Ищем эти значения в DUMP, отмечаем регист ESP и делаем FOLLOW IN DUMP.

ed0a59fbc57584d496885a08ede72c77.png

Через DUMP нам показывается содержимое стека.

65d937fdb8d58af61eb6ad139bc5dcec.png

Здесь видим сохранённые значения, можно отметить первый байт или первые четыре и установить HARDWARE BPX ON ACCESS.

349b3e35ca61941204f5479c5941b1fb.png

Всё равно, что выбрать, BYTE, WORD или DWORD, главное, что остановка произойдёт во время чтения этого значения. Нажимаем RUN.

084115077a50966024dd838ff2af23b6.png

Видим, что для восстановления сохранённых в стек значений выполняется POPAD, они считываются и происходит останов, и чуть ниже находится переход на OEP, так что можно поздравить себя, что этот метод прекрасно сработал.

Пробуем на aspack'е.

def9fa11ef23f0705010f3fad849b31c.png

Видим PUSHAD, проходим его с помощью F7, а затем делаем ESP->FOLLOW IN DUMP.

c8f7d82ac380470f80d2562e9e599d48.png

fc73a9f042eef6ec3243235e6072e601.png

И делаем RUN.

a115566358da2eb3fd12092fe9943418.png

Видим, что останавливаемся тут прямо перед переходом на OEP, который в данном случае выглядит как PUSH 404000, а затем RET. Трассируем с помощью F7.

1b7d8326718420122461760d60a5c89c.png

Без проблем доходим до OEP.

Нужно сказать, что многие новые упаковщики имеют защиту от данного метода, но ладно, нужно знать разные методы и пробовать, какой из них подойдёт.

Продолжаем рассматривать методы для поиска OEP.

5) Для программ, написанных на Visual Basic'е (NATIVE или P-CODE)

Ок, найти OEP запакованной программы на VB очень легко, так как в ней всегда есть PUSH и CALL API-функции VB в начале, так что используем OllyDbg для VB, и когда запустим программ и остановимся на точке входа, идём в M, ищем DLL VB, и на секцию кода данной DLL ставим BPM ON ACCESS.

Таким образом программы распакуется и остановится прямо на первой строке, выполняемой библиотекой, а на первой строке стека находится адрес возврата из вызова, который привёл сюда. Этот вызов находится на второй строке после OEP, а на самой OEP – PUSH. Так и находится OEP данным методом.

Понятно, что метод, использующий модифицированный для VB OllyDbg, тоже будет работать, если установить BPM ON ACCESS прямо на первую секцию программы, но есть упаковщики с защитой от этого, поэтому может потребоваться попробовать оба метода, так что вы должны знать, как они работают.

Не будем рассматривать примеры, так как это всё очень легко понять. В будущем, если попадётся подходящий запакованный VB-крэкми, опробуем этот метод в деле.

6) Метод исключений

Если у нас есть упаковщик, который генерирует множество исключений при распаковке, то существует следующий метод, который мы рассмотрим на примере приложенного к главе крэкми bitarts.

Открываем его в OllyDbg для VB с необходимыми плагинами для защиты от обнаружения.

Включаем все опции во вкладке EXCEPTIONS и запускаем крэкми в OllyDbg, после чего прибываем сюда.

fd6d16ba37165d57194e1cd49940b19c.png

Как только оказались здесь, открываем логи OllyDbg, нажав L.

021ef5bd3953d9a223015ea0959489a5.png

260a82c30a333e002403b1d3751258a5.png

Определяем последнее произошедшее исключение, которое произошло не в первой секции, то есть произошдшее не во время выполнения программы, самое последнее во время распаковки, в данном случае – это 46e88f.

Теперь перегружаем и выключаем все опции в исключениям, оставляя только первую.

cd50dacf3f47a84ed93918532356da17.png

Жмём RUN и каждый раз при остановке проходим её с помощю SHIFT+F9, пока не доходим до последнего, которое запомнили, в данном случае это 46e88f.

caf5b91349a891e7512b88e4d9fcaa5f.png

Останавливаемся здесь, это не то, что нам нужно, нажимаем shift+f9, чтобы выйти из исключения. Обратите внимание, что OllyDbg останавливается на следующей после INT3 строки, то есть на 46e890.

5e243e1be244dc1ae0e1f751f5986218.png

Находимся прямо после последнего исключения, сгенерирвоанного распаковщиком, после чего запускается сама программа.

Здесь есть несколько возможностей. Можно установить BPM ON ACCESS на секцию кода. Многие спростя, почему не установить его в самом начале, и ответ состоит в том, что многие упаковщики могут определять BPM, если он установлен в самом начала, а так мы уже, скорее всего, прошли этап обнаружения.

70c5eb564e351e94da54fae468e959fd.png

На забываем, что нужно нажимать SHIFT+F9, так как находимся в исключении.

81d09f658f010e9e8b144e9caec73f7e.png

Останавливаемся на OEP, можем попробовать узнать, определяет ли упаковщи BPM, установленный в самом начале.

Перегрузим программу и снова включим все опции во вкладке настроек исключений, затем идём в M, где устанавливаемя BPM ON ACCESS на первую секцию, и так как будем использовать OllyDbg для нахождения OEP, то есть время сходить за парой матэ, хе-хе, так как в данном случае это может занять несколько минут.

915ccce31b0bceec3ead95c42133de21.png

Видим, что всё сработало замечательно, но знать метод исключений полезно, если BPM ON ACCESS определяется упаковщиком, и в этом случае необходимо сначала подобраться к OEP так близко, как это возможно, чтобы избежать обнаружения.

Видим, что для этого этого упаковщика метод поиска OEP с помощью OllyDbg также прекрасно работает.

f00c164eafc882565c882441c5ebd46b.png

Метод PUSHAD тоже работает, смотрим вначале:

299b521a1ee645b7c86d03af7ca29f0c.png

Если оттрассируем немного с помощью F7, то через несколько строк встретим PUSHAD.

06a5c9a64b3952e4e390f3a84cd37b66.png

Проходим его с помощью F7 и помечаем (ESP-FOLLOW IN DUMP).

428587abfdf63dcc21732d51b4405b30.png

Делаем RUN.

546c63a1203e3a39e846aca3543798d7.png

И останавливаемся на считывании сохранённых значений, трассируем до RET, пройдя который окажемся на OEP.

8798266ad966f94e55ccb78123612b9e.png

7) Метод, использующий самую используемую распаковщиком API-функцию

Перегружаем этот bitarts в OllyDbg для OEP'ов, отметив все опции во вкладке исключений, и ищем часто используемую API-функцию, обычно это GetProcAddress. LoadLibrary также может довольно часть использоваться, ExitThread, в общем, зависит от упаковщика. Попробуем этот метод с GetProcAddress.

caea04ac7336fcfc3fbdd0dee949eace.png

Смотрим в CommandBar'е адрес API-функции и устанавливаем на неё BP.

c1c7903ab7989772dfc6b1bd1a652bfa.png

Мы не можем поставить такой BP, какой нам нужен, поэтому нам придётся подправить это вручную.

Делаем RUN.

c8bfead5cf41a6045beb884bc1b84ee6.png

Меняем BP на BP CONDITIONAL LOG, который не производит остановку, а только пишет в лог.

25bba443d564555082e9e79b4bd17067.png

323b0c6528a704a1190f76a13df5477a.png

Устанавливаем, чтобы не происходило остановки, но всегда записывалось в лог, и кроме того, чтобы записывалось значение [esp], то есть значение возврата. Это позволит нам узнать, откуда был произведён вызов API-функции. Очищаем лог (правая кнопка мыши-CLEAR LOG).

67899166238b3e1e32acd08669fbcc36.png

7ddfa82f27dd0f00c88266970c037970.png

Делаем RUN и оказываемся в собственно программе, смотрим последний вызов нашей API-функции, производимой не из первой секции.

1b9d03b19c1b5d459991842060faae09.png

Видим, что все вызовы были произведены из распаковщика, кроме последнего из 428c2B, то есть уже из первой секции программе, а значит, относящегося уже к самой программе. Таким образом, последний раз эта функция была вызвана распаковщиком из 47009A, так что можем установить условие, чтобы там происходил останов.

Перегружаем.

d2a58ad55b5f04cf5467e2374f4105fd.png

Редактируем BPX CONDITIONAL.

cf019544d8cf334e359040894ce58656.png

Устанавливаем условие, отмечая опцию останова на ON CONDITION.

И делаем RUN.

8517d037fa33c50a72ef02b4197011c2.png

Здесь произошёл останов. Это метод можно сочетать с установкой BPM ON ACCESS на секцию кода, чтобы избежать определение BPM, так как тут у нас такая проблема, что многие новые упаковщики определяют BPX, установленные на API-функции, поэтому во многих случаях лучше подобраться поближе к OEP с помощью BPM ON ACCESS, и оттуда трассировать с помощью автоматического трассировщика, встроенного в OllyDbg (TRACE INTO).

Если подобрались достаточно близко, то можно использовать метод поиска с помощью API-функцию, если повезёт, если нет, можем поменять её. Если заглянем в лог, то увидим, что одна из функций – это завершение треда.

7626b7d5a17b92448cdb75cb607fede9.png

Поэтому можем установить BP ExitThread, подредактировав его конфигурацию в OllyDbg таким образом, чтобы останов происходил каждый раз, когда тред завершается.

745a1536237b0f77ae7477eecee40e22.png

Делаем RUN.

65f28d23afe8f69586acbaf592566782.png

Останавливаемся на завершении треда.

c1d9a7dc559d04bef377de378bc3da29.png

abd4728ede4c9da60c56b1039fd261f9.png

Если установлен BPM ON ACCESS на секцию CODE, то останов также прекрасно произойдёт.

8) Метод первой API-функции, выполняемой программой

Этот метод заключается в установке BP напрямую на API-функцию, относительно которой есть подозрение, что это первая, выполняемая программа. Обычно вначале программы выполняют GetVersion, GetModuleHandleA, ряд распакованных программ получают список наиболее часто используемых API-функций. В случае с bitarts посмотрим GetVersion, а в CrueHead'е – GetModuleHandleA. Устанавливаем в bitarts BP GetVersion.

f99af26eca702f0a2684c8faad027ac6.png

Делаем RUN.

f3e0fe40cc2428bfea3d00a78d6ddae4.png

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

Когда останавливаемся, смотрим первое значение в стеке (адрес возврата) и идём туда.

1818f30f0e667b4d0f03f8a22408a91b.png

4c4d518df6e418b4d2d172d07107cfe0.png

Тут видим OEP, полученный нами с помощью метода первой используемой программой API-функции. Если программа обнаружит, что на GetVersion установлен BP, можно попробовать установить его на RET из неё.

Думаю, что методов множество, здесь были приведены примеры наиболее часто используемых. Эти методы можно расширять и адаптировать к конкретному случаю, но главно, чтобы вы поняли общую идею, стоящую за ними, чтобы у вас была прочная база.

К главе приложен крэкми UnPackMe_tElock0.98.exe, на котором можно попрактиковаться в поиске OEP. В нём используются несколько трюков, и не все из вышеперечисленных методов будут работать. Будет прекрасно, если вы сумеете сами найти OEP.

Помните, что если распаковщик обнаруживает BP или HBP и не запускается, то нужно следить, чтобы не было установлено никаких BP или HBP, а когда он запустится, можно попробовать метод PUSHAD. Если он не сработает, то нужно попробовать другой.

Следующая глава будет посвящена дампу и починке IAT'ов.
 

О нас

  • Наше сообщество существует уже много лет и гордится тем, что предлагает непредвзятое, критическое обсуждение различных тем среди людей разных слоев общества. Мы работаем каждый день, чтобы убедиться, что наше сообщество является одним из лучших.

    Dark-Time 2015 - 2022

    При поддержке: XenForo.Info

Быстрая навигация

Меню пользователя