Главная / Блог / Ghidra реверс инжиниринг: разбираем первый бинарник в CTF-задаче по реверсу

13 мин.00

Ghidra реверс инжиниринг: разбираем первый бинарник в CTF-задаче по реверсу

Ghidra реверс инжиниринг: разбираем первый бинарник в CTF-задаче по реверсу

Ghidra реверс инжиниринг: разбираем первый бинарник в CTF-задаче по реверсу

Первый бинарник в категории reverse на CTF я ковырял больше двух часов — и не решил. Функция main утонула где-то среди тысячи символов, декомпилятор выдал стену из undefined8 и DAT_00104020, а ассемблерный листинг выглядел как случайный набор мнемоник. Через полгода практики аналогичная задача занимала десять минут. Разница — не в знании ассемблера наизусть, а в одном конкретном навыке: умении открыть бинарник в Ghidra, найти нужную функцию и прочитать псевдокод декомпилятора так, чтобы восстановить логику программы. Эта статья — тот гайд, который я хотел бы иметь в самом начале.

Ghidra для начинающих: что это за инструмент и почему именно он

Ghidra — фреймворк для реверс-инжиниринга с открытым исходным кодом, выпущенный NSA в 2019 году. Репозиторий на GitHub обновляется регулярно, актуальная ветка требует JDK 21, версия — 11.x (начало 2025 года, данные с crackmes.one и GitHub). Подробнее — в нашем обзоре бинарный анализ уязвимостей.

Главная фишка Ghidra для новичка — встроенный декомпилятор. Он берёт машинный код и превращает его обратно в псевдокод, похожий на C. Не идеально, не один-в-один с оригиналом — но достаточно, чтобы понять логику программы без глубокого знания ассемблера.

Ghidra — инструмент статического анализа бинарника. Программу не нужно запускать, чтобы понять, что она делает. Для CTF это удобно, а для анализа малвари — необходимость: запускать неизвестный бинарник на рабочей машине — прямой путь к переустановке ОС. В терминах MITRE ATT&CK, статический анализ позволяет разбирать техники вроде Obfuscated Files or Information (T1027, Defense Evasion) и Stripped Payloads (T1027.008, Defense Evasion) без риска выполнения вредоносного кода.

Чем Ghidra отличается от IDA Pro и radare2

Три основных инструмента для реверса — и выбор между ними сбивает с толку на старте. Вот сравнение по критериям, которые реально важны, когда ты только начинаешь:

Критерий Ghidra IDA Pro (Free) radare2 (v6.1.x)
Цена Бесплатно, open source Бесплатно (с ограничениями) Бесплатно, open source
Встроенный декомпилятор Да Нет (только платная IDA) Через Cutter (GUI-обёртка)
Графический интерфейс Да Да CLI по умолчанию
Архитектуры x86, ARM, MIPS, PowerPC, AVR, десятки других x86 в бесплатной Широкая поддержка
Порог входа Средний Низкий Высокий (CLI-ориентирован)
Скриптинг Java, Python (PyGhidra) Нет (в бесплатной) r2pipe, JavaScript
Командная работа Ghidra Server Нет в бесплатной Нет из коробки
Когда использовать CTF, обучение, malware analysis Быстрый просмотр x86-бинарников Автоматизация, скрипты в пайплайне
Когда НЕ использовать Очень крупные бинарники (>100 МБ) — жрёт RAM Любые задачи с декомпиляцией Первый опыт в реверсе (CLI — отдельная боль)

Для CTF-задач и обучения реверс-инжинирингу Ghidra — оптимальный старт. Бесплатный декомпилятор — вот главное. IDA Pro Free декомпилятор не даёт, а платная версия стоит тысячи долларов. radare2 мощен, но его CLI — отдельная кривая обучения, и для первых задач это лишний барьер.

Установка Ghidra и требования к окружению

Перед началом работы — минимальные требования:

  • ОС: Windows, Linux или macOS (Ghidra написана на Java, кроссплатформенна)
  • RAM: минимум 4 ГБ, рекомендуется 8 ГБ (автоанализ крупных бинарников активно ест память)
  • JDK: версия 21, 64-bit (без Java Ghidra не запустится — даже не пытайтесь)
  • Диск: ~500 МБ на саму Ghidra + место под проекты
  • Сеть: не требуется (Ghidra работает полностью offline после установки)

Установка на Linux (Ubuntu/Debian/Mint):

sudo apt update && sudo apt install openjdk-21-jdk
# Проверяем: java -version → должна показать 21.x
# Скачиваем ZIP с github.com/NationalSecurityAgency/ghidra/releases
unzip ghidra_*_PUBLIC_*.zip
cd ghidra_*_PUBLIC && ./ghidraRun

На Windows: качаете JDK 21 (Adoptium или Oracle), затем ZIP-архив Ghidra с GitHub, распаковываете и запускаете ghidraRun.bat. Никакого инсталлятора, никаких дополнительных зависимостей.

При первом запуске Ghidra покажет окно "Tip of the Day" — советы там полезные, но можно закрыть. Перед вами — окно менеджера проектов. Пока оно пустое.

Создаём проект и импортируем бинарник для анализа в Ghidra

Ghidra работает с проектами. Один проект — одна CTF-задача или один бинарник для анализа.

Последовательность: File → New Project → Non-Shared Project (вы работаете один) → выбираете директорию и имя проекта. После создания — File → Import File → указываете бинарник.

Ghidra автоматически определит формат файла: ELF (Linux), PE (Windows), Mach-O (macOS). Появится окно с метаданными: архитектура процессора, разрядность, предполагаемый компилятор. Жмёте OK — на этом этапе менять ничего не нужно.

Дальше: двойной клик по импортированному файлу открывает CodeBrowser — основное рабочее окно Ghidra. Инструмент спросит: "Analyze now?" — соглашайтесь и оставляйте все опции по умолчанию. Автоанализ занимает от секунд до пары минут. За это время Ghidra определяет границы функций, строит перекрёстные ссылки, вытаскивает текстовые строки, идентифицирует библиотечные вызовы.

Интерфейс декомпилятора Ghidra: как читать то, что он показывает

После автоанализа перед вами — интерфейс, от которого хочется закрыть вкладку. Десятки панелей, кнопок, деревьев. На практике для решения CTF-задач по реверсу нужны четыре окна. Остальные можно закрыть — ничего не потеряете.

Symbol Tree, Listing и Decompile — три окна для работы

Symbol Tree (левая панель) — дерево всех символов программы: функции, импорты, экспорты, метки. Здесь вы ищете main — точку входа. Раскрываете ветку Functions, находите main, двойной клик — оба центральных окна перемещаются к этой функции. Если main в списке нет — бинарник stripped, и нужен отдельный подход (разберём ниже).

Listing (центральная панель) — ассемблерный код. Адреса слева, мнемоники справа: MOV, CMP, JNE, CALL. Для начинающего этот вид пугает, но в 90% CTF-задач начального уровня вам не придётся читать ассемблер напрямую.

Decompile (правая панель) — псевдокод на C, сгенерированный декомпилятором Ghidra. Вот это — ваше главное оружие. Кликаете на функцию в Symbol Tree — декомпилятор показывает её приблизительный исходный код. Не идеальный, но читаемый.

Четвёртое полезное окно — Defined Strings (меню Window → Defined Strings). Показывает все текстовые строки из бинарника: сообщения пользователю, пароли, пути к файлам. Иногда задача решается одним взглядом на этот список — и такое бывает чаще, чем кажется.

Почему псевдокод Ghidra выглядит страшно и как с этим работать

Декомпилятор понятия не имеет, как автор назвал переменные. Вместо password вы увидите local_28, вместо char input[100] — что-то вроде char local_78[104], а вместо осмысленного типа — undefined8. Глобальные переменные получают имена по адресу: DAT_00104020 — это просто ячейка памяти по адресу 0x00104020.

Переименование — ключевой навык работы с Ghidra. Правый клик на переменную → Rename Variable (или клавиша L). Увидели, что local_28 передаётся вторым аргументом в strcmp? Переименуйте в user_input. Строка по адресу DAT_00104020 содержит текст "Enter password:"? Переименуйте в prompt_msg. После 5-10 переименований псевдокод из нечитаемого становится понятным. Я не преувеличиваю — это буквально ощущение, будто туман рассеивается.

Тип undefined8 означает 8 байт данных, тип которых Ghidra не определила автоматически. Для возвращаемого значения main это практически всегда int. Правый клик на тип → Retype Variable позволяет задать тип вручную. Необязательно для решения задачи, но делает код читабельнее.

Ещё один частый элемент: функции с именами вида FUN_00401234. Ghidra не смогла сопоставить их с известными библиотечными вызовами. Двойной клик — и вы увидите их декомпилированный код.

Решаем CTF задачу по реверсу: от бинарника до флага

Типичная задача категории reverse для начинающих: дан бинарник, нужно найти флаг. Программа либо проверяет пароль и выводит флаг при правильном вводе (password checker), либо проверяет введённый флаг на корректность (flag checker). Подход к каждому типу отличается.

Разведка: strings, file и первый осмотр

Прежде чем запускать Ghidra, потратьте 30 секунд на командную строку. Это реально экономит время.

Команда file crackme покажет тип файла: ELF 64-bit, PE 32-bit, статическая или динамическая линковка. Эта информация определяет, какие инструменты применимы дальше.

Команда strings crackme выведет все читаемые строки из бинарника. По опыту решения задач на PicoCTF и crackmes.one — удивительное количество задач начального уровня решается одной этой командой. Пароль или флаг валяется в бинарнике открытым текстом. Если в выводе видите строку формата flag{...} или CTF{...} — проверяйте сразу, задача может закрыться за секунды.

Третий приём — ltrace ./crackme. Утилита перехватывает вызовы библиотечных функций в реальном времени. Если программа сравнивает ваш ввод с захардкоженным паролем через strcmp, то ltrace покажет оба аргумента: strcmp("ваш_ввод", "реальный_пароль"). Ограничение: современные версии gcc иногда инлайнят strcmp, и ltrace их не видит. Но проверка занимает две секунды — пропускать её нет смысла.

Анализ бинарника в Ghidra: ищем main и читаем декомпилированный код

Допустим, strings и ltrace не дали результата: пароль зашифрован или логика проверки нестандартная. Переходим к Ghidra.

Импортируем бинарник, запускаем автоанализ. В Symbol Tree → Functions находим main. Двойной клик — декомпилятор показывает псевдокод. Вот как может выглядеть простой password checker после автоанализа и переименования переменных:

void main(void) {
  char user_input[104];
  puts("Enter password:");
  gets(user_input);
  result = strcmp(user_input, s_S3cr3tP@ss_00104020);
  if (result == 0) {
    puts("Access Granted!");
  } else {
    puts("Access Denied!");
  }
}

Программа принимает ввод через gets, сравнивает его со строкой по адресу 00104020 через strcmp. Совпало — доступ получен. Двойной клик на s_S3cr3tP@ss_00104020 в окне Decompile переведёт вас к месту в памяти, где хранится сама строка. Задача решена — вводим пароль, получаем флаг.

Но это тривиальный случай. Интереснее — flag checker, где программа трансформирует ваш ввод перед сравнением.

Восстанавливаем логику шифрования и пишем решение

Представим задачу посложнее. Декомпилятор показывает: программа берёт каждый символ ввода, XOR-ит с ключом 5, затем прибавляет 5, и сравнивает результат с захардкоженным массивом байт. Такие паттерны (XOR, сдвиги, арифметические операции) — самые частые в CTF reverse начального уровня.

Логика прямого преобразования: (input[i] ^ 5) + 5 == expected[i]

Обратная операция для получения флага: input[i] = (expected[i] - 5) ^ 5

Двойной клик на массив в декомпиляторе покажет конкретные байты. Выписываем их и пишем скрипт:

expected = [0x48, 0x4e, 0x49, 0x47, 0x83, 0x82, 0x3a, 0x7c, 0x5f]
flag = ""
for b in expected:
    flag += chr((b - 5) ^ 5)
print(flag)

Запускаем — получаем флаг. Эта методология — прочитать декомпилированный код, определить трансформацию, написать обратный скрипт на Python — покрывает основную массу CTF-задач начального и среднего уровня.

Совет, который сэкономит часы: не пытайтесь понять каждую строку декомпилятора. Ищите strcmp, memcmp, strncmp — это точки сравнения. От них раскручивайте логику назад: что именно сравнивается, откуда берётся, какие операции применяются к вводу. Всё остальное — шум.

Реверс бинарника Linux: что делать со stripped binary

На реальных CTF и при анализе малвари бинарники часто stripped — из них удалена таблица символов. В классификации MITRE ATT&CK это техника Stripped Payloads (T1027.008, Defense Evasion): удаление символов затрудняет анализ.

В Symbol Tree stripped бинарника вы не найдёте функции main. Вместо неё — набор безликих FUN_00401234, FUN_004015a0. Выглядит безнадёжно, но есть чёткий алгоритм.

Алгоритм поиска main в stripped ELF-бинарнике:

  1. Ищете функцию entry в Symbol Tree — она есть всегда, это точка входа ELF-файла.
  2. Внутри entry находите вызов __libc_start_main. Это стандартная функция glibc, инициализирующая рантайм перед вызовом main.
  3. Первый аргумент __libc_start_main — адрес функции main. В декомпиляторе вы увидите что-то вроде __libc_start_main(FUN_00401196, ...).
  4. Переходите к FUN_00401196, переименовываете её в main (правый клик → Rename Function) — и работаете как обычно.

Этот приём работает для ELF-бинарников с динамической линковкой к glibc. Для статически слинкованных бинарников или программ на Go/Rust ситуация сложнее — __libc_start_main может отсутствовать. Тогда используйте окно Defined Strings: найдите текст, который программа выводит пользователю (например, "Wrong password" или "Enter flag"), двойной клик → правый клик → References → Show References to Address. Ghidra покажет, какая функция обращается к этой строке. С высокой вероятностью это и есть основная логика. Строки — лучший друг реверсера, когда символы вычищены.

Ограничения статического анализа и когда подключать отладчик

Ghidra — инструмент статического анализа. Она читает код, не выполняя его. И у этого подхода есть конкретные ограничения, о которых лучше знать до того, как вы потратите три часа на задачу, которая не решается чисто статически.

Динамически вычисляемые значения. Если программа генерирует ключ шифрования в рантайме на основе времени, PID процесса или переменных окружения — Ghidra покажет код генерации, но не конкретное значение ключа. Для этого нужен отладчик: GDB с расширением pwndbg на Linux или x64dbg на Windows.

Упакованные бинарники. Продвинутые CTF-задачи и реальная малварь используют техники вроде Software Packing (T1027.002, Defense Evasion) и Encrypted/Encoded File (T1027.013). Упакованный бинарник при статическом анализе покажет только код распаковщика, а не реальную логику. Для распаковки нужна комбинация: GDB для снятия дампа памяти после распаковки, затем загрузка дампа в Ghidra.

Anti-debug и Debugger Evasion (T1622). Некоторые бинарники определяют наличие отладчика и меняют поведение. Ghidra этому не подвержена — вы видите обе ветки кода (с отладчиком и без). А вот в динамическом анализе придётся обходить эти проверки.

Оптимизации компилятора. Код, скомпилированный с -O2 или -O3, может выглядеть в декомпиляторе совершенно непохоже на оригинал: циклы развёрнуты, переменные объединены, условия инвертированы. Декомпилятор честно восстанавливает логику — но компилятор её уже трансформировал при оптимизации. Тут Ghidra не виновата.

Когда подключать GDB и ltrace к процессу анализа

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

Практический пример: функция decrypt(key, data) вызывается в коде, но key формируется через длинную цепочку вычислений. Вместо ручной трассировки: запускаете gdb ./crackme, ставите точку останова на адрес вызова decrypt (адрес берёте из Ghidra — он виден в окне Listing), запускаете программу, и в момент остановки читаете аргументы из регистров RDI и RSI. Пять минут вместо часа ручных вычислений.

Ghidra и GDB — не конкуренты, а связка. Статический анализ в Ghidra даёт понимание структуры и логики. Динамический анализ в GDB — конкретные значения в конкретный момент исполнения. Вместе они покрывают подавляющее большинство задач по реверсу.

Методология CTF задач по реверсу: порядок действий

Выработанный порядок действий при решении задачи категории reverse — от быстрых проверок к глубокому анализу:

  1. file — определяем тип бинарника и архитектуру процессора
  2. strings — ищем флаг или пароль в открытом виде
  3. ltrace — перехватываем библиотечные вызовы (strcmp, memcmp)
  4. Ghidra — находим main, читаем декомпилированный код, переименовываем переменные
  5. Определяем тип задачи: password checker (извлекаем пароль) или flag checker (обращаем трансформацию)
  6. Для flag checker — пишем скрипт на Python, применяющий обратные операции в обратном порядке
  7. Если статический анализ не даёт ответа — подключаем GDB для динамического анализа

Этот подход работает на crackmes.one, PicoCTF и большинстве CTF-площадок. По мере роста навыков добавляются инструменты: angr (версия 9.2.x, активно развивается) для символьного исполнения — автоматического поиска входных данных, приводящих к нужному результату; Frida для динамической инструментации; собственные скрипты внутри Ghidra на Java или Python для автоматизации рутины вроде массового переименования функций.

Ghidra поддерживает встроенный Script Manager (Window → Script Manager) с десятками готовых скриптов. Для начала это избыточно, но через десяток решённых задач автоматизация начнёт экономить время. Одна из полезных категорий — поиск криптографических констант в бинарнике, что помогает быстро определить используемый алгоритм шифрования.

Большинство гайдов по реверс-инжинирингу начинаются с ассемблера: «выучите MOV, LEA, CMP, JNE — потом поговорим про инструменты». Я считаю этот подход ошибочным для тех, кто только начинает. Декомпилятор Ghidra существует именно для того, чтобы не читать ассемблер напрямую. На первых порах достаточно узнавать три мнемоники: CMP (сравнение), JE/JNE (условный переход), CALL (вызов функции) — и то только когда декомпилятор даёт неоднозначный результат.

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

На первом уровне — десятки задач, решаемых чтением псевдокода в окне Decompile, переименованием переменных и десятистрочным Python-скриптом. Проблема подхода «сначала ассемблер» в том, что он убивает мотивацию. Человек тратит месяц на мнемоники и регистры, пытается решить задачу через листинг — не может, и уходит. А мог бы за один вечер разобрать три crackme в Ghidra, почувствовать результат, и уже с этим контекстом вернуться к ассемблеру, понимая, зачем он нужен.

Реверс — ремесло, где один решённый бинарник даёт больше понимания, чем десять часов чтения про регистры общего назначения. Скачайте Ghidra, возьмите любой crackme с crackmes.one уровня 1.0 — и попробуйте найти пароль. Если застрянете — вернитесь к этой статье и пройдите по шагам заново. На третьем бинарнике руки будут делать всё сами.

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

Поделиться

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

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

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