Новый робот — та же блокировка: как мы разлочили Quectel RG255C-GL

Я Лев Ларин, инженер Unibot. Мы развиваем UNIBOT Fleet, подключаем к ней коммерческих роботов и дорабатываем их управление, интеграции и работу с данными.
В предыдущей статье мы запустили LTE в роботе-официанте с Quectel EG25-GC. Теперь такая же проблема обнаружилась в другом роботе — Agibot X2 Ultra. В нём стоит более новый Quectel RG255C-GL: SIM читается, российские сети видны, регистрации нет.
Старый патч сюда не подходил. Пришлось разбирать другое сжатие кода и другой формат подписи. В итоге снова хватило одного бита в радиокоде, но на этот раз российскую проверку выключили, не назначая вместо России другую страну.
Обложка сгенерирована по фотографии автора. Робот и электроника на ней — иллюстрация.
Что поменялось относительно первого робота
В первом случае модем был подключён к Android на Rockchip. В X2 Ultra работают Linux-компьютеры; RG255C-GL подключён по USB к одному из них, на NVIDIA Jetson. Доступ к этому компьютеру у нас уже был по SSH.

Изображение семейства RG255C с сайта Quectel. В нашем роботе установлен RG255C-GL; это изображение модуля, а не фотография платы робота.
Параметр | Наш экземпляр |
|---|---|
Модем | Quectel RG255C-GL, платформа SDX35 |
Исходная прошивка |
|
USB VID:PID |
|
Linux модема | OpenWrt 22.03.5, ARMv7, ядро 5.15.167-perf |
Компьютер робота | ARM64, Linux 5.15.148-tegra |
Сжатие исследуемых сегментов | CLADE и CLADE2 |
Контейнер подписи | MBNv7, SHA-384, ECDSA-P384 |
RG255C — семейство модулей 5G RedCap с поддержкой LTE Cat 4. В этом кейсе мы проверяли именно LTE.
Сначала отделили SIM и настройки от регистрации
Поставили ту же активированную SIM Т-Мобайла, которая заработала в предыдущем роботе. Начальное состояние было таким:
CPIN: READY
CFUN: 1
CEREG: 0,0
CGATT: 0
QENG: "servingcell","SEARCH"
Сканирование находило LTE-сети с PLMN 25020, 25001, 25099, 25011 и 25002. PLMN состоит из MCC страны и MNC оператора; российский MCC — 250.
Для основного PDP-контекста задали IPv4 и APN tt. Проверили разрешённые диапазоны, автоматический и ручной выбор сети, активный профиль ROW_Commercial. Перезапускали отдельно радио и модем. Регистрации не было.
USIM находилась в состоянии ready, персонализация была отключена. В SIM-файле EF FPLMN, где хранится список запрещённых сетей, оставались только FF.
Сигнал тоже измеряли. Модем принимал широковещательные сообщения LTE; SIB1 содержал российские PLMN и разрешённый доступ к ячейкам. Сам по себе такой приём ещё не доказывает исправность передачи. Но оснований считать APN единственной причиной уже не было: нужно было разобраться, почему радиостек не регистрируется.
Зачем ADB, если на роботе Linux
ADB понадобился для доступа к Linux самого модема. У RG255C есть ARM-часть с OpenWrt и радиостек на Qualcomm Hexagon. Android для этого не нужен.
Авторизация отличается от EG25-GC: старый механизм QADBKEY здесь не сработал. В этой сборке подтвердили описанную исследователем особенность adb_auth: первым запросом после запуска модем принимал пустой ответ. Затем временно включили его USB-интерфейс ADB.
Драйвер option захватывал и этот интерфейс как последовательный порт. Отвязали только его, остальные USB-функции оставили работающими. ADB-клиент запустили на компьютере робота, который уже был USB-хостом модема.
USB AT-порт оказался занят штатным housekeeper. Второго читателя к нему не подключали: внутри OpenWrt нашёлся atcmd, который обращается к /dev/smd11.
Из /firmware/image скопировали 291 файл — около 71 МиБ. Это копия файлов прошивки, не полный бэкап NAND, EFS или QCN. Оригиналы сохранили вне модема.
Главная задержка: у CLADE оказалось два диапазона кода
Прошивка снова была разбита на modem.bNN, но совпадение имени modem.b21 со старым модемом ничего не давало. В EG25-GC использовался q6zip, здесь — CLADE. Сегмент modem.b22 отдельно использовал CLADE2.
Для разбора взяли библиотеки из qualcomm_baseband_scripts. Сначала распаковали HIGH-диапазон CLADE и долго искали проверку там. Нужной функции не было.
Поворот дали адреса из диагностической трассы: многие попадали во второй диапазон, LOW. У него собственная схема размещения сжатых строк и исключений. Его нельзя получить, просто продолжив чтение HIGH.
В нашей конфигурации LOW использовал тот же пул исключений с началом b21 + 0x1400000. Значение 0x40740 было относительной границей в этом пуле; первоначальная трактовка его как отдельного адреса давала неправильную распаковку. Для LOW требовался запрос к логическому диапазону на 0x4000000 выше HIGH.
После исправления параметров получили около 10 МиБ LOW-кода. В разборе с виртуальной базой 0xDC000000 по адресу 0xDC983800 нашлась короткая функция с проверками 250 и 432.
Та же пара MCC: Россия и Иран
Функция извлекала три полубайта аргумента. Упрощённо её логика выглядела так:
a = packed_mcc & 0xf;
b = (packed_mcc >> 4) & 0xf;
c = (packed_mcc >> 8) & 0xf;
if (a == 2 && b == 5 && c == 0)
return 1;
if (a == 4 && b == 3 && c == 2)
return 1;
return 0;
Это упакованные цифры MCC, а не сравнение аргумента с десятичным числом 250. Обе специальные ветви отправляли диагностическое сообщение и возвращали единицу. Вызывающая функция использовала этот результат для отдельной ветви обработки PLMN.
В предыдущем модеме мы заменили одну цифру константы и тем самым перенесли запрет на другой MCC. Здесь нашли вариант без такой замены страны.
Российская ветвь проверяла регистры R3 == 2, R4 == 5, R2 == 0. Первое сравнение заменили на R2 == 2. Получилась цепочка:
R2 == 2
R4 == 5
R2 == 0
Между сравнениями R2 не меняется. Условия R2 == 2 и R2 == 0 одновременно невыполнимы. Российская ветвь перестаёт срабатывать, иранская остаётся прежней.
В сжатом modem.b21 для этого хватило одного бита. После изменения заново распаковали LOW и значимую область HIGH: в 10 МиБ LOW отличался один байт одной инструкции, в 20 МиБ HIGH отличий не было. Отдельно перебрали все 4096 комбинаций трёх полубайтов — результат функции изменился только для MCC 250.
Новая подпись: MBNv7 вместо MBNv3
Изменённый код нужно было согласовать с контейнером загрузки. Формат со старого модема переносить нельзя:
EG25-GC из первой статьи | RG255C-GL из этого кейса | |
|---|---|---|
Hash-сегмент |
|
|
Формат | MBNv3 | MBNv7 |
Хеши сегментов | SHA-256 | SHA-384 |
Подпись | RSA-2048, QPSA | ECDSA-P384, SHA-384 |
Таблица | 26 позиций | 29 позиций |
В нашем modem.b28 находились 40 байт заголовка, 24 байта общих метаданных, 224 байта OEM-метаданных, 1392 байта таблицы SHA-384, 104 байта DER-подписи и область сертификатов на 3360 байт. Остаток был заполнен FF.
Сначала проверили исходные сегментные хеши и исходную подпись по открытому ключу из образа. Затем обновили хеш b21 и сформировали новую ECDSA-подпись с локальной трёхуровневой цепочкой сертификатов. Аппаратные OEM-метаданные и размеры областей сохранили.
На нашем экземпляре secboot возвращал статус 0 и нулевой root_hash. Модем принял образ с локальной цепочкой; обход включённой аппаратной проверки Secure Boot для этого не потребовался. Это существенное отличие от случая, когда корень доверия закреплён аппаратно.
modem.mdt в этой сборке равнялся modem.b00 + modem.b28. Поэтому обновлялись три файла: modem.b21, modem.b28 и modem.mdt.
Формат MBNv7 описан в обзоре Qualcomm; структуру также удобно сверять с mbn_tools.py из coreboot.
Запись и запуск
/firmware был смонтирован read-only. Свободного места там оставалось меньше, чем занимал полный modem.b21, поэтому новый большой файл рядом с оригиналом не создавали.
Сохранили оригиналы на компьютере и в /data модема, подготовили откат. В b21 изменили один байт на месте; два небольших файла метаданных записали целиком. Затем выполнили sync, вернули /firmware в read-only и прочитали все три файла обратно для проверки. Запись трёх файлов неатомарна, питание на этом этапе не прерывали.
Перезапустили только модем. В журнале появилась строка:
remote processor 4080000.remoteproc-mss is now up
Радиопроцессор загрузил изменённый образ. Компьютеры робота продолжали работать.
Что получилось
После загрузки получили:
CEREG: 0,1
COPS: 0,0,"T-Mobile",7
CGATT: 1
QENG: "servingcell","NOCONN","LTE","FDD",250,20,...
Здесь NOCONN — состояние RRC idle, а не отсутствие регистрации. Регистрация подтверждалась CEREG, пакетное присоединение — CGATT.
SIM Т-Мобайла обслуживалась сетью Tele2 25020. В этой проверке использовался LTE Band 1, RSRP был около −107 дБм.
Штатная служба с quectel-CM сама подняла wwan0, получила IPv4 и настроила мобильный выход. Wi-Fi оставался включённым, поэтому запросы привязали к сотовому интерфейсу:
curl --interface wwan0 --noproxy '*' \
--connect-timeout 8 --max-time 15 \
-sS -o /dev/null -w 'HTTP=%{http_code}\n' \
http://connectivitycheck.gstatic.com/generate_204
Проверка | Результат |
|---|---|
Регистрация LTE |
|
Пакетное присоединение |
|
HTTP через | 204 |
HTTPS через | TLS с обычной проверкой сертификата, ответ 302 от |
Повторный запуск модема | Соединение восстановилось штатной службой |
Wi-Fi и SSH | Остались доступны |
После регистрации пришло составное SMS Т-Мобайла из шести частей с рекомендацией использовать APN tt.
Вернули исходный состав USB-интерфейсов, выключили ADB модема и остановили временный сервер. После ещё одного перезапуска модем снова подключился; HTTP и HTTPS проверили повторно. Скорость и длительную устойчивость соединения в этом кейсе не измеряли.
В EG25-GC основная работа была с q6zip и QPSA. В RG255C-GL — со вторым диапазоном CLADE и MBNv7. Симптомы и проверка MCC оказались похожими, но разбор и подготовку образа пришлось делать заново.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.