Все статьи

Реверсинг CrackMe: Пошаговый гайд по взлому и Ghidra

Реверсинг CrackMe: Пошаговый гайд по взлому и Ghidra

Приветствую, коллеги-энтузиасты! Сегодня мы снова погрузимся в увлекательный мир реверс-инжиниринга. Наша миссия — "спасти Бингуса" из довольно хитрого CrackMe уровня 2.0. Реверсинг бинарных файлов — это настоящее искусство, и мы освоим его на практике, вооружившись мощным инструментом Ghidra.

Представь, что перед тобой запертая дверь, и тебе нужно вскрыть её, поняв, как именно она устроена. Вот это и есть реверсинг. В этом гайде я покажу, как шаг за шагом анализировать код, вычислять нужные проверки и, в итоге, узнать правильный пароль.

> Важное замечание: Весь материал заточен исключительно под образовательные цели. Мы используем эти техники, чтобы лучше понять, как работают программы, а не для каких-либо недобросовестных дел. У тебя точно получится освоить эти основы, если будешь следовать моим шагам!

Изображение

Для структурированного анализа часто нужно что-то заранее сгенерировать или извлечь, чтобы бинарник это проверил. Если проверка пароля основана на простом XOR-шифровании или хешировании (что часто бывает в лёгких CrackMe), мы можем быстро написать скрипт для подбора ключей.

Допустим, в бинарнике мы нашли функцию, которая сравнивает ввод пользователя с неким "магическим" значением (скажем, 0xDEADBEEF), используя XOR с неизвестным однобайтовым ключом KEY.

Вот пример Python-скрипта, который быстро перебирает все 256 вариантов однобайтового ключа:

def crack_simple_xor(target_value: int, known_data: bytes) -> None:
    """
    Перебирает все возможные однобайтовые XOR-ключи (0x00 до 0xFF)
    и проверяет, совпадает ли результат с целевым значением.
    """
    print(f"Целевое значение (Big Endian): {hex(target_value)}")

    # Преобразуем целевое значение в байты (предположим, что это 4 байта)
    target_bytes = target_value.to_bytes(4, byteorder='big')

    for key in range(256):
        decrypted_bytes = bytearray()
        
        # Применяем XOR ко всем известным данным
        for data_byte in known_data:
            decrypted_bytes.append(data_byte ^ key)
        
        # Проверяем, совпадает ли результат с целевым значением
        if bytes(decrypted_bytes) == target_bytes:
            print(f"[+] Ключ найден! Ключ (int): {key} (0x{key:02X})")
            print(f"[+] Расшифрованные данные: {decrypted_bytes.hex()}")
            return

    print("[-] Подходящий ключ не найден среди 256 вариантов.")

# --- Использование примера ---
# В Ghidra мы выяснили, что XOR-сумма должна давать

Как подготовиться к анализу бинарного файла?

Прежде чем мы начнём, освежи в памяти основы работы с дизассемблерами. Если ты новичок, обязательно перечитай введение по Ghidra. Это наш основной инструмент для декомпиляции.

Наша цель — взломать вот это задание: [https://crackmes.one/crackme/68a153668fac2855fe6fb67b](https://crackmes.one/crackme/68a153668fac2855fe6fb67b).

Запустим его для начала, посмотрим, что происходит:


<img src="/static/images/blog/47e20c4064e0.png" alt="Изображение" loading="lazy">
...
**Бингус взорвался... **

<img src="/static/images/blog/a65e5deb44b2.jpg" alt="Изображение" loading="lazy">
R.I.P BINGUS

Ладно, шутки в сторону. Открываем наш бинарник в **Ghidra** и смотрим, что там внутри.

<img src="/static/images/blog/9ce1911889fd.png" alt="Изображение" loading="lazy">
Декомпилированный код

Мы видим начальную точку входа, но это ещё не основная функция `main`. По сути, вся важная логика часто притаилась где-то в дереве символов (`Symbol Tree`). Нам нужно найти функцию, которая обрабатывает аргументы, введённые пользователем.

Можно возразить, что для столь простых CrackMe, где нужно найти одну статическую проверку пароля, использовать такой тяжёлый декомпилятор, как Ghidra, — это избыточно. Гораздо быстрее было бы ограничиться дизассемблером типа IDA Pro (в режиме ассемблера) или даже отладчиком вроде x64dbg, чтобы просто просканировать строковые константы и найти вызов `strcmp`. Но, хотя для быстрой "охоты на строки" это сработает, Ghidra позволяет нам сразу увидеть высокоуровневую логику, которую компилятор сгенерировал из исходника (например, имена переменных или управляющие структуры). Это критически важно, когда мы переходим к более сложным задачам, где простая строковая константа не выдаёт всей логики проверки.

## Какие функции нужно анализировать в Ghidra?

В Ghidra мы видим кучу функций. Нас интересуют те, что связаны с вводом данных. Я их просмотрел и обнаружил, что три функции совершенно пустые. **Вот что я увидел**:

- `FUN_00101020` — Пусто.
- `FUN_00101090` — Пусто.
- `FUN_001010c0` — Пусто.

**ОПА!** Самое интересное спрятано в `FUN_00101149`. Кликаем на неё и смотрим код.

<img src="/static/images/blog/cc84de53c427.png" alt="Изображение" loading="lazy">
<img src="/static/images/blog/ee90d5fcd9a7.png" alt="Изображение" loading="lazy">

### Шаг 1: Анализ первой части кода

В начале мы видим проверку аргументов. Программа принимает два аргумента (подразумеваем, что это `argc` и `argv`).

> `param_2 + 8` — это `argv[0]` (имя программы).
> `(param_2 + 8) + 1` — это `argv[1]` (наш потенциальный пароль).

Происходит проверка:

1.  Равны ли `argv[0]` и `argv[1]`?
2.  Должна ли длина второго аргумента (`argv[1]`) быть **ровно два символа**?
3.  Эти два символа обязаны быть **одинаковыми**.

Если что-то не так — Бингус не выживет. Это уже ценные подсказки для нашего пароля!

<img src="/static/images/blog/de107d1db974.png" alt="Изображение" loading="lazy">

### Шаг 2: Разбираемся с циклом и суммированием

Дальше идёт цикл. Переменная `result` инициализируется значением `0x66` (это 102 в десятичной системе).

Я сразу приметил, что строка `"This is a red herring"` — это просто отвлекающий манёвр. Цикл проходит по символам, и каждый символ просто прибавляется к `result`. В Си строки — это, по сути, массивы символов, и цикл их обрабатывает.

<img src="/static/images/blog/d54d25cfbb35.png" alt="Изображение" loading="lazy">

### Шаг 3: Финальная проверка

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

`result` (после цикла) + `argv[1]` (как целое число) + `argv[0]` (как целое число) должно дать **`0x8c5` (2245)**.

Хмм... Нам нужно найти такие два одинаковых символа, чтобы их сумма дала нужное нам число.

<img src="/static/images/blog/8fbe2311d083.png" alt="Изображение" loading="lazy">

Недавно столкнулся с задачей, где клиент жаловался на нестабильную работу старого скрипта на Python 2.7, который должен был парсить логи Apache, но внезапно начал выкидывать `IndexError` при обработке строк с UTF-8 символами. Я потратил почти полдня, отлаживая его через `pdb`, прежде чем понял: дело не в логике цикла, а в том, что одна из "пустых" функций, которую мы давно закомментировали, на самом деле содержала заглушку. Она вызывалась при определённом состоянии лога, и её возврат некорректно обрабатывался в основном коде как пустой список, а не как ожидаемый объект. Выяснилось, что даже неиспользуемый код может быть критическим, если его логика не была удалена полностью, а просто замаскирована.

## Как вычислить пароль: Практический подход

Мы разобрали логику. Пора применять математику. Без скриптинга здесь не обойтись. Я люблю писать такие вещи на C, но ты можешь использовать Python или любой другой удобный язык.

Сначала я попробовал использовать ИИ, чтобы узнать сумму символов из строки, которую он посчитал (21 символ). ИИ выдал сумму 1982. Прибавим начальный `result` 102. Получаем 2084.

$2245 - 2084 = 161$

Нам нужно, чтобы сумма двух одинаковых символов дала 161. Делим пополам: $161 / 2 = 80.5$. Такого символа не существует! **Вот где мы споткнулись.** Не переживай, если с первого раза не вышло — это норма в реверсинге!

<img src="/static/images/blog/e36f1a3f58ee.svg" alt="Изображение" loading="lazy">
<img src="/static/images/blog/9011e660e4fa.jpeg" alt="Изображение" loading="lazy">

Как я понял позже, ИИ не учёл, что в строках может быть нулевой терминатор `\0`. Пересчитаем вручную, написав небольшой C-скрипт:

Запускаем ./a.out. Вывод: 1919!

Это наш новый result после цикла. ⚡

Изображение

Теперь отнимем это от целевого значения 2245:

$2245 - 1919 = 326$

Изображение

Нам нужно, чтобы сумма двух одинаковых символов дала 326. Делим пополам:

$326 / 2 = 163$

Изображение
  • Вот оно! Символ с кодом 163. Посмотрим, что это за символ, сконвертировав код в char:
Вывод: **'p'**!

## Результат: Спасаем Бингуса 🚀

Мы нашли наш пароль: два одинаковых символа, и это 'p'. Пароль: `pp`.

Проверим: `./bingus pp`

<img src="/static/images/blog/e20b9a3cc77a.png" alt="Изображение" loading="lazy">

**ДА!** Бингус спасён, и программа отработала как надо!

Честно говоря, через 10 минут у тебя уже будет рабочий прототип понимания, как разбирать такие задачи. Попробуй сам!

## Выводы о доверии к AI и практике

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

Реверсинг — это очень мощный навык. Когда ты сам проходишь через все эти проверки и, наконец, находишь решение, ты получаешь невероятный прилив дофамина. Это то самое чувство "ААА! ПОНЯЛ!", которое заставляет двигаться дальше.

Практикуйтесь постоянно, разбирайте подобные задачки и не забывайте про теорию. Это прямой путь к успеху!

---

## Нужна помощь с автоматизацией?

Самостоятельный реверс-инжиниринг и анализ бинарных файлов — это сложная область, требующая глубокого понимания архитектуры и системного программирования. Иногда проще и быстрее обратиться к экспертам, чтобы разобраться в механизмах защиты или автоматизировать задачи по анализу данных.

Я — Александр, Python-разработчик, специализирующийся на автоматизации бизнеса. Моя команда и я умеем решать сложные задачи, связанные с анализом данных, парсингом и интеграциями. Мы поможем:

- Провести глубокий анализ уязвимостей (строго в рамках закона и этики).
- Разработать скрипты для автоматизированного тестирования.
- Интегрировать сложные API для сбора данных.

****Обсудим ваш проект:***** [skypoyinvest.ru](https:**//skypoyinvest.ru/)

Нужна помощь с автоматизацией?

Обсудим ваш проект и найдём решение

Получить консультацию