В 2024 году bjiru выложил о портативном устройстве ME2 — выпущенной примерно в 2008 игрушке, способной при помощи USB синхронизировать очки и драгоценности между устройством и онлайн-миром. Игра была крайне нишевой, поэтому никто не архивировал её ПО, драйверы и ресурсы; по крайней мере, до тех пор, пока bjiru не создал онлайн-клиент игры. Я руководитель Miuchiz Reborn — созданного в 2015 году проекта по реверс-инжинирингу, эмуляции и поддержке доступности похожей игры, состоящей из онлайн-части и портативного устройства, соединяемых через USB. ME2 тоже была старой игрой подобного типа, поэтому моя группа Miuchiz рассказала мне о ней ещё в 2018 году; она считала (ошибочно), что они могут быть архитектурно схожи. Несмотря на то, что я уже несколько лет знал о существовании устройства, именно видео bjiru подтолкнуло меня начать его исследование. Поначалу я занимался исключительно воссозданием сервера, необходимого для обеспечения функциональности копии компьютерной игры, принадлежащей bjiru, но со временем я обратил внимание и на портативное устройство. Разумеется, воссоздание онлайн-игры не было бы полным без механизма синхронизации очков с устройством. В конце концов, это общение между компьютером и устройством ME2 было основной «фишкой» игры. Я подумал, что мой предыдущий опыт работы с портативными устройствами Miuchiz поможет мне в анализе ритуала необходимого им общения... если мне удастся выполнить реверс-инжиниринг кода. Моё любопытство требовало жертвоприношения в виде ME2. EBay требовал жертвоприношения в виде фиатных денег. Спустя какое-то время я получил эти два образца. Это маленькие устройства с парой кнопок и гнездом mini-USB. В коробке был кабель, но отсутствовал диск с ПО и драйверами. При помощи USB-разъёма можно было бы синхронизировать очки между компьютером и устройством, но когда я начал анализ устройства Miuchiz, то получил полный доступ к его флэш-памяти благодаря реверс-инжинирингу работы его ПО для Windows. Для ME2 такого ПО нет, поэтому я не мог разобраться, как общаться с устройством, при помощи реверс-инжиниринга. Даже после проверки Wayback Machine и общения с bjiru мне не удалось найти сохранившихся копий ПО для коммуникации с устройствами. Кажется, оно называлось ME2 Desktop Buddy, но это приложение было отдельным от восстановленного bjiru клиента игры. При подключении ME2 отображается как съёмное устройство-накопитель, но содержимое просто перенаправляет онлайн для скачивания игры ME2, которая уже недоступна. Дальнейший маршрут понятен: остался лишь один вариант — вскрытие оборудования. Что внутри Основная прошивка ME2 хранится на SST39VF3201 — флэш-чипе объёмом 2 мегаслова (4 мегабайта с 16-битными адресуемыми блоками). Главный микроконтроллер спрятан под твёрдой каплей эпоксидки. Это называется chip-on-board (CoB) и обычно используется для экономии, но из-за этого невозможно найти информацию об интегральной схеме. На чипе в обычном корпусе есть маркировка, как в случае с флэш-чипом. Так как микроконтроллеры в подобных устройствах часто содержат внутренний ROM, возможно, что в нём полностью или частично находится весь код USB, который я хочу подвергнуть реверс-инжинирингу. Наличие этого кода в ROM может обеспечить возможность восстановления превратившегося в кирпич устройства или, если так решит производитель, прошивки устройства после сборки. Этот ROM должен находиться в чипе, идентифицировать который я не в состоянии. Извлечение данных с флэш-чипа — простой и подробно задокументированный процесс: выпаиваем чип, помещаем его в стандартный программатор флэш-памяти, например, производства XGecu, и с его помощью дампим содержимое. Если мне понадобится информация и из ROM микроконтроллера, то сделать это будет сложнее, но вполне возможно, ведь в подобном устройстве применение защиты маловероятно. В прошлом я добавлял в SPI-флэш Tamagotchi Pix код, копировавший загрузочный ROM в свободное пространство на флэш-чипе; затем я считывал этот ROM при помощи того же программатора, с помощью которого инъецировал код. Однако в том случае у микроконтроллера был настоящий корпус с напечатанной информацией о модели, поэтому я смог найти его даташит, позволивший узнать используемый набор команд и местоположение его ROM в пространстве памяти. Пока этой информации о загадочном chip-on-board ME2 у меня не было. Извлечение Несмотря на отсутствие определённости, единственное следующее очевидное действие заключается в выпаивании флэш-чипа. Кроме того, я приобрёл несколько сокетов для флэш-чипа в надежде, что мне удастся припаять один из них к печатной плате портативного устройства. Это позволило бы мне перепрограммировать ME2 и быстро выполнять итерации на случай, если бы мне понадобилось записывать код для создания дампа внутреннего ROM из микроконтроллера. К сожалению, мне не удалось снять флэш-чип при помощи паяльника, не повредив при этом чип. Чтобы компенсировать нехватку моих навыков, спустя множество безуспешных попыток я приобрёл паяльную станцию (по сути, термофен, который, по заявлению продавца, должен обеспечивать температуры до 500 °C) и снял чип, расплавив припой горячим воздухом. Это позволило мне без проблем сдампить содержимое флэш-памяти, однако выяснилось, что пользоваться приобретёнными мной сокетами не удастся из-за нехватки опыта. Мне не только пришлось бы припаять все 48 крошечных контактов к печатной плате, но и сделать это достаточно быстро, чтобы не оплавить пластик сокетов. Для этого мне не хватало ни навыков, ни инструментов, поэтому если мне нужен был ROM, придётся воспользоваться другой стратегией. Впрочем, дамп выглядел неплохо. При помощи инструмента, написанного мной специально для поиска несжатых битовых изображений в дампах прошивок, я смог найти картинки, обычно отображаемые в устройстве: Определение набора команд Я получил дамп прошивки с флэш-чипа, но прежде чем приступать к серьёзному реверс-инжинирингу, мне хотя бы нужно знать, на каком наборе команд работает устройство. На самом оборудовании не было никаких маркировок, поэтому пришлось гадать. Я попробовал анализировать код в Ghidra (опенсорсном дизассемблере, декомпиляторе и кошмаре, при помощи которого я зарабатываю на жизнь) почти со всеми типами имевшихся у него процессорных спецификаций. Может, это какой-то вариант ARM? Или потомок 6502? MIPS? Буквально что угодно, поддерживаемое Ghidra? Увы, ответом на все эти вопросы было чёткое «нет». Я пробовал всё, но ничто не могло дизассемблировать этот код. Я знал, что это код, потому что даже нашёл нужные мне части кода!
Код USB в шестнадцатеричном редакторе Благодаря прошлому опыту работы с устройством Miuchiz я получил некоторые знания о работе съёмных USB-накопителей. Я определил, что этот блок кода почти наверняка копирует куда-то 'U', 'S', 'B', 'S' — сигнатуру именно того типа коммуникаций USB-накопителя, который я искал. Код USB, необходимый мне для реверс-инжиниринга взаимодействия с устройством, частично или полностью находился в этом дампе, но не зная, на каком наборе команд он написан, я не мог его дизассемблировать. Теперь у меня уже была пара поломанных ME2. Это может показаться глупым, но, вероятно, под каплями эпоксидки может обнаружиться какая-то маркировка? Скорее всего, нет, но на этапе исследований полностью разобранное устройство будет более ценным, чем просто поломанное. Итак, у меня есть термофен и нож. Как говорится, когда у тебя в руках термофен и нож, всё вокруг превращается... в гвозди? Вроде есть такая поговорка. Установив паяльную станцию на максимум температуры и поддев каплю эпоксидки, я снял её полностью. Однако никаких обозначений на чипе я не обнаружил. Впрочем, мы видим внутренности кремниевого кристалла микроконтроллера, а в эпоксидке есть пара пузырей воздуха, через которые видны соединительные провода. Как я и сказал, иногда полностью раскуроченное устройство ценнее сломанного. Хм. Ого! Я понятия не имел, что можно таким образом извлечь CoB. Он просто выскочил без каких-либо проблем, а учитывая его температуру, я рад, что не в мою сторону. Он очень красивый; к тому же, он позволит нам двигаться дальше. Рассмотрим его повнимательнее через мой электронный микроскоп. По крайней мере, в руководстве утверждается, что он электронный. Вообще я проверил, он содержит как минимум один электрон. В процессе уничтожения «хакинга» других игрушек, подвергавших опасности моё зрение, стало очевидно, что я редко понимаю происходящее с другого конца паяльника. По рекомендации друга я приобрёл дешёвый цифровой микроскоп, предназначенный для изучения монет и пайки. Несмотря на то, что он, вопреки утверждениям из руководства пользователя, не подходит для подобной работы, я сделал этот снимок микроскопом. Его мощности явно недостаточно для расшифровки текста на кристалле, но общая структура ясна, и я понимал, что мне удастся найти другие похожие изображения. Siliconpr0n, теперь также называющийся Siliconprawn — это архив, в который множество людей загружали снимки кристаллов, хоть и обычно более высокого качества, чем мой. К сожалению, по имеющейся у меня информации выполнять поиск по сайту было невозможно, поэтому я начал работать мышкой... Мы нашли подозреваемого! В четыре ночи, спустя множество часов, я наконец-то нашёл нечто похожее. Качество снимка лучше моего, но структуру ни с чем не спутаешь. Это GPL162002A (или B), повёрнутый на снимке Джона Макмастера на 180 градусов; микроконтроллер GeneralPlus, его даташит можно найти в Интернете, а внутри него используется набор команд μ'nSP. Реверс-инжиниринг прошивки Как оказалось, μ'nSP довольно широко используется в подобных старых игрушках. Надеюсь, меня можно простить за то, что я не смог вычислить набор команд, название которого содержит символы, даже не входящие в латиницу. Несмотря на популярность, он всё равно довольно нишевый, поэтому не поддерживается Ghidra изначально. К счастью, по нему уже есть сторонний проект, благодаря которому я смог начать дизассемблировать его в Ghidra, давать имена функциям и импортировать имена регистров из даташита. Вот функция, которая, как я был уверен из шестнадцатеричного дампа, являлась кодом USB; она взаимодействует с регистрами USB в соответствии с даташитом микроконтроллера:
USB function decompilation Как я говорил выше, мне уже был знаком принцип общения USB-накопителей, особенно учитывая эксперименты по портированию USB-библиотеки Miuchiz в macOS при помощи libusb. Съёмные USB-носители, по сути, туннелируют команды SCSI, которые подробно задокументированы онлайн. Вот, например, команды для запроса чтения с устройства или записи на него. Однако я знал, что общие стандартные команды наподобие чтения или записи будут для меня бесполезны. С ними компьютер и так умеет работать при помощи встроенных драйверов съёмных накопителей. Он отправляет команды для взаимодействия с ними, как с обычными съёмными медиаустройствами. В этом случае команда чтения просто получит файловую систему с файлом справки, а команда записи не сделает ничего, потому что устройство не поддерживает запись. Они не предназначены для взаимодействия со всем флэш-чипом; по сути, это крошечный симулируемый CD-ROM, помогающий новичкам. Впрочем, часть ID команд зарезервирована под действия, которые хотел реализовать производитель. Обработчики части зарезервированных ID грубым образом инъецируются перед выполнением поиска обычных ID. При помощи статического анализа я смог найти нужные функции (для чтения, программирования и стирания флэш-памяти) и дать им имена. Это нестандартные команды, реализованные для ME2 и, возможно, других устройств GeneralPlus; почти наверняка они использовались для ПО и драйверов, которые изначально поставлялись с устройством. Для активации любого из этих путей можно составить USB-сообщение. Read позволяет извлекать данные из флэш-памяти. Program позволяет «программировать» память. Erase позволяет сбросить все биты в области флэш-памяти на 1, которые при использовании в сочетании с командой может вызвать полную запись во флэш-память, поскольку «программирование» может менять значения битов только с 1 на 0. Каждая из этих интересующих меня специализированных команд использует зарезервированный ID 0xFF, за которым следуют ID подкоманд и необходимые операции параметры. Конкретные структуры команд не особо важны для нашей статьи, зато полезной может быть методология. Для упрощения взаимодействия через USB я воспользовался libusb (точнее, rusb). Этот способ позволяет для общения с USB-устройством писать код пространства пользователя, а не создавать новый драйвер. Когда из Интернета пропало ПО ME2 для Windows, умер предпоследний говорящий на его языке. Последним осталось устройство ME2. Поэкспериментировав и изучив почти всегда правильную декомпиляцию, я научился произносить слова на этом почти вымершем языке. И когда устройство ответило, я понял, что нахожусь на верном пути. Я задавал ему вопрос: можно ли получить сектор твоей флэш-памяти? А как насчёт следующего сектора? Сможешь запрограммировать биты в этом секторе? Можешь показать свою флэш-память, чтобы я проверил, изменилась ли она? Осмелюсь попросить тебя стереть сектор... и, надеюсь, ты разберёшься, какой именно я прошу тебя уничтожить. Шаг за шагом я писал код для структурирования, заполнения и передачи сообщений, которые должно было услышать ME2. Придя к взаимопониманию, я попросил его отправить мне пару дампов флэш-памяти, имея разное количество очков. Сравнив их, я смог разобраться, где очки хранятся в памяти, и наконец-то изменить их через компьютер! Теперь я при помощи этих команд мог делать с устройством что угодно, если при этом не будет стираться активно используемый код. ME2 присоединилось к моей коллекции устройств, отображающих эмблему Miuchiz Reborn в непредназначенном для этого месте. «Embadded» ROM и «Embadded»-баги Похоже, когда я впервые попробовал читать/записывать флэш-память, я был немного опрометчивым, потому что не видел весь исполняемый код. Например, код делал часть вызовов ко внутреннему ROM микроконтроллера или к подпрограммам в ОЗУ, скопированным туда ROM. Частично мои гипотезы о других функциях были основаны на том, как подготавливались регистры DMA (direct memory access). Например, если регистры DMA настраивались для копирования из буфера USB, то, вероятно, эти данные использовались для программирования флэш-памяти, а не для чтения из неё.
SCSI erase function decompilation Например, этот код вызывает функцию, находящуюся вне флэш-памяти, поэтому он подтягивает код из другого места, а затем возвращается к коду ниже. Нужно быть очень аккуратным, чтобы не превратить устройство в кирпич. Нужная область памяти чётко описана в даташите: Здесь есть 128 килослов встроенного ROM (embedded написано с опечаткой, embadded), к которому у меня пока нет доступа, поэтому я не могу понять систему целиком. К счастью, эта функция команды чтения специальной флэш-памяти не выполняет проверку границ. Благодаря этому при помощи особым образом составленного сообщения, пытающегося выполнить чтение из очень высокого адреса флэш-памяти, можно вернуться к самому началу адресного пространства микроконтроллера. Так как это обеспечивает нам возможность произвольного чтения, на этот раз мне не нужно писать кода для дампинга, выполняемого внутри устройства! При помощи этого бага мы можем считать всю память, чем я и воспользовался для чтения Embadded ROM, который сохранил для дальнейшего реверс-инжиниринга. Получив всю память, я смог найти буфер кадров устройства в ОЗУ, в котором находилось изображение, выводимое в текущий момент на экран устройства:
Буфер кадров в дампе ОЗУ Кроме того, мне удалось считать регистры ввода-вывода общего назначения (GPIO) из адресного пространства устройства, воспользовавшись информацией о них из даташита. Выполняя баг десятки раз в секунду, я мог, по сути, опрашивать нажатия кнопок и при желании превратить устройство в USB-контроллер: (видео) В процессе тестирования я заменил странное изменение значения во флэш-памяти, которое вроде бы не должно было сохранять данные. Значение 0x00AA (напомню, что размер слова системы равен 16 битам) записывалось во флэш-память по адресу, который я не указывал. Благодаря Embadded ROM мы можем наконец увидеть код, который это объясняет.
Дизассемблирование объясняет код, вызывающий запись OOB Эти флэш-чипы получают команды, выполняя запись по конкретным смещениям. В отличие от ОЗУ, которое сразу передаёт записанные данные, флэш-чип лишь пытается интерпретировать операции записи как часть команды, поэтому одной операции записи в адресное пространство флэш-памяти недостаточно для изменения данных. Когда блок кода хочет запрограммировать флэш-память, он передаёт многоэтапную последовательность команд, которая начинается с записи 0xAA в 0x5555 и завершается записью нужных данных по нужному адресу. Если пользователь, у которого слишком много свободного времени (а ещё есть термофен и нож) решит попробовать понять, как работает устройство, в результате может оказаться, что он попросит у устройства запрограммировать сектор, находящийся вне пределов возможностей его флэш-чипа. Процессор в курсе каждого шага, но не флэш-память. Последняя команда полностью промахивается мимо адресного пространства флэш-чипа, поэтому он ждёт, какое значение и куда нужно запрограммировать.
Процессор: Эй, флэш-память! Я хочу начать программировать ещё одно слово!
Флэш: Поняла тебя! Программирую 0xAA в 0x5555!
Процессор: ...Чего?
Из-за рассинхронизованных тактов команд флэш-память путает первый такт следующей команды с последним тактом предыдущей команды, и 0xAA программируется в 0x5555. Эквивалентным адресом для типичных 8-битных байт будет 0xAAAA, что соответствует месту записи 0x00AA в мой дамп флэш-памяти. Потенциально это можно использовать для произвольной записи, если нас устроит повреждение определённого слова в памяти, но устройства и так достаточно изучены, поэтому я не хочу рисковать и снова окирпичивать их. Возможно, поломанные устройства можно спасти, поскольку реверс-инжиниринг Embadded ROM также показал, что в нём содержится собственный обработчик USB и реализации чтения/программирования/стирания флэш-памяти. Однако мне удалось перевести ROM в это состояние (вместо запуска кода флэш-чипа) только раз, и больше этого сделать не получалось. Судя по декомпилированному ROM, он может использовать незадействованный порт GPIO, но уверенности у меня нет. Как бы то ни было, моя миссия уже выполнена. Готовые продукты и выводы Результатом этих шалостей стала утилита командной строки, которая может:
Читать флэш-память.
Записывать её.
Считывать очки.
Записывать очки.
Считывать драгоценности.
Записывать драгоценности.
Дампить память (при помощи эксплойта).
Следить за вводом с кнопок (при помощи эксплойта).
Всё это вместе с кодом сервера игры и другими исследованиями выложено в репозиторий ME2-Restoration. Так как подробности реализации описаны в нём и, скорее всего, не очень интересны обычному читателю, в статье я опустил технические детали, чтобы подробнее описать процессы и методики. Ещё важнее здесь демонстрация того, что оригинальное ПО на стороне PC не обязательно для понимания или сохранения функциональности устройств. Даже если официальное ПО было утеряно, можно открыть «чёрный ящик» ME2 и воссоздать его протокол на основании оборудования и прошивки, превратив мёртвый интерфейс во что-то полезное. И это касается не только интерфейсов наподобие USB. В ещё одной моей статье можно прочитать, как я воссоздал давно мёртвый сервер лицензий, чтобы вернуть к жизни векторный редактор, заброшенный десяток лет назад. В проекте по возрождению ME2 я впервые вскрыл корпус интегральной схемы, поэтому чтобы отдать этому моменту должное, я приобрёл более мощный микроскоп и выложил на siliconprawn GPL162002A/B фото наивысшего качества (показанное ниже изображение уменьшено): -Источник