- PVSM.RU - https://www.pvsm.ru -

Как я получил root и постоянный SSH на Xiaomi Mi Smart Speaker с Марусей

У меня есть Xiaomi Mi Smart Speaker с Марусей, полученная в 2021 году вместе с телефоном по акции. Захотелось использовать её как собственное Linux‑устройство:
подключаться по сети, смотреть, что происходит внутри, и запускать свои службы.
В итоге удалось снять полную физическую копию NAND и установить SSH, который сохраняется после обычной перезагрузки. Для повторения я собрал SpeakerLab 1.0 — утилиту с графическим окном, командной строкой, исходниками и тестами.

Самое интересное оказалось в загрузке. На изученной плате включён Secure Boot, но штатный загрузчик позволяет работать с оперативной памятью после успешной проверки boot‑образа. Этот промежуток и используется для запуска временного установщика. Постоянные файлы SSH затем хранятся в /data; загрузчик и ядро на NAND при установке не перепрошиваются.

Какую колонку я исследовал

Под названием Mi Smart Speaker встречаются разные региональные версии. Здесь речь о плате AS09A VER0201 с Марусей. Утилита привязана к конкретному изученному загрузчику, а не ко всем устройствам с одинаковым корпусом.

Плата AS09A VER0201. SoC Amlogic A113X / AXG, четыре Cortex‑A53. Оперативная память 512 МиБ DDR3. NAND память KIOXIA/Toshiba TC58NVG1S3HTA00. Пользовательская прошивка 32.23.1. Версия ПО из devinfo 35.21-mi‑split. Загрузчик U‑Boot 2015.01, сборка от 27.08.2021. ОС Buildroot 2019.11.1, Linux 4.9.113.

У глобальной версии с Google Assistant есть общее аппаратное происхождение,
но переносимость прошивки из этого не следует. В исследованных материалах
различаются разметка и системное ПО. Глобальную колонку этот релиз не поддерживает.

Вход через короткое USB‑окно

При холодном включении изученный загрузчик ненадолго появляется по USB. Утилита заранее ждёт устройство, захватывает это окно и продолжает обмен. На компьютере оно определяется как 1b8e:c003; затем дополнительно проверяется ответ identify, потому что одинакового VID/PID недостаточно для определения стадии.

Практический порядок такой: сначала подготовить USB‑доступ на компьютере, запустить нужную операцию, дождаться сообщения «Ожидание USB» и только потом полностью выключить и включить штатное питание. Обычный программный reboot на моём экземпляре нужного окна не предоставлял.

Для подключения используются USB‑площадки на плате. Питание остаётся штатным. Назначение площадок проверяйте по подписям сигналов на своей плате, а не по номеру TP на чужой фотографии:

GND‑GND(черный провод), DP‑D+(зеленый провод), DM‑D‑(белый провод), VBUS +5 В (красный провод), не подключать и заизолировать.

Цвета кабеля необходимо проверить прозвонкой: это распространённая раскраска, а не гарантия. Паять нужно при отключённых USB и блоке питания. Перед включением нужно проверить отсутствие перемычек припоем, изолировать оголённые части и вернуть снятые шлейфы. Для описанного пути замыкание выводов NAND не применяется.

Идея изучать USB/U‑Boot не возникла на пустом месте: полезной отправной точкой
стало исследование глобальной L09G от Kulkan [1]. Профиль и адреса функций для своей AS09A получены отдельно. В USB‑транспорте также использован подход проекта pyamlboot [2]; его атрибуция сохранена в лицензии комплекта.

После захвата программа не начинает вызывать функции только потому, что увидела знакомое имя устройства. Она читает код BL33 и сверяет SHA256 с profile.json. RAM‑бинарники проверяются перед отправкой, а после загрузки читаются обратно. Если профиль не совпал, выполнение останавливается. Убирать эту проверку ради «похожего загрузчика» нельзя: функции вызываются по изученным адресам.

Почему сначала понадобился полный backup

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

У этой NAND 131 072 страницы, по 2048 байт основных данных и 128 байт OOB. Размер одного чтения получается из геометрии:

131 072 × (2048 + 128) = 285 212 672 байта = 272 МиБ

Это 256 МиБ main и 16 МиБ OOB. Три чтения занимают 816 МиБ без учёта отчётов. Данные передаются порциями через RAM и USB на диск компьютера; весь образ в оперативную память колонки не помещается и не собирается.

В backup.json сохраняются геометрия, отпечаток устройства, таблица плохих блоков, границы порций и контрольные суммы. Отдельная команда verify проверяет не только SHA256 целого файла, но и непрерывное покрытие страниц. Пропуск или перекрытие порций не должны превращаться в «успешный backup».

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

Такая копия содержит и персональные данные: настройки сети, ключи, идентификаторы, состояние привязки. Публичный архив инструментов не содержит моего полного dump, и для сообщения о совместимости чужой полный dump не требуется.

Что именно происходит с Secure Boot

В журнале загрузки исследованной платы виден Secure Boot. Штатная процедура проверяет и расшифровывает собственный boot‑образ колонки. SpeakerLab вызывает эту процедуру и останавливается при ошибке. Подпись для самодельной прошивки не создаётся, ключи OTP/eFuse не меняются.

Дальше проявляется свойство изученного загрузчика: после успешной проверки можно изменить уже загруженные данные в RAM и продолжить запуск отдельными стадиями bootm. Утилита оставляет штатные ядро и DTB, но заменяет initramfs на временный вариант с установщиком и публичным SSH‑ключом владельца.

Эта последовательность находится в speakerlab/root.py. Если коротко, порядок проверок и действий такой:

  1. Проверить собственный backup, публичный ключ и контрольную сумму Dropbear.

  2. Захватить USB, сверить BL33 и принадлежность копии тому же устройству.

  3. Прочитать собственный boot выбранного слота и выполнить штатную проверку.

  4. Подготовить initramfs в RAM, не заменяя ядро и DTB.

  5. Передать управление штатному ядру, которое запустит временный установщик.

Установщик присоединяет системный раздел как read‑only и существующий /data как UBIFS. Он проверяет ожидаемую схему и имя тома, затем сохраняет дополнение и продолжает обычную загрузку. Форматирования data или записи boot‑разделов в этом сценарии нет. При этом запись файлов в /data — постоянное изменение на той же NAND, поэтому говорить «память вообще не меняется» было бы неверно.

Как SSH переживает перезагрузку

Разовый запуск временной системы ещё не даёт постоянного SSH. Для него используется существующий writable overlay. Файлы сервера и управления лежат в /data/speakerlab, а запуск добавляется через /data/overlay/upper/etc/init.d/S99sysmon.sh.

У этого имени есть причина. В исследованной загрузке список init‑скриптов формируется до позднего подключения overlay. Просто положить туда новый S99speakerlab недостаточно. Поэтому hook использует имя уже существующего скрипта и сначала вызывает его оригинал из текущего /rom, сохраняя штатный sysmon.

После нормальной загрузки hook запускает Dropbear. Первый вход разрешён по публичному ключу, который выбрал владелец; личный приватный ключ остаётся на компьютере. Host key создаётся на самой колонке. Парольный вход можно включить отдельно, задав собственный пароль интерактивно. Общего пароля для всех установок нет.

Включённый Secure Boot не означает автоматическую проверку каждого файла /data. В исследованной системе зафиксирована загрузка UBIFS с overlay; штатный boot продолжает загружаться, а hook выполняется из writable‑слоя. Общий принцип слоёв описан в документации OverlayFS. [3] На других сборках конфигурация защиты и порядок загрузки могут отличаться.

Слот по умолчанию выбирается по текущим данным misc, а не по самой большой строке версии. Программа не делает постоянного переключения A/B и не угадывает, что более новая неактивная система обязательно исправна. При неоднозначных метаданных операция прекращается.

Как повторить установку

В архиве есть инструкции для Linux, Windows и macOS. Аппаратные проверки проекта проводились на Fedora, Windows, Linux Ubuntu; для macOS пока есть инструкции, но нет такого же аппаратного подтверждения. Нужны Python 3.10+, Tk, libusb и клиент OpenSSH. В Windows дополнительно используется WinUSB только для устройства колонки.

Например, после распаковки на Linux команды выполняются на компьютере, в каталоге с run.py:

sudo dnf install python3 python3-pip python3-tkinter libusb1 openssh-clients

python3 -m venv .venv

.venv/bin/python -m pip install -r requirements.txt

sudo bash install-usb-access-linux.sh

.venv/bin/python run.py

В окне программы сначала выбирается общая папка результатов и запускаются три чтения NAND. GUI сам создаёт отдельную подпапку nand‑…; затем нужно выполнить «Проверить копию». Для установки root выбираются эта завершённая копия и собственный публичный.pub, слот остаётся auto.

SSH‑ключ создаётся на компьютере один раз, если своего ещё нет:

ssh‑keygen ‑t ed25519 ‑f speaker‑owner‑key

Существующий ключ перезаписывать не нужно. Перед установкой программа проверит файлы; питание переключают только после сообщения об ожидании USB. После передачи управления следует дождаться сети и проверить обычное SSH‑подключение:

ssh ‑i./speaker‑owner‑key root@IP_КОЛОНКИ

А уже в консоли колонки id, /data/speakerlab/rootctl status. Проверяются uid=0(root), running=yes и autostart=enabled. Затем нужна обычная
перезагрузка и повторный вход. Файл с отметкой о передаче управления ядру —
ещё не доказательство, что SSH действительно установился.

Блокировка OTA обновлений

Отдельной задачей стал контроль системных обновлений. Он нужен, чтобы владелец мог сохранить изученную сборку, но включать его следует осознанно: вместе с OTA перестают поступать и исправления производителя.

В релизе 1.0 отказ в обновлении возвращает 125, а не успех. Блокируются три известные штатные точки запуска. При снятой блокировке обёртки передают управление оригиналу текущего /rom; DNS и firewall не меняются.

В консоли колонки политика включается отдельно:

/data/speakerlab/rootctl ota‑block

/data/speakerlab/rootctl ota‑status

Ожидаемый статус:

ota=blocked

blocked_exit_code=125

managed_entrypoints=3

Команда ota‑unblock разрешает штатные обновления обратно, но сама не запускает OTA. Сам механизм криптографической проверки OTA этот guard не заменяет и не исследует.

Что действительно проверено

На собственном экземпляре были проверены захват USB, чтение NAND, установка root, вход по ключу и по паролю, автозапуск после обычной перезагрузки и обновление скриптов по локальной сети. Это результаты аппаратных протоколов проекта, а не результат одного только запуска unit‑тестов.

При подготовке этой упаковки выполнены 33 программных теста: 30 прошли, три оконных сценария Tk пропущены из‑за отсутствия графического дисплея. Проверяются, в частности, испорченные и неполные копии, публичный ключ, повтор частичной установки, выбор папок и слота, старый OTA guard. USB и SSH в программных тестах подменены.

В архиве также оставлен экспериментальный writer NAND и его модель в Unicorn. Модель проверяет команды контроллера и остановку при ошибках, но не электрические тайминги и не загрузку после полного восстановления. Полная обратная запись на реальном устройстве не выполнялась. Для получения SSH она вообще не нужна.

Есть и более узкое ограничение: полный USB‑проход с текущим автоматическим выбором слота после добавления этой ветки на устройстве ещё не повторялся. Подробная матрица находится в TEST_STATUS.md. Я не хочу выдавать успешные программные тесты за проверку всех вариантов установки на железе.

Архив и дальнейшая работа

Скачать: SpeakerLab 1.0 — архив инструментов, исходники и контрольная сумма. Архив: speakerlab‑as09a‑tools-1.0-habr.zip [4]. SHA256: 421eb9812480455a77d91245d66bf23ffdc1eca99661085787230a79e667e8a7 

Внутри — README_RU.md, подготовка ОС, подключение USB, CLI, исходники RAM‑кода, установщик, Dropbear с его исходниками и лицензией, тесты и ограничения восстановления. Полной прошивки, личного NAND dump и AirPlay в этом комплекте нет. Код и часть разбора готовились с помощью Codex.

Результаты сверялись с файлами и поведением собственного устройства; сам по себе ответ модели не считается подтверждением работы на новой плате. Атрибуция использованных проектов сохранена. Следующий полезный шаг — независимые проверки на других российских экземплярах с точно записанной маркировкой платы, версией системы и результатом проверки профиля. Полные личные дампы для этого публиковать не нужно. Root уже даёт основу для своих сетевых служб и приложений; замена голосового помощника остаётся отдельной работой с аудиотрактом, активацией и выбранным сервером обработки речи.

Автор: ViRtI862

Источник [5]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/ssh/462219

Ссылки в тексте:

[1] исследование глобальной L09G от Kulkan: https://blog.kulkan.com/u-boot-as-an-attack-surface-on-a-xiaomi-l09g-smart-speaker-c689698127cf

[2] pyamlboot: https://github.com/superna9999/pyamlboot

[3] документации OverlayFS.: https://docs.kernel.org/filesystems/overlayfs.html

[4] speakerlab‑as09a‑tools-1.0-habr.zip: https://drive.google.com/file/d/1tbtR1YP10DZkMqDV-JQut5n8D8OqzwRv/view?usp=drivesdk

[5] Источник: https://habr.com/ru/articles/1107780/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1107780