转载目的,以前主要应用这里的原理解决了,手机被某个APP检测为root过的手机的问题,记录后续可能参考。html
出于安全缘由,咱们的应用程序不建议在已经root的设备上运行,因此须要检测是否设备已经root,以提示用户若继续使用会存在风险。java
那么root了会有什么风险呢,为何不root就没有风险,又怎么来检查手机是否root了?linux
Android安全架构是基于Linux多用户机制的访问控制。应用程序在默认的状况下不能够执行其余应用程序,包括读或写用户的私有数据(如联系人数据或email数据),读或写另外一个应用程序的文件。
一个应用程序的进程就是一个安全的沙盒(在受限的安全环境中运行应用程序,在沙盒中的全部改动对操做系统不会形成任何危害)。它不能干扰其它应用程序,除非显式地声明了“permissions”,以便它可以获取基本沙盒所不具有的额外的能力。
每个Android应用程序都会在安装时就分配一个独有的Linux用户ID,这就为它创建了一个沙盒,使其不能与其余应用程序进行接触。这个用户ID会在安装时分配给它,并在该设备上一直保持同一个数值。
全部的Android应用程序必须用证书进行签名认证,而这个证书的私钥是由开发者保有的。该证书能够用以识别应用程序的做者。签名影响安全性的最重要的方式是经过决定谁能够进入基于签名的permisssions,以及谁能够share 用户IDs。经过这样的机制,在不考虑root用户的状况下,每一个应用都是相互隔离的,实现了必定的安全。android
为何要把root排除在外,才能说应用的隔离是安全的呢?shell
在Linux操做系统中,root的权限是最高的,也被称为超级权限的拥有者。
在系统中,每一个文件、目录和进程,都归属于某一个用户,没有用户许可其它普通用户是没法操做的,但对root除外。安全
root用户的特权性还表如今:root能够超越任何用户和用户组来对文件或目录进行读取、修改或删除(在系统正常的许可范围内);对可执行程序的执行、终止;对硬件设备的添加、建立和移除等;也能够对文件和目录进行属主和权限进行修改,以适合系统管理的须要(由于root是系统中权限最高的特权用户);root是超越任何用户和用户组的,基于用户ID的权限机制的沙盒是隔离不了它的。ruby
一般能够分为2种:
1,不彻底Root
2,彻底Root
目前获取Android root 权限经常使用方法是经过各类系统漏洞,替换或添加SU程序到设备,获取Root权限,而在获取root权限之后,会装一个程序用以提醒用户是否给予程序最高权限,能够必定程度上防止恶意软件,一般会使用Superuser或者 SuperSU ,这种方法一般叫作“不彻底Root”。
而 “彻底ROOT”是指,替换设备原有的ROM,以实现取消secure设置。markdown
下面介绍下root检测的各类方法:架构
咱们能够查看发布的系统版本,是test-keys(测试版),仍是release-keys(发布版)。
能够先在adb shell中运行下命令查看:app
root@android:/ # cat /system/build.prop | grep ro.build.tags ro.build.tags=release-keys
这个返回结果“release-keys”,表明此系统是正式发布版。
在代码中的检测方法以下:
public static boolean checkDeviceDebuggable(){ String buildTags = android.os.Build.TAGS; if (buildTags != null && buildTags.contains("test-keys")) { Log.i(LOG_TAG,"buildTags="+buildTags); return true; } return false; }
如果非官方发布版,极可能是彻底root的版本,存在使用风险。
但是在实际状况下,我遇到过某些厂家的正式发布版本,也是test-keys,可能你们对这个标识也不是特别注意吧。因此具体是否使用,还要多考虑考虑呢。也许能解决问题,也许会给本身带来些麻烦。
Superuser.apk是一个被普遍使用的用来root安卓设备的软件,因此能够检查这个app是否存在。
检测方法以下:
public static boolean checkSuperuserApk(){ try { File file = new File("/system/app/Superuser.apk"); if (file.exists()) { Log.i(LOG_TAG,"/system/app/Superuser.apk exist"); return true; } } catch (Exception e) { } return false; }
su是Linux下切换用户的命令,在使用时不带参数,就是切换到超级用户。一般咱们获取root权限,就是使用su命令来实现的,因此能够检查这个命令是否存在。
有三个方法来测试su是否存在:
1)检测在经常使用目录下是否存在su
public static boolean checkRootPathSU() { File f=null; final String kSuSearchPaths[]={"/system/bin/","/system/xbin/","/system/sbin/","/sbin/","/vendor/bin/"}; try{ for(int i=0;i<kSuSearchPaths.length;i++) { f=new File(kSuSearchPaths[i]+"su"); if(f!=null&&f.exists()) { Log.i(LOG_TAG,"find su in : "+kSuSearchPaths[i]); return true; } } }catch(Exception e) { e.printStackTrace(); } return false; }
这个方法是检测经常使用目录,那么就有可能漏过不经常使用的目录。
因此就有了第二个方法,直接使用shell下的命令来查找。
2)使用which命令查看是否存在su
which是linux下的一个命令,能够在系统PATH变量指定的路径中搜索某个系统命令的位置而且返回第一个搜索结果。
这里,咱们就用它来查找su。
public static boolean checkRootWhichSU() { String[] strCmd = new String[] {"/system/xbin/which","su"}; ArrayList<String> execResult = executeCommand(strCmd); if (execResult != null){ Log.i(LOG_TAG,"execResult="+execResult.toString()); return true; }else{ Log.i(LOG_TAG,"execResult=null"); return false; } }
其中调用了一个函数 executeCommand(),是执行linux下的shell命令。具体实现以下:
public static ArrayList<String> executeCommand(String[] shellCmd){ String line = null; ArrayList<String> fullResponse = new ArrayList<String>(); Process localProcess = null; try { Log.i(LOG_TAG,"to shell exec which for find su :"); localProcess = Runtime.getRuntime().exec(shellCmd); } catch (Exception e) { return null; } BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream())); BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream())); try { while ((line = in.readLine()) != null) { Log.i(LOG_TAG,"–> Line received: " + line); fullResponse.add(line); } } catch (Exception e) { e.printStackTrace(); } Log.i(LOG_TAG,"–> Full response was: " + fullResponse); return fullResponse; }
然而,这个方法也存在一个缺陷,就是须要系统中存在which这个命令。我在测试过程当中,就遇到有的Android系统中没有这个命令,因此,这也不是一个彻底有保障的方法,却是能够和上一个方法(在经常使用路径下查找)进行组合,能提高成功率。
这种查找命令的方式,还有一种缺陷,就是可能系统中存在su,可是已经失效的状况。例如,我曾经root过,后来又取消了,就可能出现这种状况:有su这个文件,可是当前设备不是root的。
3)执行su,看可否获取到root权限
因为上面两种查找方法都存在可能查不到的状况,以及有su文件与设备root的差别,因此,有这第三中方法:咱们执行这个命令su。这样,系统就会在PATH路径中搜索su,若是找到,就会执行,执行成功后,就是获取到真正的超级权限了。
具体代码以下:
public static synchronized boolean checkGetRootAuth() { Process process = null; DataOutputStream os = null; try { Log.i(LOG_TAG,"to exec su"); process = Runtime.getRuntime().exec("su"); os = new DataOutputStream(process.getOutputStream()); os.writeBytes("exit\n"); os.flush(); int exitValue = process.waitFor(); Log.i(LOG_TAG, "exitValue="+exitValue); if (exitValue == 0) { return true; } else { return false; } } catch (Exception e) { Log.i(LOG_TAG, "Unexpected error - Here is what I know: " + e.getMessage()); return false; } finally { try { if (os != null) { os.close(); } process.destroy(); } catch (Exception e) { e.printStackTrace(); } } }
这种检测su的方法,应该是最靠谱的,不过,也有个问题,就是在已经root的设备上,会弹出提示框,请求给app开启root权限。这个提示不太友好,可能用户会不喜欢。
若是想安静的检测,能够用上两种方法的组合;若是须要尽可能安全的检测到,仍是执行su吧。
Android是基于Linux系统的,但是在终端Terminal中操做,会发现一些基本的命令都找不到。这是因为Android系统为了安全,将可能带来风险的命令都去掉了,最典型的,例如su,还有find、mount等。对于一个已经获取了超级权限的人来说,这是很不爽的事情,因此,便要想办法加上本身须要的命令了。一个个添加命令也麻烦,有一个很方便的方法,就是使用被称为“嵌入式Linux中的瑞士军刀”的Busybox。简单的说BusyBox就好像是个大工具箱,它集成压缩了 Linux 的许多工具和命令。
因此若设备root了,极可能Busybox也被安装上了。这样咱们运行busybox测试也是一个好的检测方法。
public static synchronized boolean checkBusybox() { try { Log.i(LOG_TAG,"to exec busybox df"); String[] strCmd = new String[] {"busybox","df"}; ArrayList<String> execResult = executeCommand(strCmd); if (execResult != null){ Log.i(LOG_TAG,"execResult="+execResult.toString()); return true; }else{ Log.i(LOG_TAG,"execResult=null"); return false; } } catch (Exception e) { Log.i(LOG_TAG, "Unexpected error - Here is what I know: " + e.getMessage()); return false; } }
在Android系统中,有些目录是普通用户不能访问的,例如 /data、/system、/etc 等。
咱们就已/data为例,来进行读写访问。本着谨慎的态度,我是先写入一个文件,而后读出,查看内容是否匹配,若匹配,才认为系统已经root了。
public static synchronized boolean checkAccessRootData() { try { Log.i(LOG_TAG,"to write /data"); String fileContent = "test_ok"; Boolean writeFlag = writeFile("/data/su_test",fileContent); if (writeFlag){ Log.i(LOG_TAG,"write ok"); }else{ Log.i(LOG_TAG,"write failed"); } Log.i(LOG_TAG,"to read /data"); String strRead = readFile("/data/su_test"); Log.i(LOG_TAG,"strRead="+strRead); if(fileContent.equals(strRead)){ return true; }else { return false; } } catch (Exception e) { Log.i(LOG_TAG, "Unexpected error - Here is what I know: " + e.getMessage()); return false; } }
上面的代码,调用了两个函数:writeFile()写文件,readFile()读文件,下面是具体实现:
//写文件 public static Boolean writeFile(String fileName,String message){ try{ FileOutputStream fout = new FileOutputStream(fileName); byte [] bytes = message.getBytes(); fout.write(bytes); fout.close(); return true; } catch(Exception e){ e.printStackTrace(); return false; } } //读文件 public static String readFile(String fileName){ File file = new File(fileName); try { FileInputStream fis= new FileInputStream(file); byte[] bytes = new byte[1024]; ByteArrayOutputStream bos = new ByteArrayOutputStream(); int len; while((len=fis.read(bytes))>0){ bos.write(bytes, 0, len); } String result = new String(bos.toByteArray()); Log.i(LOG_TAG, result); return result; } catch (Exception e) { e.printStackTrace(); return null; } }
这里说句题外话,我最初是想使用shell命令来写文件:
echo "test_ok" > /data/su_test
但是使用executeCommand()来调用执行这个命令,结果连文件都没有建立出来。在屡次失败后才想到:应该是这个shell命令涉及到了重定向(例如本例中,将原本应该屏幕输出的信息转而写入文件中),才致使的失败。这个重定向应该是须要写代码获取数据流来本身实现。不过,既然要写代码使用数据流,那么我能够更简单的直接写文件,就没有去尝试用代码来实现重定向了。
因为每种方法各有其特点与缺陷,因此我最终将这些方法加起来了。注意,检查su的3种方法,没必要都使用上,能够选第一二种查找的方法,或者选第三种执行的方法。
组合调用的代码以下:
private static String LOG_TAG = CheckRoot.class.getName(); public static boolean isDeviceRooted() { if (checkDeviceDebuggable()){return true;}//check buildTags if (checkSuperuserApk()){return true;}//Superuser.apk //if (checkRootPathSU()){return true;}//find su in some path //if (checkRootWhichSU()){return true;}//find su use 'which' if (checkBusybox()){return true;}//find su use 'which' if (checkAccessRootData()){return true;}//find su use 'which' if (checkGetRootAuth()){return true;}//exec su return false; }
http://blog.csdn.net/quanshui540/article/details/48242459
https://blog.netspi.com/android-root-detection-techniques/
http://blog.csdn.net/hudashi/article/details/8091543
http://blog.csdn.net/jia635/article/details/38514101
http://bobao.360.cn/learning/detail/144.html