Здравствуйте
Моим автомобилем является Mazda 6, занялся заменой расходников, скачал руководство по ремонту в электронном виде.
Но оболочка мне не понравилась – окно программы не увеличивало размеры, вылез горизонтальный скрол в общем бяда.
Пошарившись в каталоге программы наткнулся на файл большого размера.
Оказалось это БД MS access, при запуске появился запрос пароля.
Можно было конечно натравить спец.средства для вытаскивания пароля как например в этой статье – http://dimassoft.blogspot.ru/2014/12/samsung-c6112-samsung-c-6112.html
Но я решил пойти другим путем – мы займемся реверсом. Приступим.
Для начала узнаем компилятор и наличиеотсутствие пакеров и упаковщиков.
Используем утилиту Detect is easy – http://ntinfo.biz
Ну что ж программа написана на Delphi 7 и ничем не упакована.
Следующая замечательная программа которая нам пригодится это Interactive Delphi Reconstructor – http://kpnc.org/idr32/en/
Посылаем файл на декомпиляцию и вспоминаем матчасть. Для работы с БД используется строка инициализации в которой задается имя базы, права доступа и собственно пароль.
Выбираем в IDR вкладку String и ищем все что относится к инициализации.
В глаза кидается вот такой текст:
Данная строка используется в этом месте:
С предварительными ласками закончили, вводим тяжелую артиллерию.
Открываем файл в отладчике и ставим бряк по адресу 005F8C85 и запускаем жертву.
Программа прерывается вот здесь:
Пошагово идем дальше, одним глазком поглядывая в стек
И тут же видим подозрительную строчку – 45edfws
Еще немного трассировки и бинго ! Это действительно пароль от нашей БД.
Ну вот и все загадка разгадана, ну и собственно тормозные колодки поменяны)
До новых встреч друзья !
В предыдущей части мы попытались прояснить концепцию OEP, то есть первой строки, выполняемой изначальной программой. Это строка в 99% случаев находится в первой секции (хотя в этой главе будет рассматриваться случай, когда OEP не находится в первой секции, такой я вредный, хе-хе).
Видим, что когда доходим досюда, и смотрим память, её содержимое напоминает то, что было изначально, что даёт нам возможность попытаться сдампить и создать файл, который будет максимально близок к исходному, в целях его последующего реконструирования.
Классический метод следующий:
-
4.
Проверяем, если ли в файле антидамповые и иные проверки, которые могут препятствовать запуску программу, и исправляем эти проблемы
Это классический метод (с небольшими изменениями, зависящими от упаковщика), который обычно используется. В этой главе мы сфокусируемся на методах, которые можно использовать, чтобы добраться до OEP.
Нужно сказать, что во многих случаях необходимо пробовать и ещё раз пробовать. Во многих случаях эти методы работают, в других упаковщики могут препятствовать работоспособности этих методов, поэтому, конечно, нужно приложить немного изобретательности. Идея заключается в том, чтобы найти OEP, так что мы рассмотрим, как можно использовать для этого инструменты, имеющиеся в нашем распоряжении.
Пока для объяснений будем использовать упакованный крэкми Cruehead’а (самостоятельно упаковывали его в 31 главе). Потом рассмотрим другие упаковщики, но для общих разъяснений сойдёт и так.
Просмотр или поиск опкодов в в листинге до выполнения процедуры распаковки
Просмотр или поиск опкодов в в листинге до выполнения процедуры распаковки
Это сработает только для невинных пакеров. Мне бы не пришло в голову испробовать этот метод на ASProtect, например, хе-хе, но в некоторых случаях можно поискать опкоды JMP LARGO (0E9) или CALL LARGO (0E8), так как пакеру необходимо совершить переход или длинный вызов на первую секцию, чтобы перейти к OEP (если верны молитвы о том, что тот находится в начале).
В данном случае это было бы так:
Когда останавливаемся, смотрим, является ли это переход на первую секцию, и если нет, то нажимаем CTRL+L, чтобы искать следующий E9.
Находим несколько длинных переходов, которые не ведут к первой секции, пока, наконец, не оказываемся здесь:
Это переход на первую секцию. Можем поставить BPX, и когда остановимся, нажимаем F7 и оказываемся на OEP.
Также, если есть желание порыться в коде пакера, можно попробовать “CALL EAX”, “CALL EBX”, “JMP EAX”, так как многие упаковщики используют регистры для скрытия перехода на OEP. В данном случае, хорошо то, это полноценные команды, и их можно искать через “SEARCH FOR – ALL COMMANDS” и установить на все BPX. Рассмотрим пример.
В данном случае не находим никакого результата, но если бы он был, то появился бы список с найденными вхождениями, и можно было бы нажать правой кнопкой мыши на них, чтобы установить BPX. Останавливаясь на каждом из них, можно было бы посмотреть, какое значение находится в EAX, чтобы посмотреть, ведёт ли данный CALL или JMP на первую секцию.
Это способ поиска по листингу не очень часто используется, так как большинство современных распаковочных процедур самомодифицируемые, особенно в районе перехода на OEP, чтобы избежать подобных проверок, но в случае, если упаковщик старый или плохой, то этот метод может сработать.
Использование искателя OEP’ов, встроенного в OLLYDBG
Использование искателя OEP’ов, встроенного в OLLYDBG
Открываем крэкми UPX и идём в DEBUGGING OPTIONS-SFX:
На вкладке SFX видим две опции, чтобы OllyDBG искал OEP’ы. Одна, отмеченная красным, более быстрая, а другая, на которую указывает зелёная стрела, более медленная, но работает временами несколько лучше. Опробуем. Кликаем на опцию, отмеченную красной стрелкой.
Перегрузив крэкми, видим, что в данном случае метод не сработал. Находим объяснение этому.
В помощи к OllyDbg видим, что эта опция работает только тогда, когда OllyDbg обнаруживает, что точка входа находится в секции кода, как в большинстве упакованных программ, однако в данном случае этого не происходит, так как UPX меняет секцию кода на ту, в которой запускается распаковщик, поэтому EP находит в той же секции кода, и OllyDbg ничего не находит, так как всё запаковано, и в данном случае метод не работает, хотя упаковщики, делающее подобное изменение, довольно редки.
Чтобы продемонстрировать, как работает этот метод, используем другой крэкми, прилагающийся к статье
[ссылка]
. Он запакован с помощью aspack’а – ещё один простой упаковщик.
Отметим сначала ту опцию, которая была изначально, и увидим, что OllyDbg определяет наличие пакера, посмотрев, находит ли точка входа внутри секции CODE.
Отображает окошко с соответствующим сообщением, и если нажать на кнопку, то оказываемся в точке входа.
Теперь делаем активной опцию, отмеченную красной стрелкой. Убеждаемся также, что во вкладке EXCEPTIONS также поставлены галочки, чтобы мы не останавливались на исключениях, после чего перегружаем программу в OllDbg.
Видим, что останавливаемся в 404000, который помечается как “REAL ENTRY POINT OF SFX CODE”, т.е. “настоящая точка входа SFX-кода” (хотя она не находится в первой секции, как уже было сказано, этот крэкми – особый случай, распаковка происходит в третью секцию, что необычно, но возможно).
В данном случае метод сработал, это и есть OEP. Теперь посмотрим, насколько будет медленнее, если отметим опцию, на которую указывает зелёная стрелка.
Перезапускаем, работает не намного медленнее, так как код распаковщика невелик. В обоих случаях метод работает, и OEP был правильно определён.
Всегда нужно помнить, что после использования данного метода, нужно установить изначальную опцию, чтобы OllyDbg не искала всё время OEP потом.
Использование пропатченного OllyDbg для поиска OEP’ов
Использование пропатченного OllyDbg для поиска OEP’ов
Это тот же OllyDbg, который мы использовали в главах, посвящённых Visual Basic’у. В нём, когда устанавливается BPM ON ACCESS, он срабатывает только на запуск, но не на чтение и запись (ON READ или ON WRITE), что идеально подходит для поиска OEP’ов. Рассмотрим это на примере UPM, идём в M и смотрим секции.
Вот первая секция, в ней распаковщик раскриптовывает и записывает изначальные байты, и нам не нужно, чтобы останов происходил во время этой работы, так как это бы происходило бы тысячи раз до того, как собственно добрались до OEP. Благодаря модифицированному OllyDbg, во время чтения и записи в вышеуказанную секцию остановок происходить не будет, а только когда в ней произойдёт выполнение, а это нам и нужно – узнать, на какой строке произойдёт первое выполнение в первой секции, так как это почти всегда и будет OEP.
Убеждаемся, что во вкладке EXCEPTIONS отмечены опции, чтобы не происходило останова во время исключений, делаем RUN, и лично я отправляюсь за чашечкой матэ (те, кто не из Аргентины, могут предпочесть чай или кофе, хе-хе).
Конечно, этот метод довольно медленный, поэтому я и сказал, что можно пойти за чашечкой матэ, в зависимости от упаковщика это может занять от пары секунд до нескольких минут.
Когда возвращаемся с матэ, то видим, что OEP найден:
Если попробуем на aspack’е:
Видим первую строку, где произошло выполнение, смотрим, что случится, если нажмём F7.
Возвращаемся из процедуры распаковщика, для чего снова жмём RUN, и видим, что программа запускается без остановки. Почему это происходит?
Если посмотрим внимательно, то увидим, что когда ищем OEP с помощью OllyDbg, то в данном случае распаковщик не распаковывает в первую секцию, поэтому нужно установить BPM ON ACCESS на другую секцию, а не на первую. Для того, чтобы определить на какую именно, нужно запустить крэкми без установки BPM’ов.
Как того появится вышеуказанное окно, то мы знаем, что произошла распаковка в память. Теперь, чтобы узнать, в какой секции происходит выполнение, можем установить BPM ON ACCESS в каждой секции по очереди, и если программа продолжает выполняться и не останавливается в OllyDbg, то это значит, что в данной секции выполнения не происходит, и нужно пробовать следующую.
Устанавливаем BPM ON ACCESS на первую, и ничего не происходит, на вторую – тоже ничего, затем смотрим третью, которая начинается в 404000.
Видим, что происходит останов в OllyDdbg при попытке посмотреть окно крэкми, это значит, что это и есть секция, где происходит выполнение, так что повторяем процесс сначала, чтобы найти OEP, но BPM ON ACCESS уже ставим сразу на третью секцию.
Идём за матэ, кофе, в общем за чем хотите, и когда возвращаемся, находим, что произошёл останов на OEP, хе-хе. Это ещё один метод, который работает на множестве упаковщиков.
Этот метод работает на многих упаковщиках и основывается на предыдущем. Многие упаковщики сначала выполняют инструкцию PUSHAD, чтобы сохранить исходные значения регистров, а после распаковки, но до перехода на OEP, получают значения обратно с помощью инструкции POPAD.
Смотрим в крэкми, запакованном UPX.
Видим, что PUSHAD находится в начале, иногда он может находится немного дальше, в иногда упаковщики применяют отдельный PUSH для каждого регистра (PUSH EAX, PUSH EBX и так далее), но в случае, когда сохраняются изначальные значения регистров, они должны быть извлечены обратно до перехода на OEP.
Если выполним PUSHAD с помощью F7:
Видим, что сюда были сохранены изначальные значения регистров, и если они считываются до перехода на OEP, можем установить HARDWARE BPX ON ACCESS на одно из этих значений, чтобы остановиться, когда оно будет читаться, и окажемся непосредственно перед переходом на OEP.
Ищем эти значения в DUMP, отмечаем регист ESP и делаем FOLLOW IN DUMP.
Через DUMP нам показывается содержимое стека.
Здесь видим сохранённые значения, можно отметить первый байт или первые четыре и установить HARDWARE BPX ON ACCESS.
Всё равно, что выбрать, BYTE, WORD или DWORD, главное, что остановка произойдёт во время чтения этого значения. Нажимаем RUN.
Видим, что для восстановления сохранённых в стек значений выполняется POPAD, они считываются и происходит останов, и чуть ниже находится переход на OEP, так что можно поздравить себя, что этот метод прекрасно сработал.
Видим PUSHAD, проходим его с помощью F7, а затем делаем ESP->FOLLOW IN DUMP.
Видим, что останавливаемся тут прямо перед переходом на OEP, который в данном случае выглядит как PUSH 404000, а затем RET. Трассируем с помощью F7.
Без проблем доходим до OEP.
Нужно сказать, что многие новые упаковщики имеют защиту от данного метода, но ладно, нужно знать разные методы и пробовать, какой из них подойдёт.
Продолжаем рассматривать методы для поиска OEP.
Для программ, написанных на Visual Basic’е (NATIVE или P-CODE)
Для программ, написанных на 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-крэкми, опробуем этот метод в деле.
Если у нас есть упаковщик, который генерирует множество исключений при распаковке, то существует следующий метод, который мы рассмотрим на примере приложенного к главе крэкми bitarts
[ссылка]
.
Открываем его в OllyDbg для VB с необходимыми плагинами для защиты от обнаружения.
Включаем все опции во вкладке EXCEPTIONS и запускаем крэкми в OllyDbg, после чего прибываем сюда.
Как только оказались здесь, открываем логи OllyDbg, нажав L.
Определяем последнее произошедшее исключение, которое произошло не в первой секции, то есть произошедшее не во время выполнения программы, самое последнее во время распаковки, в данном случае – это 46e88f.
Теперь перегружаем и выключаем все опции в исключениям, оставляя только первую.
Жмём RUN и каждый раз при остановке проходим её с помощью SHIFT+F9, пока не доходим до последнего, которое запомнили, в данном случае это 46e88f.
Останавливаемся здесь, это не то, что нам нужно, нажимаем shift+f9, чтобы выйти из исключения. Обратите внимание, что OllyDbg останавливается на следующей после INT3 строки, то есть на 46e890.
Находимся прямо после последнего исключения, сгенерирвоанного распаковщиком, после чего запускается сама программа.
Здесь есть несколько возможностей. Можно установить BPM ON ACCESS на секцию кода. Многие спросят, почему не установить его в самом начале, и ответ состоит в том, что многие упаковщики могут определять BPM, если он установлен в самом начала, а так мы уже, скорее всего, прошли этап обнаружения.
На забываем, что нужно нажимать SHIFT+F9, так как находимся в исключении.
Останавливаемся на OEP, можем попробовать узнать, определяет ли упаковщик BPM, установленный в самом начале.
Перегрузим программу и снова включим все опции во вкладке настроек исключений, затем идём в M, где устанавливаем BPM ON ACCESS на первую секцию, и так как будем использовать OllyDbg для нахождения OEP, то есть время сходить за парой матэ, хе-хе, так как в данном случае это может занять несколько минут.
Видим, что всё сработало замечательно, но знать метод исключений полезно, если BPM ON ACCESS определяется упаковщиком, и в этом случае необходимо сначала подобраться к OEP так близко, как это возможно, чтобы избежать обнаружения.
Видим, что для этого этого упаковщика метод поиска OEP с помощью OllyDbg также прекрасно работает.
Метод PUSHAD тоже работает, смотрим вначале:
Если оттрассируем немного с помощью F7, то через несколько строк встретим PUSHAD.
Проходим его с помощью F7 и помечаем (ESP-FOLLOW IN DUMP).
И останавливаемся на считывании сохранённых значений, трассируем до RET, пройдя который окажемся на OEP.
Метод, использующий самую используемую распаковщиком API-функцию
Метод, использующий самую используемую распаковщиком API-функцию
Перегружаем этот bitarts в OllyDbg для OEP’ов, отметив все опции во вкладке исключений, и ищем часто используемую API-функцию, обычно это GetProcAddress. LoadLibrary также может довольно часть использоваться, ExitThread, в общем, зависит от упаковщика. Попробуем этот метод с GetProcAddress.
Смотрим в CommandBar’е адрес API-функции и устанавливаем на неё BP.
Мы не можем поставить такой BP, какой нам нужен, поэтому нам придётся подправить это вручную.
Меняем BP на BP CONDITIONAL LOG, который не производит остановку, а только пишет в лог.
Устанавливаем, чтобы не происходило остановки, но всегда записывалось в лог, и кроме того, чтобы записывалось значение [esp], то есть значение возврата. Это позволит нам узнать, откуда был произведён вызов API-функции. Очищаем лог (правая кнопка мыши-CLEAR LOG).
Делаем RUN и оказываемся в собственно программе, смотрим последний вызов нашей API-функции, производимой не из первой секции.
Видим, что все вызовы были произведены из распаковщика, кроме последнего из 428c2B, то есть уже из первой секции программы, а значит, относящегося уже к самой программе. Таким образом, последний раз эта функция была вызвана распаковщиком из 47009A, так что можем установить условие, чтобы там происходил останов.
Редактируем BPX CONDITIONAL.
Устанавливаем условие, отмечая опцию останова на ON CONDITION.
Здесь произошёл останов. Это метод можно сочетать с установкой BPM ON ACCESS на секцию кода, чтобы избежать определение BPM, так как тут у нас такая проблема, что многие новые упаковщики определяют BPX, установленные на API-функции, поэтому во многих случаях лучше подобраться поближе к OEP с помощью BPM ON ACCESS, и оттуда трассировать с помощью автоматического трассировщика, встроенного в OllyDbg (TRACE INTO).
Если подобрались достаточно близко, то можно использовать метод поиска с помощью API-функцию, если повезёт, если нет, можем поменять её. Если заглянем в лог, то увидим, что одна из функций – это завершение треда.
Поэтому можем установить BP ExitThread, подредактировав его конфигурацию в OllyDbg таким образом, чтобы останов происходил каждый раз, когда тред завершается.
Останавливаемся на завершении треда.
Если установлен BPM ON ACCESS на секцию CODE, то останов также прекрасно произойдёт.
Метод первой API-функции, выполняемой программой
Метод первой API-функции, выполняемой программой
Этот метод заключается в установке BP напрямую на API-функцию, относительно которой есть подозрение, что это первая, выполняемая программа. Обычно вначале программы выполняют GetVersion, GetModuleHandleA, ряд распакованных программ получают список наиболее часто используемых API-функций. В случае с bitarts посмотрим GetVersion, а в CrueHead’е – GetModuleHandleA. Устанавливаем в bitarts BP GetVersion.
Мы должны быть уверены, что вызов производится из программы, то есть из первой секции.
Когда останавливаемся, смотрим первое значение в стеке (адрес возврата) и идём туда.
Тут видим OEP, полученный нами с помощью метода первой используемой программой API-функции. Если программа обнаружит, что на GetVersion установлен BP, можно попробовать установить его на RET из неё.
Думаю, что методов множество, здесь были приведены примеры наиболее часто используемых. Эти методы можно расширять и адаптировать к конкретному случаю, но главное, чтобы вы поняли общую идею, стоящую за ними, чтобы у вас была прочная база.
К главе приложен крэкми UnPackMe_tElock0.98.exe
[ссылка]
, на котором можно попрактиковаться в поиске OEP. В нём используются несколько трюков, и не все из вышеперечисленных методов будут работать. Будет прекрасно, если вы сумеете сами найти OEP.
Помните, что если распаковщик обнаруживает BP или HBP и не запускается, то нужно следить, чтобы не было установлено никаких BP или HBP, а когда он запустится, можно попробовать метод PUSHAD. Если он не сработает, то нужно попробовать другой.
Следующая глава будет посвящена дампу и починке IAT’ов.
[C] Рикардо Нарваха, пер. Aquila
Введение в крэкинг с нуля, используя OllyDbg – Глава 15
Введение в крэкинг с нуля, используя OllyDbg – Глава 15 — Архив WASM.RU
Файлы к статье
Прежде, чем перейти к последнему из крэкми с жёстко-заданными серийными номерами, рассмотрим тот, который я оставил вам в прошлый раз для самостоятельной работы. Он назывался SPLISH.
Откроем его в OllyDbg.
Он открывается на точке входа (ENTRY POINT).
Смотрим используемые API-функции (правая кнопка мыши – SEARCH FOR-NAME (LABEL) IN CURRENT MODULE).
Видим список используемых API-функций.
Видим, что используется GetWindowTextA при вводе серийного номера и MessageBoxA для отображения сообщения, правилен ли он или нет. Можем установить BPX на обе функции, но сначала ещё глянем, что покажет список строк, используемых программой.
Кликаем правой кнопкой мыши на листинге и выбираем SEARCH FOR-ALL REFERENCED TEXT STRING.
Здесь видим текст окошка с поздравлением, которое отображается, если был введён правильный серийный номер, поэтому кликнем два раза на этом тексте, чтобы увидеть, где он используется. Мы окажемся в окрестностях того места, где к нему есть обращение.
Попадаем в область, где ведётся работа с серийным номером.
Видим ввод текста с помощью функции GetWindowTextA и два MessageBoxA с разным текстом: правилен ли серийный номер или нет.
Ставим здесь на вызов API-функции GetWindowTextA BPX, чтобы начать с того места, где вводится серийный номер.
Запускаем крэкми с помощью F9 или RUN.
Так как пока что мы можем решить только ту часть, которая относится к жёстко-заданным серийным номерам, пишем в поле HARDCODED, которое находится наверху, неверный серийник или нажимаем на кнопку CHECK HARDCODED.
Останавливаемся на BPX, установленный ранее.
Пояснения показывают нам, что находится в стеке, и это даёт нам сразу понять, что буфер, в котором будет сохранён введенный серийный номер находится по адресу 403215.
Посмотрим через DUMP, что находится в буфере.
Вот этот буфер, куда будет сохранён введённый серийный номер. Нажимаем F8, чтобы выполнить вызов API-функции.
Так как использовали F8, выполняется вся функция, так что неправильный серийник уже находится в буфере.
Ок, следующая строка переместит 401353 в EAX (помните, что LEA не перемещает содержимое ячейки памяти по заданному адресу, а только результат того, что находится в квадратных скобках; в данном случае это будет 401353). Видим в DUMP’е, что по вышеуказанному адресу (кликаем на строке правой кнопкой мыши и выбираем FOLLOW IN DUMP-MEMORY ADDRESS, и это нам покажет, что находится по адресу 401353).
По адресу 401353 находится строка “HARDCODED”; нажимаем F7, чтобы выполнилась LEA.
После выполнения в EAX остаётся 401353, а с правой стороны появляется пояснение, что данный адресу указывает на строку “HARDCODED”.
Следующая строка загружает в EBX другой адрес, в данному случае это 403215.
После нажатия F7 EBX содержит значение 403215.
И справа Олли показывает нам, что этот адрес указывает на строку “98989898”, являющуюся введёнными нами неправильным серийным номером, который мы посмотрим в DUMP’е так же, как сделали это для предыдущей инструкции.
Здесь 403215 указывает на наш неправильный серийный номер.
Следующая строка проверяет, равен ли нулю первый байт содержимого EAX, который равен 401353.
Так как мы уже смотрели содержимое по этому адресу через DUMP, то уже знаем, что там находятся байты строки “HARDCODED”.
Первый байт в данном случае равен 48, пояснения OllyDbg нам также говорят, что это соответствует символу “H” в кодировке ASCII, а это первая буква слова “HARDCODED”. Так как это не ноль, то продолжаем.
Так как сравнение не активирует флаг Z (операнды не равны друг другу), JE не совершает перехода (помните, что JE срабатывает только тогда, когда активируется флаг Z),
Нажимаем на F7 и продолжаем.
Здесь очень хорошо видно, что сначала будет перемещён первый байт [EAX], равный 48, что соотвтествует слову “HARDCODED”, а на следующей строке перемещается [EBX], являющийся первым байтом неправильного серийного номера, после чего оба сравниваются и если они не равны, то происходит переход на 4013D2, где выводится сообщение “SORRY, PLEASE TRY AGAIN”.
Проверим это следующим образом.
Нажмём F7 и переместим в CL значение 48.
CL, как мы помним, это регистр, соответствующий двум последним цифрам ECX, здесь перемещаем 48, и вот, что получится в результате.
Затем в DL перемещается первый байт неправильного серийного номера.
Выполним строку с помощью F7, видим, что в DL находится значение 39.
Теперь CL сравнивается с DL.
Пояснение OllyDbg не оставляет сомнение: сравнивается 39 (что есть 9, т.е. первый символ введённого мной серийного номера с H, являющейся первым символом строки “HARDCODED”).
Видим, что они не равны, поэтому совершается переход на сообщение с ошибкой, а поведение если бы они были равны можем сэмулировать, изменив значение флага Z, сделав по нему двойной щелчок.
Теперь Z равен 1, как если бы в сравнении двух байтов они были равны и их вычитание друг от друга дало бы ноль, активировав данный флаг.
Видим, что в этом случае происходит увеличение EAX и EBX и переход с помощью JMP на начало цикла.
Теперь EAX указывает на второй байт строки “HARDCODED” и видим, что цикл повторяет побайтовое сравнение, пока [EAX] не будет указывать на ноль, то есть пока не завершится строка “HARDCODED” (в конце строки за последним символом находится ноль).
Тогда после увеличения значения EAX и EBX на 1 будут считаны и сравнены друг с другом вторые байты, если равны, то цикл повторится, но уже будут рассматриваться третьи байты, а когда слово “HARDCODED” закончится, то если все сравнения на равенство CL с DL прошли успешно, перехода на окошко с ошибкой не произойдёт, и попадём вот куда:
Здесь EAX указывает не на какой-либо из байтов слова “HARDCODED”, а на ноль, так как строка уже закончилась.
Вот и тот самый ноль, а это значит, что сработает соответствующая проверка.
Срабатывает JE, и мы выходим из цикла.
И попадаем в то место, где отображается сообщение о правильно введённом номере (так как поменяли значение флага Z при побайтовом сравнении, хе-хе).
Очевидно, что менять значение флага Z пришлось при каждом сравнении CL с DL, чтобы программа поверила в одинаковость обоих серийников и не перешла на сообщение об ошибке и не вышла, но в любом случае мы теперь знаем, что правильный серийный номер – это слово “HardCoded” (с соблюдением прописных и строчных букв, так как у них разный ASCII-код).
Уберём все точки останова, введём найденный серийный номер и нажмём кнопку “CHECK HARDCODED”.
Ещё одна победа, поздравляю.
Хорошо, тогда переходим к последнему крэкми с жёстко заданным серийным номером и заканчиваем на этом с ними, а в главе 16 перейдём к следующей теме.
Алгоритм следующего крэкми, который назвается SAMBO, отличается от предыдущих. Откроем его в OllyDbg.
Выходим из этого окошка, которое говорит нам, что файл, возможно, самораспаковываемый или автомодифицируемый. Такие файлы на крэкерском жаргоне мы называем пакованными, запакованными или сжатыми. В дальнейшем мы изучим их подробно, но пока запустим его в OllyDbg и, несмотря на то, что он запакован, попробуем найти серийный номер.
Соглашаемся с предупреждением OllyDbg и прибываем в EP.
С помощью ещё одного окошка OllyDbg сообщает нам, что программа, возможно, запакована или зашифрована, поэтому анализ её будет настолько же неэффективным, как пепельница в мотоцикле, так как программа будет распаковывать себя во время выполнения, поэтому пока что выберем “Нет”.
Уже на первых строках листинга мы можем заметить, что этот крэкми выполняется не так, как его незапакованные сородичи, чья первая секция начинается с 401000.
Здесь точка входа равна 4d4001.
Посмотрим, является ли секция исполняемой, для этого идём VIEW-MEMORY или нажмём клавишу M.
Видим, что программа стартует в секции, начинающейся с 4D4000, размер которой равен 2000 (в шестнадцатеричной системе), и поэтому 4d4001 – это адрес, который относится к данной секции.
Это и есть то, о чём говорила OllyDbg – точка входа находится за пределами секции code.
Секция CODE начинается с 401000, а выполнение начинается с адреса 4D4001, который относится к другой секции. OllyDbg нам сообщила, что это характеристика многих запакованных программ.
Секция, в которой находится точка входа, соответствует распаковщику, и как только он отработает и сделает то, что должен сделать, он сделает переход на на секцию code, где и будет выполнена сама программа.
А раз так, нажимаем F9.
Мы уже знаем, что когда появится окошко крэкми для ввода серийника, программа уже распакована в памяти и выполняется в секции code, поэтому можем установить на эту секцию BPM ON ACCESS , чтобы остановить выполнение на её первой строке.
При попытке вернуться в окошко крэкми OllyDbg остановится в секции code (т.е. первой секции после PE-заголовка).
Видим, что теперь программа распакована в памяти, можем её анализировать. Кликнем правую кнопку мыши в листинге.
Видим, что OllyDbg прекрасно показывает нам проанализированный код.
Теперь, когда мы находимся в секции code, можем посмотреть, какие api используются программой – то, что мы не могли посмотреть в начале, так как в тот момент нам показывались только API-функции, используемые распаковщиком, а не те, которые используются программой; сейчас же никаких проблем нет.
Уфф, какой ужасный список и ничего знакомого, посмотрим список строк; ясно, что с этим у нас тоже были бы проблемы в начале, так как находились в другой секции, и кроме того, строки были запакованы вместе с программой, так что ничего бы мы не увидели.
Ещё один ужасный список.
“YOU DID IT” – одна из строк с поздравлением.
Хорошо, есть сравнение и переход, где мы видим строки с поздравлением и ошибкой, хотя здесь и нет MessageBoxA.
Устанавливаем BPX на условный переход, чтобы проверить, решающий ли это переход, и убираем BPM ON ACCESS щелчком правой кнопки мыши.
И делаем RUN.
В окне крэкми вводим “Narvajita” или любой другой неправильный серийный номер.
Нажимаем кнопку “Test-o-Doom”.
Видим, что произошёл переход, нажимаем F9.
Тогда соглашаемся с окошком с поздравлением, что у нас получилось.
Оно говорит, что это была шутка, и ничего не вышло на самом деле, ха ха! Делаем RUN по-новой и снова прибываем к переходу, связанному с кнопкой “Test-o-Doom”.
Инвертируем переход, чтобы посмотреть, отобразится ли окошко с поздравлением и понять, отвечает ли за это вообще данный переход.
После двойного щелчка на флаге Z и изменения его значения, делаем RUN.
И отображается окошко с поздравлением, значит, это и есть то место, где решается правильность или неправильность серийного номера. Посмотрим сравнение до перехода.
Это TEST CL, CL, где проверяется, равен ли CL нулю или единице, что задаётся где-то до этого, возможно в CALL, вызываемый ранее, на который мы можем поставить BPX.
Делаем RUN, возвращаемся в окно и снова нажимаем кнопку Test, чтобы остановиться на CALL.
Остановившись, осмотримся любопытным взором вокруг, хе-хе.
Если посмотрим в стек, то увидим введённый неправильный серийный номер, заглянем в DUMP (через FOLLOW IN DUMP).
Мой глаз крэкера видит здесь строку, которая, возможно, является верным серийником; могли бы посмотреть, так ли это, но пока делать этого не будем и продолжим поиск серьёзным образом.
Если внутри данного CALL производятся какие-либо сравнения или операции с неправильным серийником, то можно установить BPX ON ACCESS на него, чтобы программа остановилась, когда с ним будет что-нибудь делаться, то есть на какой-либо операции или сравнении.
Выделим неправильный серийный номер, нажмём правую кнопку мыши и выберем BREAKPOINT-MEMORY ON ACCESS.
После этого, когда продолжим выполнение, то если внутри call происходит какая-либо работа с этим серийником, OllyDbg остановится.
Нажимаем F9.
Видим, что остановки не произошло, возможно, сравнение происходит раньше, так что мы можем поставить точки останова на CALL’ы, находящиеся ещё выше, и повторить то, что делали прежде, но лучше пустим в ход другой способ остановиться на том месте, где вводится серийный номер: здесь не используется API-функция GetWindowTextA, но у нас есть сообщения Windows, которые послужат нам не хуже.
Убираем BPM ON ACCESS.
Ставим быстрым способом BP TranslateMessage.
А теперь RUN.
Остановшись на API-функции, нажимаем на правую кнопку мыши и выбираем:
И в окошке печатаем условие MSG==202, которое является значением WM_LBUTTONUP.
И устанавливаем переключатели так, чтобы остановка происходила по ON CONDITION, и чтобы логгировалось всё.
Здесь розовым цветом обозначен BPX CONDICIONAL. Делаем RUN.
Открывается окошко крэкми и, чтобы избежать путаницы, меняю серийный номер на тот случай, если предыдущий остался в памяти.
И по нажатию на кнопку срабатывает BPX CONDICIONAL.
Остановка произошла на WM_LBUTTONUP.
Возвращаемся в программу с помощью EXECUTE TILL RETURN.
Нажимаем F7 и возвращаемся в программу.
Здесь у меня появляется две возможности. Установить BPM ON ACCESS в первой секции, чтобы сделать переход с помощью F9 и посмотреть, куда ведёт процедура сравнения серийного номера, что мне кажется сложноватым, так как я уже видел, что эта процедура весьма большая.
Другая возможность – это посмотреть, находится ли уже в памяти введённый нами серийный номер, так что идём в окно M.
Кроме того, что оно позволяет просматривать секции, здесь можно искать строки, находящиеся в памяти, кликаем правую кнопку мыши и выбираем SEARCH.
В появившемся окне в поле ASCII введём наш неправильный серийник.
Нажимаем OK, чтобы начать поиск в памяти.
С помощью CTRL+L можно искать остальные вхождения этого слова, но больше ничего не находим ни в секции, которую мы открыли, ни в остальной памяти.
Устанавливаем BPM ON ACCESS на этот неправильный серийник, , чтобы найти момент, когда с ним производятся какие-либо операции, чтение или сравнение.
И нажимаем F9.
Останавливаемся там, где он копируется в то место, которое мы видели в последнем CALL’е.
Мы знаем, что REP MOVS копирует содержимое из ESI в EDI, так что посмотрим EDI в DUMP (правая кнопка мыши на EDI-FOLLOW IN DUMP).
Сюда REP MOVS скопирует серийник, нажмём F8, чтобы он это сделал.
Оказались после этого на RET, серийник полностью скопировался, и устанавливаем на него BPM ON ACCESS.
Выполнив RUN, останавливаемся здесь.
Точнёхонько на сравнение, хех.
ESI указывает на мой неправильный серийник, а EDI на правильный, который равен «1556555».
Убираем все BPX.
Появляется окошко с поздравлением.
В следующей главе мы начнём разбор крэкми, в который используются не только серийный номер, но и имя пользователя.
© Рикардо Нарваха, пер. Aquila
archive
New Member
- Регистрация:
- 27 фев 2017
- Публикаций:
- 532
Комментарии
-
DrochNaNoch 6 дек 2022
Жесткачь, конечно. Я столько времени угрохал на решение всего crackme SPLISH, три-четыре раза бросал и начинал по новой. Думал, что надо его решить полностью: найти пароль для первого окошка; подобрать логин и пароль для второго и третьего окошка. А надо было только первый пароль найти.
-
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
Типовая защита «Запрос пароля» или «Запрос ввода регистрационного номера»
Алгоритм работы:
-
программа запрашивает пароль у
пользователя; -
программа что-то делает с паролем
(шифрует или получает хеш-функцию); -
сравнивает полученную функцию с
правильным паролем.
Пример программы.
Рис. Пример кода программы, сравнивающей
пароли
Для обхода такой защиты злоумышленнику
достаточно найти место сравнения паролей
и либо переписать пароль, либо изменить
программу так, чтобы любой вводимый
пароль считался правильным («бит-хак»).
Запустим программу CRACKME1.EXE
и проследим за ее выполнением.
Рис. Главное окно программы
Вводим «Приложение 2», нажимаем OK,
получаем сообщение о неверном пароле.
Рис. Окно программы с сообщением о
неверном пароле
Далее злоумышленнику необходимо
определить:
-
зашифрована программа или нет;
-
на каком языке написана программа –
эта информация понадобится для
определения функций, на которые ставятся
точки останова (брейкпоинты).
Воспользуемся утилитой PEiD,
http://www.peid.info/ (4 апреля
2011 проект прекратил существование),
последняя версия есть на сайте:
http://cracklab.ru/download.php?action=list&n=MzU=
Рис. Окно программы PEiD
Откроем исследуемый файл в PEiD.
Рис. Результат анализа исследуемого
файла
Видим, что программа написана на С++
фирмы Borland и ничем не
запакована. Исследуем программу сначала
в отладчике OllyDbg (см.
Приложение 2), затем в дизассемблере IDA
Pro (см. Приложение 3).
Исследование типовой защиты в отладчике OllyDbg
Воспользуемся отладчиком OllyDbg
(см. Приложение 2). Сначала исследуем
защиту программы CRACKME1.EXE
без применения плагинов, для этого
подойдет версия 2.01:
http://www.ollydbg.de/odbg201c.zip
или более ранняя: http://www.ollydbg.de/odbg110.zip
Рис. Главное окно отладчика OllyDbg
после загрузки программы CRACKME1.EXE
OllyDbg остановился на самом
начале файла, точнее, на точке входа в
программу. Злоумышленник первым делом
может попытаться найти строки, которые
выводятся программой на экран («password
FALSE»). В главном окне кода
правой клавишей мыши переходим в
контекстное меню.
Рис. Контекстное меню окна дизассемблерного
кода
Рис. Строки, которые выводятся в окне
программы
Сообщение о неправильном пароле не
зашифровано. Можем перейти на код,
который обрабатывается в случае
неправильного ввода пароля.
Рис. Выбор контекстного меню строк
Рис. Код вывода сообщения о неправильном
пароле
Видим вызов стандартной функции
MessageBoxA.
Стрелочки, которые находятся левее кода
инструкции, показывают направление
перехода. Если нажать мышкой на стрелку,
то наглядно увидим данный переход.
Рис. Дизассемблерный код вызова функции
MessageBoxA
Видим, что переход на сообщение о неверном
пароле осуществляется со строки:
00401C58 74
1D JE SHORT 00401C77
Функция JE осуществляет
условный переход, т.е. некоторая функция
проверяет пароль на правильность и,
если он верен, переходит на строку:
00401C5A 6A
00 PUSH 0
Таким образом, функция проверки
правильности пароля находится выше
строки:
00401C58 74
1D JE SHORT 00401C77
Пролистав код немного вверх, обратим
внимание на функцию ассемблера CMP,
которая выполняет сравнение, а после –
осуществляет переход.
Рис. Дизассемблерный код функции
сравнения CMP
Поставим точку останова (клавиша F2)
на строке, содержащей CMP,
и запустим программу в отладчике (клавиша
F9). Вводим любой пароль в
появившемся окне программы и нажимаем
OK. Мы перешли в отладчик
и после этого видим строку, похожую на
пароль.
Рис. Окно дизассемблерного кода,
содержащего строку пароля
Проверяем полученный пароль.
Рис. Окно с сообщением о вводе правильного
пароля
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #