Главная / Блог / Reverse Engineering в Ghidra: разбираем простой crackme и находим условие для флага

13 мин.00

Reverse Engineering в Ghidra: разбираем простой crackme и находим условие для флага

Reverse Engineering в Ghidra: разбираем простой crackme и находим условие для флага

Reverse Engineering в Ghidra: разбираем простой crackme и находим условие для флага

На последнем CTF нашей команде попался reverse-таск на 200 очков: ELF-бинарник, 14 КБ, stripped, без внешних зависимостей. Три человека бились час — один в radare2, второй в IDA Free, третий пытался трейсить в GDB. Ghidra решила задачу за восемь минут: строка «Access Granted» нашлась через Defined Strings, xref привёл к функции-обработчику, декомпилятор показал strcmp с захардкоженной строкой. Весь секрет не в инструменте, а в методологии: куда смотреть первым делом и как читать то, что выплёвывает декомпилятор. Этот ghidra туториал — пошаговый разбор crackme, от импорта бинарника до извлечения флага, с объяснением каждого действия и типичных ловушек, в которые новички влетают раз за разом.

Подготовка окружения для статического анализа бинарных файлов

Прежде чем открывать Ghidra — подготовьте рабочее место. Без нормального окружения анализ исполняемого файла в Ghidra рискует упереться в технические проблемы на первом же шаге. Подробнее — в нашем обзоре бинарный анализ уязвимостей.

Требования к окружению:

  • ОС: Windows 10/11 или Linux (Ubuntu 20.04+). Ghidra кроссплатформенна, работает одинаково на обеих системах.
  • RAM: минимум 4 ГБ, рекомендуется 8 ГБ. Согласно требованиям курса Introduction to Reverse Engineering with Ghidra на Hackaday.io, 8 ГБ — базовое требование для комфортной работы. Ghidra крутится на JVM и при авто-анализе крупных бинарников жрёт память ощутимо.
  • JDK 17+ для Ghidra 11.x — скачивается с adoptium.net или через пакетный менеджер (apt install openjdk-17-jdk на Ubuntu).
  • Ghidra 11.x — актуальная версия с ghidra-sre.org. Установки нет: распаковка архива, запуск через ghidraRun (Linux) или ghidraRun.bat (Windows).
  • Виртуальная машина — обязательна при анализе незнакомых бинарников. VirtualBox или VMware Workstation Player, сеть внутри ВМ отключайте. Как справедливо отмечает руководство Varonis по Ghidra, анализировать подозрительные файлы на хостовой ОС — недопустимо.
  • Опционально: CFF Explorer (Windows, быстрый просмотр PE-заголовков), x64dbg (динамический анализ для случаев, когда статического анализа бинарных файлов недостаточно).

Для практики возьмите любой crackme начального уровня с crackmes.one (difficulty 1-2) или из репозитория hackaday-u — там подготовлены упражнения специально для обучения reverse engineering crackme: нужно найти пароль или обойти проверку, сложность нарастает от сессии к сессии.

Разведка до дизассемблера: что делать перед импортом в Ghidra

Частая ошибка новичка — сразу тащить бинарник в дизассемблер. Пять минут разведки экономят час мучений внутри Ghidra.

Шаг 1. Команда file. Запустите file crackme_binary в терминале. Команда покажет архитектуру (x86, x86-64, ARM), формат (ELF, PE), тип линковки (static, dynamic), наличие stripped-символов. Эта информация определяет, чего ожидать внутри Ghidra: stripped-бинарник потребует дополнительных шагов для поиска main.

Шаг 2. Команда strings. Прогоните strings crackme_binary | grep -i "pass\|flag\|correct\|wrong" — иногда флаг или подсказка лежат в открытом виде. На CTF это встречается чаще, чем хочется признавать. Ищите характерные паттерны: flag{, Correct!, Wrong password, Try Again. Если strings не выдаёт ничего читаемого — бинарник запакован или строки зашифрованы.

Шаг 3. Hex-редактор. Откройте файл в hex-редакторе и посмотрите первые байты. Сигнатуры пакеров видны сразу: UPX! для UPX, ASPack для ASPack. Нашли пакер — значит прямой разбор crackme в Ghidra покажет только распаковщик, а не реальную логику.

Предусловия и ограничения: описанный подход к разведке работает для нативных ELF и PE бинарников. Для .NET-сборок, Java JAR, Python-байткода нужны специализированные инструменты: dotPeek, JD-GUI, pycdc/decompyle3 соответственно (uncompyle6 не поддерживает Python 3.9+). Ghidra умеет анализировать Java и Dalvik-байткод, но методология поиска флага там другая.

Импорт crackme в Ghidra и запуск авто-анализа

Шаг 1. Запустите Ghidra, создайте проект: File → New Project → Non-Shared Project. Дайте проекту осмысленное имя — «CTF_TaskName» удобнее, чем «New Project». Укажите каталог для хранения файлов проекта.

Шаг 2. Перетащите бинарник в окно проекта. Ghidra автоматически определит формат и архитектуру — для типовых ELF и PE этого достаточно. В окне импорта увидите: формат файла, процессорную архитектуру, размер, точку входа. Нажмите OK. Ожидаемый результат: файл появится в списке проекта с иконкой, соответствующей его типу.

Шаг 3. Двойной клик по файлу — откроется Code Browser. Всплывающее окно предложит авто-анализ. Соглашайтесь с настройками по умолчанию. Для Windows PE-файлов рекомендую дополнительно включить опцию «WindowsPE x86 Propagate External Parameters» — по данным руководства Varonis, параметры импортированных функций отобразятся прямо в листинге, что заметно упрощает чтение кода.

Шаг 4. Дождитесь окончания авто-анализа — прогресс виден в правом нижнем углу Code Browser. На бинарнике 50 КБ это секунды, на мегабайтных файлах — минуты. Не переходите к анализу, пока полоса прогресса не исчезнет: иначе функции и перекрёстные ссылки будут определены не полностью.

Ghidra для начинающих: навигация по Code Browser

Для решения crackme CTF задач в Ghidra нужны три окна. Если какое-то из них не видно — откройте через меню Window.

Symbol Tree (левая панель) — дерево символов: импорты, экспорты, обнаруженные функции. Разверните Imports: если видите scanf, fgets, GetDlgItemTextA — бинарник читает пользовательский ввод. Видите strcmp, memcmp — где-то есть сравнение строк. Разверните Functions — здесь Ghidra покажет все обнаруженные функции. Часть будет именована (main, entry), часть получит автоматические имена вида FUN_00401234 — FUN означает function, число после — адрес в памяти.

Listing (центральная панель) — дизассемблированный код с адресами, байтами, операндами. Двойной клик на адресе в инструкции CALL переносит внутрь вызываемой функции, Alt+← возвращает назад. Для анализа ассемблерного кода это основной рабочий инструмент: здесь видно каждую инструкцию без искажений декомпилятора.

Decompiler (правая панель) — псевдокод на C, результат декомпиляции кода. Для тех, кто только начинает reverse engineering в Ghidra, это спасение. Подсветка строки в декомпиляторе автоматически подсвечивает соответствующий ассемблер в листинге. Видите if (iVar1 == 0) справа — смотрите CMP и JNZ слева. После десятка таких сопоставлений связь между C и ассемблером становится осязаемой. Нажатие F1 при наведении на любой элемент интерфейса вызывает контекстную справку — пользуйтесь, пока осваиваетесь.

Window → Function Graph открывает граф потока управления функции — аналог Graph View в IDA. Блоки кода связаны стрелками, визуально видно ветвления и циклы. По умолчанию граф открывается в уменьшенном масштабе — правый клик → Properties → Start Fully Zoomed In исправит это.

Поиск условия флага: от строк к целевой функции

Ключевой этап всего разбора crackme. Не начинайте с main или entry — начинайте со строк. Поиск условия флага в бинарнике через строки — самый быстрый путь к результату.

Шаг 1. Откройте Defined Strings: Window → Defined Strings. Ghidra покажет все текстовые строки, обнаруженные в бинарнике. Ищите характерные маркеры: Wrong password, Try Again, Correct, Access Granted, Good work, Bad password.

Шаг 2. Нашли строку? Кликните — Listing перейдёт к её адресу в секции данных. Теперь нужно найти функцию, которая её использует. Правый клик → References → Show References to Address (или клавиша X). Появится список всех мест, где строка упоминается — перекрёстные ссылки (xref). Обычно для строки вроде «Try Again!» будет один-два xref: это и есть функция проверки.

Шаг 3. Перейдите по xref. Decompiler автоматически покажет псевдокод целевой функции. Именно здесь находится условие проверки флага — логика, которая решает, выдать «Correct» или «Wrong».

Когда техника НЕ работает: если Defined Strings пуст или содержит только мусор — строки зашифрованы, сгенерированы в рантайме или бинарник запакован. Это признак обфускации (Obfuscated Files or Information, T1027, Defense Evasion). В таком случае ищите вызовы strcmp, memcmp в Symbol Tree → Imports и идите по их xref. Если и импортов нет — бинарник использует собственные реализации сравнения, и тут уже придётся копать глубже.

Ghidra Decompiler: пример разбора проверки пароля

Допустим, xref привёл к функции FUN_080485a5. Ghidra показывает в декомпиляторе C-подобный псевдокод. Вот типичная картина для простого crackme (адаптировано из разбора задачи ELF - CrackPass на root-me, описанного в материале FreeCodeCamp):

void CheckPassword(char *param_1) {
    char generated_pwd[128], input_copy[4096];
    FUN_080484f4(input_copy, param_1);         // копирование ввода без проверки длины (CWE-120)
    FUN_0804851c(DAT_08049960, generated_pwd); // генерация эталона
    int result = strcmp(generated_pwd, input_copy);
    if (result == 0)
        printf("Good work: %s\n", generated_pwd);
    else
        puts("Wrong password!");
}

Что здесь видно: функция принимает пользовательский ввод (param_1), копирует его в буфер (FUN_080484f4 — кастомная реализация memcpy), затем генерирует эталонный пароль из захардкоженной строки DAT_08049960 и сравнивает через strcmp. Результат равен нулю — пароль верный.

Переименование переменных и правка сигнатур функций

Псевдокод Ghidra по умолчанию нечитаем: переменные называются iVar1, uVar2, функции — FUN_00401234. Это нормально — не пугайтесь. Вот приёмы, которые ускоряют дизассемблирование бинарного файла:

Переименование переменных: правый клик на переменной → Rename Variable. param_1user_input, local_108cgenerated_password. Ghidra синхронизирует имена между декомпилятором и листингом автоматически. Код мгновенно становится осмысленным — я обычно трачу на это минуту-две, и дальше работать в разы приятнее.

Правка сигнатур: правый клик на функции → Edit Function Signature. Если Ghidra определила параметр как undefined4, а в него передаётся строка — измените тип на char *. Декомпилированный код станет заметно чище.

Исследование вложенных функций: двойной клик на FUN_080484f4 — внутри цикл, копирующий байты из одного буфера в другой. Кастомный memcpy, ничего интересного. Нажмите L на имени и переименуйте в custom_memcpy. Вернитесь назад Alt+← — вызов читается как custom_memcpy(input_copy, user_input). Код из окна декомпилятора копируется напрямую — удобно для CTF-отчётов и writeup'ов.

Паттерны проверки: на что обращать внимание

В решении crackme CTF задач логика проверки сводится к нескольким повторяющимся паттернам:

Прямое сравнениеstrcmp(user_input, "secret_flag"). Пароль в открытом виде. Достаточно прочитать строку. Встречается в crackme с difficulty 1 — и да, такие задачи реально попадаются на CTF.

Трансформация и сравнение — ввод проходит через XOR, битовый сдвиг, кастомную функцию, затем сравнивается с эталоном. По данным разбора FatMike's CrackMe#1 на fewstreet.com, одна из проверок выглядела так: XOR каждого символа ввода с константным массивом, затем CRC32 от результата и сравнение с 0x5a6aa47d. Результат XOR сохранялся в отдельный буфер, а 0xedb88320 — полином CRC32. Такие задачи решаются восстановлением алгоритма и подбором (z3-солвер) или патчингом.

Многослойная проверка — несколько условий одновременно. В том же FatMike's CrackMe#1 проверялись два условия: iVar1 == 0x5a6aa47d (контрольная сумма) и DAT_0040b4ec == 0x16 (длина ввода = 22 символа). Обе проверки должны пройти, иначе бинарник показывает «Try Again!».

Предусловия и ограничения: описанные паттерны характерны для crackme уровня сложности 1-3. В более сложных задачах используются виртуальные машины для обфускации кода, многопоточные проверки, самомодифицирующийся код — там уже совсем другая история.

Патчинг бинарного файла: обход проверки за один байт

Когда вычислить правильный ввод сложно или долго — проще обойти проверку. На CTF оба подхода засчитываются: чистое решение (вычислить пароль) и грязное (пропатчить). Патчинг бинарных файлов в Ghidra занимает минуту.

Работает если: бинарник не запакован, нет проверки собственной целостности, нет антиотладочных проверок до целевой функции. Не работает если: присутствует Software Packing (T1027.002, Defense Evasion) или бинарник проверяет хеш собственного кода перед исполнением.

Шаг 1. Найдите условный переход в листинге. В декомпиляторе видите if (result == 0) — кликните на эту строку, в Listing подсветится инструкция JNZ (jump if not zero) или JNE (jump if not equal). Это переход к ветке «Wrong password».

Шаг 2. Правый клик на инструкции → Patch Instruction. Замените JNZ на JZ (jump if zero). Логика инвертирована: любой неправильный пароль пройдёт проверку, а правильный — нет. Альтернатива — заменить JNZ на JMP (безусловный переход), тогда ветка «Correct» выполняется всегда.

Шаг 3. Экспортируйте пропатченный бинарник: File → Export Program → выберите формат (ELF или PE Original). Запустите — программа должна принять любой ввод.

По данным разбора crackme с root-me в материале FreeCodeCamp, автор применил три патча: инвертировал проверку ptrace (JNS → JMP), обошёл валидацию символов ввода (JZ → JMP) и инвертировал финальное сравнение (JNZ → JZ). Три изменённых байта — и бинарник выдал пароль при произвольном вводе. Красиво, хоть и грязно.

Пакеры и антиотладка: когда Ghidra показывает пустоту

Если Defined Strings пуст, а вместо функций видна только entry и блоки данных — бинарник защищён. Три основных препятствия при решении crackme CTF задач повышенной сложности.

Software Packing: распаковка UPX

Software Packing (T1027.002, Defense Evasion) — сжатие или шифрование исполняемого кода. В crackme чаще всего встречается UPX. Признаки в Ghidra: секции UPX0 и UPX1 в Program Tree, минимум функций (только entry), отсутствие читаемых строк. По данным разбора FatMike's CrackMe#1 на fewstreet.com, Windows Defender срабатывает на бинарники с UPX — косвенный признак пакинга.

Распаковка: утилита upx -d packed_binary.exe снимает UPX-упаковку. CFF Explorer даёт графический интерфейс: UPX Utility → Unpack → File → Save As. После распаковки заново импортируйте файл в Ghidra и повторите авто-анализ — теперь Defined Strings покажет реальные строки, в Symbol Tree появятся функции.

Debugger Evasion: ptrace и IsDebuggerPresent

Debugger Evasion (T1622, Defense Evasion / Discovery) — crackme проверяет наличие отладчика. На Linux распространён вызов ptrace(PTRACE_TRACEME, 0, 1, 0) — если процесс уже трейсится, ptrace вернёт ошибку и программа завершится через abort(). (Примечание: MITRE ATT&CK описывает T1622 преимущественно в контексте Windows; ptrace концептуально подпадает под эту технику, но публичные тесты Atomic Red Team для Linux-варианта отсутствуют.) На Windows: IsDebuggerPresent() или чтение флага BeingDebugged из PEB.

При чисто статическом анализе в Ghidra это не мешает — код не запускается. Но при попытке отладки пропатченного бинарника в x64dbg или GDB антиотладочные проверки сработают. Решение: найти ptrace или IsDebuggerPresent в Symbol Tree → Imports, перейти по xref, пропатчить условный переход после вызова — точно так же, как основную проверку.

Stripped-бинарники: поиск main без символов

Stripped Payloads (T1027.008, Defense Evasion) — из бинарника удалены отладочные символы. В Symbol Tree нет main, все функции именуются FUN_XXXXXXXX. На CTF это стандартная ситуация, так что привыкайте.

Как найти main в stripped ELF: функция __libc_start_main сохраняется даже в stripped-бинарниках, поскольку она из динамической библиотеки libc. По данным из материала vickieli.dev, первый аргумент __libc_start_main — адрес main. В Ghidra: найдите __libc_start_main через Symbol Tree → Imports, перейдите по xref к entry. В декомпиляторе увидите вызов вида __libc_start_main(FUN_00401136, ...)FUN_00401136 и есть main. Нажмите L и переименуйте. Дальше — стандартный путь.

Минилаб для отработки разбора crackme

Для закрепления навыков reverse engineering в Ghidra — минимальный воспроизводимый сценарий:

  1. Скачайте один crackme с crackmes.one (difficulty 1, платформа Linux/x86-64 или Windows/x86).
  2. Выполните разведку: file, strings, hex-редактор.
  3. Импортируйте в Ghidra, выполните авто-анализ.
  4. Найдите строку «Wrong» или «Correct» через Defined Strings.
  5. Перейдите по xref к целевой функции.
  6. Прочитайте декомпилированный код, переименуйте переменные.
  7. Определите условие проверки и либо вычислите пароль, либо пропатчите переход.

Репозиторий hackaday-u содержит четыре уровня упражнений с Docker-контейнером для запуска — каждое упражнение представляет собой задачу типа «найти пароль», сложность нарастает от сессии к сессии. Первый crackme этой серии проходится за 15 минут по описанной методологии. На root-me.org раздел Cracking предлагает задачи ELF разного уровня для более длительной практики.

Первый разбор займёт 30-60 минут. Третий — не больше десяти. Дело не в сложности Ghidra, а в количестве повторений.

Половина русскоязычных материалов по reverse engineering останавливается на скриншотах интерфейса Ghidra: вот Symbol Tree, вот Listing, вот кнопка Decompile. Это обзор софта, а не реверс-инжиниринг. Реверс начинается в момент, когда видишь FUN_0804851c и принимаешь решение: нырять внутрь или пропатчить переход над ней. Навык растёт от количества разобранных бинарников, не от количества прочитанных гайдов. После десятого crackme паттерны начинают повторяться — XOR с константой, CRC32 от промежуточного буфера, кастомный memcpy вместо стандартного — и псевдокод декомпилятора перестаёт выглядеть стеной из iVar.

Но есть нюанс, о котором мало кто предупреждает: декомпилятор Ghidra ошибается. Регулярно. Типы переменных определяются криво, структуры разваливаются в набор отдельных полей, иногда целые ветки кода склеиваются или исчезают. Привычка сверять псевдокод с ассемблерным листингом — не перфекционизм, а необходимость. Если декомпилятор показывает линейный код без ветвлений, а в листинге два условных перехода — верьте листингу. Декомпилятор — подсказка, не истина. Те, кто привыкает работать только с Decompiler-окном, рано или поздно упираются в задачу, где C-подобный вывод врёт, а три инструкции ассемблера содержат ответ. На WAPT эту связку «декомпилятор соврал → листинг спас» проходят с лабами в модуле по реверсу.

🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».

Поделиться

0 комментариев

Пожалуйста, войдите, чтобы оставить комментарий.

Загрузка комментариев...