Ломаем игру Spiderman при помощи отладчика Ghidra

В середине декабря в твиттер-аккаунте NSA было объявлено о релизе новой ветки Ghidra с долгожданной поддержкой отладки. Теперь с помощью GDB-заглушки и прочих механизмов можно будет выполнять ее пошагово внутри самой Ghidra. Желая отпраздновать это событие, которое совпало с моим домашним карантином, я подготовил небольшой обзор сборки этой версии, включая пример использования ее отладчика для интересной цели.

В этой статье мы: 

  • научимся собирать последнюю (да и любую) версию Ghidra при помощи Docker Container;
  • настроим плагины Ghidra Eclipse;
  • выполним сборку программного загрузчика для Ghidra;
  • прогоним через отладчик программу, использовав GDB-заглушку;
  • с помощью той же отладки разберемся, как обрабатываются пароли для игры на Game Boy Advance.

Меня очень вдохновила прекрасная работа, которую в этом направлении проделывают stackmashing и LiveOverflow. Советую заглянуть на их канал. В нашем же случае в качестве подопытной программы выступит игра Spiderman: Mysterio’s Menace. В свое время я играл в нее очень много, к тому же всегда приятно снова взглянуть на свои детские увлечения с позиции опыта. Конечная цель – показать, как правильно загружать этот образ ROM через настраиваемый загрузчик и подключать GDB-заглушку эмулятора при помощи отладчика Ghidra.

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

Проект реализуется под Ubuntu 20.04 со всеми последними обновлениями.

Сборка Ghidra

Начнем с основного. Ветка отладчика еще не была включена в официальный релиз, так что мы его будем собирать сами. К нашему везению, уважаемый dukebarman уже создал для этой задачи docker-контейнер, и нам осталось только изменить скрипт build_ghidra.sh для переключения на ветку отладчика:

Мы также настроим для этой версии Ghidra расширения разработки Eclipse, что пригодится нам позже при сборке загрузчика и написании сценариев анализа. Для этого нужно добавить в скрипт build_ghidra.sh следующее:

Далее следуйте инструкциям в README:

Это займет какое-то время, так что можете отвлечься на кофе, а к возвращению вас уже будет ждать свежесобранная Гидра. Готовая сборка находится в workdir/out:

Распакуйте файл и можете запускать Ghidra через скрипт ./ghidraRun. Я распакую содержимое в каталог ghidra-builder/workdir, так как для сборки этой версии мы будем использовать docker-контейнер. Если вы следуете за процессом, то сейчас ваша рабочая директория должна выглядеть так:

 

Сборка плагинов Eclipse

Закончив с Ghidra, можно переходить к сборке плагинов GhidraDev для Eclipse. Эти проекты находятся в каталоге ghidra-builder/workdir/ghidra/GhidraBuild/EclipsePlugins/GhidraDev.

1. Установите Eclipse

  • Выберите Java IDE

2. Установите CDT, PyDev, и Plugin Development Environment.

  • Это можно сделать из маркетплейса Eclipse.

3. Импортируйте проекты GhidraDevFeature и GhidraDevPlugin.

  • Они находятся в каталоге ghidra-builder/workdir/ghidra/GhidraBuild/EclipsePlugins/GhidraDev/
  • File -> Import -> General -> Existing Projects into Workspace
  • Добавьте ghidra-builder/workdir/ghidra/GhidraBuild/EclipsePlugins/GhidraDev
  • Выберите “Search for nested projects”
  • Импортируйте проекты.

К сведению: после импорта вы можете заметить некоторые ошибки сборки. Не обращайте внимания, так как вы просто экспортируете плагин.

4. Теперь перейдем к экспорту:

  • File -> Export
  • Plug-in Development -> Deployable Features
  • ghidradev.ghidradev
  • Выберите местоположение архива для экспорта плагина.
  • Жмите Finish.

Теперь у нас есть плагин для настраиваемой версии Ghidra, который можно скачать через Help->Install New Software.
При этом мы собрали Ghidra из ветки debugger, а также настроили расширения разработки Eclipse, получив возможность создавать плагины для нашей новой версии Ghidra.

К сведению: я хочу подчеркнуть, насколько полезно заглядывать в документацию Ghidra. В ней содержится все необходимое, начиная с мануалов по P-Code и заканчивая инструкциями по сборке и экспорту плагинов.

Создание загрузчика ROM

Для успешного анализа образа ROM нам понадобится определить все области памяти и периферийные устройства GBA. И снова, к нашей удаче, SiD3W4y уже написал для этого решение на GitHub. Ломаем игру Spiderman.

Задача загрузчика Ghidra в настройке всех необходимый областей памяти, определении отладочной информации и символов, которые могут присутствовать в файле, а также выдача всей доступной информации о целевом файле. Упомянутый выше загрузчик описывает все основные периферийные устройства GBA и прекрасно подойдет для нашей задачи, так что начнем с его копирования в тот же каталог ghidra-builder/workdir, поскольку для сборки будем использовать тот же контейнер docker, с помощью которого собирали Ghidra.

Здесь мы:
1. Запускаем docker-контейнер.
2. Собираем расширение GhidraGBA, указывая путь к месту установки.
3. Копируем каталог расширений Ghidra, чтобы он показывался под меню Install Extensions.
4. Выходим из контейнера docker.

Запустите Ghidra командой ghidraRun и перейдите в File-> Install Extensions. Выерите загрузчик GhidraGBA и кликните OK. Для применения изменений потребуется перезапустить Ghidra. Теперь при загрузке GBA ROM должно отображаться следующее:

Ломаем игру Spiderman при помощи отладчика Ghidra

После выполнения автоматического анализа Ghidra неплохо поняла этот образ ROM. В нем определено много функций и выглядит все достаточно хорошо. Следующим шагом будет найти способ сузить нашу область интереса в этом образе. Говоря условно, нам нужно найти иголку в стоге сена. Начнем с выяснения принципа работы системы паролей, для чего просто попробуем ввести несколько их вариантов.

Ломаем игру Spiderman: анализ ROM

При вводе пароля мы наблюдаем такой экран:

Ломаем игру Spiderman при помощи отладчика Ghidra

Заметьте, что используются только согласные буквы и цифры от 0 до 9. Сам же пароль состоит из 5 символов. Для реверсинга это будет неплохой отправной точкой. С помощью данной информации можно сузить область интересующих нас функций. Например, давайте просмотрим строки ROM в поиске этих значений. Если открыть окно строк, Window -> Defined Strings, и сделать выборку по пяти первым доступным символам, то мы увидим следующее:

Ломаем игру Spiderman при помощи отладчика Ghidra

Кое-какой результат имеется – мы обнаружили две точки использования этой строки. Одна расположена в 0x804c11fc, а вторая в 0x84b86f0. При проверке первой строки мы видим, что она передается функции в подпрограмме по адресу 0x8003358:

Обратите внимание на цикл, продолжающий выполнение при переменной < 5. Это говорит о том, что данная функция может оказаться полезной, поскольку пароль как раз содержит именно 5 символов. Давайте отметим ее как passwd_1 и перейдем к остальным местам использования нашей строки символов. Далее она встречается в функции по адресу 0x8002CEC. Вот декомпилированный вариант:

И снова мы видим передачу этой строки в функцию, а также очередной цикл, выполняющий 5 итераций – отметим его как passwd_2 и перейдем далее. Следующая строка встречается по адресу 0x84b86f0 и также используется в двух подпрограммах. Вот первая, расположенная в FUN_0801c37c:

В этой функции мы видим, что FUN_0801b764 вызывается со строкой @ — Accept & — Backspace. Несколько далее та же функция вызывается с переменной, содержащей интересующую нас строку. При дальнейшем рассмотренииFUN_0801b764 мы узнаем, что она копирует данные из второй переменной (строки ASCII) в адрес памяти первого аргумента. Здесь уже нельзя сказать уверенно, но меня кажется, что конкретно эта подпрограмма служит для отрисовки текста на экране, поэтому пока что я ее пропущу и перейду к следующему месту использования строки символов, которое привожу ниже:

Ломаем игру Spiderman. Что у нас здесь? Во-первых, здесь мы видим FUN_080231f4, по сути являющуюся операцией memcpy:

Ее задача – копирование строки CRDT5 в указатель ячейки памяти в local_14. Далее мы видим, что в цикле while это значение используется в сравнении:

Что же происходит здесь? В каждой итерации символ из local_14 сравнивается со значением из нашей строки доступных символов BCDFGHJKLMNPQRSTVWXYZ0123456789-. Такое поведение вполне соответствует предполагаемым действиям функции проверки пароля. Но мы знаем, что iVar1 при каждой итерации увеличивается на 1. Значит ли это, что пароли должны состоять из смежных символов в BCDFGHJKLMNPQRSTVWXYZ0123456789-? Это бы было очень глупо, к тому же строка CRDT5 никогда бы не прошла такую проверку. Если еще раз взглянуть на условие сравнения, то можно заметить, что в нем присутствует переменная param_1, которая тоже используется в качестве индекса, к которому прибавляются iVar1 и 0x520 – затем эти значения используются как INDEX в доступных для набора символах.

О чем это говорит? Переменная param_1 скорее всего указывает на массив смещений, представляющих введенные на экране пароля символы. Например, если мы введем GHDRR, то массив будет содержать [0x4,0x5,0x2,0xd,0xd].

Ломаем игру Spiderman. Но давайте не будем забегать вперед и для начала попробуем пароль CRDT5:

Ломаем игру Spiderman

Интересно! Мы попали в сцену с титрами!

Выглядит просто, не так ли? Но было бы неплохо выяснить, где именно в памяти хранится наш пароль. Если узнать, куда указывает param_1, то можно вычислить местоположение пароля в RAM и поискать перекрестные ссылки. Ну а раз у нас теперь есть нужная функция, давайте задействуем отладчик!

Отладка ROM

Те, кто повторяет процесс, должны были заметить появление нового инструмента в менеджере проектов:

Ломаем игру Spiderman

Ломаем игру Spiderman

Обратите внимание на иконку жука – с ее помощью открывается отладчик. Кликнув по ней, вы увидите следующее окно:

Ломаем игру Spiderman

В отличие от обычного представления анализатора здесь находится много дополнительных вкладок и окон. В верхнем левом углу расположено окошко Debugger Targets (цели отладчика), которое мы используем для установки соединения с отладчиком или запуска новой сессии отладки.

Ломаем игру Spiderman

Под ним располагается окно “Objects”, показывающее находящиеся в режиме отладки “Objects”. Отсюда можно делать паузу, выполнять шаги и т.д.

Ломаем игру Spiderman

В самом низу находится представление трех вкладок: Regions (области памяти), Stack (стек) и Console (консоль).

Ломаем игру Spiderman

Справа мы видим окно для показа двух других вкладок: Threads (потоки) и Time (время). Для нашей задачи отладки однопоточной ARM-системы эти окна не пригодятся.

Ломаем игру Spiderman при помощи отладчика Ghidra

И наконец, оставшаяся справа часть экрана выделена под еще несколько вкладок, которые обычно представлены в разделе анализатора Ghidra. Здесь у нас вкладка Breakpoints, отображающая заданные точки останова:

Ломаем игру Spiderman при помощи отладчика Ghidra

Вторая вкладка Registers будет обновляться значениями регистра при достижении точек останова:

Ломаем игру Spiderman при помощи отладчика Ghidra

Последняя же вкладка – это представление Modules, где при необходимости отображаются загруженные модули. Мы же в случае нашего простого приложения ничего в ней не увидим:

Ломаем игру Spiderman при помощи отладчика Ghidra

Подключение к эмулятору

Для этого проекта я использую эмулятор mGBA, главным образом потому, что он может представлять удаленную GDB-заглушку. Подключаться к нему мы будем с помощью gdb-multiarch. Чтобы выполнить это из представления отладчика нужно в окошке Debugger Targets кликнуть по зеленой вилке (Connect), что вызовет следующее окно:

Ломаем игру Spiderman при помощи отладчика Ghidra

Здесь есть много опций для удаленной отладки. В целях данной статьи я использую IN-VM GNU gdb local debugger.
Я добавил gdb-multiarch в путь команды запуска gdb. После нажатия Connect появится стандартное диалоговое окно:

Ломаем игру Spiderman при помощи отладчика Ghidra

Теперь нужно запустить сервер. Загрузите образ ROM в mGBA и выберите Tools -> Start GDB Server, всплывет такое окно:

Ломаем игру Spiderman при помощи отладчика Ghidra

Кликните Start и возвращайтесь в окно отладчика Ghidra. В диалоговом окне gdb выполните следующие команды:

Здесь мы устанавливаем gdb архитектуру, подключаемся к удаленному серверу и в завершении определяем точку останова у функции, которая, как мы считаем, проверяет, нужно ли показывать сцену с титрами. Говоря конкретнее, устанавливаем ее у сегмента, сравнивающего переданный нами символ с извлеченным из строки доступных символов. Рассматривать мы будем этот фрагмент ассемблера:

Введя все вышеприведенные команды, посмотрим, сработает ли точка останова…

Ломаем игру Spiderman при помощи отладчика Ghidra

Превосходно! Мы не только достигли точки останова, но и зафиксировали все регистры. Теперь проверим, верны ли были все наши предположения в отношении проверки пароля. Прошагаем через несколько инструкций до позиции 0801c474. Здесь мы предполагаем, что r1 будет указывать на массив индексов, представляющих введенные нами символы. Для выяснения этого заглянем в память:

К сведению: если вы делаете отладку удаленно при помощи gdb-multiarch, и при этом некоторые точки останова не срабатывают, попробуйте использовать команду stepi вместо c. Такую проблему я встречал в mGBA ранее, и она не связана с сервером GDB.

Вот оно! Что и следовало ожидать – вместо сохранения фактических символов ascii, вводимых в качестве пароля, сохраняются значения их индексов в таблице доступных символов:

Ломаем игру Spiderman при помощи отладчика Ghidra

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

Ломаем игру Spiderman при помощи отладчика Ghidra

Все так и есть! Теперь мы знаем, как сохраняются пароли, и как они выглядят в памяти, а также умеем делать отладку из Ghidra. Думаю, что для данной статьи на этом можно прерваться – в следующей же мы исследуем другие особенности пароля при помощи той же Ghidra и возможностей удаленной отладки GDB.

Ломаем игру Spiderman. Заключение

Сегодня мы познакомились с инструментами, позволяющими собрать Ghidra, рассмотрели некоторые из заявленных возможностей отладчика, с помощью которых смогли произвести удаленную отладку игры на Game Boy Advance. Многое из проделанного вы можете выполнить и без Ghidra, используя только gdb-multiarch, но я хотел познакомиться с этими возможностями и попутно поделиться с вами опытом.

Как всегда, по любым возникшим вопросам обращайтесь ко мне в Twitter. Если же вам интересно побольше узнать о Ghidra или взломе аппаратных средств в общем, можете ознакомиться с подготовленными мной обучающими материалами (англ.).

Дополнительная информация / Примечания

Ломаем игру Spiderman при помощи отладчика Ghidra

Основные выводы здесь: gvba не работает ни с какими современными GDB. По какой-то причине gdb-multiarch пропускает точки останова, а gdb из devkitarm не отвечает должным образом ghidra для предоставления регистров. Ломаем игру Spiderman.

источник