
Восстановление мертвого USB-протокола: секреты портативного устройства, раскрытые горячим ножом, междисциплинарное путешествие по возрождению забытого USB-интерфейса
В 2024 году bjiru загрузил видео о ручном устройстве ME2 — игрушке, выпущенной около 2008 года, которая позволяла использовать USB для синхронизации очков и самоцветов между устройством и онлайн-миром. Игра была крайне нишевой, поэтому никакого программного обеспечения, драйверов или ресурсов не сохранилось, по крайней мере, пока bjiru не объявился с клиентом онлайн-игры.
Я — лидер Miuchiz Reborn, проекта, начатого в 2015 году с целью сохранения, обратной разработки, эмуляции и поддержки доступности игры, похожей на эту, с онлайн-частью и ручным устройством, соединённым через USB. Из-за схожего возраста и типа игры ME2 уже привлёк моё внимание сообществом Miuchiz в 2018 году, так как они (ошибочно) считали, что эти устройства могут иметь схожую архитектуру. Несмотря на то что я знал о существовании устройства годами, видео bjiru наконец подтолкнуло меня к началу исследования.
Мои первые усилия были сосредоточены исключительно на воссоздании сервера, необходимого для восстановления работоспособности копии компьютерной игры bjiru, но по ходу дела моё внимание неизбежно переключилось на ручное устройство. Воссоздание онлайн-игры, безусловно, не могло быть полным без механизма синхронизации очков с устройством и обратно. Ведь эта связь между компьютером и устройством ME2 была главной «фишкой» игры. Я решил, что мой предыдущий опыт работы с ручными устройствами Miuchiz поможет мне быстро разобраться в их протоколе связи... при условии, что я смогу достать какой-нибудь код для обратной разработки.
Любопытство требовало жертвоприношения ME2. eBay требовал жертвоприношения фиатных денег. Вскоре передо мной лежали эти экземпляры.

Это маленькие блоки всего с несколькими кнопками и разъёмом mini-USB type female. В комплекте есть кабель, но нет диска с ПО и драйверами. Этот USB-порт должен был использоваться для синхронизации очков между компьютером и ручным устройством, но когда я проделывал тот же путь для ручного устройства Miuchiz, я получил полный доступ к его флеш-памяти, обратно разработав работу сопутствующего ПО для Windows. Поскольку для ME2 такого ПО не существует, нечего и обратно разрабатывать, чтобы понять, как общаться с устройством. Даже после проверки Wayback Machine и bjiru, по-видимому, не сохранилось ни одной копии того ПО, которое использовалось для связи с этими штуками. Кажется, оно называлось ME2 Desktop Buddy, но это приложение было отдельным от игрового клиента, который восстановил bjiru. Ручное устройство представляется как съёмный носитель, но его содержимое просто направляет вас в интернет для загрузки игры ME2, которая уже недоступна.
Путь вперёд ясен, поскольку остаётся только один вариант: вскрыть аппаратуру.

Основная прошивка ME2 хранится на SST39VF3201 — флеш-чипе ёмкостью 2 мегаслова (4 мегабайта, 16-битные адресуемые единицы). Главный микроконтроллер... находится под твёрдой эпоксидной каплей. Это называется «чип на плате» (CoB) и обычно является мерой экономии, но побочным эффектом является сокрытие любой идентифицирующей информации об интегральной схеме внутри. Обычный чип в корпусе обычно имеет маркировку, идентифицирующую его, как у флеш-чипа. Поскольку микроконтроллеры, используемые в таких устройствах, часто содержат внутреннюю ПЗУ, возможно, что часть или весь USB-код, который я хотел обратно разработать, находился в этой ПЗУ. Наличие кода в ПЗУ даёт преимущество восстановления «кирпичного» устройства или, если производитель решит так сделать, прошивки устройства после сборки. Эта ПЗУ существовала бы внутри чипа, который я никак не мог идентифицировать.
Извлечение данных из флеш-чипа — это простой и хорошо документированный процесс: выпаять чип, поместить в стандартный программатор флеш-памяти, например от XGecu, и использовать его для дампа содержимого. Если мне также нужна информация из ПЗУ микроконтроллера, это менее прямолинейно, но всё же выполнимо для такого устройства, где защита маловероятна. В прошлом я добавлял код в SPI-флеш на Tamagotchi Pix, который копировал его загрузочную ПЗУ в свободное место на флеш-чипе, а затем эту ПЗУ можно было прочитать тем же программатором, который я использовал для внедрения кода. Однако в том случае микроконтроллер был в обычном корпусе и имел маркировку с информацией о модели, так что я мог найти его даташит, чтобы узнать систему команд и расположение ПЗУ в адресном пространстве. На тот момент у меня не было никакой этой информации о загадочном чипе на плате ME2.
Несмотря на неопределённость, единственный очевидный путь отсюда — выпаять флеш-чип. Я также купил несколько панелек для флеш-чипа в надежде припаять одну из них к плате ручного устройства. Это позволило бы мне перепрограммировать ME2 и быстро итерировать, если бы мне понадобилось написать код для дампа внутренней ПЗУ микроконтроллера.
К сожалению, мне не удалось снять флеш-чип паяльником, не повредив его. Чтобы решить эту проблему после нескольких неудачных попыток, я приобрёл паяльную станцию (по сути, тепловой пистолет, который якобы может достигать 500 °C) и удалил чип, расплавив припой горячим воздухом. Это позволило мне без проблем снять дамп флеш-памяти, но стало ясно, что фактическое использование купленных панелек будет выше моих возможностей. Мне пришлось бы не только припаять все 48 крошечных выводов к плате, но и сделать это достаточно быстро, чтобы не расплавить пластик панелек. У меня не было ни инструментов, ни навыков для этого, поэтому, если бы мне понадобилась ПЗУ, пришлось бы придумать другую стратегию.
Однако дамп выглядел хорошо, и с помощью инструмента, который я уже создал специально для поиска несжатых растровых изображений в дампах прошивок, я смог найти изображения, которые обычно отображаются на устройстве:

Я получил дамп прошивки из флеш-чипа, но прежде чем можно будет приступить к серьёзной обратной разработке, мне хотя бы нужно знать, какую систему команд использует устройство. На самом оборудовании не было маркировки, поэтому пришлось гадать. Я попробовал проанализировать код в Ghidra — дизассемблере, декомпиляторе и кривом кошмаре с открытым исходным кодом, который меня кормит — с почти каждым типом процессорной спецификации, который в нём был. Какая-нибудь разновидность ARM? Потомок 6502? MIPS? Буквально что-нибудь ещё, поддерживаемое Ghidra? Всё — решительное «нет».
Я перепробовал всё, и ничто не могло дизассемблировать этот код. Я знал, что это код, потому что мог найти даже части того кода, который искал!

Благодаря моей предыдущей работе над ручным устройством Miuchiz, я был немного знаком с тем, как работают USB-устройства массового хранения. Я определил этот фрагмент как почти наверняка перемещающий 'U', 'S', 'B', 'S' куда-то, что является сигнатурой, специфичной именно для того типа USB-связи массового хранения, который я искал. Часть или весь USB-код, который мне нужно было обратно разработать, чтобы понять, как взаимодействовать с этим устройством, определённо был в этом дампе флеш-памяти, но без подсказки о системе команд я не мог его дизассемблировать.
К этому моменту у меня было несколько сломанных устройств ME2. Кажется глупым, но, возможно, где-то под эпоксидными каплями есть маркировка? Наверное, нет, но на этапе исследования иногда полностью разрушенное устройство ценнее просто сломанного.
Что ж, у меня был тепловой пистолет, и у меня был нож. Как говорится, когда у тебя есть тепловой пистолет и нож, всё выглядит как... гвоздь? Кажется, так говорится.

Установив паяльную станцию на максимальную температуру и поддев ножом, я освободил всю эпоксидную каплю. Её удаление не выявило никакой маркировки под чипом на плате, теперь уже не на плате. Однако видна нижняя сторона кремниевого кристалла микроконтроллера, а в эпоксидке есть пара воздушных пузырьков, через которые видны соединительные проводки.
Как я и сказал, иногда разрушенное устройство ценнее сломанного, и у меня были тепловой пистолет, нож и острая необходимость освежить свои идиомы.

Ого.
Хм.
Я понятия не имел, что можно так декапсулировать CoB. Он просто чисто выскочил, и, учитывая его температуру, я рад, что это не случилось в мою сторону. Это очень красиво, но на самом деле отсюда есть путь вперёд. Позвольте мне взглянуть поближе с помощью моего электронного микроскопа.

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

Несмотря на то что он не очень подходит для этой работы, вопреки заявлениям руководства пользователя, я сделал этот снимок с помощью микроскопа. Он далёк от возможности расшифровать какой-либо текст на кристалле, но общая компоновка ясна, и я знал, где можно найти больше подобных изображений.
Siliconpr0n, ныне известный как Siliconprawn, имеет архив, куда многие загружают снимки кристаллов, хотя обычно лучшего качества, чем у меня. К сожалению, на сайте нет полезного способа поиска с учётом имеющейся у меня информации, поэтому я начал кликать, листать и повторять...

Его отпечатки пальцев есть в записях!
Через несколько часов, в 4 утра, я наконец заметил что-то знакомое. Качество лучше моего, но компоновка неоспорима. У меня есть GPL162002A (или B), повёрнутый на 180 градусов по сравнению с соответствующим изображением, сделанным Джоном Макмастером. Это микроконтроллер GeneralPlus, его даташит доступен в интернете, а система команд — μ'nSP.
Как оказалось, μ'nSP довольно распространён для игрушек такого типа и возраста. Надеюсь, мне простят, что я не угадал систему команд, название которой содержит символы, отсутствующие даже в латинском алфавите. Несмотря на это, она всё ещё достаточно нишевая, чтобы не поддерживаться Ghidra «из коробки». К счастью, есть некоторая существующая сторонняя работа для неё, так что я смог начать дизассемблирование в Ghidra, давать имена функциям и импортировать имена регистров из даташита.
Вот функция, в которой я был так уверен, что это USB-код, просто по её шестнадцатеричному дампу, взаимодействующая с USB-регистрами, как указано в даташите микроконтроллера:

Как я уже отмечал, я был уже немного знаком с тем, как общаются подобные USB-устройства массового хранения, особенно благодаря экспериментальной работе по портированию моей USB-библиотеки Miuchiz на macOS с использованием libusb. USB-устройства массового хранения, по сути, туннелируют SCSI-команды, и они хорошо документированы в интернете. Например, есть команды для запроса чтения или записи на устройство.

Однако я знал, что в целом стандартные команды, такие как чтение или запись, мне не пригодятся. Это то, что ваш компьютер уже умеет делать с помощью встроенных драйверов массового хранения. Он будет выдавать их для взаимодействия с устройством как с обычным съёмным носителем. В данном случае команда чтения просто извлечёт файловую систему, содержащую файл справки, а команда записи ничего не сделает, так как устройство не предназначено для записи. Они не предназначены для взаимодействия со всей флеш-памятью; вместо этого они представляют собой крошечный эмулированный CD-ROM, помогающий новичкам.
Однако некоторые идентификаторы команд зарезервированы для того, что захочет реализовать производитель. Обработчики некоторых зарезервированных идентификаторов грубо вставляются перед выполнением обычного поиска идентификатора.

С помощью статического анализа я смог идентифицировать и назвать функции, которые искал: чтение, программирование и стирание флеш-памяти. Все это нестандартные команды, реализованные для ME2 и, возможно, других устройств на GeneralPlus, и почти наверняка использовавшиеся тем ПО и драйверами, которые изначально поставлялись с устройством. Вы можете сформировать USB-сообщение, чтобы запустить любой из этих путей. Чтение позволяет извлечь данные из флеш-памяти. Программирование позволяет «программировать» флеш-память. Стирание сбрасывает все биты в области флеш-памяти в 1, что в сочетании с командой программирования позволяет осуществить полную запись во флеш-память, поскольку «программирование» может переключать биты только с 1 на 0. Каждая из интересующих меня пользовательских команд использует зарезервированный идентификатор 0xFF, за которым следуют идентификаторы подкоманд и любые параметры, необходимые для операции.
Точная структура команд не особенно важна для читателя, но методология может быть полезной. Я использовал libusb (ну, на самом деле rusb) для организации всего моего взаимодействия через USB. Этот метод позволяет писать код пользовательского пространства для взаимодействия с USB-устройством, а не писать новый драйвер.
Когда ПО ME2 для Windows исчезло из интернета, это было похоже на смерть предпоследнего носителя его языка. Ручное устройство ME2 стало, в некотором смысле, терминальным носителем. Немного экспериментируя и читая иногда неточную декомпиляцию, я читал его мысли, чтобы выучить слова его почти вымершего языка. Когда оно отвечало, я понимал, что на правильном пути.
Č̶̯a̴̩͗n̵͉͆ ̴͍͠Ǐ̶̜ ̴͈͌h̷̙̔á̶͉v̸͈̽é̴̢ ̵͍͛a̵̞͝ ̴̤̉s̵̡͊ē̴̮c̸̭̅t̶̛͖o̸̡͠r̶̺̊ ̶̥̀ǫ̸̀f̸̦́ ̷̈́ͅỳ̷͎o̶̦̐u̵͙̚r̶͙͒ ̵̥̕f̸̡͝l̷͈̄a̶͍͋s̸̢̓h̸̗͝?̴̪̕
...Нет? Должно быть, мой акцент. Позвольте мне подстроиться и спросить ещё раз: Можно мне сектор вашей флеш-памяти? А следующий сектор? Не хотите ли запрограммировать какие-нибудь биты в этом секторе? Можно мне снова вашу флеш-память, чтобы увидеть, изменилась ли она? Осмелюсь ли я попросить вас стереть сектор... и, надеюсь, понять, какой именно я прошу уничтожить?
Один за другим я писал код для структурирования, заполнения и передачи сообщений, которые должны были услышать ME2. Как только мы оказались на одной волне, я попросил его прислать мне пару дампов флеш-памяти при разном количестве очков. Сравнив их, я смог определить, где в флеш-памяти хранятся очки, и наконец с помощью компьютера изменить их!

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

Честно говоря, когда я впервые попытался читать/записывать флеш-память, я был немного безрассуден, потому что не мог видеть весь исполняемый код. Например, некоторый код вызывает внутреннюю ПЗУ микроконтроллера или процедуры в ОЗУ, скопированные туда из ПЗУ. Некоторые мои догадки о других функциях основывались на том, как настраивались регистры прямого доступа к памяти (DMA). Если DMA настроено на копирование из USB-буфера, например, вероятно, эти данные используются для программирования флеш-памяти, а не для чтения из неё.

Этот код, например, вызывает функцию, находящуюся за пределами флеш-памяти, чтобы не выдергивать код из-под самого себя, а затем возвращается в код, который он только что выдернул из-под себя. Нужно быть очень осторожным, чтобы не «убить» устройство.
Соответствующая область памяти чётко описана в даташите:

Есть 128 килослов особенно «встроенной» ПЗУ, к которой у меня пока не было доступа, что мешало полностью понять систему.
К счастью, функция пользовательской команды чтения флеш-памяти не выполняет проверку границ. Это означает, что с помощью специально сформированного сообщения, которое пытается выполнить чтение с очень высокого адреса флеш-памяти, можно обернуть всё адресное пространство микроконтроллера и вернуться к началу. Поскольку это даёт возможность произвольного чтения, на этот раз мне не нужно было писать код для дампа на самом устройстве! Вся память читаема с помощью этой ошибки, поэтому я использовал её для чтения встроенной ПЗУ, которую сохранил для последующей обратной разработки.
Со всей памятью я смог обнаружить кадровый буфер устройства в ОЗУ, отображающий изображение, которое было на экране устройства в тот момент:

Я также смог прочитать регистры ввода-вывода общего назначения (GPIO) из адресного пространства устройства, обращаясь к даташиту, где они должны были находиться. Вызывая ошибку десятки раз в секунду, я мог эффективно опрашивать нажатия кнопок и превращать устройство в USB-контроллер, если бы захотел: (Видео)

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

0x00AA (помните, размер слова в этой системе — 16 бит) было записано в место флеш-памяти, которое я не запрашивал. С помощью встроенной ПЗУ мы наконец можем увидеть код, объясняющий это.

Эти флеш-чипы принимают команды, записывая данные по определённым смещениям. В отличие от ОЗУ, которая немедленно фиксирует записанные данные, флеш-чип будет пытаться интерпретировать записи только как часть команды, поэтому одной записи в адресное пространство флеш-памяти недостаточно для фактического изменения каких-либо данных. Когда часть кода хочет запрограммировать флеш-память, она выдает многошаговую последовательность команд, которая начинается с записи 0xAA по адресу 0x5555 и заканчивается записью желаемых данных по желаемому адресу.Если у пользователя, у которого слишком много свободного времени (и, возможно, термопистолет и нож), возникает идея попробовать разобраться, как работает устройство, он может в итоге запросить программирование сектора, выходящего за пределы емкости чипа флеш-памяти. Процессор осведомлен о каждом шаге, а чип флеш-памяти — нет. Вместо этого последний цикл команды полностью пропускает адресное пространство чипа флеш-памяти, поэтому флеш-память с нетерпением ждет, какое значение и куда следует запрограммировать.
Процессор: Эй, флеш! Я хочу начать программировать еще одно слово!
Флеш: Слышу четко! Программирую 0xAA по адресу 0x5555!
Процессор: ...Что?
Из-за рассинхронизации циклов команд флеш-память принимает первый цикл следующей команды за последний цикл предыдущей, и 0xAA записывается по адресу 0x5555. Эквивалентный адрес для типичных 8-битных байтов — 0xAAAA, что соответствует месту, куда в моем дампе флеш-памяти был записан 0x00AA.
Это потенциально можно было бы использовать для произвольной записи, при условии, что вы готовы повредить это конкретное слово во флеш-памяти, но устройство уже достаточно взломано, так что я не заинтересован в «окирпичивании» дополнительных экземпляров. Возможно, эти устройства можно спасти, поскольку реверс-инжиниринг встроенного ПЗУ также показал, что оно содержит собственный обработчик USB и реализации чтения, программирования и стирания флеш-памяти. Однако мне удалось заставить ПЗУ войти в это состояние (вместо загрузки кода из флеш-памяти) только один раз, и больше никогда. Основываясь на декомпиляции ПЗУ, я подозреваю, что оно может полагаться на порт GPIO, оставленный в неопределенном состоянии, но я не уверен. В любом случае, моя миссия здесь уже была выполнена.
Конечный результат этих шалостей — утилита командной строки, которая может:
Это вместе с кодом игрового сервера и другими исследованиями доступно в репозитории ME2-Restoration. Поскольку детали реализации доступны там и, вероятно, неинтересны обычному читателю, здесь были опущены конкретные технические подробности в пользу описания процессов и методов.
Что более важно, это демонстрирует, что оригинальное программное обеспечение для ПК не является строго необходимым для понимания или сохранения его функциональности. Даже если официальное программное обеспечение было утеряно со временем, оказалось возможным открыть черный ящик, которым был ME2, и восстановить его протокол на основе аппаратного и программного обеспечения, превратив иначе мертвый интерфейс во что-то снова пригодное к использованию. Это не ограничивается такими интерфейсами, как USB. Прочитайте, как я воссоздал давно умерший сервер лицензий, чтобы вернуть к жизни десятилетний векторный редактор.
Это также мой первый опыт декапсуляции интегральной схемы, поэтому, чтобы отдать ей должное уважение, я купил лучший микроскоп и с тех пор внес самую качественную фотографию GPL162002A/B на siliconprawn, предварительно просмотренную здесь с уменьшенным разрешением для удобства просмотра в сети:
