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

—

от автора

Я Лев Ларин, инженер Unibot. Мы развиваем UNIBOT Fleet, подключаем к ней коммерческих роботов и дорабатываем их управление, интеграции и работу с данными.

В предыдущей статье мы запустили LTE в роботе-официанте с Quectel EG25-GC. Теперь такая же проблема обнаружилась в другом роботе — Agibot X2 Ultra. В нём стоит более новый Quectel RG255C-GL: SIM читается, российские сети видны, регистрации нет.

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

Автор и модем RG255C-GL — иллюстрация к кейсу

Автор и модем RG255C-GL — иллюстрация к кейсу

Обложка сгенерирована по фотографии автора. Робот и электроника на ней — иллюстрация.

Что поменялось относительно первого робота

В первом случае модем был подключён к Android на Rockchip. В X2 Ultra работают Linux-компьютеры; RG255C-GL подключён по USB к одному из них, на NVIDIA Jetson. Доступ к этому компьютеру у нас уже был по SSH.

Модуль Quectel RG255C

Модуль Quectel RG255C

Изображение семейства RG255C с сайта Quectel. В нашем роботе установлен RG255C-GL; это изображение модуля, а не фотография платы робота.

Параметр

Наш экземпляр

Модем

Quectel RG255C-GL, платформа SDX35

Исходная прошивка

RG255CGLABR01A04M4G_A0.300.A0.300

USB VID:PID

2c7c:0316

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: READYCFUN: 1CEREG: 0,0CGATT: 0QENG: "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 == 2R4 == 5R2 == 0

Между сравнениями R2 не меняется. Условия R2 == 2 и R2 == 0 одновременно невыполнимы. Российская ветвь перестаёт срабатывать, иранская остаётся прежней.

В сжатом modem.b21 для этого хватило одного бита. После изменения заново распаковали LOW и значимую область HIGH: в 10 МиБ LOW отличался один байт одной инструкции, в 20 МиБ HIGH отличий не было. Отдельно перебрали все 4096 комбинаций трёх полубайтов — результат функции изменился только для MCC 250.

Новая подпись: MBNv7 вместо MBNv3

Изменённый код нужно было согласовать с контейнером загрузки. Формат со старого модема переносить нельзя:

EG25-GC из первой статьи

RG255C-GL из этого кейса

Hash-сегмент

modem.b01

modem.b28

Формат

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,1COPS: 0,0,"T-Mobile",7CGATT: 1QENG: "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

CEREG: 0,1

Пакетное присоединение

CGATT: 1

HTTP через wwan0

204

HTTPS через wwan0

TLS с обычной проверкой сертификата, ответ 302 от ya.ru

Повторный запуск модема

Соединение восстановилось штатной службой

Wi-Fi и SSH

Остались доступны

После регистрации пришло составное SMS Т-Мобайла из шести частей с рекомендацией использовать APN tt.

Вернули исходный состав USB-интерфейсов, выключили ADB модема и остановили временный сервер. После ещё одного перезапуска модем снова подключился; HTTP и HTTPS проверили повторно. Скорость и длительную устойчивость соединения в этом кейсе не измеряли.

В EG25-GC основная работа была с q6zip и QPSA. В RG255C-GL — со вторым диапазоном CLADE и MBNv7. Симптомы и проверка MCC оказались похожими, но разбор и подготовку образа пришлось делать заново.

ссылка на оригинал статьи https://habr.com/ru/articles/1099170/