Главная / Блог / Reverse engineering с нуля: crackme в Ghidra

12 мин.00

Reverse engineering с нуля: crackme в Ghidra

Reverse engineering с нуля: crackme в Ghidra

На разборе первого crackme с crackmes.one я убил шесть часов. Четыре из них — на попытки читать сырой ассемблер строка за строкой, не понимая, где заканчивается инициализация переменных и начинается проверка пароля. Потом открыл окно декомпилятора Ghidra, увидел знакомый if (strcmp(...) == 0) и нашёл пароль за двадцать минут. Эта статья — тот путь от установки до найденного пароля, сжатый в пошаговый разбор. Без предварительного курса по ассемблеру, без многостраничных лекций про архитектуру x86. Только Ghidra, один простой crackme и метод, который работает.

Что нужно для дизассемблирования программ

Для reverse engineering с нуля хватит трёх компонентов: Ghidra, Java Development Kit и виртуальной машины. Подробнее — в нашем статье о бинарный анализ уязвимостей.

Ghidra — бесплатный дизассемблер (переводит машинный код в ассемблерные инструкции) и декомпилятор (восстанавливает подобие исходного кода на C). Скачивается с официального репозитория NSA на GitHub. Перед IDA Free у Ghidra одно убийственное преимущество для обучения reverse engineering — полноценный декомпилятор для x86, x64, ARM и MIPS без ограничений. Вместо чтения мнемоник вроде MOV, CMP, JNZ вы видите псевдо-C с переменными, условиями и циклами. Для начала — за глаза.

JDK 21 или новее — Ghidra написана на Java. На Linux установка в одну команду: apt install openjdk-21-jdk. На Windows — инсталлятор с adoptium.net.

Виртуальная машина — даже учебные crackme стоит запускать в изоляции. VirtualBox бесплатен и достаточен. Создайте VM с Windows 10/11, поставьте Ghidra внутри и сделайте снапшот «Clean State» до запуска любых бинарников. Сетевой адаптер переключите на Host-Only после установки софта — это изолирует VM от основной сети. Паранойя? Может быть. Но привычка пригодится, когда вместо учебного crackme на столе окажется подозрительный .exe из дикой природы.

Зачем вообще учиться анализу бинарных файлов? Три направления: анализ вредоносного ПО (понять, что делает подозрительный файл), поиск уязвимостей в закрытом ПО (когда исходников нет и не будет) и CTF-соревнования. Crackme — учебная площадка, где те же приёмы отрабатываются без юридических рисков и без угрозы для рабочей инфраструктуры.

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

Для первого разбора crackme в Ghidra подойдут наборы с открытых платформ. Классика — IOLI CrackMe (набор бинарников с возрастающей сложностью, упоминается в обучающей серии Shogun Lab «Here Be Dragons»). Уровень crackme0x00 — идеальная точка входа: одна функция проверки, один пароль, никакой обфускации.

Шаг 1. Откройте Ghidra и создайте проект: File → New Project → Non-Shared Project. Имя — что угодно осмысленное, например «CrackMe_Practice». Проект — контейнер, внутри которого Ghidra хранит все результаты анализа.

Шаг 2. Импортируйте бинарник. Перетащите .exe или ELF-файл в окно проекта, либо через File → Import File. Ghidra покажет карточку файла: архитектура (x86 или x64), формат (PE для Windows, ELF для Linux), предполагаемый компилятор. Это не просто информационная табличка — архитектура определяет набор регистров и соглашения вызова функций (calling convention), с которыми вы столкнётесь в листинге.

Шаг 3. Двойной клик по файлу в проекте открывает CodeBrowser — основное рабочее пространство. Ghidra спросит, запустить ли автоматический анализ. Жмите «да». Настройки по умолчанию для простых crackme подходят. Анализ занимает от нескольких секунд до пары минут.

После анализа перед вами три окна, которые нужно запомнить:

Symbol Tree (левая панель) — дерево символов: функции, импорты, экспорты. Если бинарник собран с отладочной информацией (debug symbols), здесь будут осмысленные имена: main, checkPassword, validate. Если без — Ghidra нагенерирует технические имена вроде FUN_00401000. Не пугайтесь: даже автоматические имена можно переименовать в процессе анализа.

Listing (центр) — дизассемблированный код: адреса, байты, ассемблерные инструкции. На первых порах это окно больше пугает, чем помогает. Нормально — мы почти не будем им пользоваться напрямую.

Decompiler (правая панель) — декомпилированный псевдо-C. Вот сюда смотрите. Ghidra превращает набор инструкций в читаемый код. Важная деталь: если выделить строку в декомпиляторе, Ghidra подсветит соответствующие инструкции в Listing. Связь двусторонняя, и со временем она поможет выучить ассемблер естественным путём — через сопоставление с уже понятным C-кодом. Я так запомнил больше мнемоник за неделю, чем за месяц чтения учебника по x86.

Как найти пароль в программе Ghidra: метод Defined Strings

Это центральный приём для решения простых crackme и стартовая точка для обучения reverse engineering. Логика проста: программа, которая выводит текст «Wrong password» или «Invalid!», хранит эти строки внутри бинарника. Нашли строку — нашли код, который её вызывает. А рядом с кодом вывода «неправильно» — проверка пароля.

Делай раз. Откройте окно строк: Window → Defined Strings. Ghidra покажет все текстовые строки, обнаруженные в бинарнике. В учебном crackme здесь будут строки результата: «Enter password», «Password OK», «Invalid Password!», «Try Again!». Именно их ищите глазами.

Делай два. Нашли строку, связанную с проверкой — например «Invalid Password!». Правый клик → References → Show References to Address. Ghidra покажет все места в коде, где эта строка используется. Обычно одно-два вхождения.

Делай три. Двойной клик по ссылке перенесёт вас к функции, которая решает — правильный пароль или нет. Окно Decompiler справа покажет псевдо-C код этой функции. Вы увидите условие if, вызов strcmp или аналогичную проверку и — на расстоянии одного взгляда — сам пароль или механизм его генерации.

Приём работает для подавляющего большинства учебных crackme. Причина банальна: авторы шифруют пароль, но почти никогда не шифруют строки уведомлений. Сообщение «Try Again!» — ниточка, которая ведёт напрямую к проверочной логике.

Декомпиляция кода: читаем проверку пароля

Допустим, Defined Strings привёл вас к функции проверки. Декомпилятор Ghidra покажет что-то подобное:

// Пример для демонстрации концепции
void main(void) {
    char input[32];
    printf("Enter password: ");
    scanf("%s", input);
    if (strcmp(input, "s3cr3t_k3y") == 0) {
        puts("Correct!");
    } else {
        puts("Invalid Password!");
    }
}

strcmp — стандартная функция сравнения строк. Возвращает 0, если строки совпадают. Одна сторона сравнения — input (пользовательский ввод), другая — захардкоженный пароль. Ответ буквально лежит в декомпилированном коде.

На практике Ghidra не всегда генерирует настолько чистый вывод. Переменные будут называться local_28, param_1, iVar2. Функция сравнения может отображаться как FUN_00401120 вместо strcmp. Вот что с этим делать:

Переименование переменных. Правый клик на переменной в декомпиляторе → Rename Variable. Назовите local_28 как user_input, iVar2 как comparison_result. Ghidra обновит имена везде, включая окно Listing. Это не косметика — через пять минут вы забудете, что такое local_28, а user_input понятен сразу.

Идентификация функций. Вместо strcmp видите FUN_00401120? Кликните на имя функции, посмотрите её декомпилированный код. Увидели цикл побайтового сравнения двух строк? Это кастомный strcmp. Переименуйте функцию через L (Label) в custom_strcmp и продолжайте анализ.

Перекрёстные ссылки. Выделите строку в декомпиляторе — Ghidra подсветит соответствующий ассемблер в Listing. Со временем вы начнёте замечать: «if (iVar2 == 0) — это пара инструкций TEST EAX, EAX + JNZ». Так выучивается ассемблер без зубрёжки — через контекст реального анализа бинарных файлов.

Статический анализ бинарников: три паттерна проверки

Простой strcmp с открытым текстом — уровень «crackme для первого дня». Реальные задачи и даже учебные crackme средней сложности используют три устойчивых паттерна.

Жёстко зашитая строка

Самый простой вариант. Пароль лежит в бинарнике как текстовая константа. Функция strcmp (или memcmp для бинарных данных) сравнивает ввод пользователя с этой константой. Обнаруживается через Defined Strings или прямо в декомпиляторе. Большинство crackme уровня «beginner» на crackmes.one используют именно этот паттерн. Тупо, но работает — и встречается чаще, чем хотелось бы.

XOR-обфускация пароля

Автор crackme хранит пароль в зашифрованном виде и расшифровывает при проверке операцией XOR (побитовое исключающее ИЛИ) с фиксированным ключом. В декомпиляторе Ghidra это выглядит как цикл, который проходит по массиву байтов и применяет ^ (XOR) к каждому элементу. Как описано в разборе FatMike's CrackMe#1 (fewstreet.com), такой цикл может XOR'ить пользовательский ввод с константным массивом, а затем сравнивать результат с эталоном:

// Пример для демонстрации концепции
for (i = 0; i < length; i++) {
    xor_output[i] = input[i] ^ xor_key[i];
}
result = check_hash(xor_output);

Чтобы восстановить пароль, нужно найти оба массива — зашифрованный эталон и ключ — и выполнить обратную операцию. Поскольку XOR обратим (A ^ B ^ B = A), достаточно XOR'нуть эталон с ключом. Оба массива обычно видны в декомпиляторе как глобальные переменные. Красивая математика — и ломается за минуту, если знаешь, куда смотреть.

Хеш-сравнение

Пароль не хранится вообще — ни в открытом виде, ни в зашифрованном. Программа вычисляет хеш (CRC32, MD5, SHA-256) от пользовательского ввода и сравнивает с захардкоженным значением. В декомпиляторе Ghidra ищите вызов функции, принимающей строку и возвращающей число или массив байтов. Характерный маркер табличной реализации CRC-32 — константа 0xEDB88320 (reflected-полином, используемый в zlib и большинстве стандартных реализаций). Побитовые реализации могут содержать прямой полином 0x04C11DB7 или не иметь явной константы вовсе. Увидели одну из этих констант в теле функции — перед вами CRC32, и пароль придётся подбирать, а не извлекать напрямую. Тут уже без брутфорса или радужных таблиц не обойтись.

Обфускация и пакеры: когда Defined Strings не помогает

Открыли crackme, зашли в Defined Strings — а там пусто. Ни одной осмысленной строки. Ghidra нашла всего одну функцию — entry. Знакомьтесь: бинарник упакован пакером.

Самый распространённый пакер — UPX. Он сжимает секции бинарника и добавляет распаковщик, который восстанавливает оригинальный код в памяти при запуске. В терминах MITRE ATT&CK это техника Software Packing (T1027.002) — тот же приём используют авторы вредоносного ПО для обхода статического анализа.

Как распознать UPX в Ghidra? Откройте Program Tree (левая верхняя панель). Если вместо стандартных секций .text, .data, .rdata видите UPX0 и UPX1 — бинарник упакован. Это наблюдение подтверждается в разборе FatMike's CrackMe#1: Ghidra не нашла функций кроме entry point, пока бинарник не распаковали.

Распаковка UPX тривиальна: upx -d crackme.exe в командной строке. После этого импортируйте распакованный файл в Ghidra заново и запустите анализ — строки и функции появятся. Если автор crackme использовал модифицированный UPX (подменил сигнатуру), штатная распаковка не сработает — но для учебных задач такое редкость.

Другой приём, который встречается в crackme средней сложности — защита от отладки (Debugger Evasion, T1622 по MITRE ATT&CK). Программа вызывает IsDebuggerPresent() на Windows или ptrace(PTRACE_TRACEME, ...) на Linux. Обнаружен отладчик — программа завершается или выдаёт ложный результат. (Примечание: MITRE T1622 задокументирована преимущественно для Windows; на Linux аналогичные приёмы применяются на практике, но формально менее покрыты тестами.) В декомпиляторе такие проверки видны как вызовы этих функций в начале main. Как показано в материале FreeCodeCamp о решении ELF-crackme, проверку можно обойти патчингом — заменой инструкции условного перехода (JNZJMP) прямо в Ghidra через правый клик → Patch Instruction. По документам — нельзя просто так взять и поменять инструкцию. На практике — один клик.

Частые ошибки при обучении reverse engineering

Путаница между виртуальным адресом и файловым смещением (file offset). Адрес 0x00401000 в Listing — виртуальный адрес, по которому код будет загружен в память. Он НЕ совпадает с позицией этих байтов в файле на диске. Если открываете бинарник в hex-редакторе и ищете байты по адресу из Ghidra — не найдёте. Для перевода используйте Window → Bytes в Ghidra, где показаны оба значения. Я сам на этом потерял час, пока не разобрался.

Попытка читать ассемблер без декомпилятора. Новички часто считают, что «настоящий» reverse engineer читает только ассемблер. На практике декомпилятор — основной инструмент даже у опытных аналитиков. Ассемблер нужен для уточнения деталей, которые декомпилятор интерпретировал неточно: порядок аргументов, побочные эффекты инструкций, самомодифицирующийся код. Но стартовая точка — всегда декомпилятор. Пуризм тут не помогает, а тормозит.

Игнорирование переименования переменных. Ghidra позволяет переименовывать переменные, функции и метки. Новички этим не пользуются, а через десять минут анализа теряются в local_14, local_18, local_1c. Переименуйте каждую переменную сразу, как только поняли её назначение. Это не опция — это обязательный этап статического анализа бинарников.

Анализ stripped-бинарника без поиска main. Если в Symbol Tree нет функции main — бинарник собран без отладочных символов (stripped). Ищите вызов __libc_start_main (для ELF) — один из его аргументов и есть адрес main. Ghidra обычно подсвечивает этот вызов в функции entry. Для PE-файлов ищите mainCRTStartup или начните с точки входа (entry point) и проследите вызовы вглубь.

Чек-лист: от запуска Ghidra до найденного пароля

  1. Создать проект в Ghidra, импортировать бинарник.
  2. Запустить автоанализ с настройками по умолчанию.
  3. Проверить Program Tree: если секции UPX0/UPX1 — распаковать через upx -d и импортировать заново.
  4. Открыть Window → Defined Strings, искать строки результата: «Correct», «Wrong», «Invalid», «Try Again».
  5. По найденной строке → References → Show References to Address → перейти к функции проверки.
  6. В окне Decompiler прочитать псевдо-C код функции. Искать strcmp, memcmp, циклы с XOR, вызовы хеш-функций.
  7. Переименовать переменные и функции для читаемости.
  8. Извлечь пароль: из аргумента strcmp, из XOR-ключа и эталона или подбором к хешу.
  9. Проверить: запустить crackme в VM и ввести найденный пароль.

Если на шаге 4 строк нет, а на шаге 3 пакер не обнаружен — вероятно, строки генерируются динамически. Это уже задача для динамического анализа с отладчиком (x64dbg, GDB). Но для первых десяти crackme Defined Strings покрывает задачу.

Каждый разобранный crackme формирует паттерн-матчинг — способность мгновенно узнавать конструкцию if (hash(input) == 0x5A6AA47D) или for (i=0; i<len; i++) output[i] = input[i] ^ key[i] в декомпиляторе. Первые три-четыре crackme занимают часы, пятый-шестой — минуты. Никакой магии: набор паттернов проверки конечен, а навык декомпиляции кода накапливается быстрее, чем кажется на старте.

Отдельно про обучение reverse engineering в целом. Распространённое мнение — сначала нужно пройти курс по ассемблеру x86, потом по архитектуре процессоров, потом по структуре PE-файлов, и только после этого открывать Ghidra. Я считаю этот подход перевёрнутым. Ассемблер запоминается в десять раз быстрее, когда вы видите его рядом с декомпилированным C-кодом реального бинарника, а не в вакууме учебника. Открывайте Ghidra в первый день. Берите crackme0x00 из IOLI-набора. Разбирайте. Ассемблерные мнемоники выучатся сами — через сопоставление с псевдо-C в окне Decompiler. Теория без практики в реверсе бесполезна, а практика без теории — просто медленнее, но всё равно работает. Потренировавшись «на кошках» и освоив базовый метод — двигайтесь дальше: crackme посложнее, CTF-таски, реальные семплы. Если хотите системно закрыть базу по ИБ шире реверса — на IB Basics это делают без академического тона, от первых задач к реальным навыкам.

🚀 Хочешь закрепить на практике? CTF-задачи и лабы ждут на HackerLab.

Поделиться

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

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

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

Читайте также

Wireshark для CTF: ищем флаги в PCAP

13 мин.

4

Wireshark для CTF: ищем флаги в PCAP

Пошаговый алгоритм анализа PCAP в Wireshark для CTF: от Protocol Hierarchy до DNS exfiltration. Фильтры, tshark-однострочники и типовые ловушки.

2 СЕНТЯБРЬ, 2026

Burp Suite для начинающих: прокси, Repeater, первый баг

14 мин.

8

Burp Suite для начинающих: прокси, Repeater, первый баг

Пошаговая настройка Burp Suite, FoxyProxy и SSL-сертификата. Перехват запросов, Repeater, Intruder и разбор CTF web-таска с нахождением IDOR за 5 минут.

1 СЕНТЯБРЬ, 2026

Форматы CTF соревнований: jeopardy vs AD

14 мин.

14

Форматы CTF соревнований: jeopardy vs AD

Как устроены jeopardy и attack-defense CTF: категории задач, скоринг, gamebox, чекеры. Чек-лист инструментов и площадок для первого турнира.

1 СЕНТЯБРЬ, 2026