В тихом омуте… или интересный режим работы смартфона OnePlus 6T20.12.2024 11:15
Несколько лет назад один из членов нашей команды заказал себе OnePlus 6T прямо из Китая. Телефон пришел в оригинальной упаковке и типовой комплектации: с зарядным устройством, кабелем и чехлом. Смартфон без проблем проработал год, ничем, на первый взгляд, не отличаясь от тех, что продаются в России. Но однажды приложения начали предупреждать о наличии root-доступа, а некоторые, особенно банковские, вообще перестали запускаться. При этом прошивка никаким образом не модифицировалась, а обновления устанавливались исключительно из официальных источников, относящихся к ОС. Такое странное поведение смартфона побудило нас провести исследование, результаты которого описаны в этой статье.
0. Поиск причины
Какое-то время нам не удавалось понять, в чем причина такого поведения приложений. Устройство проходило проверку Play Integrity Strong, а также подтверждалась подлинность цепочки загрузки при проверке аттестации приложением Key Attestation.
Через некоторое время стало ясно, что причиной такого поведения стали установленные параметры ro.debuggable=1 и ro.adb.secure=0. Разобраться в этом оказалось довольно просто: после множества тщетных попыток понять, в чем проблема, мы попытались выполнить команду adb root. В результате adb shell запустился с правами root без каких-либо ошибок:
Демон adbd осуществляет проверку параметра ro.debuggable при сбросе привилегий с root до shell (без него невозможно подключиться клиентом, сохранив права root), а параметр ro.adb.secure влияет на необходимость аутентификации при подключении adb-клиента.
Встал вопрос: как такое возможно? Наличие параметра androidboot.verifiedbootstate=green в командной строке ядра свидетельствует о заблокированном с ключами производителя загрузчике, что в свою очередь исключает компрометацию посредством повторной блокировки устройства. Это подтверждается тем, что устройство успешно проходило проверку play integrity. В случае разблокировки параметр verifiedbootstate изменяется на orange (или yellow после повторной блокировки), и при включении устройства отображается соответствующее окно .
При перезапуске устройства в режиме recovery, оно также предоставляло рутовый шел через adb — это и начинало наводить на определенные мысли.
1. Как прошивка с включенной возможностью отладки попала в release-сборку?
Для анализа ситуации с использованием root-доступа был получен лог загрузки устройства (/proc/bootloader_log). Наиболее интересным в нем оказался фрагмент, содержащий командную строку запуска ядра:
В глаза бросился нестандартный параметр androidboot.type=sdebug. Хотя сам по себе флаг androidboot.type не является чем-то уникальным, упоминаний его выставления в sdebug оказалось немного. В ходе поиска удалось найти скрипт init.oem.rc, включающий adb (в том числе в режиме зарядки) в случае, если устройство запущено в режиме sdebug:
on property:sys.boot_completed=1 && persist.vendor.usb.config=none && property:ro.boot.type=sdebug
setprop persist.sys.usb.config adb
...
on charger && property:ro.boot.type=sdebug
setprop sys.usb.config adb
Однако на исследуемом устройстве этот файл найти не удалось (позднее выяснилось, что он был встроен в бинарный файл init). Тогда было принято решение изучить все элементы цепочки загрузки устройства в надежде найти место включения режима отладки. К этому моменту стало очевидно, что данный функционал устройства каким-то образом связан с оставленным производителем бэкдором. В 2017 году в устройствах OnePlus уже находили бэкдор. Однако с того момента прошло уже 4 года (на устройстве стоит прошивка 2021 года).
Анализ процесса загрузки показал, что в бинарном файле init присутствует логика обработки данного параметра. В функции android::init::PropertyLoadBootDefaults происходит обработка параметра ro.boot.type. В случае, если он имеет значение sdebug, выставляются параметры ro.debuggable=1 и ro.adb.secure=0, что и можно наблюдать на устройстве. Кроме того, в приведенном ниже фрагменте кода видно, что параметр ro.boot.verifiedbootstate при этом выставляется в orange:
Но если проверить свойство ro.boot.verifiedbootstate, его значение будет green. Неужели этот код не выполняется, и выставление параметров осуществляется в каком-то другом месте?
OnePlus6T:/ # getprop ro.boot.verifiedbootstate
green
Как оказалось, все намного проще: свойства типа ro в init процессе с использованием функции android::base::SetProperty можно выставить только один раз. Первоначально init реализует обработку командной строки ядра в функции ProcessKernelCmdline, где и выставляет передаваемые с префиксом androidboot. аргументы ядра в качестве параметров с префиксом ro.boot.. В том числе выставляется параметр ro.boot.verifiedbootstate=green, поскольку в командной строке было передано значение androidboot.verifiedbootstate. И только после этого вызывается функция android::init::PropertyLoadBootDefaults.
Не углубляясь в исходный код init, достаточно заметить, что в функции android::init::PropertyLoadBootDefaults уже используется параметр ro.boot.type. Следовательно он должен быть выставлен ранее.
Погрузимся глубже.
2. Как в командную строку ядра Android попала строка androidboot.type=sdebug?
Если вы не знакомы с тем, как устроен процесс загрузки современного Android-устройства, рекомендуем познакомиться с этой статьей. Опустим детали и сразу перейдем к фрагменту реализации загрузчика LinuxLoader, запускаемого на финальном шаге.
Данный загрузчик имеет GUID F536D559-459F-48FA-8BBC-43B554ECAE8D и, судя по всему, основан на предоставляемом qualcomm edk2 приложении.
При поиске по строкам значения sdebug можно достаточно быстро обнаружить следующий участок кода:
В зависимости от возвращаемого функцией get_boot_tag значения выставляется аргумент androidboot.type, который затем помещается в передаваемую ядру командную строку (детали этого процесса опустим). Функция get_boot_tag в свою очередь реализует получение параметра из хранилища param (размещается в одноименном разделе прошивки). Для изменения этого значения в коде загрузчика реализована функция set_boot_tag:
int get_boot_tag(){
int tmp;
get_param_by_index_and_offset(0x12Cu, 0x84u, &tmp, 4u);
return tmp;
}
void set_boot_tag(int* tag){
set_param_by_index_and_offset(0x12Cu, 0x84u, tag, 4u);
}
Парсер param секции устройств OnePlus реализован тут, более подробно с форматом можно ознакомиться там же.
Таким образом, приходим к выводу, что появление параметра androidboot.type=sdebug в командной строке ядра напрямую связано с неким параметром, хранимым в секции param устройства. Попробуем продолжить цепочку и разобраться, как же он туда попал.
Если взглянуть на вызовы функции set_boot_tag, можно наткнуться на единственную ссылку из обработчика «секретной» команды ops boottype:
Команда «секретная», поскольку ее обработчик объявлен в общей таблице со всеми командами типа ops, наименование которых хранится в закодированном виде. Полный список команд приведен под спойлером. Помимо безобидных команд также поддерживается изменение режима работы selinux (ops selinux), отключение и включение dm verity (ops disable_dm_verity и ops enable_dm_verity). И все это — на заблокированном загрузчике без каких-либо следов вмешательства после восстановления оригинального значения параметров.
Все эти функции добавляются в связный список обработчиков команд `fastboot` в декодированном виде функцией `fastboot_add_encoded_cmd` (наименование автора). Декодирование наименований команд производится при помощи простого алгоритма:
def decode(cmd):
res = ""
for i in cmd[::-1]:
res += chr(ord(i)-5)
return res
В основном цикле обработки fastboot-команд можно видеть некоторые условия их выполнения. Для большей загадочности в функции `is_ops_commands` проверяемая команда предварительно кодируется и в таком виде сравнивается со значением `xut`:
Остается разобраться с функцией get_ops_allowed. Она возвращает некоторое значение op_token_count, которое обнуляется в нескольких случаях: загадочной командой oem unoproot, по истечении 10 загрузок устройства (не во всех случаях) или выполнения команды ops 4F50040TR18FTR7FSTD5F02. Ненулевым значением данная переменная инициализируется только в случае выполнения команды flash:oproot с некоторым подписанным на ключе платформы токеном. Куда интереснее непонятный параметр intranet, выставление которого в значение 3 также приводит к разблокировке описанных выше команд.
3. Как выключить этот режим?
Целью было выключение неведомого режима, а поскольку стандартная реализация fastboot не позволяет отправлять команды типа ops для взаимодействия с устройством, был использован пакет adb для python:
В результате выполнения скрипта root-доступ на устройстве исчез, а выполнение команд ops стало невозможным.
4. А как вернуть это режим?
После выполнения пункта 3 возник вопрос:, а возможно ли вернуть этот режим? Уже после блокировки данного режима выяснилось, что для его разблокировки необходимо иметь специальный подписанный на ключе производителя токен. Получить помещаемые в токен данные можно командой oem oproot, а разблокируется данный режим путем прошивки специального файла oproot (flash:oproot).
Дабы продолжить эксперименты с рутованным девайсом, было принято решение воспользоваться инструментом edl для прошивки на устройство модифицированного раздела param. Путем выполнения нехитрых команд root-доступ был успешно восстановлен:
Также удивило и то, что в репозитории edl нашелся скрипт oneplus_param.py, судя по всему основанный на результатах исследований APK-файла из выявленного в 2017 году бэкдора. В скрипте подробно описаны варианты значений различных флагов этого раздела и перечислены команды ops. Также в нем представлен код для генерации QR для разблокировки с использованием инженерного приложения. На прошивке 2021 года данная процедура не актуальна.
Послесловие
Точно выяснить историю включения этого режима на устройстве, увы, уже не представляется возможным. Предположительно его появление связано с перепрошивкой на глобальную версию с помощью MSM Download Tool, проведенной перед продажей устройства. Впоследствии смартфон получал обновления, но активированный режим продолжал функционировать и был замечен благодаря усилению antiroot-механизмов в банковских приложениях. Надеемся, что данная статья позволила вам глубже погрузиться в особенности работы смартфонов OnePlus и осознать, что даже самые современные устройства могут таить в себе неожиданные сюрпризы. Дальнейшие исследования этого функционала вряд ли имеют смысл, так как сейчас он представляет больше исторический интерес и отсутствует на новых моделях (по крайней мере, проверялся OnePlus 12). Если у вас есть схожий опыт или интересные мысли по теме, делитесь ими в комментариях!