
На PicoCTF 2022 задание bbbloat из категории reverse решалось за три минуты. Открыть бинарник в Ghidra, найти в декомпиляторе функцию с вызовом strcmp, прочитать число рядом с условным переходом — флаг готов. Но большинство новичков тратят на первую подобную задачу часы, потому что не знают куда смотреть: блуждают по дизассемблерному листингу, пытаются читать ассемблер без контекста, не открывают окно строк.
Я через это прошёл. И видел, как через это проходят другие — одни и те же грабли, одни и те же потерянные вечера. Эта статья — пошаговый маршрут от загрузки бинарника до нахождения ответа, с конкретными окнами, кнопками и приёмами Ghidra, которые сэкономят те самые часы.
Прежде чем открывать дизассемблер, подготовьте рабочее место. Конкретные минимальные пороги: Подробнее — в нашем руководстве по бинарный анализ уязвимостей.
sudo apt install openjdk-21-jdk на Ubuntu). Проверяйте требования на странице релиза конкретной версии Ghidra.ghidraRun (Linux/macOS) или ghidraRun.bat (Windows).file и strings (в Linux встроены, для Windows — через WSL или GNU binutils), hex-редактор для беглого просмотра заголовков.Для типовых CTF reverse-заданий хватает ноутбука с 8 ГБ RAM — за глаза. Docker-контейнер с подготовленными упражнениями, как в курсе Introduction to Reverse Engineering with Ghidra на Hackaday.io, ускоряет старт, но не обязателен.
Три понятия, которые дальше будут на каждом шагу. Объясняю на пальцах.
Дизассемблирование — превращение машинного кода (последовательности байтов вроде 0x55, 0x48, 0x89, 0xE5) в читаемые ассемблерные инструкции (push rbp; mov rbp, rsp). Вы видите каждую команду процессора, но логику программы приходится восстанавливать в голове. Как читать книгу по буквам — возможно, но медленно.
Декомпиляция — следующий уровень. Декомпилятор берёт ассемблерные инструкции и пытается восстановить из них код на языке C. Вместо десятков строк ассемблера вы видите if (password == 42) { puts("Correct!"); }. Качество не идеальное — переменные называются iVar1, типы иногда определяются криво — но для понимания логики хватает.
Псевдокод — результат работы декомпилятора. Не настоящий C, а приближение: с нечитаемыми именами переменных, иногда с ошибочными типами, но с правильной структурой ветвлений и циклов. Именно псевдокод — главное оружие новичка при анализе бинарного файла. Читать его на порядок проще, чем ассемблер.
В CTF reverse-заданиях задача обычно сводится к одному: понять, какой ввод программа считает правильным. Программа сравнивает пользовательский ввод с эталоном — нужно найти этот эталон или восстановить логику преобразования.
Ghidra — не единственный инструмент для статического анализа бинарников. Прежде чем залезать в интерфейс, разберёмся, когда Ghidra — верный выбор.
| Критерий | Ghidra | IDA Free | radare2 |
|---|---|---|---|
| Стоимость | Бесплатно, открытый код (NSA) | Бесплатно (ограниченная версия) | Бесплатно, открытый код |
| Декомпилятор | Встроенный, все архитектуры | Нет (только в платной IDA Pro) | Через плагин r2ghidra |
| Архитектуры | 50+ (x86, ARM, MIPS, PowerPC) | x86/x64, ARM, MIPS и другие | Десятки архитектур |
| Интерфейс | GUI (Java/Swing) | GUI (нативный) | Консоль (CLI) |
| Скорость запуска | 5–15 сек (JVM) | 1–2 сек | Мгновенно |
| Скриптинг | Python (Jython), Java | IDC, IDAPython | r2pipe, JavaScript |
| Инструмент | Преимущества | Ограничения | Когда использовать | Когда не использовать |
|---|---|---|---|---|
| Ghidra | Бесплатный декомпилятор, 50+ архитектур, открытый код, активное сообщество | JVM жрёт память, интерфейс местами неинтуитивен, горячие клавиши непривычные | CTF любого уровня, анализ ELF/PE, учебные задачи, препарирование малвари | Экспресс-анализ мелких бинарников, когда критична скорость запуска |
| IDA Free | Быстрый запуск, отточенная навигация, Graph View из коробки | Декомпилятора нет — только ассемблер | Быстрая проверка гипотез на x86-бинарниках, визуальный анализ графа | Задачи, где нужен псевдокод |
| radare2 (6.1.x) | Мгновенный старт, автоматизация через CLI, мощные скрипты | Кривая обучения — вертикальная, GUI нет по умолчанию | Автоматизация анализа сотен бинарников, скриптовые пайплайны | Первое знакомство с реверсом |
Для первых шагов в reverse engineering выбор — Ghidra. Бесплатный встроенный декомпилятор — решающее преимущество. IDA Free без Hex-Rays заставит читать чистый ассемблер — навык полезный, но на старте болезненный. А radare2 потребует нескольких недель только на освоение командного интерфейса (серьёзно, там своя философия). Держать IDA Free как вторую точку зрения на бинарник — разумно, но основной рабочий инструмент на старте — Ghidra.
Запускаем Ghidra (ghidraRun на Linux/macOS, ghidraRun.bat на Windows). Откроется Project Manager — окно управления проектами.
Создание проекта. File → New Project → Non-Shared Project. Задайте имя и расположение. Проект в Ghidra — аналог рабочей папки: хранит все результаты анализа, аннотации, переименования. Один проект = одна задача (CTF-таска, один образец малвари).
Импорт бинарника. Перетащите файл в окно проекта или File → Import File. Ghidra автоматически определит формат (ELF для Linux, PE для Windows) и архитектуру процессора (x86, x64, ARM). Для типовых CTF-бинарников дефолтные настройки импорта работают корректно — ничего трогать не нужно.
Авто-анализ. Двойной клик по импортированному файлу откроет Code Browser — основное рабочее пространство. Всплывающее окно предложит запустить авто-анализ — соглашайтесь с дефолтами. Авто-анализ идентифицирует функции, восстановит перекрёстные ссылки, распознает строки и типы данных. На бинарнике в 100 КБ это секунды, на мегабайтном — до минуты.
Четыре окна Code Browser, которые нужны с первой минуты:
Symbol Tree (левая панель) — дерево символов: функции, импорты, экспорты, метки. Разверните Imports, чтобы увидеть библиотечные функции, которые использует программа. Видите strcmp, memcmp — это точки сравнения ввода. Видите printf, puts — точки вывода сообщений. Каждый импорт — подсказка о поведении бинарника. Если попадается WriteProcessMemory или VirtualAlloc — бинарник может модифицировать код в рантайме, и одним статическим анализом можно не обойтись.
Listing (центральная панель) — дизассемблированный код с адресами слева и инструкциями справа. Двойной клик на адресе в инструкции CALL перенесёт внутрь вызываемой функции. Alt+← возвращает назад. Правый клик → Patch Instruction позволяет менять инструкции прямо в листинге — пригодится для «грязного» решения CTF-задач (и никто вас за это не осудит).
Decompiler (правая панель) — псевдокод текущей функции. Подсветка строки в декомпиляторе автоматически подсвечивает соответствующий ассемблер в Listing, и наоборот. Для тех, кто учится читать ассемблер, эта синхронизация бесценна: видите if (iVar1 == 0) справа, смотрите CMP и JNZ слева — и связь между высокоуровневым и низкоуровневым кодом становится осязаемой.
Defined Strings (Window → Defined Strings) — список всех строк, найденных в бинарнике. Это окно открывается отдельно и не видно по умолчанию — запомните путь: Window → Defined Strings. Кстати, нажатие F1 при наведении на любой элемент интерфейса вызывает контекстную справку — пользуйтесь, пока осваиваетесь.
Прежде чем загружать бинарник в Ghidra, полезно пройтись внешними утилитами. На Linux или в WSL:
file crackme1
strings crackme1 | grep -i "password\|flag\|correct\|wrong"
Команда file покажет архитектуру и формат: ELF 64-bit LSB executable, x86-64. Команда strings вытянет все читаемые текстовые последовательности длиннее четырёх символов. Фильтрация через grep сузит результат до строк, которые обычно встречаются в crackme: сообщения об успехе и неудаче, слово «password», иногда сам флаг в открытом виде.
Если флаг нашёлся в выводе strings — задача решена без дизассемблера. Такое бывает в заданиях начального уровня, и да, это считается. Если нет — переходим в Ghidra.
В Code Browser откройте Window → Defined Strings. Появится таблица строк с адресами. Отсортируйте по содержимому или воспользуйтесь фильтром в верхней части окна. Ищите характерные фразы: «Wrong», «Correct», «Enter password», «Access denied».
Нашли строку? Правый клик → Show References To Address. Ghidra покажет перекрёстные ссылки (xrefs) — все места в коде, где строка используется. Обычно это одна-две функции. Двойной клик по ссылке перенесёт в функцию, а окно Decompiler справа покажет её псевдокод.
Перекрёстные ссылки — главный навигационный приём при анализе бинарного файла. От строки «Wrong password» вы попадаете прямиком в функцию проверки. Внутри этой функции — логика: что программа сравнивает с вводом пользователя.
Допустим, вы перешли по xref от строки «Correct!» и видите в окне Decompiler типичный псевдокод:
void FUN_00401136(void) {
int iVar1;
char local_48[56];
printf("Enter the password: ");
fgets(local_48, 50, stdin);
iVar1 = strcmp(local_48, "s3cr3t_fl4g");
if (iVar1 == 0) {
puts("Correct!");
} else {
puts("Wrong password!");
}
}
Разберём каждый элемент — пример учебный, но именно такой псевдокод вы увидите в реальных CTF reverse-заданиях начального уровня.
FUN_00401136 — имя функции. Ghidra не знает настоящего имени (отладочные символы отсутствуют), поэтому генерирует его из адреса: FUN_ + hex-адрес. Правый клик → Rename Function: задайте осмысленное имя, например check_password. Сразу. Не откладывайте.
iVar1 — автоматически сгенерированное имя переменной. Префикс i означает тип int, Var — переменная, 1 — порядковый номер. Правый клик → Rename Variable: переименуйте в cmp_result.
local_48 — локальная переменная на стеке. Число 48 — смещение от базы кадра стека в hex (0x48 = 72 в десятичной). Это не размер массива, а позиция на стеке — реальный размер буфера Ghidra определяет отдельно. Переименуйте в user_input.
strcmp(local_48, "s3cr3t_fl4g") — сравнение ввода с эталонной строкой. Если результат равен нулю (строки совпадают) — выводится «Correct!». Строка s3cr3t_fl4g и есть ответ.
В этом примере флаг лежит в открытом виде. Но даже когда логика сложнее, алгоритм чтения псевдокода тот же:
scanf, fgets, read, GetDlgItemTextA.strcmp, memcmp, прямой оператор ==.Ghidra иногда ошибается в определении типов. Видите undefined4 local_1c — это четырёхбайтовая переменная, тип которой Ghidra не определила. Правый клик → Retype Variable позволяет указать тип вручную: int, char *, uint32_t. После исправления типа декомпилированный код станет значительно читабельнее: вместо *(int *)(param_1 + 4) появится обращение к полю структуры.
Ещё частый случай: Ghidra показывает цепочку приведений типов вроде uVar2 = (ulong)(uint)(int)local_28. Это артефакт декомпиляции — шум. Для понимания логики цепочку можно игнорировать, переменная uVar2 просто получает значение local_28.
Правый клик на функции → Edit Function Signature позволяет исправить сигнатуру. Если Ghidra определила функцию как void FUN_00401136(void), но вы видите, что она принимает аргументы — укажите правильные типы. Декомпилированный код мгновенно станет адекватнее: параметры получат осмысленные позиции вместо прямых обращений к стеку.
Подход, который работает на большинстве CTF reverse-заданий начального и среднего уровня. Алгоритм проверен на сотнях crackme и перекликается с методологией курса Introduction to Reverse Engineering with Ghidra на Hackaday.io.
Шаг 1 — внешняя разведка (до дизассемблера). Выполните file на бинарнике: архитектура, формат, тип линковки. Прогоните strings с фильтрацией. Откройте в hex-редакторе: сигнатуры пакеров видны в первых байтах — заголовок UPX! означает упаковку утилитой UPX.
Шаг 2 — определите препятствия. Если бинарник упакован, распакуйте командой upx -d crackme перед загрузкой в Ghidra. Если в hex-редакторе нет узнаваемых строк, а секция .text выглядит как «шум» — бинарник может быть зашифрован. Проверка энтропии через binwalk -E crackme покажет график по секциям: протяжённые участки с высокими значениями (близкими к максимуму) указывают на упакованные или зашифрованные фрагменты. Секции с обычным кодом дают энтропию пониже и более «рваную».
Шаг 3 — импорт и авто-анализ. Создайте проект в Ghidra, импортируйте файл, запустите авто-анализ с дефолтными настройками. Дождитесь завершения — индикатор прогресса внизу окна Code Browser.
Шаг 4 — найдите точку входа в логику. Не начинайте с main — начинайте со строк. Window → Defined Strings, ищите «Wrong», «Correct», «Try Again». Перейдите по перекрёстной ссылке к функции-обработчику. Это ваша целевая функция.
Шаг 5 — прочитайте псевдокод. В окне Decompiler изучите логику целевой функции. Ищите: ввод данных (вызовы scanf, fgets), трансформации ввода (XOR, арифметика, циклы), точку сравнения с эталоном.
Шаг 6 — извлеките ответ. Два пути: вычислить правильный ввод из логики (чистое решение) или пропатчить условный переход — инвертировать JNE на JE через Patch Instruction в листинге. На CTF оба подхода засчитываются, не стесняйтесь. Если в функции нет явного strcmp со строкой, а сравнение идёт с числовой константой — правый клик на константе → Convert → String может раскрыть её ASCII-представление.
Не все CTF-задачи решаются поиском строк в Defined Strings. Авторы заданий среднего уровня прячут ответы, и тут начинается самое интересное. Эти техники в классификации MITRE ATT&CK относятся к тактике Defense Evasion.
XOR-кодирование строк — техника Encrypted/Encoded File (T1027.013). Строка «password123» превращается в последовательность байтов, которая в strings выглядит как мусор. В псевдокоде Ghidra это проявляется как цикл, который XOR-ит массив байтов с ключом: local_buf[i] = encoded_data[i] ^ 0x42. Чтобы получить исходную строку, воспроизведите операцию вручную или однострочником на Python.
UPX — самый распространённый пакер в CTF. Он сжимает бинарник, а при запуске распаковывает код в память. Ghidra не умеет анализировать упакованный код — декомпилятор покажет процедуру распаковки, а не реальную логику программы. Решение: распакуйте перед анализом командой upx -d binary. Определить упаковку можно по сигнатуре в заголовке или по аномально высокой энтропии секций.
Динамическое разрешение API — техника Dynamic API Resolution (T1027.007). Вместо прямого вызова strcmp программа вычисляет адрес функции в рантайме через GetProcAddress или dlsym. В импортах функция не видна, в Ghidra перекрёстные ссылки пусты. Тут статический анализ упирается в стену — нужен отладчик (x64dbg, GDB) для динамического анализа.
Обфускация потока управления — техника Obfuscated Files or Information (T1027). Авторы продвинутых заданий вставляют мёртвый код, непрозрачные предикаты, избыточные switch-конструкции. Ghidra покажет декомпилированный код, но он будет запутанным. Стратегия: ищите не всю логику, а только точку принятия решения — то место, где программа решает, верный ввод или нет.
Если ни строки, ни xrefs не дают зацепки — попробуйте искать характерные криптографические константы. S-box для AES, начальные значения хеш-функций — всё это фиксированные числа. Плагин FindCrypt-Ghidra автоматически находит такие константы в бинарнике и размечает их. Штука полезная, рекомендую поставить сразу.
Ошибки, которые стоят новичкам часов на первых CTF. Знаю, потому что видел каждую из них не раз.
Неправильная архитектура при импорте. Ghidra обычно определяет архитектуру корректно для ELF и PE. Но для raw-бинарников — firmware, дампов памяти — придётся указывать вручную. ARM-бинарник, загруженный как x86, даст полностью невалидный дизассемблер. Если декомпилированный код выглядит как бессмысленный набор операций — проверьте архитектуру первым делом.
Пропуск авто-анализа. При открытии Code Browser всплывает предложение запустить анализ. Если нажать «No», Ghidra не распознает функции, не построит перекрёстные ссылки, не найдёт строки. Всё придётся делать руками. Всегда соглашайтесь — дефолтные настройки работают для подавляющего большинства бинарников.
Начало с main() вместо строк. Функция main может содержать сотни строк с инициализацией, парсингом аргументов, обработкой ошибок. Начинать с неё — идти длинным путём. Строки «Wrong password» или «Correct» ведут напрямую к целевой функции, минуя весь служебный код. Это короткая дорога, и глупо ей не пользоваться.
Отказ от переименования. После третьей функции с именами FUN_00401136, FUN_004011a0, FUN_00401210 вы гарантированно потеряете контекст. Переименовывайте функции и переменные сразу, как поняли их назначение. Ghidra сохраняет все изменения в проекте — ничего не пропадёт.
Попытка прочитать весь ассемблер. Для решения CTF-задачи не нужно понимать каждую инструкцию. Декомпилятор Ghidra справляется с восстановлением логики. Ассемблер стоит смотреть только когда псевдокод выдаёт что-то непонятное — и то точечно, для конкретного фрагмента, а не всей программы целиком.
Забытая распаковка. Если анализ ELF файла в Ghidra показывает странный код без осмысленных функций — проверьте file binary. Упоминание UPX в выводе означает, что бинарник упакован. Распакуйте первым делом, потом препарируйте.
Для самостоятельной отработки возьмите одну из задач на crackmes.one уровня сложности 1–2 или на PicoCTF в категории Reverse Engineering начального уровня. Упражнения из репозитория курса Hackaday (github.com/wrongbaud/hackaday-u) тоже подойдут — они идут от простых crackme с паролем в открытом тексте до задач с трансформацией ввода.
Порядок действий:
file и strings с grep по ключевым словам.Если на шаге 6 вместо прямого сравнения видите XOR-цикл — скопируйте массив байтов и ключ из псевдокода, воспроизведите операцию на Python. Это покрывает добрую половину задач среднего уровня.
За несколько сотен разобранных бинарников в Ghidra — от учебных crackme до реальных образцов — у меня сформировался один устойчивый вывод: новички переоценивают сложность ассемблера и катастрофически недооценивают силу декомпилятора. Ghidra — инструмент NSA с бюджетом разработки, о котором большинство open-source проектов могут только мечтать. Декомпилятор справляется с подавляющим большинством задач начального уровня без необходимости читать хоть строку ассемблера.
Но тут ловушка. Через месяц работы исключительно с псевдокодом вы упрётесь в потолок. Задачи среднего уровня требуют понимания того, что происходит под капотом: как работает стек, зачем нужен регистр RSP, что означает инструкция LEA. Псевдокод маскирует детали, которые на определённом этапе становятся критичными.
Вторая вещь, которую постоянно замечаю: страх перед «неправильным» решением. Пропатчить условный переход вместо восстановления алгоритма — не позорно. На CTF с тайм-лимитом это рациональная стратегия. Понимание алгоритма придёт позже, когда появится база. Начинать стоит с побед, а не с академической строгости. А если хочется эту базу собрать системно, а не по обрывкам writeups — на codeby.school в IB Basics разбирают «как разобраться», а не «запомните терминологию».
🚀 Хочешь закрепить на практике? Реши задачи по теме на HackerLab — категория «pentest-machines».
0 комментариев
Пожалуйста, войдите, чтобы оставить комментарий.
Загрузка комментариев...