我的应用程序有一个特定的功能,只能在根可用的设备上工作。与其让这个特性在使用时失败(然后向用户显示适当的错误消息),我更希望能够先静默地检查根目录是否可用,如果不可用,则首先隐藏相应的选项。

有办法做到这一点吗?


当前回答

除了@Kevins的回答之外,我最近在使用他的系统时发现,Nexus 7.1对所有三个方法都返回false——没有哪个命令,没有测试键,SuperSU没有安装在/system/app中。

我补充说:

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

这在某些情况下有点不太有用(如果你需要保证root访问),因为SuperSU完全有可能安装在没有SU访问的设备上。

然而,由于SuperSU可以安装并工作,但不在/system/app目录下,这个额外的情况将会根除(哈哈)这样的情况。

其他回答

忘记所有那些检测根应用程序和子二进制文件。检查根守护进程。这可以从终端完成,你可以在应用程序中运行终端命令。试试这一行。

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

您也不需要root权限来实现这一点。

编辑:现在使用这种方法更好的检测!

if [ $(ps -A | grep -e ^shell -e ^root | grep -v "\[" | tr -s ' ' | cut -d ' ' -f 9 | grep -c su) ] || [ $(which su) ]; then echo 'rooted'; else echo 'not rooted'; fi

使用c++和ndk是最好的方法来检测根,即使用户使用的应用程序隐藏了他的根,如RootCloak。我用RootCloak测试了这段代码,即使用户试图隐藏它,我也能够检测到根。 所以你的cpp文件是这样的:

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

您将从java代码中调用该函数,如下所示

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

下面是一个类,它将以三种方式之一检查Root。

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

使用谷歌SafetyNet认证API,您可以轻松检查您的设备是否已根:

Add dependency in build.gradle(:app) implementation 'com.google.android.gms:play-services-safetynet:17.0.0' Get Api key and enable Android Device Verification API using link public static void sendSafetyNetRequest(Activity context) { if(GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context, 13000000) == ConnectionResult.SUCCESS) { Log.e(TAG, "The SafetyNet Attestation API is available"); // TODO(developer): Change the nonce generation to include your own, used once value, // ideally from your remote server. String nonceData = "Safety Net Sample: " + System.currentTimeMillis(); ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); Random mRandom = new SecureRandom(); byte[] bytes = new byte[24]; mRandom.nextBytes(bytes); try { byteStream.write(bytes); byteStream.write(nonceData.getBytes()); } catch (IOException e) { e.printStackTrace(); } byte[] nonce = byteStream.toByteArray(); SafetyNetClient client = SafetyNet.getClient(context); Task<SafetyNetApi.AttestationResponse> task = client.attest(nonce, API_KEY_FROM_STEP_2_LINK); task.addOnSuccessListener(context, attestationResponse -> { /* TODO(developer): Forward this result to your server together with the nonce for verification. You can also parse the JwsResult locally to confirm that the API returned a response by checking for an 'error' field first and before retrying the request with an exponential backoff. NOTE: Do NOT rely on a local, client-side only check for security, you must verify the response on a remote server! */ String jwsResult = attestationResponse.getJwsResult(); Log.e(TAG, "Success! SafetyNet result:\n" + jwsResult + "\n"); if (jwsResult == null) { Log.e(TAG, "jwsResult Null"); } final String[] jwtParts = jwsResult.split("\\."); if (jwtParts.length == 3) { String decodedPayload = new String(Base64.decode(jwtParts[1], Base64.DEFAULT)); Log.e(TAG, "decodedPayload : " + decodedPayload); } }); task.addOnFailureListener(context, e -> { // An error occurred while communicating with the service. String mResult = null; if (e instanceof ApiException) { // An error with the Google Play Services API contains some additional details. ApiException apiException = (ApiException) e; Util.showLog(TAG, "Error: " + CommonStatusCodes.getStatusCodeString(apiException.getStatusCode()) + ": " + apiException.getStatusMessage()); } else { // A different, unknown type of error occurred. Log.e(TAG, "ERROR! " + e.getMessage()); } }); } else { Log.e(TAG, "Prompt user to update Google Play services."; } } ` Check your logs for decodedPayload if ctsProfileMatch and basicIntegrity both are false it means your device is rooted . The Attestation API returns a JWS response which looks like:

{"nonce": "6pLrr9zWyl6TNzj+kpbR4LZcfPY3U2FmZXR5IE5ldCBTYW1wbGU6IDE2MTQ2NzkwMTIzNjc=", " timestamms ": 9860437986543, "apkPackageName": "你的包名将显示在这里","ctsProfileMatch": true, "apkDigestSha256": ["base64编码,用于签名请求应用程序的证书的SHA-256哈希值"],"basicIntegrity": true, "evaluationType": "BASIC"}

欲了解更多信息,请查看此链接。

更新2017

你现在可以用谷歌Safetynet API做到这一点。SafetyNet API提供了认证API,它可以帮助您评估应用程序运行的Android环境的安全性和兼容性。

这种认证可以帮助确定特定设备是否被篡改或修改过。

认证API返回如下所示的JWS响应

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

解析此响应可以帮助您确定设备是否已扎根

根设备似乎导致ctsProfileMatch=false。

您可以在客户端执行此操作,但建议在服务器端解析响应。 一个基本的客户端服务器架构与安全网API将看起来像这样