转载目的,之前主要应用这里的原理解决了,手机被某个APP检测为root过的手机的问题,记录后续可能参考。

出于安全原因,我们的应用程序不建议在已经root的设备上运行,所以需要检测是否设备已经root,以提示用户若继续使用会存在风险。

那么root了会有什么风险呢,为什么不root就没有风险,又怎么来检查手机是否root了?

我们先来了解下Android安全机制:

Android安全架构是基于Linux多用户机制的访问控制。应用程序在默认的情况下不可以执行其他应用程序,包括读或写用户的私有数据(如联系人数据或email数据),读或写另一个应用程序的文件。
一个应用程序的进程就是一个安全的沙盒(在受限的安全环境中运行应用程序,在沙盒中的所有改动对操作系统不会造成任何危害)。它不能干扰其它应用程序,除非显式地声明了“permissions”,以便它能够获取基本沙盒所不具备的额外的能力。
每一个Android应用程序都会在安装时就分配一个独有的Linux用户ID,这就为它建立了一个沙盒,使其不能与其他应用程序进行接触。这个用户ID会在安装时分配给它,并在该设备上一直保持同一个数值。
所有的Android应用程序必须用证书进行签名认证,而这个证书的私钥是由开发者保有的。该证书可以用以识别应用程序的作者。签名影响安全性的最重要的方式是通过决定谁可以进入基于签名的permisssions,以及谁可以share
用户IDs。通过这样的机制,在不考虑root用户的情况下,每个应用都是相互隔离的,实现了一定的安全。

为什么要把root排除在外,才能说应用的隔离是安全的呢?

在Linux操作系统中,root的权限是最高的,也被称为超级权限的拥有者。
在系统中,每个文件、目录和进程,都归属于某一个用户,没有用户许可其它普通用户是无法操作的,但对root除外。

root用户的特权性还表现在:root可以超越任何用户和用户组来对文件或目录进行读取、修改或删除(在系统正常的许可范围内);对可执行程序的执行、终止;对硬件设备的添加、创建和移除等;也可以对文件和目录进行属主和权限进行修改,以适合系统管理的需要(因为root是系统中权限最高的特权用户);root是超越任何用户和用户组的,基于用户ID的权限机制的沙盒是隔离不了它的。

接下来了解下root的方式

通常可以分为2种:
1,不完全Root
2,完全Root
目前获取Android root
权限常用方法是通过各种系统漏洞,替换或添加SU程序到设备,获取Root权限,而在获取root权限以后,会装一个程序用以提醒用户是否给予程序最高权限,可以一定程度上防止恶意软件,通常会使用Superuser或者
SuperSU ,这种方法通常叫做“不完全Root”。
而 “完全ROOT”是指,替换设备原有的ROM,以实现取消secure设置。

root检测的方法

下面介绍下root检测的各种方法:

1,查看系统是否测试版

我们可以查看发布的系统版本,是test-keys(测试版),还是release-keys(发布版)。
可以先在adb shell中运行下命令查看:

  1. root@android:/ # cat /system/build.prop | grep ro.build.tags
  2. ro.build.tags=release-keys

这个返回结果“release-keys”,代表此系统是正式发布版。
在代码中的检测方法如下:

  1. public static boolean checkDeviceDebuggable(){
  2. String buildTags = android.os.Build.TAGS;
  3. if (buildTags != null && buildTags.contains("test-keys")) {
  4. Log.i(LOG_TAG,"buildTags="+buildTags);
  5. return true;
  6. }
  7. return false;
  8. }

若是非官方发布版,很可能是完全root的版本,存在使用风险。
可是在实际情况下,我遇到过某些厂家的正式发布版本,也是test-keys,可能大家对这个标识也不是特别注意吧。所以具体是否使用,还要多考虑考虑呢。也许能解决问题,也许会给自己带来些麻烦。

2,检查是否存在Superuser.apk

Superuser.apk是一个被广泛使用的用来root安卓设备的软件,所以可以检查这个app是否存在。
检测方法如下:

  1. public static boolean checkSuperuserApk(){
  2. try {
  3. File file = new File("/system/app/Superuser.apk");
  4. if (file.exists()) {
  5. Log.i(LOG_TAG,"/system/app/Superuser.apk exist");
  6. return true;
  7. }
  8. } catch (Exception e) { }
  9. return false;
  10. }

3,检查su命令

su是Linux下切换用户的命令,在使用时不带参数,就是切换到超级用户。通常我们获取root权限,就是使用su命令来实现的,所以可以检查这个命令是否存在。
有三个方法来测试su是否存在:
1)检测在常用目录下是否存在su

  1. public static boolean checkRootPathSU()
  2. {
  3. File f=null;
  4. final String kSuSearchPaths[]={"/system/bin/","/system/xbin/","/system/sbin/","/sbin/","/vendor/bin/"};
  5. try{
  6. for(int i=0;i<kSuSearchPaths.length;i++)
  7. {
  8. f=new File(kSuSearchPaths[i]+"su");
  9. if(f!=null&&f.exists())
  10. {
  11. Log.i(LOG_TAG,"find su in : "+kSuSearchPaths[i]);
  12. return true;
  13. }
  14. }
  15. }catch(Exception e)
  16. {
  17. e.printStackTrace();
  18. }
  19. return false;
  20. }

这个方法是检测常用目录,那么就有可能漏过不常用的目录。
所以就有了第二个方法,直接使用shell下的命令来查找。

2)使用which命令查看是否存在su
which是linux下的一个命令,可以在系统PATH变量指定的路径中搜索某个系统命令的位置并且返回第一个搜索结果。
这里,我们就用它来查找su。

  1. public static boolean checkRootWhichSU() {
  2. String[] strCmd = new String[] {"/system/xbin/which","su"};
  3. ArrayList<String> execResult = executeCommand(strCmd);
  4. if (execResult != null){
  5. Log.i(LOG_TAG,"execResult="+execResult.toString());
  6. return true;
  7. }else{
  8. Log.i(LOG_TAG,"execResult=null");
  9. return false;
  10. }
  11. }

其中调用了一个函数 executeCommand(),是执行linux下的shell命令。具体实现如下:

  1. public static ArrayList<String> executeCommand(String[] shellCmd){
  2. String line = null;
  3. ArrayList<String> fullResponse = new ArrayList<String>();
  4. Process localProcess = null;
  5. try {
  6. Log.i(LOG_TAG,"to shell exec which for find su :");
  7. localProcess = Runtime.getRuntime().exec(shellCmd);
  8. } catch (Exception e) {
  9. return null;
  10. }
  11. BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
  12. BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
  13. try {
  14. while ((line = in.readLine()) != null) {
  15. Log.i(LOG_TAG,"–> Line received: " + line);
  16. fullResponse.add(line);
  17. }
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. Log.i(LOG_TAG,"–> Full response was: " + fullResponse);
  22. return fullResponse;
  23. }

然而,这个方法也存在一个缺陷,就是需要系统中存在which这个命令。我在测试过程中,就遇到有的Android系统中没有这个命令,所以,这也不是一个完全有保障的方法,倒是可以和上一个方法(在常用路径下查找)进行组合,能提升成功率。
这种查找命令的方式,还有一种缺陷,就是可能系统中存在su,但是已经失效的情况。例如,我曾经root过,后来又取消了,就可能出现这种情况:有su这个文件,但是当前设备不是root的。

3)执行su,看能否获取到root权限
由于上面两种查找方法都存在可能查不到的情况,以及有su文件与设备root的差异,所以,有这第三中方法:我们执行这个命令su。这样,系统就会在PATH路径中搜索su,如果找到,就会执行,执行成功后,就是获取到真正的超级权限了。
具体代码如下:

  1. public static synchronized boolean checkGetRootAuth()
  2. {
  3. Process process = null;
  4. DataOutputStream os = null;
  5. try
  6. {
  7. Log.i(LOG_TAG,"to exec su");
  8. process = Runtime.getRuntime().exec("su");
  9. os = new DataOutputStream(process.getOutputStream());
  10. os.writeBytes("exit\n");
  11. os.flush();
  12. int exitValue = process.waitFor();
  13. Log.i(LOG_TAG, "exitValue="+exitValue);
  14. if (exitValue == 0)
  15. {
  16. return true;
  17. } else
  18. {
  19. return false;
  20. }
  21. } catch (Exception e)
  22. {
  23. Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
  24. + e.getMessage());
  25. return false;
  26. } finally
  27. {
  28. try
  29. {
  30. if (os != null)
  31. {
  32. os.close();
  33. }
  34. process.destroy();
  35. } catch (Exception e)
  36. {
  37. e.printStackTrace();
  38. }
  39. }
  40. }

这种检测su的方法,应该是最靠谱的,不过,也有个问题,就是在已经root的设备上,会弹出提示框,请求给app开启root权限。这个提示不太友好,可能用户会不喜欢。
如果想安静的检测,可以用上两种方法的组合;如果需要尽量安全的检测到,还是执行su吧。

4,执行busybox

Android是基于Linux系统的,可是在终端Terminal中操作,会发现一些基本的命令都找不到。这是由于Android系统为了安全,将可能带来风险的命令都去掉了,最典型的,例如su,还有find、mount等。对于一个已经获取了超级权限的人来讲,这是很不爽的事情,所以,便要想办法加上自己需要的命令了。一个个添加命令也麻烦,有一个很方便的方法,就是使用被称为“嵌入式Linux中的瑞士军刀”的Busybox。简单的说BusyBox就好像是个大工具箱,它集成压缩了
Linux 的许多工具和命令。
所以若设备root了,很可能Busybox也被安装上了。这样我们运行busybox测试也是一个好的检测方法。

  1. public static synchronized boolean checkBusybox()
  2. {
  3. try
  4. {
  5. Log.i(LOG_TAG,"to exec busybox df");
  6. String[] strCmd = new String[] {"busybox","df"};
  7. ArrayList<String> execResult = executeCommand(strCmd);
  8. if (execResult != null){
  9. Log.i(LOG_TAG,"execResult="+execResult.toString());
  10. return true;
  11. }else{
  12. Log.i(LOG_TAG,"execResult=null");
  13. return false;
  14. }
  15. } catch (Exception e)
  16. {
  17. Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
  18. + e.getMessage());
  19. return false;
  20. }
  21. }

5,访问/data目录,查看读写权限

在Android系统中,有些目录是普通用户不能访问的,例如 /data、/system、/etc 等。
我们就已/data为例,来进行读写访问。本着谨慎的态度,我是先写入一个文件,然后读出,查看内容是否匹配,若匹配,才认为系统已经root了。

  1. public static synchronized boolean checkAccessRootData()
  2. {
  3. try
  4. {
  5. Log.i(LOG_TAG,"to write /data");
  6. String fileContent = "test_ok";
  7. Boolean writeFlag = writeFile("/data/su_test",fileContent);
  8. if (writeFlag){
  9. Log.i(LOG_TAG,"write ok");
  10. }else{
  11. Log.i(LOG_TAG,"write failed");
  12. }
  13. Log.i(LOG_TAG,"to read /data");
  14. String strRead = readFile("/data/su_test");
  15. Log.i(LOG_TAG,"strRead="+strRead);
  16. if(fileContent.equals(strRead)){
  17. return true;
  18. }else {
  19. return false;
  20. }
  21. } catch (Exception e)
  22. {
  23. Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
  24. + e.getMessage());
  25. return false;
  26. }
  27. }

上面的代码,调用了两个函数:writeFile()写文件,readFile()读文件,下面是具体实现:

  1. //写文件
  2. public static Boolean writeFile(String fileName,String message){
  3. try{
  4. FileOutputStream fout = new FileOutputStream(fileName);
  5. byte [] bytes = message.getBytes();
  6. fout.write(bytes);
  7. fout.close();
  8. return true;
  9. }
  10. catch(Exception e){
  11. e.printStackTrace();
  12. return false;
  13. }
  14. }
  15. //读文件
  16. public static String readFile(String fileName){
  17. File file = new File(fileName);
  18. try {
  19. FileInputStream fis= new FileInputStream(file);
  20. byte[] bytes = new byte[1024];
  21. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  22. int len;
  23. while((len=fis.read(bytes))>0){
  24. bos.write(bytes, 0, len);
  25. }
  26. String result = new String(bos.toByteArray());
  27. Log.i(LOG_TAG, result);
  28. return result;
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. return null;
  32. }
  33. }

这里说句题外话,我最初是想使用shell命令来写文件:

  1. echo "test_ok" > /data/su_test

可是使用executeCommand()来调用执行这个命令,结果连文件都没有创建出来。在多次失败后才想到:应该是这个shell命令涉及到了重定向(例如本例中,将本来应该屏幕输出的信息转而写入文件中),才导致的失败。这个重定向应该是需要写代码获取数据流来自己实现。不过,既然要写代码使用数据流,那么我可以更简单的直接写文件,就没有去尝试用代码来实现重定向了。

小结:

由于每种方法各有其特色与缺陷,所以我最终将这些方法加起来了。注意,检查su的3种方法,不必都使用上,可以选第一二种查找的方法,或者选第三种执行的方法。
组合调用的代码如下:

  1. private static String LOG_TAG = CheckRoot.class.getName();
  2. public static boolean isDeviceRooted() {
  3. if (checkDeviceDebuggable()){return true;}//check buildTags
  4. if (checkSuperuserApk()){return true;}//Superuser.apk
  5. //if (checkRootPathSU()){return true;}//find su in some path
  6. //if (checkRootWhichSU()){return true;}//find su use 'which'
  7. if (checkBusybox()){return true;}//find su use 'which'
  8. if (checkAccessRootData()){return true;}//find su use 'which'
  9. if (checkGetRootAuth()){return true;}//exec su
  10. return false;
  11. }

参考:

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

Android root检测方法小结的更多相关文章

  1. 【转】Android root检测方法总结

    一 为什么要进行root检测?出于安全原因,我们的应用程序不建议在已经root的设备上运行,所以需要检测是否设备已经root,以提示用户若继续使用会存在风险. 二 root了会有什么风险?在Linux ...

  2. Android ViewPager使用方法小结

    android-support-v4.jar 是谷歌提供给我们的一个兼容低版本安卓设备的软件包,里面包囊了只有在 Android 3.0 以上可用的API.而 ViewPager 就是其中之一.利用它 ...

  3. Android PopupWindow使用方法小结

    前几天要用到PopupWindow,一时竟想不起来怎么用,赶紧上网查了查,自己写了个demo,并在此记录一下PopupWindow的用法. 使用场景 PopupWindow,顾名思义,就是弹窗,在很多 ...

  4. android代码格式化方法小结

    转载:http://blog.csdn.net/androidzhaoxiaogang/article/details/7692526 Download the android-formatting. ...

  5. Android手机安全软件的恶意程序检测靠谱吗--LBE安全大师、腾讯手机管家、360手机卫士恶意软件检测方法研究

    转载请注明出处,谢谢. Android系统开放,各大论坛活跃,应用程序分发渠道广泛,这也就为恶意软件的传播提供了良好的环境.好在手机上安装了安全软件,是否能有效的检测出恶意软件呢?下边针对LBE安全大 ...

  6. Android Root原理

    概述:通过阅读本文可以深刻理解Android系统中获得Root权限的方法和原理.本文会详细介绍Root的目的,原理和代码层次的具体实现方法. Android Root介绍: 1. Root目的 手机获 ...

  7. 【转】Root检测与反检测

    0x00背景需要在手机上构建一个环境对root过的设备进行伪装,让设备里面的应用将该设备当成未root的设备.10x01 Root检测手段1.检查已安装的APK包:SuperSU应用程序或者一键roo ...

  8. Android抓包方法(二)之Tcpdump命令+Wireshark

    Android抓包方法(二) 之Tcpdump命令+Wireshark 前言 做前端测试,基本要求会抓包,会分析请求数据包,查看接口是否调用正确,数据返回是否正确,问题产生是定位根本原因等.学会抓包分 ...

  9. 十、Android学习第九天——小结(转)

    (转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 十.Android学习第九天——小结 通过这段时间的学习,今晚上来做个小小 ...

随机推荐

  1. 8.Odoo产品分析 (二) – 商业板块(3) –CRM(2)

    查看Odoo产品分析系列--目录 接上一篇Odoo产品分析 (二) – 商业板块(3) –CRM(1) 4. 设置 在配置–>设置中:    在分析"销售"模块时已经将其他的 ...

  2. ThinkPHP框架知识

    php框架 一.真实项目开发步骤: 多人同时开发项目,协作开发项目.分工合理.效率有提高(代码风格不一样.分工不好) 测试阶段 上线运行 对项目进行维护.修改.升级(单个人维护项目,十分困难,代码风格 ...

  3. 嵌套RecyclerView左右滑动替代自定义view

    以前的左右滑动效果采用自定义scrollview或者linearlayout来实现,recyclerview可以很好的做这个功能,一般的需求就是要么一个独立的左右滑动效果,要么在一个列表里的中间部分一 ...

  4. Android 设计模式对比

    引言: Android框架的发展的过程就是一个不断化繁为简的过程,大家都在研究如何正确方便高效的规范代码.当然这条路也永远不会停止,就像新的芽儿,随着时间的流逝,每天都在长出新的枝叶,每天都在成长.对 ...

  5. C#生成唯一订单号

    今天系统出了一个问题,发现生成的订单号存在重复的情况了,这是要命的bug,不马上解决,就会有投诉了 经过改进后的代码我先简单的放一下,后面在慢慢的写清楚整个流程 string key = " ...

  6. 智能POS相关FAQ

    1.安卓智能POS(一体机)的口碑点餐已知问题: 1.由于口碑的组合套餐接口不稳定,强烈建议商户不要使用组合套餐商品.已开通口碑后付的门店,如果有组合套餐商品,暂时不要使用组合套餐商品:有组合套餐需求 ...

  7. ASP.NET Core 1.0、ASP.NET MVC Core 1.0和Entity Framework Core 1.0

    ASP.NET 5.0 将改名为 ASP.NET Core 1.0 ASP.NET MVC 6  将改名为 ASP.NET MVC Core 1.0 Entity Framework 7.0    将 ...

  8. 【Git学习二】深入了解git checkout命令

    检出命令(git checkout)是Git最常用的命令之一,同时也是一个很危险的命令,因为这条命令会重写工作区.检出命令的用法如下: 用法一:git checkout[-q][<commit& ...

  9. SAP 维护视图创建与修改

    维护视图创建与修改 维护视图创建 T-CODE:SE54 维护ABAP数据字典 维护已生产的对象 注意:当维护视图修改后,需要删除已生成的对象,重新创建已生成的对象,否则无法显示,这个小窍门我花了半天 ...

  10. JavaScript数据类型之文本类型

    引言 字符串(string)是一组由16位值组成的不可变的有序序列,每个字符通常来自于Unicode字符集.JavaScript通过字符串类型来表示文本.字符串的长度(length)是其所含16位值的 ...