Как пропатчить ОС 1996 года с помощью Ghidra

в 22:11, , рубрики: 1996, C, kurzweil, skillfactory, бинарники, Блог компании SkillFactory, декомпиляция, звук, реверс-инжиниринг, ретро, Си, синтезаторы, старое железо, форматы

Не так давно автор материала работал над сложной головоломкой: как пропатчить операционную систему сэмплового синтезатора 1996 года Kurzweil K2500. О путешествии автора в старый код музыкального инструмента рассказываем к старту курса по разработке на C++.

Как пропатчить ОС 1996 года с помощью Ghidra - 1

Как и многие цифровые музыкальные инструменты, этот синтезатор — просто компьютер с дополнительными микросхемами, здесь — на основе популярного в то время процессора Motorola 68000, который широко применялся в первых Macintosh и Sega Genesis. Я хочу пропатчить ОС этого зверя, чтобы делать много вещей, большинство из которых оставлю воображению читателя.

Поиск операционной системы

«Пропатчить ОС» — звучит прекрасно, но как вообще добраться до кода? К счастью, операционная система K2500 по-прежнему лежит на старом FTP производителя. В архиве по ссылке архиве находится файл .KOS. Видимо, это пользовательский формат.

Hex Fiend отображает его байты:

Как пропатчить ОС 1996 года с помощью Ghidra - 2

В глаза не бросается ничего. Кажется, вверху — удобочитаемый 4-байтовый заголовок: SYS0, за которым, возможно, идут другие байты заголовка, но утверждать это сложно. Хотя уже известно, что ОС работает на процессоре Motorola 68000: просто попробуем интерпретировать данные как бинарники и посмотрим, что сможем сделать.

Ghidra

Файл операционной системы — вероятно, сырой машинный код: буквально инструкции и данные, интерпретируемые самим процессором. Чтобы сделать файл хоть немного понятнее, потребуется дизассемблировать и, надеюсь, декомпилировать его в код на C.

Для этого воспользуемся Ghidra — программой для реверс-инжиниринга с открытым исходным кодом, созданной, поддерживаемой и выпущенной АНБ США. Да, тот самый. Правда. Импортируем файл .KOS в Ghidra и проанализируем с настройками по умолчанию.

Как пропатчить ОС 1996 года с помощью Ghidra - 3

Прокрутка файла показывает, что части данных разобраны Ghidra как допустимые инструкции Motorola 68000, но большая часть файла остаётся загадкой. Как ни странно, Ghidra правильно определила в файле несколько удобочитаемых строк.

Отлично! Но выглядит так, как будто код ссылается к строкам, смещённым на некоторую величину: они отображаются обрезанными.

Как пропатчить ОС 1996 года с помощью Ghidra - 4

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

Все перекрёстные ссылки функций неточные: мы реконструируем данные, отправляясь в неверном направлении после почти каждой попытки пройти по ссылке. Исправить это нужно в первую очередь.

Реверс-инжиниринг загрузчика

Для реверс-инжиниринга .KOS крайне полезно погрузиться в код, который создаёт или использует эти файлы. Кода создания файлов у нас нет, но есть доступ к тому коду, что использует эти файлы: можно скачать загрузчик самого синтезатора!

Откроем его в Ghidra и, чтобы облегчить себе жизнь, предположим, что первые 8 байт файла — часть заголовка. Откуда взялась эта цифра? Я попробовал 0, +4, +8, +12, +16 и +20 байт смещения, и +8 дизассемблировался наиболее правильно. Да, попытки заняли некоторое время. 

Оглядываясь назад, отмечу, что всё это работает, потому что код файла загружается по адресу памяти 0x0. Если бы он загружался где-то ещё, до дизассемблирования пришлось бы выяснить этот адрес.

Как и прежде, сначала поищем что-нибудь понятное человеку. Поиск по строкам выдаёт пару строк с описанием ошибок, которые выглядят как ошибки, бросаемые нужным кодом:

Как пропатчить ОС 1996 года с помощью Ghidra - 5

Ghidra определила то, что здесь называется XREF, как перекрёстные ссылки. Они указывают, что эти строки вызываются из определённого места. Пройдём по ссылке:

Как пропатчить ОС 1996 года с помощью Ghidra - 6

Ага! Мы куда-то движемся. Это очень похоже на оператор switch, декомпилированный в дерево if. Выглядит как ряд кодов ошибок от 0x100 до 0x105, от 0x200, 0x201 и так далее. Каждый код связан со строкой описания ошибки, которая предположительно выводится на экран. Продолжаем тянуть за эту ниточку. Через «Find References» в Ghidra находим эту функцию:

Как пропатчить ОС 1996 года с помощью Ghidra - 7

Мы всё ближе! Здесь Ghidra сделала кое-что очень важное: декомпилированный код содержит имена некоторых переменных, автоматически определяемые по строкам, на которые эти переменные указывают. 

Некоторые из этих переменных — собственно строки, так что можно предположить их значение и воспользоваться переименованием и повторным вводом Ghidra, чтобы намного легче читать функцию:

Как пропатчить ОС 1996 года с помощью Ghidra - 8

Это выглядит как двухэтапный процесс:

  1. Новый файл ОС проверяется вызовом  ActuallyCheckOrFlashTheOS?(0, ?).

  2. Если проверка пройдена, то та же функция снова вызывается с аргументом 1. 

Похоже, эта функция читает формат файла .KOS. Давайте углубимся в неё:

Как пропатчить ОС 1996 года с помощью Ghidra - 9

Здесь нет ни понятных сразу строк, ни имён функций, но можно взглянуть на структуру, чтобы понять, что эта функция делает. Даже без имён переменных структура этого кода очень похожа на открытие файла в C!

Похоже, есть вызов в стиле  fopen, за которым следует  fread, а после  fread снова в цикле while. Для ясности добавим комментарии:

Как пропатчить ОС 1996 года с помощью Ghidra - 10

Судя по ним, у нас есть ответы на несколько вопросов:

  • Файл .KOS начинается с заголовка в 4 байта: SYS0.

  • После заголовка он делится на фрагменты фиксированного размера.

  • Каждый фрагмент начинается с одного 4-байтового целого числа.

  • Читается неизвестное количество байтов данных.

  • Каждый фрагмент заканчивается одним 4-байтовым целым числом, похожим на контрольную сумму.

И появился ещё один новый вопрос, на который нужен ответ: почему ссылки на определённые константы и функции имеют очень высокие адреса в памяти? То есть 0x021317ac, похоже, содержит то самое неизвестное количество байтов в каждом блоке .KOS, но данные в ПЗУ не достигают настолько высоких адресов!

Чтобы лучше понять, что находится по этим старшим адресам, обратимся к сервис-мануалу синтезатора (огромный привет Дэвиду Рыскальчику). Этот полезный, лакомый кусочек информации, скрытый глубоко в нераспознанном PDF-файле, в списке диагностических процедур:

Как пропатчить ОС 1996 года с помощью Ghidra - 11

Спасибо, сервис-мануал! Видимо,  0x021317ac ведёт прямо в середине «энергозависимой оперативной памяти», то есть памяти, используемой процессором во время его работы. Здорово, что у нас есть руководство по обслуживанию.

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

Теперь скажем Ghidra рассматривать эту память как ОЗУ в окне «Memory Map», и перейти к интересующему нас адресу 0x021317ac:

Как пропатчить ОС 1996 года с помощью Ghidra - 12

Здесь нет данных (Ghidra знает, что это оперативная память, которая случайным образом инициализируется при запуске компьютера), и похоже, что 0x021317ac читается из ((R)), но никогда не записывается в ((W)). Может, записи происходят дальше?

Как пропатчить ОС 1996 года с помощью Ghidra - 13

Ага! Ghidra показывает нам, что функция пишет прямо в начало оперативной памяти. Нам пришлось прокрутить 6060 байт, поэтому, возможно, чтобы найти первую запись, этот метод копирует много данных в ОЗУ. Кликнем, чтобы посмотреть, что там.

Как пропатчить ОС 1996 года с помощью Ghidra - 14

Одну секунду. Снова переименуем переменные:

Как пропатчить ОС 1996 года с помощью Ghidra - 15

Так намного лучше. Похоже, много данных копируется из ПЗУ в ОЗУ, а именно из  0x0001860a в 0x02130000. Насколько много? Ну, это 0x69016 32-битных слова, то есть 6720 байт. Этот фрагмент кода затем обнуляет следующие 0x1e116 32-битных слова или 1924 байта. 

Теперь, когда понятно, что код, вероятно, инициализирован теми же данными, что и в этой части ПЗУ, можно сказать Ghidra напрямую сопоставить данную часть ПЗУ с данной частью ОЗУ.

Как пропатчить ОС 1996 года с помощью Ghidra - 16

Возвращаясь к прочитанной части оперативной памяти, вместо вопросительных знаков мы видим байты.

Как пропатчить ОС 1996 года с помощью Ghidra - 17

Значение по адресу 0x021317ac равно 0x20000, что соответствует 131 072 байтов, то есть 128 Кб! Замечательно!

Итак, мы выяснили, что каждый фрагмент файла формата .KOS  имеет размер 128 КБ. И это всё, что нужно знать, чтобы построить декодер .KOS, удалить заголовки фрагментов и получить файл с корректными относительными смещениями. 

Этот файл позволит Ghidra правильно дизассемблировать и декомпилировать файл, а ещё — копаться в коде операционной системы. Я уже делал это — вот упаковщик/распаковщик файлов .KOS на GitHub.

Исследуем ОС

Теперь у нас есть «чистый» дамп операционной системы. Давайте откроем этот файл в Ghidra, как мы уже пытались. Чтобы найти несколько интересных строк, воспользуемся поиском Ghidra.

Как пропатчить ОС 1996 года с помощью Ghidra - 18

Проведём быстрый тест, чтобы увидеть, можно ли изменить ОС. Ghidra позволяет изменять инструкции или данные в двоичном формате; давайте изменим одну из строк выше. Чтобы не перемещать код, длину строки нужно сохранить.

Переупаковав операционную систему, загрузим её на дискету, установим на реальное железо — и…

Как пропатчить ОС 1996 года с помощью Ghidra - 19

Помните поле «некой контрольной суммы», которое мы видели в .KOS? Оказывается, эта сумма действительно проверяется железом при установке новой ОС. К счастью, Ghidra может помочь нам и здесь: вернёмся к загрузчику и нажмём  FUN_0x021302b2, которая, видимо, вычисляет какую-то контрольную сумму:

Как пропатчить ОС 1996 года с помощью Ghidra - 20

Снова угадав имена некоторых переменных, имеем:

Как пропатчить ОС 1996 года с помощью Ghidra - 21

Эта функция контрольной суммы довольно проста: для каждого байта x checksum равна значению x + checksum, сдвинутому на один бит влево, а затем сложенному побитовым ИЛИ со значением x + checksum, которое на 31 бит сдвинуто вправо.

Это точная контрольная сумма, которую я раньше вообще не видел, не видят её и мощные инструменты реверсирования контрольных сумм, такие как замечательный delsum.

С этой контрольной суммой изменить наш скрипт дампа .KOS, чтобы он переупаковывал данные правильно. И, как только это будет сделано, снова попробуем прошить ОС:

Как пропатчить ОС 1996 года с помощью Ghidra - 22

Теперь на это железо можно установить новую операционную систему, по желанию изменяя или расширяя её. Эту часть, однако, я оставляю читателю.

Что мы узнали

Ух, это было немного сурово. Я никогда не использовал Ghidra до этого проекта, и теперь чувствую себя довольно комфортно, чтобы использовать его в будущем. Вот методы, которые, как оказалось, работают лучше всего:

  • В первую очередь ищите читаемые строки.

  • Не бойтесь  выполнять «побочные квесты» (например, реинжиниринг загрузчика), чтобы сделать ваши основные усилия (исправление операционной системы) успешнее.

  • Используйте декомпилятор Ghidra. Он по-настоящему удивительный.

  • Ищите структуру в декомпилированном коде.

  • Переименовывайте функции, переменные и типы данных, как только хотя бы предположите, что они могут делать.

  • Просмотрите документацию и ресурсы по системе, если они есть.

  • Иногда приходится вручную перебирать десятки возможных примеров, чтобы найти искомое. Чем больше вы это делаете, тем легче будет.

И отчасти благодаря этому реверс-инжинирингу эмуляция K2000 в проекте MAME теперь загружается!

Особенно благодарен Дэвиду Рыскальчику за то, что на полпути сдвинул мою работу с мёртвой точки, и снова Дэвиду Рыскальчику с Замиром Манджи за рецензирование черновиков этого поста.

А мы поможем вам прокачать навыки или с самого начала освоить профессию, актуальную в любое время:

Выбрать другую востребованную профессию.

Как пропатчить ОС 1996 года с помощью Ghidra - 23
Краткий список курсов и профессий

Автор:
honyaki

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js