DOOM на экране осциллографа Siglent SDS5000X

Запуск легендарного шутера DOOM на устройствах, которые для этого совершенно не предназначены, давно стал своего рода технологическим ритуалом. В ход идет всё: от домофонов и калькуляторов до электронных книг и осциллографов. Глубокий разбор самого феномена «думозапуска» можно найти в материале от @shiru8bit — крайне рекомендую к ознакомлению.

Признаюсь, я довольно скептически отношусь к подобным экспериментам, если они попадают под один из следующих критериев:
-
Подмена оригинальной начинки на современное железо, способное без труда запустить игру (как это было в нашумевшей истории с тестом на беременность);
-
Процесс портирования не потребовал от автора серьезных интеллектуальных усилий.
Стоит конкретизировать понятие «серьезных усилий» в моем понимании:
-
Развертывание собственной системы сборки под специфическую архитектуру (когда стандартные компиляторы недоступны или не работают);
-
Глубокая адаптация подсистемы ввода-вывода под специфические органы управления (простое подключение USB-периферии не считается);
-
Получение низкоуровневого доступа к системе (рутирование смартфона по шаблону и установка APK-файла — это слишком просто).
Разобравшись с моими принципами «честного портирования», перейдем к делу: запуску DOOM на цифровом осциллографе Siglent SDS5034X. Для реализации задуманного пришлось найти способ получения шелла (о чем был отправлен отчет производителю), переписать систему управления под энкодеры передней панели и задействовать встроенный пьезодинамик для вывода звука.
В поисках доступа к системе

Наиболее очевидный путь — использование протоколов Telnet или SSH. Однако в свежих версиях прошивки вендор предусмотрительно их отключил.
Существовал и «железный» вариант: вскрытие корпуса, выпаивание флеш-памяти для записи игровых данных. Но такой деструктивный метод не принес бы должного удовлетворения, да и гарантию на новый прибор терять не хотелось. Я выбрал более изящный путь — исследование механизма обновления ПО.
Анализ файлов обновления
Официальные апдейты для линейки SDS5034X имеют расширение ADS. Сравнение бинарных данных моделей SDS2000X и SDS5000X выявило структурные различия. Тем не менее я решил протестировать специализированную утилиту для SDS2000X, найденную на просторах сети.

Вместо самой программы мне удалось разыскать только её хеш в теме, где пользователи обсуждали ложные срабатывания антивирусов на VirusTotal. Как бывший специалист в сфере кибербезопасности, я воспользовался сервисом VirusTotal Intelligence для загрузки искомого файла.
Реверс-инжиниринг Nuitka
Программа оказалась Python-скриптом, скомпилированным в исполняемый файл с помощью Nuitka. Вместо того чтобы просто запустить его, я решил разобраться в алгоритмах работы утилиты.

Подобные механизмы защиты кода подробно разбирались специалистами на профильных конференциях, но мне было важно пройти этот путь самостоятельно. Вот основные тезисы:
-
Внешняя оболочка — это контейнер с основным бинарем и runtime-компонентами Python. Распаковывается при помощи nuitka-extractor;
-
Полное автоматическое восстановление исходников невозможно, но логику можно воссоздать вручную;
-
Текстовые строки хранятся в открытом виде в секции ресурсов, что позволяет анализировать поведение программы.
В результате кропотливого анализа мне удалось извлечь:
-
Ключ для дешифровки заголовка ADS-файла;
-
Модифицированный алгоритм 3DES (на базе pyDes.py, вот сравнение реализаций);
-
Функцию деобфускации контента, которая оказалась идентичной версии для SDS2000X.

Декомпозиция прошивки
Полученные данные позволили расшифровать первые 0x70 байт (заголовок) файла прошивки:


Структура стала прозрачной: видны версии, размеры и контрольные суммы. Однако основной массив данных не поддавался расшифровке имеющимся скриптом. Анализ хвоста файла выявил инвертированные пути к файлам.

После простого разворота байтов через data[::-1] проявилась структура ZIP-архива, хотя и в искаженном виде.

Применив алгоритм деобфускации к инвертированным данным, я получил полноценный ZIP-архив. Характерные байты PK\x01\x02 окончательно подтвердили успех операции.
Сборка модифицированных обновлений
Следующим этапом стала подготовка кастомного ADS-файла. Здесь я позволю себе небольшое отступление:
В этом проекте я впервые активно использовал ИИ для генерации кода. Это кардинально меняет подход к работе. То, на что раньше уходили часы рутинного написания кода, теперь делается за минуты. Да, это вызывает определенную профессиональную рефлексию, но эффективность процесса неоспорима.
С помощью нейросети был написан инверсный деобфускатор. Соединив его с шифрованием заголовка, я получил почти идеальный клон оригинального файла. Камнем преткновения стали лишь 4 байта в заголовке, ради которых все же пришлось прибегнуть к разборке прибора.
Загадка контрольной суммы
Для детального изучения пришлось вскрыть Siglent SDS5034X и сделать дамп памяти.

Как выяснилось позже, механическое вмешательство было избыточным — все ключи лежали внутри ZIP-архива обновления. Пройдя через цепочку вложенных архивов (uImage -> Linux Kernel -> многократные gz), я добрался до файловой системы и бинарного файла adsdec, отвечающего за валидацию обновлений.
Алгоритм контрольной суммы оказался специфической вариацией суммы байт со знаком. После нескольких попыток осциллограф наконец принял модифицированный файл через веб-интерфейс.

Нюанс заключался в том, что системный архиватор осциллографа требовал сохранения прав доступа (POSIX permissions) внутри ZIP-файла, что удалось реализовать только средствами Linux-системы.

Получение Shell-доступа
Добавив telnetd в скрипты автозагрузки, я успешно открыл порт управления. Использование порта выше 10000 позволило избежать конфликтов, а отсутствие пароля в системе значительно упростило задачу.

Шелл получен. Настало время для DOOM.
Гайд по сборке DOOM для встраиваемых систем
Этот раздел может служить универсальной инструкцией для портирования игры на практически любое Linux-совместимое устройство со специфической периферией.
Основные препятствия при портировании:
-
Ограниченный объем памяти для хранения MIDI-семплов;
-
Специфический ввод: необходимость маппинга физических кнопок и энкодеров вместо клавиатуры;
-
Отсутствие графических фреймворков вроде SDL2;
-
Вывод звука через низкоуровневые интерфейсы (в нашем случае — зуммер).

Компиляция
За основу взят fbDOOM. Сначала определяем архитектуру целевого устройства командой file на любом системном бинаре. Наша цель — добиться полной совместимости форматов ELF.
file ./bin/busybox
./bin/busybox: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.3, for GNU/Linux 2.6.16, stripped
Работа с зуммером buzzer_zynq потребовала прямого обращения к памяти. Экспериментально через devmem был определен диапазон частот (14000-65536) по адресу 0xF800100C.
В Makefile проекта fbDOOM вносим следующие правки:
-
NOSDL=1— отключаем зависимости от SDL; -
Указываем корректный кросс-компилятор (
arm-linux-gnueabi-); -
Добавляем флаг
-staticдля статической линковки.
Система ввода
Адаптация органов управления была реализована через чтение устройства /dev/siglent_kb. Анализ показал, что каждое действие на панели генерирует 4-байтовое значение в потоке данных.
#include#include #include int main(int argc, char* argv[]) { int fd = open("/dev/siglent_kb", O_RDONLY); if (fd == -1) return -1;
while (1) { int val = 0; if (read(fd, &val, 4) > 0) { printf("%08X\n", val); } } return 0;}
Интеграция в
i_input_tty.cпотребовала реализации искусственных задержек между событиями нажатия и отпускания клавиш, чтобы движок игры корректно распознавал команды.Аудиосистема
Самая трудоемкая часть — звук. Стандартные звуковые схемы DOOM не подходят для пьезодинамика. Был создан новый тип аудиоустройства
SNDDEVICE_SIGLENTс собственной реализацией вi_sdsmusic.cиi_sdssound.c.Музыка в игре была преобразована в последовательность команд {частота, длительность}, которые воспроизводятся в отдельном потоке. Для корректной работы звуковых эффектов пришлось реализовать механизм микширования, так как физический излучатель в системе всего один, а звуковых каналов в игре — шестнадцать.
Особое внимание уделили управлению потоками: использование
pthread_detach()решило проблему переполнения таблицы тредов, из-за которой звуки пропадали спустя пару минут игры.Графический вывод
Для повышения производительности стандартный метод
write()во фреймбуфер/dev/fb0был заменен наmmap()с последующимmemcpy(). Это позволило добиться плавной частоты кадров без артефактов отрисовки.Итоги
Проект завершен: игра загружается в
/tmp, права доступа выданы, и адские легионы на экране осциллографа готовы к бою. Результат можно увидеть на видео. Это был увлекательный опыт реверс-инжиниринга и низкоуровневой разработки.Исходный код форка доступен в репозитории: https://github.com/lab313ru/fbDOOM_SDS5000X.
Бонус
P.S. Больше интересных исследований в области Hardware Security ищите в нашем телеграм-канале.
Операционный директор Xbox Хелен Чианг уходит из Microsoft, проработав в компании 21 год
Пиксельный боди-хоррор Look Outside появится на консолях
Эксперт по ценообразованию раскритиковала Hollow Knight: Silksong за слишком низкую стоимость
Лидер канадской НДП Ави Льюис раскритиковал закрытие BioWare и предложил помочь сотрудникам выкупить студию у EA
Глава Saber Interactive считает AA-игры вроде Hellraiser: Revival будущим индустрии
Два дополнения для The Elder Scrolls Online раздают бесплатно поклонникам Skyrim
Проблемы Xbox сказались на зарплате главы Microsoft
Энтузиаст перенёс GTA IV на PS5, часть работы выполнила нейросеть