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() {
// Build flags check
String buildTags = Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
return true;
}
// Checking /system mount
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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також