这里有一个类,可以通过三种方式之一来检查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();
}
}
}
su
二进制文件。 - neevek如果您已经在使用 Fabric/Firebase Crashlytics,您可以调用:
CommonUtils.isRooted(context)
这是该方法的当前实现:
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();
}
}
}
public static boolean isEmulator(Context context) {
String androidId = Secure.getString(context.getContentResolver(), "android_id");
return "sdk".equals(Build.PRODUCT) || "google_sdk".equals(Build.PRODUCT) || androidId == null;
}
/system/xbin/su
重命名为/system/xbin/ru
,这种方法会失败。 - Jorge F. Sanchez$ adb shell ps
String commandToExecute = "su";
executeShellCommand(commandToExecute);
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) {
}
}
}
}
2017更新
现在你可以使用Google Safetynet API。SafetyNet API提供了Attestation API,可以帮助您评估应用程序运行的Android环境的安全性和兼容性。
此认证可以帮助确定特定设备是否被篡改或修改过。
Attestation 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,
}
解析此响应可帮助您确定设备是否已经取得了 root 权限。
已取得 root 权限的设备似乎会导致 ctsProfileMatch=false。
您可以在客户端上进行,但建议在服务器端解析响应。 具有 SafetyNet API 的基本客户端服务器架构如下所示:
这里列出的许多答案存在固有问题:
Stericson的RootTools库似乎更可靠地检查root。它还有很多额外的工具和实用程序,因此我强烈推荐它。然而,没有解释如何特别检查root,并且它可能比大多数应用程序更重。
我做了几个实用方法,它们基于RootTools库。如果您只想检查设备上是否有“su”可执行文件,则可以使用以下方法:
public static boolean isRootAvailable(){
for(String pathDir : System.getenv("PATH").split(":")){
if(new File(pathDir, "su").exists()) {
return true;
}
}
return false;
}
这种方法简单地遍历“PATH”环境变量中列出的目录,检查其中是否存在一个“su”文件。
要真正检查root访问权限,必须实际运行“su”命令。如果已安装类似SuperUser的应用程序,则此时可能会请求root访问权限,或者如果已经授予/拒绝了访问权限,则可能显示一个toast指示是否已授予/拒绝了访问权限。一个好的命令是“id”,以便您可以验证用户ID是否为0(即root)。
以下是一个示例方法,用于确定是否已授予root访问权限:
public static boolean isRootGiven(){
if (isRootAvailable()) {
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[]{"su", "-c", "id"});
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String output = in.readLine();
if (output != null && output.toLowerCase().contains("uid=0"))
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (process != null)
process.destroy();
}
}
return false;
}
测试运行“su”命令非常重要,因为一些模拟器预装了“su”可执行文件,但只允许某些用户访问它,比如adb shell。
在尝试运行“su”之前检查“su”可执行文件的存在也很重要,因为Android已知无法正确处理试图运行缺失命令的进程。这些幽灵进程会随着时间的推移增加内存消耗。
在Java级别进行根检查不是一个安全的解决方案。如果您的应用程序有安全问题在Rooted设备上运行,则请使用此解决方案。
Kevin的答案有效,除非手机还安装了像RootCloak这样的应用程序。这些应用程序在手机root后具有Java API的处理功能,并将这些API模拟为返回未root的手机。
我基于Kevin的回答编写了本地级别的代码,它甚至可以与RootCloak一起使用!而且它不会导致任何内存泄漏问题。
#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;
}
在您的Java代码中,您需要创建包装器类RootUtils来进行本地调用。
public boolean checkRooted() {
if( rootUtils.checkRootAccessMethod3() == 0 || rootUtils.checkRootAccessMethod1() == 0 || rootUtils.checkRootAccessMethod2() == 0 )
return true;
return false;
}
http://code.google.com/p/roottools/
如果您不想使用jar文件,请使用以下代码:
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;
}
程序将尝试查找su文件夹:
private static boolean isRooted() {
return findBinary("su");
}
例子:
if (isRooted()) {
textView.setText("Device Rooted");
} else {
textView.setText("Device Unrooted");
}
== true
,这无意义且显得不好看。 - minipifif (isRooted())
检查而不是明确地写 true。最好遵循代码编写模式。 - bluewareRootBeer是由Scott和Matthew开发的一个Android设备root检查库。它使用各种检查来判断设备是否已被root。
Java检查
CheckRootManagementApps
CheckPotentiallyDangerousApps
CheckRootCloakingApps
CheckTestKeys
checkForDangerousProps
checkForBusyBoxBinary
checkForSuBinary
checkSuExists
checkForRWSystem
本地检查
我们通过调用本地root检查器来运行一些自己的检查。本地检查通常更难以伪装,因此一些root伪装应用程序会阻止包含特定关键字的本地库的加载。
- checkForSuBinary