Określ, czy działa na zrootowanym urządzeniu

292

Moja aplikacja ma pewną funkcjonalność, która będzie działać tylko na urządzeniu, na którym jest dostępny root. Zamiast tego, że ta funkcja zawodzi, gdy jest używana (a następnie wyświetla odpowiedni komunikat o błędzie dla użytkownika), wolałbym możliwość cichego sprawdzenia, czy root jest dostępny jako pierwszy, a jeśli nie, ukrywanie odpowiednich opcji w pierwszej kolejności .

Czy jest na to sposób?

miracle2k
źródło
11
Nie ma na to niezawodnego sposobu; poniższe odpowiedzi sprawdzają wspólne cechy, ale dane urządzenie może nie być zrootowane we wspólny sposób. Jeśli sprawdzanie roota stanie się powszechne, rozwiązania rootowania prawdopodobnie zaczną próbować się ukryć. Ponieważ mogą modyfikować zachowanie systemu operacyjnego, mają wiele opcji.
Chris Stratton,
Wskazane może być wskazanie, że funkcja nie jest dostępna z powodu braku możliwości rootowania, dostarczania użytkownikowi większej ilości informacji, niż ukrywania możliwości aplikacji, co powoduje niejasności w ogólnym doświadczeniu.
nick lis
Czy poniższe odpowiedzi działają dla rootowania systemowego ?
Piyush Kukadiya

Odpowiedzi:

260

Oto klasa, która sprawdzi rootowanie na jeden z trzech sposobów.

/** @author Kevin Kowalewski */
public class RootUtil {
    public static boolean isDeviceRooted() {
        return checkRootMethod1() || checkRootMethod2() || checkRootMethod3();
    }

    private static boolean checkRootMethod1() {
        String buildTags = android.os.Build.TAGS;
        return buildTags != null && buildTags.contains("test-keys");
    }

    private static boolean checkRootMethod2() {
        String[] paths = { "/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su",
                "/system/bin/failsafe/su", "/data/local/su", "/su/bin/su"};
        for (String path : paths) {
            if (new File(path).exists()) return true;
        }
        return false;
    }

    private static boolean checkRootMethod3() {
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(new String[] { "/system/xbin/which", "su" });
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            if (in.readLine() != null) return true;
            return false;
        } catch (Throwable t) {
            return false;
        } finally {
            if (process != null) process.destroy();
        }
    }
}
Kevin Parker
źródło
8
Jeśli dwa pytania uzasadniają identyczne odpowiedzi, to są one w 99% razy duplikowane, więc oflaguj jako duplikaty zamiast umieszczać tę samą odpowiedź na obu. Dzięki.
Kev
2
Być może tak jest, ale informuję tylko, że społeczność oznaczyła dokładnie duplikaty odpowiedzi . Powinieneś dopasować swoje odpowiedzi i zająć się specyfiką problemu PO. Kopiowanie i wklejanie odpowiedzi jest zagrożone przyciąganiem głosów negatywnych.
Kev
9
-1, ta metoda nie jest wykonalna, ponieważ niektóre telefony zawierają subinarny, ale nie są zrootowane.
neevek
12
Chcę cię poinformować, że aplikacja Fox Digital Copy (Beta) używa twojego kodu prawie dosłownie, w tym klas Root i ExecShell, a także metod checkRootMethod1 / 2/3. Uważam to za bardzo zabawne.
Matt Joseph
8
Mogę ich pozwać, tak jak Fox pozwał niezliczoną liczbę innych?
Kevin Parker,
58

Jeśli już używasz Crashlytics typu Fabric / Firebase, możesz zadzwonić

CommonUtils.isRooted(context)

To jest obecne wdrożenie tej metody:

public static boolean isRooted(Context context) {
    boolean isEmulator = isEmulator(context);
    String buildTags = Build.TAGS;
    if(!isEmulator && buildTags != null && buildTags.contains("test-keys")) {
        return true;
    } else {
        File file = new File("/system/app/Superuser.apk");
        if(file.exists()) {
            return true;
        } else {
            file = new File("/system/xbin/su");
            return !isEmulator && file.exists();
        }
    }
}
Kingston
źródło
Najlepsza odpowiedź w historii. Użyj tego w dowolnej bibliotece, na chińskich urządzeniach działa wiele fałszywych alarmów.
Pedro Paulo Amorim,
Czy w tej metodzie jest jakikolwiek fałszywy wynik dodatni?
Ehsan Mashhadi
Testowałem to na Nexusie 5 z download.chainfire.eu/363/CF-Root/CF-Auto-Root/... , ten nie jest dokładny.
Jeffrey Liu
54

Biblioteka RootTools oferuje proste metody sprawdzania rootowania:

RootTools.isRootAvailable()

Odniesienie

Zawiłości
źródło
10
isRootAvailable () sprawdza po prostu obecność su w ścieżce i niektórych innych zakodowanych katalogach. Słyszałem, że niektóre narzędzia do usuwania systemu zostawią tam su, więc da to fałszywy pozytyw.
Bob Whiteman,
13
RootTools.isAccessGiven () nie tylko sprawdzi, czy root, ale także poprosi o uprawnienia roota; więc nieukończone urządzenie zawsze zwróci false przy użyciu tej metody.
aggregate1166877
2
@ aggregate1166877, masz rację, ale to nie wystarczy, co jeśli nie będę wymagał uprawnień roota, kiedy o to poproszę? Chcę tylko wiedzieć, czy jest zrootowany, ale w tej chwili nie potrzebuję pozwolenia na rootowanie.
neevek
4
isAccessGiven () zwraca false, gdy użytkownik odmawia uprawnienia, nawet jeśli urządzenie było zrootowane.
subair_a
To jedyna odpowiedź, którą uważam za wartą głosowania. Sprawdź moją odpowiedź poniżej, jeśli chcesz czegoś podobnego po prostu wkleić lub wkleić więcej szczegółów
rsimp
52

W mojej aplikacji sprawdzałem, czy urządzenie jest zrootowane, wykonując polecenie „su”. Ale dziś usunąłem tę część mojego kodu. Dlaczego?

Ponieważ moja aplikacja stała się zabójcą pamięci. W jaki sposób? Pozwól, że opowiem ci moją historię.

Pojawiły się skargi, że moja aplikacja spowalnia urządzenia (oczywiście myślałem, że to nieprawda). Próbowałem dowiedzieć się, dlaczego. Użyłem MAT, aby uzyskać zrzuty sterty i analizować, i wszystko wydawało się idealne. Ale po ponownym uruchomieniu mojej aplikacji wiele razy zdałem sobie sprawę, że urządzenie naprawdę działa wolniej, a zatrzymanie mojej aplikacji nie przyspieszyło (chyba że ponownie uruchomię urządzenie). Ponownie przeanalizowałem pliki zrzutu, gdy urządzenie jest bardzo wolne. Ale wszystko nadal było idealne do pliku zrzutu. Potem zrobiłem to, co najpierw trzeba zrobić. Wymieniłem procesy.

$ adb shell ps

Niespodzianka; dla mojej aplikacji było wiele procesów (ze znacznikiem procesu mojej aplikacji w manifeście). Niektóre z nich były zombie, niektóre nie.

Dzięki przykładowej aplikacji, która ma jedno działanie i wykonuje po prostu polecenie „su”, zdałem sobie sprawę, że proces zombie jest tworzony przy każdym uruchomieniu aplikacji. Na początku te zombie przydzielają 0 KB, ale potem coś się dzieje, a procesy zombie mają prawie takie same KB jak główny proces mojej aplikacji i stały się standardowymi procesami.

Istnieje raport o błędzie dotyczący tego samego problemu na stronie bugs.sun.com: http://bugs.sun.com/view_bug.do?bug_id=6474073 wyjaśnia to, że jeśli nie znaleziono polecenia, zombie zostaną utworzone za pomocą metody exec () . Ale nadal nie rozumiem, dlaczego i jak mogą stać się standardowymi procesami i posiadać znaczące bazy wiedzy. (Nie dzieje się tak cały czas)

Możesz spróbować, jeśli chcesz, z próbką kodu poniżej;

String commandToExecute = "su";
executeShellCommand(commandToExecute);

Prosta metoda wykonywania poleceń;

private boolean executeShellCommand(String command){
    Process process = null;            
    try{
        process = Runtime.getRuntime().exec(command);
        return true;
    } catch (Exception e) {
        return false;
    } finally{
        if(process != null){
            try{
                process.destroy();
            }catch (Exception e) {
            }
        }
    }
}

Podsumowując; Nie mam porady, aby ustalić, czy urządzenie jest zrootowane, czy nie. Ale gdybym był tobą, nie użyłbym Runtime.getRuntime (). Exec ().

Tak poza tym; RootTools.isRootAvailable () powoduje ten sam problem.

Devrim
źródło
5
To bardzo niepokojące. Miałem zrootowaną klasę wykrywania urządzeń, która zrobiła to samo - po przeczytaniu tego potwierdziłem, co wyszczególniono powyżej. Sporadyczne procesy zombie są pozostawione w tyle, spowolnienia urządzeń itp.
AWT
1
Potwierdzam problem z RootTools 3.4 na Androidzie GT-S5830i 2.3.6. Większość zombie ma przydzieloną pamięć, a problem jest systematyczny. Muszę ponownie uruchomić urządzenie po 3-4 testach. Zalecam zapisanie wyniku testu we wspólnych preferencjach.
Chrystus
2
Google zaleca teraz użycie ProcessBuilder () i komendy start ().
EntangledLoops,
1
@NickS Ciekawe, ale jakie polecenie uruchomiłeś? Nie mam tutaj tego samego problemu z wydawaniem poleceń na wielu telefonach z Androidem o różnych poziomach API od 9 do 23.
EntangledLoops
1
@EntangledLoops. Dziękuję Ci. Uruchamiam swój własny plik binarny i wchodzę z nim w interakcję poprzez stdin / stdout. Ponownie sprawdziłem, jak to zatrzymać i odkryłem, że w jednym z przypadków przegapiłem Process.destroy (). Więc nie ma zombie.
Nick S
36

Wiele z wymienionych tutaj odpowiedzi ma nieodłączne problemy:

  • Sprawdzanie kluczy testowych jest skorelowane z dostępem do katalogu głównego, ale niekoniecznie gwarantuje to
  • Katalogi „PATH” powinny pochodzić z rzeczywistej zmiennej środowiskowej „PATH”, a nie być zapisane na stałe
  • Istnienie pliku wykonywalnego „su” niekoniecznie oznacza, że ​​urządzenie zostało zrootowane
  • Plik wykonywalny „który” może, ale nie musi, być zainstalowany, a jeśli to możliwe, pozwól systemowi rozpoznać ścieżkę
  • To, że aplikacja SuperUser jest zainstalowana na urządzeniu, nie oznacza, że ​​urządzenie ma jeszcze dostęp do konta root

Wydaje się, że biblioteka RootTools ze Stericson sprawdza poprawność rootowania. Ma również wiele dodatkowych narzędzi i narzędzi, więc bardzo go polecam. Jednak nie ma wyjaśnienia, w jaki sposób konkretnie sprawdza roota i może być nieco cięższy niż większość aplikacji naprawdę potrzebuje.

Zrobiłem kilka metod narzędziowych, które są luźno oparte na bibliotece RootTools. Jeśli chcesz po prostu sprawdzić, czy plik wykonywalny „su” znajduje się na urządzeniu, możesz użyć następującej metody:

public static boolean isRootAvailable(){
    for(String pathDir : System.getenv("PATH").split(":")){
        if(new File(pathDir, "su").exists()) {
            return true;
        }
    }
    return false;
}

Ta metoda po prostu zapętla katalogi wymienione w zmiennej środowiskowej „PATH” i sprawdza, czy plik „su” istnieje w jednym z nich.

Aby naprawdę sprawdzić dostęp do konta root, należy faktycznie uruchomić polecenie „su”. Jeśli zainstalowana jest aplikacja taka jak SuperUser, w tym momencie może poprosić o dostęp do konta root, lub jeśli została już przyznana / odmówiona, może zostać wyświetlony toast wskazujący, czy dostęp został udzielony / odmówiony. Dobrym poleceniem do uruchomienia jest „id”, abyś mógł sprawdzić, czy identyfikator użytkownika to w rzeczywistości 0 (root).

Oto przykładowa metoda ustalenia, czy przyznano dostęp do konta root:

public static boolean isRootGiven(){
    if (isRootAvailable()) {
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(new String[]{"su", "-c", "id"});
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String output = in.readLine();
            if (output != null && output.toLowerCase().contains("uid=0"))
                return true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (process != null)
                process.destroy();
        }
    }

    return false;
}

Ważne jest, aby faktycznie przetestować uruchomienie polecenia „su”, ponieważ niektóre emulatory mają preinstalowane pliki wykonywalne „su”, ale zezwalają tylko niektórym użytkownikom na dostęp do niego, podobnie jak powłoka adb.

Ważne jest również sprawdzenie, czy istnieje plik wykonywalny „su” przed próbą jego uruchomienia, ponieważ wiadomo, że Android nie usuwa poprawnie procesów, które próbują uruchomić brakujące polecenia. Te procesy-duchy mogą z czasem zużywać więcej pamięci.

rsimp
źródło
Metoda isRootAvailable () działa świetnie, dziękuję. Nie polecam jednak uruchamiania tego w głównym wątku, aby uniknąć ANR, na przykład wywołania z AsyncTask
Thunderstick
1
Myślę, że to różnica między chęcią upewnienia się, że root nie jest dostępny, a chęcią upewnienia się, że jest. Jeśli chcesz się upewnić, że urządzenie nie jest zrootowane, sugerowane kontrole są dobre. Otrzymasz fałszywe alarmy, ale to w porządku, jeśli nie uruchamiasz kodu na zaatakowanym urządzeniu.
Jeffrey Blattman
1
@ DAC84 Nie jestem pewien, czy rozumiem twoje pytanie. Jeśli uruchomisz isRootGiven i odmówisz aplikacji do rootowania, powinna ona zwrócić false. Czy to nie to, co się dzieje? Jeśli chcesz uniknąć alertu, możesz po prostu użyć isRootAvailable, którą można również nazwać doesSUExist. Możesz także spróbować skonfigurować swoją aplikację root, aby dawała root swobodnie i nie zarządzała nim.
rsimp
1
@BeeingJk nie, nie bardzo, chociaż to naprawdę najwięcej, co możesz sprawdzić bez uruchamiania su, co jest prawdziwym testem. Musisz jednak sprawdzić su w PATH, zanim spróbujesz go wykonać. Jednak w rzeczywistości wykonanie su często powoduje wyświetlenie wiadomości toast lub interakcję z aplikacją do zarządzania rootem, co może nie być tym, czego chcesz. Dla własnej logiki możesz uznać, że samo istnienie su jest wystarczające. Może to nadal dawać fałszywe alarmy w niektórych emulatorach, które mogą zawierać wykonywalny su, ale blokować dostęp do niego.
rsimp
1
@BeeingJk isRootAvailable jest prawdopodobnie wszystkim, czego potrzebujesz, ale próbuję zrobić to, aby taka nazwa lub nawet SUExist zapewnia lepszą semantykę niż nazwa metody, taka jak isDeviceRooted, co nie jest całkiem poprawne. Jeśli naprawdę musisz zweryfikować pełny dostęp do roota przed kontynuowaniem, musisz spróbować uruchomić komendę su, taką jak ta zakodowana w isRootGiven
rsimp
35

Aktualizacja 2017

Możesz to zrobić teraz dzięki interfejsowi API Google Safetynet . SafetyNet API zapewnia Attestation API, który pomaga ocenić bezpieczeństwo i kompatybilność środowisk Androida, w których działają Twoje aplikacje.

Poświadczenie to może pomóc ustalić, czy dane urządzenie zostało zmodyfikowane, czy nie.

Interfejs API atestacji zwraca odpowiedź JWS w ten sposób

{
  "nonce": "R2Rra24fVm5xa2Mg",
  "timestampMs": 9860437986543,
  "apkPackageName": "com.package.name.of.requesting.app",
  "apkCertificateDigestSha256": ["base64 encoded, SHA-256 hash of the
                                  certificate used to sign requesting app"],
  "apkDigestSha256": "base64 encoded, SHA-256 hash of the app's APK",
  "ctsProfileMatch": true,
  "basicIntegrity": true,
}

Analiza tej odpowiedzi może pomóc ustalić, czy urządzenie jest zrootowane, czy nie

Zrootowane urządzenia wydają się powodować ctsProfileMatch = false.

Możesz to zrobić po stronie klienta, ale zalecane jest analizowanie odpowiedzi po stronie serwera. Podstawowa architektura serwera klienta z interfejsem API sieci bezpieczeństwa będzie wyglądać następująco: -

wprowadź opis zdjęcia tutaj

Hitesh Sahu
źródło
3
Doskonała informacja iw innym kontekście uważam, że byłaby to prawidłowa odpowiedź. Niestety pytanie OP nie dotyczy obrony jego aplikacji przed niebezpiecznymi środowiskami, ale wykrycia roota, aby włączyć funkcje tylko root w jego aplikacji. Dla zamierzonego celu PO proces ten wydaje się zbyt skomplikowany.
rsimp
31

Kontrola root na poziomie Java nie jest bezpiecznym rozwiązaniem. Jeśli Twoja aplikacja ma problemy z bezpieczeństwem, aby działać na zrootowanym urządzeniu, skorzystaj z tego rozwiązania.

Odpowiedź Kevina działa, chyba że telefon ma również aplikację taką jak RootCloak. Takie aplikacje mają funkcję obsługi interfejsów API Java po zrootowaniu telefonu i kpią z tych interfejsów API, aby zwrócić telefon nie zrootowany.

Napisałem kod poziomu natywnego na podstawie odpowiedzi Kevina, działa nawet z RootCloak! Ponadto nie powoduje żadnych problemów z wyciekiem pamięci.

#include <string.h>
#include <jni.h>
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include "android_log.h"
#include <errno.h>
#include <unistd.h>
#include <sys/system_properties.h>

JNIEXPORT int JNICALL Java_com_test_RootUtils_checkRootAccessMethod1(
        JNIEnv* env, jobject thiz) {


    //Access function checks whether a particular file can be accessed
    int result = access("/system/app/Superuser.apk",F_OK);

    ANDROID_LOGV( "File Access Result %d\n", result);

    int len;
    char build_tags[PROP_VALUE_MAX]; // PROP_VALUE_MAX from <sys/system_properties.h>.
    len = __system_property_get(ANDROID_OS_BUILD_TAGS, build_tags); // On return, len will equal (int)strlen(model_id).
    if(strcmp(build_tags,"test-keys") == 0){
        ANDROID_LOGV( "Device has test keys\n", build_tags);
        result = 0;
    }
    ANDROID_LOGV( "File Access Result %s\n", build_tags);
    return result;

}

JNIEXPORT int JNICALL Java_com_test_RootUtils_checkRootAccessMethod2(
        JNIEnv* env, jobject thiz) {
    //which command is enabled only after Busy box is installed on a rooted device
    //Outpput of which command is the path to su file. On a non rooted device , we will get a null/ empty path
    //char* cmd = const_cast<char *>"which su";
    FILE* pipe = popen("which su", "r");
    if (!pipe) return -1;
    char buffer[128];
    std::string resultCmd = "";
    while(!feof(pipe)) {
        if(fgets(buffer, 128, pipe) != NULL)
            resultCmd += buffer;
    }
    pclose(pipe);

    const char *cstr = resultCmd.c_str();
    int result = -1;
    if(cstr == NULL || (strlen(cstr) == 0)){
        ANDROID_LOGV( "Result of Which command is Null");
    }else{
        result = 0;
        ANDROID_LOGV( "Result of Which command %s\n", cstr);
        }
    return result;

}

JNIEXPORT int JNICALL Java_com_test_RootUtils_checkRootAccessMethod3(
        JNIEnv* env, jobject thiz) {


    int len;
    char build_tags[PROP_VALUE_MAX]; // PROP_VALUE_MAX from <sys/system_properties.h>.
    int result = -1;
    len = __system_property_get(ANDROID_OS_BUILD_TAGS, build_tags); // On return, len will equal (int)strlen(model_id).
    if(len >0 && strstr(build_tags,"test-keys") != NULL){
        ANDROID_LOGV( "Device has test keys\n", build_tags);
        result = 0;
    }

    return result;

}

W kodzie Java musisz utworzyć klasę opakowania RootUtils, aby wykonywać natywne połączenia

    public boolean checkRooted() {

       if( rootUtils.checkRootAccessMethod3()  == 0 || rootUtils.checkRootAccessMethod1()  == 0 || rootUtils.checkRootAccessMethod2()  == 0 )
           return true;
      return false;
     }
Alok Kulkarni
źródło
1
Myślę, że wykrywanie rootów dzieli się na dwie kategorie, włączając funkcje zależne od roota, a następnie środki oparte na bezpieczeństwie, aby spróbować złagodzić problemy z bezpieczeństwem zrootowanych telefonów. W przypadku funkcji zależnych od roota odpowiedź Kevina jest dość słaba. W kontekście tej odpowiedzi metody te mają większy sens. Chociaż przepisałbym metodę 2, aby nie używać tej i zamiast tego iterować zmienną środowiskową PATH w celu wyszukania „su”. „które” nie jest gwarantowane przez telefon.
rsimp
czy możesz podać przykład użycia tego kodu c w Javie?
mrid
@mrid Sprawdź, jak wykonywać połączenia JNI z Javy na Androida.
Alok Kulkarni
Ta metoda zapobiega wykrywaniu korzeni przy użyciu aplikacji RootCloak. Czy są jakieś znane techniki obejścia głównego, które zawodzą tę metodę?
Nidhin
20

http://code.google.com/p/roottools/

Jeśli nie chcesz używać pliku jar, skorzystaj z kodu:

public static boolean findBinary(String binaryName) {
        boolean found = false;
        if (!found) {
            String[] places = { "/sbin/", "/system/bin/", "/system/xbin/",
                    "/data/local/xbin/", "/data/local/bin/",
                    "/system/sd/xbin/", "/system/bin/failsafe/", "/data/local/" };
            for (String where : places) {
                if (new File(where + binaryName).exists()) {
                    found = true;

                    break;
                }
            }
        }
        return found;
    }

Program spróbuje znaleźć folder su:

private static boolean isRooted() {
        return findBinary("su");
    }

Przykład:

if (isRooted()) {
   textView.setText("Device Rooted");

} else {
   textView.setText("Device Unrooted");
}
noobProgrammer
źródło
Dzięki! Używam tego jako checkRootMethod4()z Kevina Odpowiedź .
Sheharyar
1
Nigdy nie dodawaj == trueboolean, nic nie dodaje i nie wygląda dobrze.
minipif
2
@smoothBlue Dlaczego miałoby to być? Nie tworzy żadnych procesów, jak rozwiązanie DevrimTuncer.
FD_
1
Lepszym pomysłem byłoby iteracja po PATH, zamiast twardego kodowania typowych katalogów PATH
rsimp
1
Użyj, if (isRooted())sprawdź zamiast jawnie wpisać true. Lepiej podążać za wzorami pisania kodu
blueware
13

Zamiast korzystania z isRootAvailable () możesz użyć isAccessGiven (). Bezpośrednio z wiki RootTools :

if (RootTools.isAccessGiven()) {
    // your app has been granted root access
}

RootTools.isAccessGiven () nie tylko sprawdza, czy urządzenie jest zrootowane, ale również wywołuje su dla Twojej aplikacji, żąda pozwolenia i zwraca true, jeśli aplikacja uzyskała uprawnienia root. Może to być użyte jako pierwsze sprawdzenie w Twojej aplikacji, aby mieć pewność, że otrzymasz dostęp w razie potrzeby.

Odniesienie

saulobrito
źródło
ale użytkownik musi przyznać uprawnienia roota? więc jeśli moim celem było zatrzymanie uruchamiania aplikacji, jeśli urządzenie jest
zrootowane,
11

Niektóre zmodyfikowane kompilacje używane do ustawiania właściwości systemowej ro.modversion w tym celu. Wydaje się, że wszystko poszło dalej; mój build z TheDude kilka miesięcy temu ma to:

cmb@apollo:~$ adb -d shell getprop |grep build
[ro.build.id]: [CUPCAKE]
[ro.build.display.id]: [htc_dream-eng 1.5 CUPCAKE eng.TheDudeAbides.20090427.235325 test-keys]
[ro.build.version.incremental]: [eng.TheDude.2009027.235325]
[ro.build.version.sdk]: [3]
[ro.build.version.release]: [1.5]
[ro.build.date]: [Mon Apr 20 01:42:32 CDT 2009]
[ro.build.date.utc]: [1240209752]
[ro.build.type]: [eng]
[ro.build.user]: [TheDude]
[ro.build.host]: [ender]
[ro.build.tags]: [test-keys]
[ro.build.product]: [dream]
[ro.build.description]: [kila-user 1.1 PLAT-RC33 126986 ota-rel-keys,release-keys]
[ro.build.fingerprint]: [tmobile/kila/dream/trout:1.1/PLAT-RC33/126986:user/ota-rel-keys,release-keys]
[ro.build.changelist]: [17615# end build properties]

Z drugiej strony emulator z 1.5 SDK, uruchamiający obraz 1.5, również ma root, jest prawdopodobnie podobny do Androida Dev Phone 1 (na co prawdopodobnie chcesz zezwolić) i ma to:

cmb@apollo:~$ adb -e shell getprop |grep build
[ro.build.id]: [CUPCAKE]
[ro.build.display.id]: [sdk-eng 1.5 CUPCAKE 148875 test-keys]
[ro.build.version.incremental]: [148875]
[ro.build.version.sdk]: [3]
[ro.build.version.release]: [1.5]
[ro.build.date]: [Thu May 14 18:09:10 PDT 2009]
[ro.build.date.utc]: [1242349750]
[ro.build.type]: [eng]
[ro.build.user]: [android-build]
[ro.build.host]: [undroid16.mtv.corp.google.com]
[ro.build.tags]: [test-keys]
[ro.build.product]: [generic]
[ro.build.description]: [sdk-eng 1.5 CUPCAKE 148875 test-keys]
[ro.build.fingerprint]: [generic/sdk/generic/:1.5/CUPCAKE/148875:eng/test-keys]

Jeśli chodzi o wersje detaliczne, nie mam jednego pod ręką, ale różne wyszukiwania poniżej site:xda-developers.comsą pouczające. Oto G1 w Holandii , widać, że ro.build.tagsnie ma test-keys, i myślę, że jest to prawdopodobnie najbardziej niezawodna właściwość do użycia.

Chris Boyle
źródło
Wygląda to interesująco, ale: Chociaż emulator (i ADP) zezwala na rootowanie jako takie, nie pozwala aplikacjom na korzystanie z niego, tj .: $ su app_29 $ su su: uid 10029 nie wolno su
miracle2k
Ach, przypuszczam, że nie mogliby ... połączyć go z czekiem na ro.build.host (nie) kończącym się na google.com, jeśli tylko oni mają klucze testowe, ale blokują su bez pytając użytkownika. Zależy, jaki jest host kompilacji dla nowszych urządzeń, rzeczy, które nie są telefonami ... nie jest to łatwe.
Chris Boyle,
11

RootBeer to biblioteka systemu Android sprawdzająca rootowanie autorstwa Scotta i Matthew. Wykorzystuje różne kontrole, aby wskazać, czy urządzenie jest zrootowane, czy nie.

Kontrole Java

  • CheckRootManagementApps

  • CheckPotentialDangerousAppss

  • CheckRootCloakingApps

  • CheckTestKeys

  • checkForDangerousProps

  • checkForBusyBoxBinary

  • checkForSuBinary

  • checkSuExists

  • checkForRWSystem

Czeki natywne

Dzwonimy do naszego natywnego programu sprawdzającego roota, aby uruchomić niektóre z własnych testów. Rodzime kontrole są zwykle trudniejsze do ukrycia, więc niektóre aplikacje do maskowania root po prostu blokują ładowanie rodzimych bibliotek zawierających określone słowa kluczowe.

  • checkForSuBinary
Programista Androida
źródło
8

Sugeruję użycie natywnego kodu do wykrywania root. Oto pełny działający przykład .

wprowadź opis zdjęcia tutaj

Owijka JAVA :

package com.kozhevin.rootchecks.util;


import android.support.annotation.NonNull;

import com.kozhevin.rootchecks.BuildConfig;

public class MeatGrinder {
    private final static String LIB_NAME = "native-lib";
    private static boolean isLoaded;
    private static boolean isUnderTest = false;

    private MeatGrinder() {

    }

    public boolean isLibraryLoaded() {
        if (isLoaded) {
            return true;
        }
        try {
            if(isUnderTest) {
                throw new UnsatisfiedLinkError("under test");
            }
            System.loadLibrary(LIB_NAME);
            isLoaded = true;
        } catch (UnsatisfiedLinkError e) {
            if (BuildConfig.DEBUG) {
                e.printStackTrace();
            }
        }
        return isLoaded;
    }

    public native boolean isDetectedDevKeys();

    public native boolean isDetectedTestKeys();

    public native boolean isNotFoundReleaseKeys();

    public native boolean isFoundDangerousProps();

    public native boolean isPermissiveSelinux();

    public native boolean isSuExists();

    public native boolean isAccessedSuperuserApk();

    public native boolean isFoundSuBinary();

    public native boolean isFoundBusyboxBinary();

    public native boolean isFoundXposed();

    public native boolean isFoundResetprop();

    public native boolean isFoundWrongPathPermission();

    public native boolean isFoundHooks();

    @NonNull
    public static MeatGrinder getInstance() {
        return InstanceHolder.INSTANCE;
    }

    private static class InstanceHolder {
        private static final MeatGrinder INSTANCE = new MeatGrinder();
    }
}

Opakowanie JNI (native-lib.c) :

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isDetectedTestKeys(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isDetectedTestKeys();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isDetectedDevKeys(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isDetectedDevKeys();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isNotFoundReleaseKeys(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isNotFoundReleaseKeys();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundDangerousProps(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundDangerousProps();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isPermissiveSelinux(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isPermissiveSelinux();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isSuExists(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isSuExists();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isAccessedSuperuserApk(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isAccessedSuperuserApk();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundSuBinary(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundSuBinary();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundBusyboxBinary(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundBusyboxBinary();
}


JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundXposed(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundXposed();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundResetprop(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundResetprop();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundWrongPathPermission(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundWrongPathPermission();
}

JNIEXPORT jboolean JNICALL
Java_com_kozhevin_rootchecks_util_MeatGrinder_isFoundHooks(
        JNIEnv *env,
        jobject  this ) {

    return (jboolean) isFoundHooks();
}

stałe:

// Comma-separated tags describing the build, like= "unsigned,debug".
const char *const ANDROID_OS_BUILD_TAGS = "ro.build.tags";

// A string that uniquely identifies this build. 'BRAND/PRODUCT/DEVICE:RELEASE/ID/VERSION.INCREMENTAL:TYPE/TAGS'.
const char *const ANDROID_OS_BUILD_FINGERPRINT = "ro.build.fingerprint";

const char *const ANDROID_OS_SECURE = "ro.secure";

const char *const ANDROID_OS_DEBUGGABLE = "ro.debuggable";
const char *const ANDROID_OS_SYS_INITD = "sys.initd";
const char *const ANDROID_OS_BUILD_SELINUX = "ro.build.selinux";
//see https://android.googlesource.com/platform/system/core/+/master/adb/services.cpp#86
const char *const SERVICE_ADB_ROOT = "service.adb.root";

const char * const MG_SU_PATH[] = {
        "/data/local/",
        "/data/local/bin/",
        "/data/local/xbin/",
        "/sbin/",
        "/system/bin/",
        "/system/bin/.ext/",
        "/system/bin/failsafe/",
        "/system/sd/xbin/",
        "/su/xbin/",
        "/su/bin/",
        "/magisk/.core/bin/",
        "/system/usr/we-need-root/",
        "/system/xbin/",
        0
};

const char * const MG_EXPOSED_FILES[] = {
        "/system/lib/libxposed_art.so",
        "/system/lib64/libxposed_art.so",
        "/system/xposed.prop",
        "/cache/recovery/xposed.zip",
        "/system/framework/XposedBridge.jar",
        "/system/bin/app_process64_xposed",
        "/system/bin/app_process32_xposed",
        "/magisk/xposed/system/lib/libsigchain.so",
        "/magisk/xposed/system/lib/libart.so",
        "/magisk/xposed/system/lib/libart-disassembler.so",
        "/magisk/xposed/system/lib/libart-compiler.so",
        "/system/bin/app_process32_orig",
        "/system/bin/app_process64_orig",
        0
};

const char * const MG_READ_ONLY_PATH[] = {
        "/system",
        "/system/bin",
        "/system/sbin",
        "/system/xbin",
        "/vendor/bin",
        "/sbin",
        "/etc",
        0
};

wykrycia root z kodu natywnego:

struct mntent *getMntent(FILE *fp, struct mntent *e, char *buf, int buf_len) {

    while (fgets(buf, buf_len, fp) != NULL) {
        // Entries look like "/dev/block/vda /system ext4 ro,seclabel,relatime,data=ordered 0 0".
        // That is: mnt_fsname mnt_dir mnt_type mnt_opts mnt_freq mnt_passno.
        int fsname0, fsname1, dir0, dir1, type0, type1, opts0, opts1;
        if (sscanf(buf, " %n%*s%n %n%*s%n %n%*s%n %n%*s%n %d %d",
                   &fsname0, &fsname1, &dir0, &dir1, &type0, &type1, &opts0, &opts1,
                   &e->mnt_freq, &e->mnt_passno) == 2) {
            e->mnt_fsname = &buf[fsname0];
            buf[fsname1] = '\0';
            e->mnt_dir = &buf[dir0];
            buf[dir1] = '\0';
            e->mnt_type = &buf[type0];
            buf[type1] = '\0';
            e->mnt_opts = &buf[opts0];
            buf[opts1] = '\0';
            return e;
        }
    }
    return NULL;
}


bool isPresentMntOpt(const struct mntent *pMnt, const char *pOpt) {
    char *token = pMnt->mnt_opts;
    const char *end = pMnt->mnt_opts + strlen(pMnt->mnt_opts);
    const size_t optLen = strlen(pOpt);
    while (token != NULL) {
        const char *tokenEnd = token + optLen;
        if (tokenEnd > end) break;
        if (memcmp(token, pOpt, optLen) == 0 &&
            (*tokenEnd == '\0' || *tokenEnd == ',' || *tokenEnd == '=')) {
            return true;
        }
        token = strchr(token, ',');
        if (token != NULL) {
            token++;
        }
    }
    return false;
}

static char *concat2str(const char *pString1, const char *pString2) {
    char *result;
    size_t lengthBuffer = 0;

    lengthBuffer = strlen(pString1) +
                   strlen(pString2) + 1;
    result = malloc(lengthBuffer);
    if (result == NULL) {
        GR_LOGW("malloc failed\n");
        return NULL;
    }
    memset(result, 0, lengthBuffer);
    strcpy(result, pString1);
    strcat(result, pString2);
    return result;
}

static bool
isBadPropertyState(const char *key, const char *badValue, bool isObligatoryProperty, bool isExact) {
    if (badValue == NULL) {
        GR_LOGE("badValue may not be NULL");
        return false;
    }
    if (key == NULL) {
        GR_LOGE("key may not be NULL");
        return false;
    }
    char value[PROP_VALUE_MAX + 1];
    int length = __system_property_get(key, value);
    bool result = false;
    /* A length 0 value indicates that the property is not defined */
    if (length > 0) {
        GR_LOGI("property:[%s]==[%s]", key, value);
        if (isExact) {
            if (strcmp(value, badValue) == 0) {
                GR_LOGW("bad value[%s] equals to [%s] in the property [%s]", value, badValue, key);
                result = true;
            }
        } else {
            if (strlen(value) >= strlen(badValue) && strstr(value, badValue) != NULL) {
                GR_LOGW("bad value[%s] found in [%s] in the property [%s]", value, badValue, key);
                result = true;
            }
        }
    } else {
        GR_LOGI("[%s] property not found", key);
        if (isObligatoryProperty) {
            result = true;
        }
    }
    return result;
}

bool isDetectedTestKeys() {
    const char *TEST_KEYS_VALUE = "test-keys";
    return isBadPropertyState(ANDROID_OS_BUILD_TAGS, TEST_KEYS_VALUE, true, false);
}

bool isDetectedDevKeys() {
    const char *DEV_KEYS_VALUE = "dev-keys";
    return isBadPropertyState(ANDROID_OS_BUILD_TAGS, DEV_KEYS_VALUE, true, false);
}

bool isNotFoundReleaseKeys() {
    const char *RELEASE_KEYS_VALUE = "release-keys";
    return !isBadPropertyState(ANDROID_OS_BUILD_TAGS, RELEASE_KEYS_VALUE, false, true);
}

bool isFoundWrongPathPermission() {

    bool result = false;
    FILE *file = fopen("/proc/mounts", "r");
    char mntent_strings[BUFSIZ];
    if (file == NULL) {
        GR_LOGE("setmntent");
        return result;
    }

    struct mntent ent = {0};
    while (NULL != getMntent(file, &ent, mntent_strings, sizeof(mntent_strings))) {
        for (size_t i = 0; MG_READ_ONLY_PATH[i]; i++) {
            if (strcmp((&ent)->mnt_dir, MG_READ_ONLY_PATH[i]) == 0 &&
                isPresentMntOpt(&ent, "rw")) {
                GR_LOGI("%s %s %s %s\n", (&ent)->mnt_fsname, (&ent)->mnt_dir, (&ent)->mnt_opts,
                        (&ent)->mnt_type);
                result = true;
                break;
            }
        }
        memset(&ent, 0, sizeof(ent));
    }
    fclose(file);
    return result;
}


bool isFoundDangerousProps() {
    const char *BAD_DEBUGGABLE_VALUE = "1";
    const char *BAD_SECURE_VALUE = "0";
    const char *BAD_SYS_INITD_VALUE = "1";
    const char *BAD_SERVICE_ADB_ROOT_VALUE = "1";

    bool result = isBadPropertyState(ANDROID_OS_DEBUGGABLE, BAD_DEBUGGABLE_VALUE, true, true) ||
                  isBadPropertyState(SERVICE_ADB_ROOT, BAD_SERVICE_ADB_ROOT_VALUE, false, true) ||
                  isBadPropertyState(ANDROID_OS_SECURE, BAD_SECURE_VALUE, true, true) ||
                  isBadPropertyState(ANDROID_OS_SYS_INITD, BAD_SYS_INITD_VALUE, false, true);

    return result;
}

bool isPermissiveSelinux() {
    const char *BAD_VALUE = "0";
    return isBadPropertyState(ANDROID_OS_BUILD_SELINUX, BAD_VALUE, false, false);
}

bool isSuExists() {
    char buf[BUFSIZ];
    char *str = NULL;
    char *temp = NULL;
    size_t size = 1;  // start with size of 1 to make room for null terminator
    size_t strlength;

    FILE *pipe = popen("which su", "r");
    if (pipe == NULL) {
        GR_LOGI("pipe is null");
        return false;
    }

    while (fgets(buf, sizeof(buf), pipe) != NULL) {
        strlength = strlen(buf);
        temp = realloc(str, size + strlength);  // allocate room for the buf that gets appended
        if (temp == NULL) {
            // allocation error
            GR_LOGE("Error (re)allocating memory");
            pclose(pipe);
            if (str != NULL) {
                free(str);
            }
            return false;
        } else {
            str = temp;
        }
        strcpy(str + size - 1, buf);
        size += strlength;
    }
    pclose(pipe);
    GR_LOGW("A size of the result from pipe is [%zu], result:\n [%s] ", size, str);
    if (str != NULL) {
        free(str);
    }
    return size > 1 ? true : false;
}

static bool isAccessedFile(const char *path) {
    int result = access(path, F_OK);
    GR_LOGV("[%s] has been accessed with result: [%d]", path, result);
    return result == 0 ? true : false;
}

static bool isFoundBinaryFromArray(const char *const *array, const char *binary) {
    for (size_t i = 0; array[i]; ++i) {
        char *checkedPath = concat2str(array[i], binary);
        if (checkedPath == NULL) { // malloc failed
            return false;
        }
        bool result = isAccessedFile(checkedPath);
        free(checkedPath);
        if (result) {
            return result;
        }
    }
    return false;
}

bool isAccessedSuperuserApk() {
    return isAccessedFile("/system/app/Superuser.apk");
}

bool isFoundResetprop() {
    return isAccessedFile("/data/magisk/resetprop");
}

bool isFoundSuBinary() {
    return isFoundBinaryFromArray(MG_SU_PATH, "su");
}

bool isFoundBusyboxBinary() {
    return isFoundBinaryFromArray(MG_SU_PATH, "busybox");
}

bool isFoundXposed() {
    for (size_t i = 0; MG_EXPOSED_FILES[i]; ++i) {
        bool result = isAccessedFile(MG_EXPOSED_FILES[i]);
        if (result) {
            return result;
        }
    }
    return false;
}

bool isFoundHooks() {
    bool result = false;
    pid_t pid = getpid();
    char maps_file_name[512];
    sprintf(maps_file_name, "/proc/%d/maps", pid);
    GR_LOGI("try to open [%s]", maps_file_name);
    const size_t line_size = BUFSIZ;
    char *line = malloc(line_size);
    if (line == NULL) {
        return result;
    }
    FILE *fp = fopen(maps_file_name, "r");
    if (fp == NULL) {
        free(line);
        return result;
    }
    memset(line, 0, line_size);
    const char *substrate = "com.saurik.substrate";
    const char *xposed = "XposedBridge.jar";
    while (fgets(line, line_size, fp) != NULL) {
        const size_t real_line_size = strlen(line);
        if ((real_line_size >= strlen(substrate) && strstr(line, substrate) != NULL) ||
            (real_line_size >= strlen(xposed) && strstr(line, xposed) != NULL)) {
            GR_LOGI("found in [%s]: [%s]", maps_file_name, line);
            result = true;
            break;
        }
    }
    free(line);
    fclose(fp);
    return result;
}
Dima Kozhevin
źródło
4
Niesamowite narzędzie, Dima. Wielkie dzięki. Łapie nawet magisk.
ekspert
To jest prawdziwa okazja.
Vahid Amiri,
@klutch jest link do działającego przykładu (github) w pierwszej linii mojego postu
Dima Kozhevin
7

Oto mój kod oparty na niektórych odpowiedziach tutaj:

 /**
   * Checks if the phone is rooted.
   * 
   * @return <code>true</code> if the phone is rooted, <code>false</code>
   * otherwise.
   */
  public static boolean isPhoneRooted() {

    // get from build info
    String buildTags = android.os.Build.TAGS;
    if (buildTags != null && buildTags.contains("test-keys")) {
      return true;
    }

    // check if /system/app/Superuser.apk is present
    try {
      File file = new File("/system/app/Superuser.apk");
      if (file.exists()) {
        return true;
      }
    } catch (Throwable e1) {
      // ignore
    }

    return false;
  }
Dzioby
źródło
7

W odpowiedzi na odpowiedź @Kevins, niedawno odkryłem podczas korzystania z jego systemu, że Nexus 7.1 wraca falsedla wszystkich trzech metod - Bez whichpolecenia, nie test-keysi SuperSUnie został zainstalowany /system/app.

Dodałem to:

public static boolean checkRootMethod4(Context context) {
    return isPackageInstalled("eu.chainfire.supersu", context);     
}

private static boolean isPackageInstalled(String packagename, Context context) {
    PackageManager pm = context.getPackageManager();
    try {
        pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
        return true;
    } catch (NameNotFoundException e) {
        return false;
    }
}

Jest to nieco mniej przydatne w niektórych sytuacjach (jeśli potrzebujesz gwarantowanego dostępu do konta root), ponieważ całkowicie możliwe jest zainstalowanie SuperSU na urządzeniach, które nie mają dostępu do SU.

Ponieważ jednak możliwe jest, aby SuperSU było zainstalowane i działało, ale nie w /system/appkatalogu, ten dodatkowy przypadek wykorzeni (haha) takie przypadki.

Graeme
źródło
To nie jest dobra odpowiedź, ponieważ masz inne pakiety root, które można zainstalować na swoim urządzeniu. Twarde kodowanie innych pakietów aplikacji byłoby trudne, ponieważ nie można się spodziewać i wymienić je wszystkie
blueware
5
    public static boolean isRootAvailable(){
            Process p = null;
            try{
               p = Runtime.getRuntime().exec(new String[] {"su"});
               writeCommandToConsole(p,"exit 0");
               int result = p.waitFor();
               if(result != 0)
                   throw new Exception("Root check result with exit command " + result);
               return true;
            } catch (IOException e) {
                Log.e(LOG_TAG, "Su executable is not available ", e);
            } catch (Exception e) {
                Log.e(LOG_TAG, "Root is unavailable ", e);
            }finally {
                if(p != null)
                    p.destroy();
            }
            return false;
        }
 private static String writeCommandToConsole(Process proc, String command, boolean ignoreError) throws Exception{
            byte[] tmpArray = new byte[1024];
            proc.getOutputStream().write((command + "\n").getBytes());
            proc.getOutputStream().flush();
            int bytesRead = 0;
            if(proc.getErrorStream().available() > 0){
                if((bytesRead = proc.getErrorStream().read(tmpArray)) > 1){
                    Log.e(LOG_TAG,new String(tmpArray,0,bytesRead));
                    if(!ignoreError)
                        throw new Exception(new String(tmpArray,0,bytesRead));
                }
            }
            if(proc.getInputStream().available() > 0){
                bytesRead = proc.getInputStream().read(tmpArray);
                Log.i(LOG_TAG, new String(tmpArray,0,bytesRead));
            }
            return new String(tmpArray);
        }
Kvant
źródło
4

Dwa dodatkowe pomysły, jeśli chcesz sprawdzić, czy urządzenie ma uprawnienia root do twojej aplikacji:

  1. Sprawdź, czy istnieje plik binarny „su”: ​​uruchom „z którego su” Runtime.getRuntime().exec()
  2. Poszukaj SuperUser.apk w /system/app/Superuser.apklokalizacji
Projekt Guardian
źródło
3

Używanie C ++ z ndk jest najlepszym podejściem do wykrywania roota, nawet jeśli użytkownik używa aplikacji, które ukrywają jego root, takich jak RootCloak. Przetestowałem ten kod za pomocą RootCloak i byłem w stanie wykryć root, nawet jeśli użytkownik próbuje go ukryć. Twój plik CPP chciałby:

#include <jni.h>
#include <string>


/**
 *
 * function that checks for the su binary files and operates even if 
 * root cloak is installed
 * @return integer 1: device is rooted, 0: device is not 
 *rooted
*/
extern "C"
JNIEXPORT int JNICALL


Java_com_example_user_root_1native_rootFunction(JNIEnv *env,jobject thiz){
const char *paths[] ={"/system/app/Superuser.apk", "/sbin/su", "/system/bin/su",
                      "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su",
                      "/system/bin/failsafe/su", "/data/local/su", "/su/bin/su"};

int counter =0;
while (counter<9){
    if(FILE *file = fopen(paths[counter],"r")){
        fclose(file);
        return 1;
    }
    counter++;
}
return 0;
}

I wywołasz tę funkcję z kodu Java w następujący sposób

public class Root_detect {



   /**
    *
    * function that calls a native function to check if the device is 
    *rooted or not
    * @return boolean: true if the device is rooted, false if the 
    *device is not rooted
   */
   public boolean check_rooted(){

        int checker = rootFunction();

        if(checker==1){
           return true;
        }else {
           return false;
        }
   }
   static {
    System.loadLibrary("cpp-root-lib");//name of your cpp file
   }

   public native int rootFunction();
}
Sami Kanafani
źródło
1
if [[ "`adb shell which su | grep -io "permission denied"`" != "permission denied" ]]; then
   echo "Yes. Rooted device."
 else
   echo "No. Device not rooted. Only limited tasks can be performed. Done."
    zenity --warning --title="Device Not Rooted" --text="The connected Android Device is <b>NOT ROOTED</b>. Only limited tasks can be performed." --no-wrap
fi
ESSPEE
źródło
1

Istnieje interfejs API Safety Net Attestation usług Google Play pomocą którego możemy ocenić urządzenie i ustalić, czy jest ono zrootowane / naruszone.

Proszę przejść przez moją odpowiedź, aby zająć się urządzeniami zrootowanymi:
https://stackoverflow.com/a/58304556/3908895

Kalpesh Wadekar
źródło
1

Zapomnij o wykrywaniu aplikacji root i plików binarnych Su. Sprawdź proces demona roota. Można to zrobić z terminala i można uruchamiać polecenia terminala w aplikacji. Wypróbuj tę jednowarstwową.

if [ ! -z "$(/system/bin/ps -A | grep -v grep | grep -c daemonsu)" ]; then echo "device is rooted"; else echo "device is not rooted"; fi

Aby to osiągnąć, nie potrzebujesz uprawnień administratora.

5p0ng3b0b
źródło
0

Rzeczywiście jest to interesujące pytanie i jak dotąd nikt nie zasłużył na nagrodę. Używam następującego kodu:

  boolean isRooted() {
      try {
                ServerSocket ss = new ServerSocket(81);
                ss.close();
                                    return true;
            } catch (Exception e) {
                // not sure
            }
    return false;
  }

Kod z pewnością nie jest kuloodporny, ponieważ sieć może być niedostępna, więc otrzymujesz wyjątek. Jeśli ta metoda zwróci wartość true, wówczas 99% możesz być pewien, w przeciwnym razie tylko 50% nie. Zezwolenie na korzystanie z sieci może również zepsuć rozwiązanie.

Singagirl
źródło
Testowałem to i nie zwraca to prawdy na moim zrootowanym urządzeniu.
tricknology
Ciekawie jest zobaczyć, jakiego rodzaju wyjątek się pojawia. Możesz otrzymać wyjątek związany już z portem, jednak jeśli nie możesz utworzyć portu serwera w zakresie poniżej 1024, zmniejsza to wartość rootowania, ponieważ nadal masz pewne ograniczenia.
Singagirl,
-1

Korzystanie z mojej biblioteki w rootbox jest dość łatwe. Sprawdź wymagany kod poniżej:

    //Pass true to <Shell>.start(...) call to run as superuser
    Shell shell = null;
    try {
            shell = Shell.start(true);
    } catch (IOException exception) {
            exception.printStackTrace();
    }
    if (shell == null)
            // We failed to execute su binary
            return;
    if (shell.isRoot()) {
            // Verified running as uid 0 (root), can continue with commands
            ...
    } else
            throw Exception("Unable to gain root access. Make sure you pressed Allow/Grant in superuser prompt.");
VPZ
źródło