Stellen Sie fest, ob es auf einem gerooteten Gerät ausgeführt wird

292

Meine App verfügt über eine bestimmte Funktionalität, die nur auf einem Gerät funktioniert, auf dem root verfügbar ist. Anstatt diese Funktion bei Verwendung fehlschlagen zu lassen (und dem Benutzer dann eine entsprechende Fehlermeldung anzuzeigen), würde ich es vorziehen, stillschweigend zu prüfen, ob root zuerst verfügbar ist, und wenn nicht, die entsprechenden Optionen zunächst auszublenden .

Gibt es eine Möglichkeit, dies zu tun?

miracle2k
quelle
11
Es gibt keinen zuverlässigen Weg, dies zu tun. Die folgenden Antworten überprüfen allgemeine Merkmale, aber ein bestimmtes Gerät ist möglicherweise nicht auf gemeinsame Weise gerootet. Wenn die Suche nach Root weit verbreitet ist, werden Root-Lösungen wahrscheinlich versuchen, sich zu verstecken. Da sie das Verhalten des Betriebssystems ändern können, haben sie viele Möglichkeiten, dies zu tun.
Chris Stratton
Es ist möglicherweise besser anzuzeigen, dass die Funktion nicht verfügbar ist, da keine Root-Funktionen verfügbar sind, die dem Benutzer mehr Informationen bieten, als die Funktionen Ihrer App auszublenden, was die Gesamterfahrung mehrdeutig macht.
Nick Fox
Funktionieren die folgenden Antworten für Systemless Root ?
Piyush Kukadiya

Antworten:

260

Hier ist eine Klasse, die auf drei Arten nach Root sucht.

/** @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
quelle
8
Wenn zwei Fragen identische Antworten erfordern, handelt es sich in 99% der Fälle um Duplikate. Markieren Sie sie daher als Dupes, anstatt auf beiden die gleiche Antwort zu veröffentlichen. Vielen Dank.
Kev
2
Das mag sein, aber ich möchte Sie nur wissen lassen, dass die Community genaue doppelte Antworten kennzeichnet . Sie sollten Ihre Antworten anpassen und auf die Besonderheiten des OP-Problems eingehen. Das Kopieren und Einfügen von Antworten kann zu Abstimmungen führen.
Kev
9
-1, diese Methode ist nicht praktikabel, da einige Telefone die suBinärdatei enthalten, während sie nicht gerootet sind.
Neevek
12
Ich wollte Sie nur wissen lassen, dass die Fox Digital Copy (Beta) -App Ihren Code fast wörtlich verwendet, einschließlich der Root- und ExecShell-Klassen sowie der checkRootMethod1 / 2/3-Methoden. Fand es sehr amüsant.
Matt Joseph
8
Ich kann sie verklagen, als hätte Fox unzählige andere verklagt?
Kevin Parker
58

Wenn Sie bereits Fabric / Firebase Crashlytics verwenden, können Sie anrufen

CommonUtils.isRooted(context)

Dies ist die aktuelle Implementierung dieser Methode:

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
quelle
Beste Antwort aller Zeiten. Bitte verwenden Sie dies für jede Bibliothek. Auf chinesischen Geräten laufen viele falsch positive Ergebnisse.
Pedro Paulo Amorim
Gibt es bei dieser Methode ein falsches Positiv?
Ehsan Mashhadi
Ich habe dies auf Nexus 5 mit download.chainfire.eu/363/CF-Root/CF-Auto-Root/… getestet. Dieser ist nicht korrekt.
Jeffrey Liu
54

Die RootTools-Bibliothek bietet einfache Methoden zum Überprüfen auf root:

RootTools.isRootAvailable()

Referenz

Komplikationen
quelle
10
isRootAvailable () überprüft nur das Vorhandensein von su im Pfad und einigen anderen fest codierten Verzeichnissen. Ich habe gehört, dass einige Tools zum Entwurzeln su dort belassen werden, sodass dies ein falsches Positiv ergibt.
Bob Whiteman
13
RootTools.isAccessGiven () sucht nicht nur nach root, sondern fordert auch root-Berechtigungen an. Daher gibt ein nicht gerootetes Gerät bei dieser Methode immer false zurück.
Aggregat1166877
2
@ aggregate1166877, Sie haben Recht, aber es ist nicht gut genug. Was ist, wenn ich auf Anfrage keine Root-Berechtigung benötige? Ich möchte nur wissen, ob es verwurzelt ist, benötige aber im Moment keine Root-Berechtigung.
Neevek
4
isAccessGiven () gibt false zurück, wenn der Benutzer die Berechtigung verweigert, obwohl das Gerät gerootet war.
Subair_a
Dies ist die einzige Antwort, die meiner Meinung nach eine Abstimmung wert ist. Schauen Sie sich meine Antwort unten an, wenn Sie etwas Ähnliches möchten, um es einfach zu kopieren und
einzufügen
52

In meiner Anwendung habe ich durch Ausführen des Befehls "su" überprüft, ob das Gerät gerootet ist oder nicht. Aber heute habe ich diesen Teil meines Codes entfernt. Warum?

Weil meine Anwendung zum Speicherkiller wurde. Wie? Lass mich dir meine Geschichte erzählen.

Es gab einige Beschwerden, dass meine Anwendung Geräte verlangsamte (natürlich dachte ich, dass das nicht wahr sein kann). Ich versuchte herauszufinden warum. Also habe ich MAT verwendet, um Heap-Dumps zu erhalten und zu analysieren, und alles schien perfekt zu sein. Aber nachdem ich meine App viele Male neu gestartet hatte, stellte ich fest, dass das Gerät wirklich langsamer wird und das Stoppen meiner Anwendung es nicht schneller machte (es sei denn, ich starte das Gerät neu). Ich habe die Dump-Dateien erneut analysiert, während das Gerät sehr langsam ist. Aber alles war immer noch perfekt für Dump-Dateien. Dann habe ich getan, was zuerst getan werden muss. Ich habe Prozesse aufgelistet.

$ adb shell ps

Überraschen; Es gab viele Prozesse für meine Anwendung (mit dem Prozess-Tag meiner Anwendung im Manifest). Einige von ihnen waren Zombies, andere nicht.

Bei einer Beispielanwendung mit einer einzelnen Aktivität, die nur den Befehl "su" ausführt, wurde mir klar, dass bei jedem Start der Anwendung ein Zombie-Prozess erstellt wird. Zuerst weisen diese Zombies 0 KB zu, aber dann passiert etwas und Zombie-Prozesse enthalten fast die gleichen KBs wie der Hauptprozess meiner Anwendung, und sie wurden zu Standardprozessen.

Auf bugs.sun.com gibt es einen Fehlerbericht für dasselbe Problem: http://bugs.sun.com/view_bug.do?bug_id=6474073 Dies erklärt, wenn kein Befehl gefunden wird. Zombies werden mit der exec () -Methode erstellt . Aber ich verstehe immer noch nicht, warum und wie sie zu Standardprozessen werden und wichtige KBs enthalten können. (Dies passiert nicht immer)

Sie können versuchen, wenn Sie mit dem folgenden Codebeispiel möchten.

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

Einfache Befehlsausführungsmethode;

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) {
            }
        }
    }
}

Um zusammenzufassen; Ich habe keinen Rat für Sie, um festzustellen, ob das Gerät gerootet ist oder nicht. Aber wenn ich Sie wäre, würde ich Runtime.getRuntime (). Exec () nicht verwenden.

Apropos; RootTools.isRootAvailable () verursacht dasselbe Problem.

Devrim
quelle
5
Das ist sehr besorgniserregend. Ich hatte eine verwurzelte Geräteerkennungsklasse, die das Gleiche tat - nachdem ich dies gelesen hatte, bestätigte ich, was oben beschrieben wurde. Gelegentliche Zombie-Prozesse werden zurückgelassen, Geräte verlangsamen sich usw.
AWT
1
Ich bestätige das Problem mit RootTools 3.4 auf einem GT-S5830i Android 2.3.6. Den meisten Zombies wurde Speicher zugewiesen, und das Problem ist systematisch. Ich muss das Gerät nach 3-4 Tests neu starten. Ich empfehle, das Testergebnis in den gemeinsamen Einstellungen zu speichern.
Christus
2
Google empfiehlt jetzt die Verwendung von ProcessBuilder () und des Befehls start ().
EntangledLoops
1
@NickS Interessant, aber welchen Befehl haben Sie gestartet? Ich habe hier nicht das gleiche Problem mit der Ausgabe von Befehlen auf zahlreichen Android-Handys unterschiedlicher API-Ebene von 9 bis 23.
EntangledLoops
1
@EntangledLoops. Danke dir. Ich starte meine eigene Binärdatei und interagiere mit ihr über stdin / stdout. Ich habe erneut überprüft, wie ich es stoppe, und festgestellt, dass ich in einem Fall Process.destroy () verpasst habe. Also keine Zombies.
Nick S
36

Viele der hier aufgeführten Antworten haben inhärente Probleme:

  • Das Überprüfen auf Testschlüssel korreliert mit dem Root-Zugriff, garantiert dies jedoch nicht unbedingt
  • "PATH" -Verzeichnisse sollten von der tatsächlichen Umgebungsvariablen "PATH" abgeleitet werden, anstatt fest codiert zu sein
  • Das Vorhandensein der ausführbaren Datei "su" bedeutet nicht unbedingt, dass das Gerät gerootet wurde
  • Die ausführbare Datei "welche" kann installiert sein oder nicht, und Sie sollten das System nach Möglichkeit ihren Pfad auflösen lassen
  • Nur weil die SuperUser-App auf dem Gerät installiert ist, bedeutet dies nicht, dass das Gerät noch Root-Zugriff hat

Die RootTools- Bibliothek von Stericson scheint legitimer nach Root zu suchen. Es hat auch viele zusätzliche Tools und Dienstprogramme, daher kann ich es nur empfehlen. Es gibt jedoch keine Erklärung dafür, wie speziell nach Root gesucht wird, und es ist möglicherweise etwas schwerer als die meisten Apps wirklich benötigen.

Ich habe einige Dienstprogrammmethoden erstellt, die lose auf der RootTools-Bibliothek basieren. Wenn Sie einfach überprüfen möchten, ob sich die ausführbare Datei "su" auf dem Gerät befindet, können Sie die folgende Methode verwenden:

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

Diese Methode durchläuft einfach die in der Umgebungsvariablen "PATH" aufgeführten Verzeichnisse und prüft, ob in einer von ihnen eine "su" -Datei vorhanden ist.

Um wirklich nach Root-Zugriff zu suchen, muss der Befehl "su" tatsächlich ausgeführt werden. Wenn eine App wie SuperUser installiert ist, fordert sie an dieser Stelle möglicherweise den Root-Zugriff an, oder wenn ihr bereits gewährt / verweigert wurde, wird möglicherweise ein Toast angezeigt, der angibt, ob der Zugriff gewährt / verweigert wurde. Ein guter Befehl zum Ausführen ist "id", damit Sie überprüfen können, ob die Benutzer-ID tatsächlich 0 (root) ist.

Hier ist eine Beispielmethode, um festzustellen, ob Root-Zugriff gewährt wurde:

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;
}

Es ist wichtig, die Ausführung des Befehls "su" tatsächlich zu testen, da in einigen Emulatoren die ausführbare Datei "su" vorinstalliert ist, aber nur bestimmten Benutzern wie der adb-Shell der Zugriff darauf gestattet ist.

Es ist auch wichtig, die Existenz der ausführbaren Datei "su" zu überprüfen, bevor Sie versuchen, sie auszuführen, da bekannt ist, dass Android Prozesse, die versuchen, fehlende Befehle auszuführen, nicht ordnungsgemäß entsorgt. Diese Geisterprozesse können den Speicherverbrauch im Laufe der Zeit erhöhen.

rsimp
quelle
Die isRootAvailable () -Methode funktioniert hervorragend, danke. Ich empfehle jedoch, dies nicht auf dem Hauptthread auszuführen, um eine ANR zu vermeiden, wie z. B. einen Aufruf von einer AsyncTask
Thunderstick
1
Ich denke, es ist der Unterschied zwischen dem Wunsch, sicherzustellen, dass root nicht verfügbar ist, und dem Wunsch, sicherzustellen, dass es verfügbar ist. Wenn Sie sicherstellen möchten, dass ein Gerät nicht gerootet ist, sind die vorgeschlagenen Überprüfungen gut. Sie erhalten Fehlalarme, aber das ist in Ordnung, wenn Sie Ihren Code nicht auf einem kompromittierten Gerät ausführen. Dies ist Ihr Hauptanliegen.
Jeffrey Blattman
1
@ DAC84 Ich bin nicht sicher, ob ich deine Frage verstehe. Wenn Sie isRootGiven ausführen und Ihre Root-Anwendung ablehnen, sollte false zurückgegeben werden. Passiert das nicht? Wenn Sie die Warnung vermeiden möchten, können Sie einfach isRootAvailable verwenden, das auch als doesSUExist bezeichnet werden kann. Sie können auch versuchen, Ihre Root-Anwendung so zu konfigurieren, dass Root frei ausgegeben und nicht verwaltet wird.
Rsimp
1
@BeeingJk nein nicht wirklich, obwohl das wirklich das Beste ist, nach dem Sie suchen können, ohne su auszuführen, was der eigentliche Test ist. Sie müssen jedoch in PATH nach su suchen, bevor Sie versuchen, es auszuführen. Die tatsächliche Ausführung von su führt jedoch häufig zu einer Toastnachricht oder einer Interaktion mit einer Root-Verwaltungs-App, die möglicherweise nicht Ihren Wünschen entspricht. Für Ihre eigene Logik können Sie die bloße Existenz von su für ausreichend halten. Dies kann in einigen Emulatoren immer noch zu Fehlalarmen führen, die möglicherweise eine ausführbare Datei enthalten, aber den Zugriff darauf sperren.
Rsimp
1
@BeeingJk isRootAvailable ist wahrscheinlich alles, was Sie brauchen, aber der Punkt, den ich ansprechen möchte, ist, dass ein solcher Name oder sogar ein doSxx eine bessere Semantik bietet als ein Methodenname wie isDeviceRooted, was nicht ganz richtig ist. Wenn Sie wirklich den vollständigen Root-Zugriff überprüfen müssen, bevor Sie fortfahren, müssen Sie versuchen, einen Befehl mit su auszuführen, wie er in isRootGiven
rsimp
35

Update 2017

Sie können dies jetzt mit der Google Safetynet-API tun . Die SafetyNet-API bietet eine Attestierungs-API, mit der Sie die Sicherheit und Kompatibilität der Android-Umgebungen bewerten können, in denen Ihre Apps ausgeführt werden.

Diese Bescheinigung kann dazu beitragen, festzustellen, ob das jeweilige Gerät manipuliert oder anderweitig geändert wurde.

Die Attestation-API gibt eine solche JWS-Antwort zurück

{
  "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,
}

Durch Analysieren dieser Antwort können Sie feststellen, ob das Gerät gerootet ist oder nicht

Verwurzelte Geräte scheinen ctsProfileMatch = false zu verursachen.

Sie können dies auf der Clientseite tun, es wird jedoch empfohlen, die Antwort auf der Serverseite zu analysieren. Eine grundlegende Client-Server-Architektur mit Sicherheitsnetz-API sieht folgendermaßen aus:

Geben Sie hier die Bildbeschreibung ein

Hitesh Sahu
quelle
3
Hervorragende Informationen, und in einem anderen Kontext glaube ich, dass dies die richtige Antwort wäre. Leider geht es bei der OP-Frage nicht darum, seine App vor unsicheren Umgebungen zu schützen, sondern Root zu erkennen, um Nur-Root-Funktionen in seiner App zu aktivieren. Für den beabsichtigten Zweck der OP scheint dieser Prozess zu komplex zu sein.
Rsimp
31

Root Check auf Java-Ebene ist keine sichere Lösung. Wenn Ihre App Sicherheitsbedenken hat, die auf einem gerooteten Gerät ausgeführt werden sollen, verwenden Sie diese Lösung.

Kevins Antwort funktioniert nur, wenn das Telefon auch eine App wie RootCloak hat. Solche Apps haben eine Handle-over-Java-API, sobald das Telefon gerootet ist, und sie verspotten diese APIs, um zurückzugeben, dass das Telefon nicht gerootet ist.

Ich habe einen Code auf nativer Ebene geschrieben, der auf Kevins Antwort basiert. Er funktioniert sogar mit RootCloak! Außerdem verursacht es keine Speicherverlustprobleme.

#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;

}

In Ihrem Java-Code müssen Sie die Wrapper-Klasse RootUtils erstellen, um die nativen Aufrufe durchzuführen

    public boolean checkRooted() {

       if( rootUtils.checkRootAccessMethod3()  == 0 || rootUtils.checkRootAccessMethod1()  == 0 || rootUtils.checkRootAccessMethod2()  == 0 )
           return true;
      return false;
     }
Alok Kulkarni
quelle
1
Ich denke, die Root-Erkennung fällt in zwei Kategorien, die root-abhängige Funktionen und dann sicherheitsbasierte Maßnahmen ermöglichen, um Sicherheitsprobleme mit gerooteten Telefonen zu mindern. Für wurzelabhängige Funktionen finde ich Kevins Antwort ziemlich schlecht. Im Zusammenhang mit dieser Antwort sind diese Methoden sinnvoller. Obwohl ich Methode 2 umschreiben würde, um nicht welche zu verwenden und stattdessen über die Umgebungsvariable PATH iterieren würde, um nach "su" zu suchen. "welche" ist nicht garantiert am Telefon.
Rsimp
Können Sie bitte ein Beispiel für die Verwendung dieses C-Codes in Java angeben?
Mrid
@mrid Bitte überprüfen Sie, wie Sie JNI-Anrufe von Java unter Android tätigen.
Alok Kulkarni
Diese Methode verhindert die Umgehung der Stammerkennung mithilfe der RootCloak-App. Gibt es bekannte Root-Bypass-Techniken, die diese Methode nicht bestehen?
Nidhin
20

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

Wenn Sie die JAR-Datei nicht verwenden möchten, verwenden Sie einfach den folgenden Code:

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;
    }

Das Programm versucht, den Ordner su zu finden:

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

Beispiel:

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

} else {
   textView.setText("Device Unrooted");
}
noobProgrammer
quelle
Vielen Dank! Ich benutze dies wie checkRootMethod4()bei Kevins Antwort .
Sheharyar
1
Fügen Sie niemals == trueeinen Booleschen Wert hinzu, er fügt nichts hinzu und sieht nicht gut aus.
Minipif
2
@smoothBlue Warum sollte es? Es werden keine Prozesse erzeugt, wie es die Lösung von DevrimTuncer ist.
FD_
1
Eine bessere Idee wäre, über PATH zu iterieren, anstatt typische PATH-Verzeichnisse fest zu codieren
rsimp
1
Verwenden Sie, if (isRooted())überprüfen Sie, anstatt explizit true zu schreiben. Es ist besser, Code-Schreibmuster zu folgen
Blueware
13

Anstelle von isRootAvailable () können Sie isAccessGiven () verwenden. Direkt aus dem RootTools- Wiki :

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

RootTools.isAccessGiven () überprüft nicht nur, ob ein Gerät gerootet ist, sondern ruft auch su für Ihre App auf, fordert die Berechtigung an und gibt true zurück, wenn Ihrer App erfolgreich Root-Berechtigungen erteilt wurden. Dies kann als erste Überprüfung in Ihrer App verwendet werden, um sicherzustellen, dass Sie bei Bedarf Zugriff erhalten.

Referenz

Saulobrito
quelle
aber der Benutzer muss Root-Zugriff geben, oder? Wenn mein Ziel darin bestand, die Ausführung meiner App zu stoppen, wenn das Gerät
gerootet
11

Einige modifizierte Builds, mit denen die Systemeigenschaft ro.modversion für diesen Zweck festgelegt wurde. Die Dinge scheinen sich weiterentwickelt zu haben; Mein Build von TheDude vor ein paar Monaten hat folgendes:

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]

Der Emulator aus dem 1.5 SDK, auf dem das 1.5 Image ausgeführt wird, hat ebenfalls root, ähnelt wahrscheinlich dem Android Dev Phone 1 (das Sie vermutlich zulassen möchten) und hat Folgendes:

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]

Was die Einzelhandelsbauten betrifft, habe ich keine zur Hand, aber verschiedene Suchanfragen unter site:xda-developers.comsind informativ. Hier ist ein G1 in den Niederlanden , Sie können sehen, dass ro.build.tagsdies nicht der Fall ist test-keys, und ich denke, dass dies wahrscheinlich die zuverlässigste Eigenschaft ist, die verwendet werden kann.

Chris Boyle
quelle
Das sieht interessant aus, aber: Während der Emulator (und ADP) Root per se zulassen, erlauben sie Anwendungen nicht, ihn zu verwenden, dh: $ su app_29 $ su su: uid 10029 darf nicht su
miracle2k
Ah, ich nehme an, sie würden nicht ... Sie könnten es mit einem Check für ro.build.host (nicht) kombinieren, der auf google.com endet, wenn sie die einzigen sind, die Testschlüssel haben, aber su ohne blockieren den Benutzer fragen. Kommt darauf an, was der Build-Host für neuere Geräte ist, Dinge, die keine Telefone sind ... nicht einfach.
Chris Boyle
11

RootBeer ist eine Root-Checking-Android-Bibliothek von Scott und Matthew. Es werden verschiedene Überprüfungen verwendet, um anzuzeigen, ob das Gerät gerootet ist oder nicht.

Java prüft

  • CheckRootManagementApps

  • CheckPotentialDangerousAppss

  • CheckRootCloakingApps

  • CheckTestKeys

  • checkForDangerousProps

  • checkForBusyBoxBinary

  • checkForSuBinary

  • checkSuExists

  • checkForRWSystem

Native Checks

Wir rufen unseren nativen Root-Checker an, um einige seiner eigenen Checks durchzuführen. Native Checks sind normalerweise schwieriger zu tarnen, daher blockieren einige Root-Cloak-Apps nur das Laden von nativen Bibliotheken, die bestimmte Schlüsselwörter enthalten.

  • checkForSuBinary
Android-Entwickler
quelle
8

Ich schlage vor, nativen Code für die Stammerkennung zu verwenden. Hier ist ein voll funktionsfähiges Beispiel .

Geben Sie hier die Bildbeschreibung ein

JAVA-Wrapper :

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();
    }
}

JNI-Wrapper (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();
}

Konstanten:

// 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
};

Root-Erkennungen aus nativem Code:

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
quelle
4
Tolles Werkzeug, Dima. Vielen Dank. Es fängt sogar Magisk.
Experte
Das ist der wahre Deal.
Vahid Amiri
@klutch gibt es den Link zum Arbeitsbeispiel (Github) in der ersten Zeile meines Beitrags
Dima Kozhevin
7

Hier ist mein Code basierend auf einigen Antworten hier:

 /**
   * 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;
  }
Peceps
quelle
7

Neben der Antwort von @ Kevin habe ich kürzlich bei der Verwendung seines Systems festgestellt, dass das Nexus 7.1 falsefür alle drei Methoden zurückgegeben wurde - Kein whichBefehl, nein test-keysund SuperSUnicht installiert/system/app .

Ich habe folgendes hinzugefügt:

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;
    }
}

Dies ist in einigen Situationen etwas weniger nützlich (wenn Sie einen garantierten Root-Zugriff benötigen), da SuperSU möglicherweise auf Geräten installiert werden kann, die keinen SU-Zugriff haben.

Da es jedoch möglich ist, SuperSU installiert zu haben und zu funktionieren, jedoch nicht im /system/appVerzeichnis, werden in diesem zusätzlichen Fall solche Fälle verwurzelt (haha).

Graeme
quelle
Dies ist keine gute Antwort, da Sie andere Root-Pakete haben, die auf Ihrem Gerät installiert werden können. Das harte Codieren anderer App-Pakete wäre hart, da Sie nicht alle erwarten und
auflisten
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
quelle
4

Zwei zusätzliche Ideen, wenn Sie überprüfen möchten, ob ein Gerät über Ihre App root-fähig ist:

  1. Überprüfen Sie, ob die Binärdatei 'su' vorhanden ist: Führen Sie "which su" aus Runtime.getRuntime().exec()
  2. Achten Sie auf die SuperUser.apk in /system/app/Superuser.apkLage
Guardian-Projekt
quelle
3

Die Verwendung von C ++ mit dem ndk ist der beste Ansatz, um root zu erkennen, selbst wenn der Benutzer Anwendungen verwendet, die sein root verbergen, wie z. B. RootCloak. Ich habe diesen Code mit RootCloak getestet und konnte den Root erkennen, auch wenn der Benutzer versucht, ihn auszublenden. Ihre CPP-Datei möchte also:

#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;
}

Und Sie werden die Funktion aus Ihrem Java-Code wie folgt aufrufen

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
quelle
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
quelle
1

Es gibt die Safety Net Attestation API von Google Play Services, mit der wir das Gerät bewerten und feststellen können, ob es gerootet / manipuliert ist.

Bitte gehen Sie meine Antwort durch, um sich mit gerooteten Geräten zu befassen:
https://stackoverflow.com/a/58304556/3908895

Kalpesh Wadekar
quelle
1

Vergessen Sie all das Erkennen von Root-Apps und su-Binärdateien. Überprüfen Sie den Root-Daemon-Prozess. Dies kann vom Terminal aus erfolgen und Sie können Terminalbefehle innerhalb einer App ausführen. Probieren Sie diesen Einzeiler.

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

Sie benötigen auch keine Root-Berechtigung, um dies zu erreichen.

5p0ng3b0b
quelle
0

In der Tat ist es eine interessante Frage und bisher hat niemand eine Auszeichnung verdient. Ich benutze den folgenden Code:

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

Der Code ist sicherlich nicht kugelsicher, da das Netzwerk möglicherweise nicht verfügbar ist und Sie eine Ausnahme erhalten. Wenn diese Methode true zurückgibt, können Sie sicher sein, 99%, andernfalls nur 50%. Eine Netzwerkberechtigung kann die Lösung ebenfalls beeinträchtigen.

Singagirl
quelle
Ich habe dies getestet und es wird mit meinem gerooteten Gerät nicht true zurückgegeben.
Tricknologie
Es ist interessant zu sehen, welche Art von Ausnahme Sie bekommen. Möglicherweise wird eine Port-gebundene Ausnahme angezeigt. Wenn Sie jedoch keinen Server-Port in einem Bereich unter 1024 erstellen können, wird der Root-Wert verringert, da Sie dennoch bestimmte Einschränkungen haben.
Singagirl
-1

Mit meiner Bibliothek bei rootbox ist das ziemlich einfach. Überprüfen Sie den erforderlichen Code unten:

    //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
quelle