
Запуск легендарного шутера 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 ищите в нашем телеграм-канале.


