Root Detection — механизм защиты Android-приложений от запуска на устройствах с полученными привилегиями суперпользователя. Банковские, платёжные и корпоративные приложения блокируют или ограничивают функциональность на рутованных девайсах, поскольку root-доступ снимает ограничения песочницы Android и открывает возможность перехвата трафика, чтения памяти процессов и подмены данных. По данным OWASP Mobile Top 10 (2024), отсутствие Root Detection относится к категории M8 (Security Decisions via Untrusted Inputs). Root Detection строится на комбинации статических проверок файловой системы и динамического анализа runtime-поведения.
Главное
Root Detection — программный механизм, определяющий наличие root-доступа на Android-устройстве. Root-доступ предоставляет полный контроль над операционной системой, позволяя приложениям и скриптам выполнять команды с UID 0. На рутованном устройстве теряется изоляция приложений (Android Sandbox), что делает возможным перехват ввода с клавиатуры, чтение SQLite-баз данных других приложений, инъекцию кода в процессы и подмену SSL-сертификатов в доверенном хранилище.
Для финансовых и корпоративных приложений работа на рутованном устройстве представляет неприемлемый риск: взломщик получает доступ к токенам, сессионным ключам и персональным данным. Регуляторы, включая PCI Security Standards Council, требуют от платёжных приложений обнаружения и реакции на root-доступ. В ответ на это Android-разработчики встраивают Root Detection как часть стратегии проактивной защиты.
Существует два подхода к обнаружению: статический, анализирующий файловую систему и установленные пакеты, и динамический, выполняющий проверки в runtime. Комбинированный подход считается наиболее надёжным, так как покрывает разные векторы обхода. По данным исследования NowSecure (2025), 76% банковских приложений в топ-100 Google Play содержат ту или иную форму Root Detection.
Статические методы выполняются при старте приложения и проверяют признаки root-доступа, оставляемые инструментами рутинга в файловой системе. Эти методы не требуют выполнения привилегированных команд и работают в контексте обычного приложения.
Основной признак root-доступа — наличие исполняемого файла su в стандартных путях: /system/bin/su, /system/xbin/su, /sbin/su, /su/bin/su. Приложение проверяет существование файла через File.exists() или нативную реализацию access() из libc. Дополнительно можно попытаться выполнить su --version или su -c id и проверить exit code.
Типичные приложения для управления root-доступом: Superuser, SuperSU, Magisk Manager, KingRoot. Их наличие проверяется через PackageManager.getPackageInfo() или чтение /data/app/ директории. Пакеты для проверки: com.topjohnwu.magisk, eu.chainfire.supersu, com.noshufou.android.su, com.thirdparty.superuser, com.koushikdutta.superuser, com.zacharee1.systemuituner.
Android сохраняет информацию о состоянии системы в системных свойствах, доступных через System.getProperty и Build.TAGS. Если значение Build.TAGS содержит test-keys, а не release-keys — это указывает на кастомную прошивку, часто с root-доступом. Дополнительно проверяются ro.build.tags, ro.debuggable и ro.secure через чтение /system/build.prop.
public class RootDetectionChecker {
private static final String[] SU_PATHS = {
"/system/bin/su",
"/system/xbin/su",
"/sbin/su",
"/su/bin/su",
"/system/sd/xbin/su"
};
public boolean checkRootByFiles() {
for (String path : SU_PATHS) {
if (new File(path).exists()) {
return true;
}
}
return false;
}
public boolean checkRootByPackages(Context ctx) {
String[] packages = {
"com.topjohnwu.magisk",
"eu.chainfire.supersu",
"com.noshufou.android.su",
"com.koushikdutta.superuser"
};
for (String pkg : packages) {
try {
ctx.getPackageManager().getPackageInfo(pkg, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
// package not found
}
}
return false;
}
}
Динамические методы выполняются во время работы приложения и анализируют среду выполнения. В отличие от статических, они могут обнаружить рутинг, скрытый через Magisk Hide или Zygisk, поскольку проверяют поведение системы, а не только файловую структуру.
При root-доступе некоторые системные разделы монтируются с флагом rw (read-write) вместо ro (read-only). Приложение читает /proc/mounts и проверяет, что /system смонтирован как ro. Если /system смонтирован как rw — это признак модифицированной системы. Дополнительно проверяется наличие монтирования /su через Magisk.
Android Safe Mode отключает сторонние приложения, включая root-менеджеры. Корректная реализация Root Detection может проверить, работает ли устройство в безопасном режиме. Если приложение обнаруживает, что root-менеджеры не видны, но su-бинарник существует — это признак Magisk Hide.
Попытка выполнения su -c id через ProcessBuilder или Runtime.exec — прямой тест root-доступа. Однако Magisk может перехватывать этот вызов. Более надёжный вариант — проверка через нативный код: открыть /proc/1/limits или /proc/self/maps и проанализировать UID запущенных процессов. Если приложение может получить UID 0 или прочитать файлы, доступные только root — устройство скомпрометировано.
public boolean checkRootDynamically() {
// Проверка флагов сборки
String buildTags = Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
return true;
}
// Проверка монтирования /system
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream("/proc/mounts"))
);
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("/system")
&& line.contains("rw")) {
reader.close();
return true;
}
}
reader.close();
} catch (IOException e) {
// error reading mounts
}
return false;
}
Root Detection, реализованный на Java, легко обходится через Xposed-модули или Frida, которые перехватывают Java-методы и подменяют возвращаемые значения. Нативная реализация на C++ через JNI значительно устойчивее: инструменты динамического анализа, работающие на Java-уровне, не видят нативных вызовов libc, таких как stat, access, popen и dlopen.
#include <unistd.h>
#include <sys/stat.h>
#include <cstring>
#include <vector>
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_checker_RootCheck_nativeCheck(
JNIEnv* env, jobject instance) {
std::vector<const char*> paths = {
"/system/bin/su",
"/system/xbin/su",
"/sbin/su",
"/data/local/su"
};
struct stat st;
for (const char* path : paths) {
if (stat(path, &st) == 0) {
return JNI_TRUE;
}
}
return JNI_FALSE;
}
Нативная проверка не использует Java API, что делает её невидимой для инструментов обхода, работающих на Dalvik/ART уровне. Для дополнительной защиты рекомендуется хранить константы (список путей) не в read-only секции, а вычислять их через простые обратимые функции. Stat-вызов из libc напрямую обращается к ядру Linux, минуя Java-обёртки, и его невозможно перехватить через Xposed.
Разработчикам защиты необходимо понимать существующие методы обхода, чтобы строить устойчивую систему обнаружения. Каждый метод обхода требует контр-меры на соответствующем уровне.
Magisk — самый популярный инструмент рутинга на Android 9–14. Magisk Hide скрывает наличие su из /proc и подменяет результаты проверки путей. Magisk работает на уровне ядра и перехватывает stat() и access() до того, как их увидит приложение. Контр-мера: проверка наличия самого Magisk через наличие /sbin/.magisk или проверка через чтение собственного maps — Magisk встраивает свою библиотеку в каждый процесс.
Frida — инструмент динамической инструментации, который может перехватывать нативные функции через Ptrace или Dobby. Frida заменяет return value любой проверки, подменяя результат stat на ENOENT. Контр-мера: проверка целостности нативных функций через подсчёт контрольной суммы инструкций в памяти и обнаружение Frida через анализ /proc/self/maps на наличие frida-agent.so или frida-helper.
Root Detection, реализованный в Java, удаляется за 2–3 минуты: APK распаковывается через apktool, в smali-коде исправляется return-значение метода на false, APK пересобирается и подписывается. Контр-мера: перенос критической логики в нативный код и проверка цифровой подписи приложения в runtime через Signature API или сверку хеша APK с эталоном на сервере.
Эффективный Root Detection строится на многослойной архитектуре. Ни один метод по отдельности не обеспечивает достаточного уровня защиты. Комбинация статических и динамических проверок, нативного кода и серверной верификации даёт максимальную устойчивость.
Не полагайтесь только на client-side проверку. Отправляйте на сервер результаты Root Detection вместе с одноразовым токеном сессии. Сервер принимает решение о блокировке или ограничении функциональности. Это предотвращает атаки на уровне API, где клиентское приложение может быть модифицировано, а сервер остаётся доверенной стороной.
Код Root Detection должен быть обфусцирован. Если злоумышленник видит в jadx чёткую последовательность проверок su-путей — обход займёт минуты. Используйте ProGuard или DexGuard для запутывания потока управления и шифрования строк. Obfuscation повышает время анализа кода защиты с нескольких минут до нескольких часов.
Список проверяемых путей, пакетов и индикаторов должен обновляться с каждым релизом приложения. Новые инструменты рутинга и обхода появляются ежемесячно. Статический список, не менявшийся год, не обнаруживает современные методы. Рекомендуется загружать актуальные сигнатуры с сервера при старте приложения перед выполнением проверок.
Часто задаваемые вопросы
Root Detection защищает от запуска приложения на устройстве, где песочница Android отключена. На рутованном устройстве любое приложение может читать данные других приложений. Банковские и платёжные приложения обязаны блокировать работу на рутованных устройствах по требованиям PCI DSS и рекомендациям OWASP Mobile Security.
Magisk Hide использует механизм монтирования пространства имён (mount namespace). Для каждого процесса, указанного в списке исключений, Magisk создаёт изолированный namespace, где su-бинарник невидим. Системные вызовы stat, access и open в этом namespace не видят файлов Magisk. Обнаружить Magisk можно через проверку наличия /proc/self/maps и поиск magisk-дампа.
Да, если приложение не проверяет целостность своего кода. Через Frida можно перехватить Java-метод проверки и принудительно вернуть false. Контр-мера — нативная реализация критической логики на C++ и проверка целостности через хеш DEX-файла. Без обфускации любой Root Detection на Java обходится за 5–10 минут.
SafetyNet (устарел) и Play Integrity API — это серверные проверки от Google, подтверждающие целостность устройства. Они включают проверку bootloader, системной подписи и root-статуса. Play Integrity API — рекомендуемая замена SafetyNet, дающая три уровня: BASIC, DEVICE и STRONG. Root Detection на клиенте дополняет серверную аттестацию.
Установите приложение на реальное рутованное устройство (например, Pixel с Magisk). Проверьте, срабатывает ли блокировка. Затем попробуйте скрыть рут через Magisk Hide для вашего приложения и перезапустите тест. Для углублённой проверки используйте Frida для перехвата целевых методов и убедитесь, что нативная защита не поддаётся обходу.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также