Determine if running on a rooted device

后端 未结 24 2246
無奈伤痛
無奈伤痛 2020-11-22 06:43

My app has a certain piece of functionality that will only work on a device where root is available. Rather than having this feature fail when it is used (and then show an a

相关标签:
24条回答
  • 2020-11-22 06:53

    Forget all that detecting root apps and su binaries. Check for the root daemon process. This can be done from the terminal and you can run terminal commands within an app. Try this one-liner.

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

    You don't need root permission to achieve this either.

    0 讨论(0)
  • 2020-11-22 06:54

    Update 2017

    You can do it now with Google Safetynet API. The SafetyNet API provides Attestation API which helps you assess the security and compatibility of the Android environments in which your apps run.

    This attestation can helps to determine whether or not the particular device has been tampered with or otherwise modified.

    The Attestation API returns a JWS response like this

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

    Parsing this response can help you determine if device is rooted or not

    Rooted devices seem to cause ctsProfileMatch=false.

    You can do it on client side but parsing response on server side is recommend. A basic client server archtecture with safety net API will look like this:-

    0 讨论(0)
  • 2020-11-22 06:55

    Instead of using isRootAvailable() you can use isAccessGiven(). Direct from RootTools wiki:

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

    RootTools.isAccessGiven() not only checks that a device is rooted, it also calls su for your app, requests permission, and returns true if your app was successfully granted root permissions. This can be used as the first check in your app to make sure that you will be granted access when you need it.

    Reference

    0 讨论(0)
  • 2020-11-22 06:55

    You can do this by following code :

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

    This solution will work in maximum cases.

    Alternate solution (not advisable to use it due to memory usage)

    boolean rooted=true;
            try {
                  Process process = Runtime.getRuntime().exec("su"); 
                  Toast.makeText(getApplicationContext(), "Device is rooted", Toast.LENGTH_SHORT).show();                
                } 
                catch(IOException e){
                  rooted=false;
                  Toast.makeText(getApplicationContext(), "Device is not rooted", Toast.LENGTH_SHORT).show();
                  e.printStackTrace();
                }
    

    If device is rooted then "su" command will be executed otherwise it will throw an exception, through that we can determine whether device is rooted or not. You can also check this library RootBeer.

    0 讨论(0)
  • 2020-11-22 06:57

    Many of the answers listed here have inherent issues:

    • Checking for test-keys is correlated with root access but doesn't necessarily guarantee it
    • "PATH" directories should be derived from the actual "PATH" environment variable instead of being hard coded
    • The existence of the "su" executable doesn't necessarily mean the device has been rooted
    • The "which" executable may or may not be installed, and you should let the system resolve its path if possible
    • Just because the SuperUser app is installed on the device does not mean the device has root access yet

    The RootTools library from Stericson seems to be checking for root more legitimately. It also has lots of extra tools and utilities so I highly recommend it. However, there's no explanation of how it specifically checks for root, and it may be a bit heavier than most apps really need.

    I've made a couple of utility methods that are loosely based on the RootTools library. If you simply want to check if the "su" executable is on the device you can use the following method:

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

    This method simply loops through the directories listed in the "PATH" environment variable and checks if a "su" file exists in one of them.

    In order to truly check for root access the "su" command must actually be run. If an app like SuperUser is installed, then at this point it may ask for root access, or if its already been granted/denied a toast may be shown indicating whether access was granted/denied. A good command to run is "id" so that you can verify that the user id is in fact 0 (root).

    Here's a sample method to determine whether root access has been granted:

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

    It's important to actually test running the "su" command because some emulators have the "su" executable pre-installed, but only allow certain users to access it like the adb shell.

    It's also important to check for the existence of the "su" executable before trying to run it, because android has been known to not properly dispose of processes that try to run missing commands. These ghost processes can run up memory consumption over time.

    0 讨论(0)
  • 2020-11-22 06:58

    Root check at Java level is not a safe solution. If your app has Security Concerns to run on a Rooted device , then please use this solution.

    Kevin's answer works unless the phone also has an app like RootCloak . Such apps have a Handle over Java APIs once phone is rooted and they mock these APIs to return phone is not rooted.

    I have written a native level code based on Kevin's answer , it works even with RootCloak ! Also it does not cause any memory leak issues.

    #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 your Java code , you need to create wrapper class RootUtils to make the native calls

        public boolean checkRooted() {
    
           if( rootUtils.checkRootAccessMethod3()  == 0 || rootUtils.checkRootAccessMethod1()  == 0 || rootUtils.checkRootAccessMethod2()  == 0 )
               return true;
          return false;
         }
    
    0 讨论(0)
提交回复
热议问题