Déterminez si vous exécutez sur un appareil rooté


292

Mon application a une certaine fonctionnalité qui ne fonctionnera que sur un appareil où root est disponible. Plutôt que de voir cette fonctionnalité échouer lorsqu'elle est utilisée (puis afficher un message d'erreur approprié à l'utilisateur), je préférerais avoir la possibilité de vérifier en silence si la racine est disponible en premier, et sinon, masquer les options respectives en premier lieu .

Y a-t-il un moyen de faire cela?


11
Il n'y a aucun moyen fiable de le faire; les réponses ci-dessous vérifient les caractéristiques communes mais un appareil donné peut ne pas être enraciné de manière commune. Si la vérification de root devient courante, les solutions root commenceront probablement à s'efforcer de se cacher. Puisqu'ils peuvent modifier le comportement du système d'exploitation, ils disposent de nombreuses options pour le faire.
Chris Stratton

Il pourrait être préférable d'indiquer que la fonction n'est pas disponible en raison du manque de capacité racine fournissant plus d'informations à l'utilisateur plutôt que de masquer les capacités de votre application, ajoutant une ambiguïté à l'expérience globale.
nick fox

Les réponses ci-dessous fonctionnent-elles pour Systemless Root ?
Piyush Kukadiya

Réponses:


260

Voici une classe qui vérifiera la racine de l'une des trois manières suivantes.

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

8
Si deux questions justifient des réponses identiques, ce sont 99% des doublons de temps, alors signalez comme dupes au lieu de poster la même réponse sur les deux. Merci.
Kev

2
C'est peut-être le cas, mais je vous informe simplement que les réponses exactes en double sont signalées par la communauté. Vous devez adapter vos réponses et répondre aux spécificités du problème du PO. Le copier-coller des réponses risque d'attirer des votes négatifs.
Kev

9
-1, cette méthode n'est pas viable, car certains téléphones incluent le subinaire sans être rooté.
neevek

12
Je voulais juste vous le faire savoir, l'application Fox Digital Copy (Beta) utilise votre code presque mot pour mot, y compris les classes Root et ExecShell, ainsi que les méthodes checkRootMethod1 / 2/3. Je l'ai trouvé très amusant.
Matt Joseph

8
Je peux les poursuivre comme Fox a poursuivi d'innombrables autres?
Kevin Parker

58

Si vous utilisez déjà Fabric / Firebase Crashlytics, vous pouvez appeler

CommonUtils.isRooted(context)

Voici l'implémentation actuelle de cette méthode:

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

La meilleure réponse de tous les temps. Veuillez l'utiliser sur n'importe quelle bibliothèque, il y a beaucoup de faux positifs sur les appareils chinois.
Pedro Paulo Amorim

Y a-t-il des faux positifs dans cette méthode?
Ehsan Mashhadi

J'ai testé cela sur nexus 5 avec download.chainfire.eu/363/CF-Root/CF-Auto-Root/… , celui-ci n'est pas précis.
Jeffrey Liu

54

La bibliothèque RootTools propose des méthodes simples pour vérifier la racine:

RootTools.isRootAvailable()

Référence


10
isRootAvailable () vérifie simplement l'existence de su dans le chemin d'accès et certains autres répertoires codés en dur. J'ai entendu dire que certains outils non enracinés laisseraient Su là, donc cela donnerait un faux positif.
Bob Whiteman

13
RootTools.isAccessGiven () vérifiera non seulement root, mais demandera également l'autorisation root; donc un appareil sans racine retournera toujours faux avec cette méthode.
aggregate1166877

2
@greg1166877, vous avez raison, mais ce n'est pas suffisant, que se passe-t-il si je n'ai pas besoin de l'autorisation root lorsque je le demande? Je veux juste savoir s'il est enraciné, mais je n'ai pas besoin de l'autorisation root pour le moment.
neevek

4
isAccessGiven () renvoie false lorsque l'utilisateur refuse l'autorisation même si le périphérique était enraciné.
subair_a

C'est la seule réponse que je trouve intéressante à voter. Consultez ma réponse ci-dessous si vous voulez quelque chose de similaire à copier-coller ou si vous souhaitez plus de détails
rsimp

52

Dans mon application, je vérifiais si le périphérique était enraciné ou non en exécutant la commande "su". Mais aujourd'hui, j'ai supprimé cette partie de mon code. Pourquoi?

Parce que mon application est devenue un tueur de mémoire. Comment? Permettez-moi de vous raconter mon histoire.

Il y avait quelques plaintes que mon application ralentissait les appareils (bien sûr, je pensais que cela ne pouvait pas être vrai). J'ai essayé de comprendre pourquoi. J'ai donc utilisé MAT pour obtenir des vidages de tas et analyser, et tout semblait parfait. Mais après avoir relancé mon application à plusieurs reprises, je me suis rendu compte que l'appareil devenait vraiment plus lent et l'arrêt de mon application ne le rendait pas plus rapide (à moins que je ne redémarre l'appareil). J'ai à nouveau analysé les fichiers de vidage alors que l'appareil est très lent. Mais tout était toujours parfait pour le fichier de vidage. Ensuite, j'ai fait ce qui devait être fait au début. J'ai énuméré les processus.

$ adb shell ps

Surprize; il y avait de nombreux processus pour mon application (avec la balise de processus de mon application au manifeste). Certains d'entre eux étaient des zombies, d'autres non.

Avec un exemple d'application qui a une seule activité et exécute simplement la commande "su", je me suis rendu compte qu'un processus zombie était créé à chaque lancement d'application. Au début, ces zombies allouent 0 Ko, mais il se passe quelque chose et les processus zombies contiennent presque les mêmes Ko que le processus principal de mon application et ils sont devenus des processus standard.

Il existe un rapport de bogue pour le même problème sur bugs.sun.com: http://bugs.sun.com/view_bug.do?bug_id=6474073 cela explique si la commande n'est pas trouvée, des zombies vont être créés avec la méthode exec () . Mais je ne comprends toujours pas pourquoi et comment peuvent-ils devenir des processus standard et contenir des Ko importants. (Cela ne se produit pas tout le temps)

Vous pouvez essayer si vous le souhaitez avec l'exemple de code ci-dessous;

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

Méthode d'exécution de commande simple;

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

Pour résumer; Je n'ai aucun conseil à vous donner pour déterminer si l'appareil est enraciné ou non. Mais si j'étais vous, je n'utiliserais pas Runtime.getRuntime (). Exec ().

Au fait; RootTools.isRootAvailable () provoque le même problème.


5
C'est très inquiétant. J'avais une classe de détection de périphérique rooté qui faisait la même chose - après avoir lu cela, j'ai confirmé ce que la mer Égée a détaillé ci-dessus. Processus zombies occasionnels laissés pour compte, ralentissements des appareils, etc ...
AWT

1
Je confirme le problème avec RootTools 3.4 sur un android 2.3.6 GT-S5830i. La plupart des zombies ont reçu de la mémoire et le problème est systématique. Je dois redémarrer l'appareil après 3-4 tests. Je recommande d'enregistrer le résultat du test dans les préférences partagées.
Christ

2
Google recommande désormais d' utiliser ProcessBuilder () et la commande start ().
EntangledLoops

1
@NickS Intéressant, mais quelle commande avez-vous lancée? Je n'ai pas le même problème ici en émettant des commandes sur de nombreux téléphones Android de différents niveaux d'API du 9 au 23.
EntangledLoops

1
@EntangledLoops. Je vous remercie. Je lance mon propre binaire et interagis avec lui via stdin / stdout. J'ai vérifié à nouveau comment je l'arrête et j'ai découvert que j'avais raté Process.destroy () dans l'un des cas. Donc, pas de zombies.
Nick S

36

Bon nombre des réponses énumérées ici ont des problèmes inhérents:

  • La vérification des clés de test est corrélée avec l'accès root mais ne le garantit pas nécessairement
  • Les répertoires "PATH" doivent être dérivés de la variable d'environnement "PATH" réelle au lieu d'être codés en dur
  • L'existence de l'exécutable "su" ne signifie pas nécessairement que le périphérique a été rooté
  • L'exécutable "which" peut ou non être installé, et vous devriez laisser le système résoudre son chemin si possible
  • Le fait que l'application SuperUser soit installée sur l'appareil ne signifie pas que l'appareil dispose encore d'un accès root

La bibliothèque RootTools de Stericson semble rechercher la racine plus légitimement. Il dispose également de nombreux outils et utilitaires supplémentaires, je le recommande vivement. Cependant, il n'y a aucune explication sur la façon dont il vérifie spécifiquement la racine, et il peut être un peu plus lourd que la plupart des applications n'en ont vraiment besoin.

J'ai créé quelques méthodes utilitaires qui sont vaguement basées sur la bibliothèque RootTools. Si vous souhaitez simplement vérifier si l'exécutable "su" se trouve sur le périphérique, vous pouvez utiliser la méthode suivante:

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

Cette méthode parcourt simplement les répertoires répertoriés dans la variable d'environnement "PATH" et vérifie s'il existe un fichier "su" dans l'un d'eux.

Afin de vraiment vérifier l'accès root, la commande "su" doit être exécutée. Si une application comme SuperUser est installée, à ce stade, elle peut demander un accès root, ou si son déjà accordé / refusé un toast peut être affiché indiquant si l'accès a été accordé / refusé. Une bonne commande à exécuter est "id" afin que vous puissiez vérifier que l'ID utilisateur est bien 0 (root).

Voici un exemple de méthode pour déterminer si l'accès root a été accordé:

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

Il est important de tester l'exécution de la commande "su" car certains émulateurs ont l'exécutable "su" préinstallé, mais ne permettent à certains utilisateurs d'y accéder que le shell adb.

Il est également important de vérifier l'existence de l'exécutable "su" avant d'essayer de l'exécuter, car Android est connu pour ne pas disposer correctement des processus qui tentent d'exécuter des commandes manquantes. Ces processus fantômes peuvent augmenter la consommation de mémoire au fil du temps.


La méthode isRootAvailable () fonctionne très bien, merci. Je recommande toutefois de ne pas l'exécuter sur le thread principal pour éviter un ANR, tel qu'un appel depuis une AsyncTask
Thunderstick

1
Je pense que c'est la différence entre vouloir s'assurer que root n'est pas disponible et vouloir le faire. Si vous voulez vous assurer qu'un périphérique n'est pas enraciné, les vérifications suggérées sont bonnes. Vous obtiendrez des faux positifs, mais cela ne vous dérange pas lorsque l'exécution de votre code sur un appareil compromis est votre principale préoccupation.
Jeffrey Blattman

1
@ DAC84 Je ne suis pas sûr de comprendre votre question. Si vous exécutez isRootGiven et refusez sur votre application d'enracinement, il devrait retourner false. N'est-ce pas ce qui se passe? Si vous voulez éviter l'alerte, vous pouvez simplement utiliser isRootAvailable qui pourrait également être nommé doesSUExist. Vous pouvez également essayer de configurer votre application racine pour distribuer librement la racine et ne pas la gérer.
rsimp

1
@BeeingJk non pas vraiment, bien que ce soit vraiment le plus que vous pouvez vérifier sans exécuter su qui est le vrai test. Vous devez cependant vérifier su dans PATH avant d'essayer de l'exécuter. Cependant, l'exécution de su entraîne souvent un message toast ou une interaction avec une application de gestion racine qui peut ne pas être ce que vous voulez. Pour votre propre logique, vous pouvez juger la simple existence de su suffisante. Cela peut toujours donner des faux positifs dans certains émulateurs qui peuvent contenir un exécutable su mais un accès de verrouillage à celui-ci.
rsimp

1
@BeeingJk isRootAvailable est probablement tout ce dont vous avez besoin, mais le point que j'essaye de faire est qu'un nom comme ça ou même doesSUExist fournit une meilleure sémantique qu'un nom de méthode comme isDeviceRooted qui n'est pas tout à fait raison. Si vous devez vraiment vérifier l'accès root complet avant de continuer, vous devez essayer d'exécuter une commande avec su comme celle codée dans isRootGiven
rsimp

35

Mise à jour 2017

Vous pouvez le faire maintenant avec l' API Google Safetynet . L'API SafetyNet fournit l'API d'attestation qui vous aide à évaluer la sécurité et la compatibilité des environnements Android dans lesquels vos applications s'exécutent.

Cette attestation peut aider à déterminer si l'appareil particulier a été falsifié ou modifié d'une autre manière.

L'API d'attestation renvoie une réponse JWS comme celle-ci

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

L'analyse de cette réponse peut vous aider à déterminer si le périphérique est enraciné ou non

Les périphériques enracinés semblent provoquer ctsProfileMatch = false.

Vous pouvez le faire côté client mais l'analyse de la réponse côté serveur est recommandée. Une architecture client-serveur de base avec une API de filet de sécurité ressemblera à ceci: -

entrez la description de l'image ici


3
Excellente information, et dans un contexte différent, je pense que ce serait la bonne réponse. Malheureusement, la question des OP ne concerne pas la défense de son application contre les environnements dangereux, mais la détection de root pour activer les fonctionnalités root uniquement dans son application. Aux fins prévues par les PO, ce processus semble trop complexe.
rsimp

31

La vérification des racines au niveau Java n'est pas une solution sûre. Si votre application a des problèmes de sécurité à exécuter sur un appareil rooté, veuillez utiliser cette solution.

La réponse de Kevin fonctionne à moins que le téléphone ne dispose également d'une application comme RootCloak. De telles applications ont une API Handle over Java une fois que le téléphone est enraciné et elles se moquent de ces API pour renvoyer le téléphone n'est pas enraciné.

J'ai écrit un code de niveau natif basé sur la réponse de Kevin, cela fonctionne même avec RootCloak! De plus, cela ne provoque aucun problème de fuite de mémoire.

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

}

Dans votre code Java, vous devez créer la classe wrapper RootUtils pour effectuer les appels natifs

    public boolean checkRooted() {

       if( rootUtils.checkRootAccessMethod3()  == 0 || rootUtils.checkRootAccessMethod1()  == 0 || rootUtils.checkRootAccessMethod2()  == 0 )
           return true;
      return false;
     }

1
Je pense que la détection de la racine se divise en deux catégories, activant les fonctionnalités dépendantes de la racine, puis les mesures basées sur la sécurité pour essayer d'atténuer les problèmes de sécurité avec les téléphones rootés. Pour les fonctionnalités dépendantes de la racine, je trouve que la réponse de Kevin est assez mauvaise. Dans le contexte de cette réponse, ces méthodes ont plus de sens. Bien que je réécrive la méthode 2 pour ne pas utiliser laquelle et au lieu de cela, itérer sur la variable d'environnement PATH pour rechercher "su". "qui" n'est pas garanti d'être au téléphone.
rsimp

pouvez-vous fournir un exemple d'utilisation de ce code c en java?
mrid

@mrid Veuillez vérifier comment passer des appels JNI à partir de Java sur Android.
Alok Kulkarni

Cette méthode empêche le contournement de la détection de racine à l'aide de l'application RootCloak. Existe-t-il des techniques de contournement racine connues qui échouent à cette méthode?
Nidhin

20

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

Si vous ne souhaitez pas utiliser le fichier jar, utilisez simplement le 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;
    }

Le programme essaiera de trouver le dossier su:

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

Exemple:

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

} else {
   textView.setText("Device Unrooted");
}

Merci! J'utilise cela comme checkRootMethod4()avec la réponse de Kevin .
Sheharyar

1
N'ajoutez jamais == trueà un booléen, il n'ajoute rien et n'a pas l'air bien.
minipif

2
@smoothBlue Pourquoi le ferait-il? Il ne génère aucun processus comme la solution DevrimTuncer.
FD_

1
Une meilleure idée serait d'itérer sur PATH, au lieu de coder en dur les répertoires PATH typiques
rsimp

1
Utilisez, if (isRooted())vérifiez au lieu d'écrire explicitement vrai. Il est préférable de suivre les modèles d'écriture de code
blueware

13

Au lieu d'utiliser isRootAvailable (), vous pouvez utiliser isAccessGiven (). Directement à partir du wiki RootTools :

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

RootTools.isAccessGiven () vérifie non seulement qu'un appareil est enraciné, il appelle également su pour votre application, demande l'autorisation et renvoie true si votre application a réussi à obtenir les autorisations root. Cela peut être utilisé comme premier contrôle dans votre application pour vous assurer que vous aurez accès lorsque vous en aurez besoin.

Référence


mais l'utilisateur doit donner un accès root à droite? donc si mon objectif était d'empêcher mon application de fonctionner si l'appareil est enraciné, mes options sont vraiment limitées
Nasz Njoka Sr.

11

Certaines versions modifiées utilisées pour définir la propriété système ro.modversion à cet effet. Les choses semblent avoir évolué; ma construction de TheDude il y a quelques mois a ceci:

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]

L'émulateur du SDK 1.5 d'autre part, exécutant l'image 1.5, a également la racine, est probablement similaire à Android Dev Phone 1 (que vous voulez probablement autoriser) et a ceci:

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]

En ce qui concerne les constructions de vente au détail, je n'en ai pas sous la main, mais diverses recherches sous site:xda-developers.comsont informatives. Voici un G1 aux Pays-Bas , vous pouvez voir qu'il ro.build.tagsn'en a pas test-keys, et je pense que c'est probablement la propriété la plus fiable à utiliser.


Cela semble intéressant, mais: bien que l'émulateur (et ADP) autorise la racine en soi, ils n'autorisent pas les applications à l'utiliser, c'est-à-dire: $ su app_29 $ su su: uid 10029 non autorisé à su
miracle2k

Ah, je suppose qu'ils ne le feraient pas ... vous pouvez le combiner avec une vérification pour ro.build.host (pas) se terminant par google.com alors, s'ils sont les seuls à avoir des clés de test mais à bloquer su sans demander à l'utilisateur. Cela dépend de l'hôte de construction pour les nouveaux appareils, des choses qui ne sont pas des téléphones ... pas facile.
Chris Boyle

11

RootBeer est une bibliothèque Android de vérification de racine par Scott et Matthew. Il utilise diverses vérifications pour indiquer si le périphérique est enraciné ou non.

Vérifications Java

  • CheckRootManagementApps

  • CheckPotiallyDangerousAppss

  • CheckRootCloakingApps

  • CheckTestKeys

  • checkForDangerousProps

  • checkForBusyBoxBinary

  • checkForSuBinary

  • checkSuExists

  • checkForRWSystem

Vérifications natives

Nous appelons notre vérificateur racine natif pour exécuter certains de ses propres contrôles. Les vérifications natives sont généralement plus difficiles à masquer, de sorte que certaines applications de masquage racine bloquent simplement le chargement des bibliothèques natives qui contiennent certains mots clés.

  • checkForSuBinary

8

Je suggère d'utiliser du code natif pour la détection des racines. Voici un exemple de travail complet .

entrez la description de l'image ici

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

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

constantes:

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

détections root du code natif:

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

4
Outil génial, Dima. Merci beaucoup. Il attrape même magisk.
expert

C'est la vraie affaire.
Vahid Amiri

@klutch il y a le lien vers l'exemple de travail (github) à la première ligne de mon message
Dima Kozhevin

7

Voici mon code basé sur quelques réponses ici:

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

7

Suite à la réponse de @Kevins, j'ai récemment découvert en utilisant son système, que le Nexus 7.1 revenait falsepour les trois méthodes - Aucune whichcommande, non test-keyset SuperSUn'était pas installé dans /system/app.

J'ai ajouté ceci:

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

Ceci est légèrement moins utile dans certaines situations (si vous avez besoin d'un accès root garanti) car il est tout à fait possible que SuperSU soit installé sur des appareils qui n'ont pas d'accès SU.

Cependant, puisqu'il est possible d'avoir SuperSU installé et fonctionnel mais pas dans le /system/apprépertoire, ce cas supplémentaire déracinera (haha) de tels cas.


Ce n'est pas une bonne réponse car vous avez d'autres packages racine qui peuvent être installés sur votre appareil. Il serait difficile de coder en dur d'autres packages d'applications, car vous ne pouvez pas vous y attendre et répertoriez-les tous
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);
        }

4

Deux idées supplémentaires, si vous souhaitez vérifier si un appareil est capable de rooter depuis votre application:

  1. Vérifiez l'existant du binaire 'su': exécutez "which su" à partir de Runtime.getRuntime().exec()
  2. Recherchez le SuperUser.apk à l' /system/app/Superuser.apkemplacement

3

L'utilisation de C ++ avec le ndk est la meilleure approche pour détecter root même si l'utilisateur utilise des applications qui cachent sa racine telles que RootCloak. J'ai testé ce code avec RootCloak et j'ai pu détecter la racine même si l'utilisateur essayait de la cacher. Votre fichier cpp souhaite donc:

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

Et vous appellerez la fonction à partir de votre code java comme suit

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

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


1

Oubliez tout ce qui détecte les applications root et les binaires su. Recherchez le processus du démon racine. Cela peut être fait à partir du terminal et vous pouvez exécuter des commandes de terminal dans une application. Essayez cette doublure.

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

Vous n'avez pas non plus besoin de l'autorisation root pour y parvenir.


0

En effet, c'est une question intéressante et jusqu'à présent, personne n'a mérité de récompense. J'utilise le code suivant:

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

Le code n'est certainement pas à l'épreuve des balles, car le réseau peut ne pas être disponible, vous obtenez donc une exception. Si cette méthode retourne vrai, alors 99% vous pouvez être sûr, sinon seulement 50% que non. L'autorisation de mise en réseau peut également gâcher la solution.


J'ai testé cela et cela ne revient pas vrai avec mon appareil enraciné.
tricknology

Il est intéressant de voir quel type d'exception vous obtenez. Vous pouvez obtenir une exception de port déjà lié, mais si vous ne pouvez pas créer de port de serveur dans une plage inférieure à 1024, cela diminue la valeur de l'enracinement, car vous avez toujours certaines limitations.
Singagirl

-1

En utilisant ma bibliothèque sur rootbox , c'est assez facile. Vérifiez le code requis ci-dessous:

    //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.");
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.