在Android中如何判断手机是否Root以及应用是否获取了Root权限,下面我们将对开源项目RootTools的源码进行分析。

RootTools的源码地址:https://github.com/Stericson/RootTools

一、RootTools.isRootAvailable()判断手机是否已经Root
下面RootTools这个类中,RootTools.isRootAvailable()可以判断手机是否已经Root,下面我们来看看它的源码。

public static boolean isRootAvailable()
{return RootShell.isRootAvailable();
}

它实质调用的是RootShell.isRootAvailable()函数,我们接着来看看这个函数的源码。

public static boolean isRootAvailable() {return (findBinary("su")).size() > 0;
}

从上面的代码中我们可以看到它实际是在查找su这个执行文件是否存在,我们知道,我们切换到root用户下,使用的就是su命令,如果这个文件存在,就基本可以知道手机已经Root,因为我们可以运行su命令切换到root用户下。

下面我们来看看RootShell.findBinary(“su”)这个函数。

public static List<String> findBinary(final String binaryName) {return findBinary(binaryName, null);
}public static List<String> findBinary(final String binaryName, List<String> searchPaths) {final List<String> foundPaths = new ArrayList<String>();boolean found = false;// 1、如果搜索路径为空,得到环境变量PATH路径if(searchPaths == null){searchPaths = RootShell.getPath();}RootShell.log("Checking for " + binaryName);// 2、使用了两种方法来检查// 2.1、遍历所有路径,尝试使用stat命令来查看su文件信息//Try to use stat firsttry {for (String path : searchPaths) {if(!path.endsWith("/")){path += "/";}final String currentPath = path;Command cc = new Command(0, false, "stat " + path + binaryName) {// 对执行stat命令后的输出信息进行判断// 如果包含了"File: "和"su"就表示这个路径存在su命令@Overridepublic void commandOutput(int id, String line) {if (line.contains("File: ") && line.contains(binaryName)) {foundPaths.add(currentPath);RootShell.log(binaryName + " was found here: " + currentPath);}RootShell.log(line);super.commandOutput(id, line);}};RootShell.getShell(false).add(cc);commandWait(RootShell.getShell(false), cc);}found = !foundPaths.isEmpty();} catch (Exception e) {RootShell.log(binaryName + " was not found, more information MAY be available with Debugging on.");}// 2.2、如果第一种方法没有找到,就使用下面这种方法// 遍历所有路径,使用ls命令查看su文件是否存在if (!found) {RootShell.log("Trying second method");for (String path : searchPaths) {if(!path.endsWith("/")){path += "/";}if (RootShell.exists(path + binaryName)) {RootShell.log(binaryName + " was found here: " + path);foundPaths.add(path);} else {RootShell.log(binaryName + " was NOT found here: " + path);}}}Collections.reverse(foundPaths);return foundPaths;
}

1、如果搜索路径为空,得到环境变量PATH路径
这一步的关键就是RootShell.getPath()这个函数。我们来重点看看这个函数。

public static List<String> getPath() {return Arrays.asList(System.getenv("PATH").split(":"));
}

我们可以很容易的看出,它首先获取环境变量PATH的值,然后得到PATH里面配置的所有路径。

2、针对上面得到的路径,这些路径可能就是su文件所在的路径,进行遍历,主要提供了两种方法。

2.1、遍历所有路径,尝试使用stat命令来查看su文件信息
(1)使用for循环依次得到每个路径,然后将su文件拼接在后面。例如我们得到一个/system/bin/路径,然后将su拼接在后面就是/system/bin/su。
(2)创建一个”stat /system/bin/su”的Command命令。并且重写了Command的commandOutput方法,这个方法可以得到执行”stat /system/bin/su”的输出信息,然后对输出信息进行比较判断,如果输出信息包含了”File: “和”su”,那么就说明这个路径下面包含了su命令,就将它添加到foundPaths列表中,最终如果foundPaths为空,表示没有找到,如果不为空就表示找到了,如果为空,就使用第二种方法接着查找。

2.2、遍历所有路径,使用ls命令查看su文件是否存在

它的核心就是RootShell.exists(path + binaryName)这个检查函数。

public static boolean exists(final String file) {return exists(file, false);
}public static boolean exists(final String file, boolean isDir) {final List<String> result = new ArrayList<String>();String cmdToExecute = "ls " + (isDir ? "-d " : " ");Command command = new Command(0, false, cmdToExecute + file) {@Overridepublic void commandOutput(int id, String line) {RootShell.log(line);result.add(line);super.commandOutput(id, line);}};try {//Try without root...RootShell.getShell(false).add(command);commandWait(RootShell.getShell(false), command);} catch (Exception e) {return false;}for (String line : result) {if (line.trim().equals(file)) {return true;}}result.clear();try {RootShell.getShell(true).add(command);commandWait(RootShell.getShell(true), command);} catch (Exception e) {return false;}//Avoid concurrent modification...List<String> final_result = new ArrayList<String>();final_result.addAll(result);for (String line : final_result) {if (line.trim().equals(file)) {return true;}}return false;}

可以看到,这个逻辑跟上面2.1的基本一样,就是执行的Command命令不一样,2.1执行的是stat命令,这里执行的是ls命令。

既然这两种方法的思想是一样的,那么下面,我们来看看这个Command命令是如何执行的。

1、创建Command命令

Command cc = new Command(0, false, "stat " + path + binaryName) {@Overridepublic void commandOutput(int id, String line) {if (line.contains("File: ") && line.contains(binaryName)) {foundPaths.add(currentPath);RootShell.log(binaryName + " was found here: " + currentPath);}RootShell.log(line);super.commandOutput(id, line);}
};

2、执行RootShell.getShell(false)

public static Shell getShell(boolean root) throws IOException, TimeoutException, RootDeniedException {return RootShell.getShell(root, 0);
}public static Shell getShell(boolean root, int timeout) throws IOException, TimeoutException, RootDeniedException {return getShell(root, timeout, Shell.defaultContext, 3);
}public static Shell getShell(boolean root, int timeout, Shell.ShellContext shellContext, int retry) throws IOException, TimeoutException, RootDeniedException {if (root) {return Shell.startRootShell(timeout, shellContext, retry);} else {return Shell.startShell(timeout);}
}

我们可以看到,最终执行的是Shell.startShell(timeout)函数。

public static Shell startShell(int timeout) throws IOException, TimeoutException {try {if (Shell.shell == null) {RootShell.log("Starting Shell!");Shell.shell = new Shell("/system/bin/sh", ShellType.NORMAL, ShellContext.NORMAL, timeout);} else {RootShell.log("Using Existing Shell!");}return Shell.shell;} catch (RootDeniedException e) {//Root Denied should never be thrown.throw new IOException();}
}

如果shell为空,就创建一个shell,我们来看看这个创建过程new Shell(“/system/bin/sh”, ShellType.NORMAL, ShellContext.NORMAL, timeout)。

private Shell(String cmd, ShellType shellType, ShellContext shellContext, int shellTimeout) throws IOException, TimeoutException, RootDeniedException {RootShell.log("Starting shell: " + cmd);RootShell.log("Context: " + shellContext.getValue());RootShell.log("Timeout: " + shellTimeout);this.shellType = shellType;this.shellTimeout = shellTimeout > 0 ? shellTimeout : this.shellTimeout;this.shellContext = shellContext;// 1、这里会执行cmd命令,就是上面传过来的"/system/bin/sh"if (this.shellContext == ShellContext.NORMAL) {this.proc = Runtime.getRuntime().exec(cmd);} else {String display = getSuVersion(false);String internal = getSuVersion(true);//only done for root shell...//Right now only SUPERSU supports the --context switchif (isSELinuxEnforcing() &&(display != null) &&(internal != null) &&(display.endsWith("SUPERSU")) &&(Integer.valueOf(internal) >= 190)) {cmd += " --context " + this.shellContext.getValue();} else {RootShell.log("Su binary --context switch not supported!");RootShell.log("Su binary display version: " + display);RootShell.log("Su binary internal version: " + internal);RootShell.log("SELinuxEnforcing: " + isSELinuxEnforcing());}this.proc = Runtime.getRuntime().exec(cmd);}// 2、得到执行cmd命令之后的输入流、输出流和错误流this.inputStream = new BufferedReader(new InputStreamReader(this.proc.getInputStream(), "UTF-8"));this.errorStream = new BufferedReader(new InputStreamReader(this.proc.getErrorStream(), "UTF-8"));this.outputStream = new OutputStreamWriter(this.proc.getOutputStream(), "UTF-8");//3、启动一个Worker线程去不断的读取输入流Worker worker = new Worker(this);worker.start();try {worker.join(this.shellTimeout);if (worker.exit == -911) {try {this.proc.destroy();} catch (Exception e) {}closeQuietly(this.inputStream);closeQuietly(this.errorStream);closeQuietly(this.outputStream);throw new TimeoutException(this.error);}/*** Root access denied?*/else if (worker.exit == -42) {try {this.proc.destroy();} catch (Exception e) {}closeQuietly(this.inputStream);closeQuietly(this.errorStream);closeQuietly(this.outputStream);throw new RootDeniedException("Root Access Denied");}/*** Normal exit*/else {// 4、启动一个线程,向输入流中输入命令Thread si = new Thread(this.input, "Shell Input");si.setPriority(Thread.NORM_PRIORITY);si.start();// 5、启动一个线程,得到执行命令之后输出流的信息Thread so = new Thread(this.output, "Shell Output");so.setPriority(Thread.NORM_PRIORITY);so.start();}} catch (InterruptedException ex) {worker.interrupt();Thread.currentThread().interrupt();throw new TimeoutException();}
}

1、执行”/system/bin/sh”命令

this.proc = Runtime.getRuntime().exec(cmd);

2、得到执行命令之后的输入流、输出流、错误流

this.inputStream = new BufferedReader(new InputStreamReader(this.proc.getInputStream(), "UTF-8"));
this.errorStream = new BufferedReader(new InputStreamReader(this.proc.getErrorStream(), "UTF-8"));
this.outputStream = new OutputStreamWriter(this.proc.getOutputStream(), "UTF-8");

3、启动一个Worker线程去不断的读取输入流

protected static class Worker extends Thread {public int exit = -911;public Shell shell;private Worker(Shell shell) {this.shell = shell;}public void run() {try {shell.outputStream.write("echo Started\n");shell.outputStream.flush();// 这里有一个死循环,不断的读取输入流while (true) {String line = shell.inputStream.readLine();if (line == null) {throw new EOFException();} else if ("".equals(line)) {continue;} else if ("Started".equals(line)) {this.exit = 1;setShellOom();break;}shell.error = "unknown error occurred.";}} catch (IOException e) {exit = -42;if (e.getMessage() != null) {shell.error = e.getMessage();} else {shell.error = "RootAccess denied?.";}}}
}

4、启动一个线程,向输入流中输入命令

我们来看看input这个传入的Runnable参数

private Runnable input = new Runnable() {public void run() {try {while (true) {synchronized (commands) {while (!close && write >= commands.size()) {isExecuting = false;commands.wait();}}if (write >= maxCommands) {while (read != write) {RootShell.log("Waiting for read and write to catch up before cleanup.");}/*** Clean up the commands, stay neat.*/cleanCommands();}// 向outputStream中写入一条命令,也就是sh执行了一条命令if (write < commands.size()) {isExecuting = true;Command cmd = commands.get(write);cmd.startExecution();RootShell.log("Executing: " + cmd.getCommand() + " with context: " + shellContext);outputStream.write(cmd.getCommand());String line = "\necho " + token + " " + totalExecuted + " $?\n";outputStream.write(line);outputStream.flush();write++;totalExecuted++;} else if (close) {isExecuting = false;outputStream.write("\nexit 0\n");outputStream.flush();RootShell.log("Closing shell");return;}}} catch (IOException e) {RootShell.log(e.getMessage(), RootShell.LogLevel.ERROR, e);} catch (InterruptedException e) {RootShell.log(e.getMessage(), RootShell.LogLevel.ERROR, e);} finally {write = 0;closeQuietly(outputStream);}}
};

5、启动一个线程,得到执行命令之后输出流的信息

我们来看看output这个传入的Runnable参数

private Runnable output = new Runnable() {public void run() {try {Command command = null;while (!close || inputStream.ready() || read < commands.size()) {isReading = false;// 从输入流中读出一行String outputLine = inputStream.readLine();isReading = true;if (outputLine == null) {break;}if (command == null) {if (read >= commands.size()) {if (close) {break;}continue;}command = commands.get(read);}int pos = -1;pos = outputLine.indexOf(token);// 最终调用我们Command重新的那个函数,用来处理输出信息if (pos == -1) {command.output(command.id, outputLine);} else if (pos > 0) {command.output(command.id, outputLine.substring(0, pos));}if (pos >= 0) {outputLine = outputLine.substring(pos);String fields[] = outputLine.split(" ");if (fields.length >= 2 && fields[1] != null) {int id = 0;try {id = Integer.parseInt(fields[1]);} catch (NumberFormatException e) {}int exitCode = -1;try {exitCode = Integer.parseInt(fields[2]);} catch (NumberFormatException e) {}if (id == totalRead) {processErrors(command);int iterations = 0;while (command.totalOutput > command.totalOutputProcessed) {if(iterations == 0){iterations++;RootShell.log("Waiting for output to be processed. " + command.totalOutputProcessed + " Of " + command.totalOutput);}try {synchronized (this){this.wait(2000);}} catch (Exception e) {RootShell.log(e.getMessage());}}RootShell.log("Read all output");command.setExitCode(exitCode);command.commandFinished();command = null;read++;totalRead++;continue;}}}}try {proc.waitFor();proc.destroy();} catch (Exception e) {}while (read < commands.size()) {if (command == null) {command = commands.get(read);}if(command.totalOutput < command.totalOutputProcessed){command.terminated("All output not processed!");command.terminated("Did you forget the super.commandOutput call or are you waiting on the command object?");}else{command.terminated("Unexpected Termination.");}command = null;read++;}read = 0;} catch (IOException e) {RootShell.log(e.getMessage(), RootShell.LogLevel.ERROR, e);} finally {closeQuietly(outputStream);closeQuietly(errorStream);closeQuietly(inputStream);RootShell.log("Shell destroyed");isClosed = true;isReading = false;}}
};

二、RootTools.isAccessGiven()判断app是否被授予root权限

public static boolean isAccessGiven()
{return RootShell.isAccessGiven();
}

可以看到它实际调用了RootShell.isAccessGiven()方法,所以我们来看看它的源码。

public static boolean isAccessGiven() {final Set<String> ID = new HashSet<String>();final int IAG = 158;try {RootShell.log("Checking for Root access");Command command = new Command(IAG, false, "id") {@Overridepublic void commandOutput(int id, String line) {if (id == IAG) {ID.addAll(Arrays.asList(line.split(" ")));}super.commandOutput(id, line);}};Shell.startRootShell().add(command);commandWait(Shell.startRootShell(), command);//parse the useridfor (String userid : ID) {RootShell.log(userid);if (userid.toLowerCase().contains("uid=0")) {RootShell.log("Access Given");return true;}}return false;} catch (Exception e) {e.printStackTrace();return false;}
}

可以看到这里的思想跟上面2.1和2.2的也基本相同,都是执行Command命令,这里执行的是id这个命令。它的原理就是如果使用id得到所有的id信息,然后对这些id信息进行判断,如果里面包含”uid=0”就表示应用获取到了Root权限。

参考文章:

检查Android是否已经获取root权限

Android中判断手机是否已经Root

http://bbs.csdn.net/topics/390885158

Android检查手机是否Root以及应用是否获取Root权限相关推荐

  1. 4x root 红米_红米Note 4X root教程 红米Note4X获取root权限的方法

    2017-09-13 11:53:45 红米Note 4X root教程 红米Note4X获取root权限的方法 来源:刷机之家 标签:红米Note 4X,root教程,获取root 刷机之家小编来说 ...

  2. 【转】Android检查手机是否被root

    目前来说Android平台并没有提供能够root检查的工具.但是我们可以通过两种方式来判断 手机里面是否有su文件 这个su文件是不是能够执行 但是这两种检查方式都存在缺点. 第一种存在误测和漏测的情 ...

  3. check root android,Android检查手机是否被root

    目前来说Android平台并没有提供能够root检查的工具.但是我们可以通过两种方式来判断 手机里面是否有su文件 这个su文件是不是能够执行 但是这两种检查方式都存在缺点. 第一种存在误测和漏测的情 ...

  4. 三星 android 5.0 root,三星N9150 Android 5.0 (GALAXY Note Edge 双4G)ROOT教程,一键获取ROOT权限...

    三星N9150 Android 5.0 (GALAXY Note Edge 双4G)怎么ROOT?三星N9150 Android 5.0 (GALAXY Note Edge 双4G) ROOT工具选用 ...

  5. nubia android root权限,中兴NX402 (Nubia Z5 Mini Android 4.2)ROOT教程,一键获取ROOT权限

    中兴NX402 (Nubia Z5 Mini Android 4.2)怎么ROOT?中兴NX402 (Nubia Z5 Mini Android 4.2) ROOT工具选用哪些?如何避免中兴NX402 ...

  6. android 最新漏洞 root,新漏洞可获取root权限 所有安卓机躺枪

    原标题:新漏洞可获取root权限 所有安卓机躺枪 [中关村在线软件资讯]10月25日消息:Android系统的安全问题可以用源源不断来形容,最近一个新的严重漏洞被发现,该漏洞甚至可以获取root权限. ...

  7. nexus6 android 6.0 root,Nexus6 root教程_Nexus6一键获取root权限教程

    这一节主要是来说说有关Nexus6的root教程,这个手机还是真不好root呢,因为这个手机是5.0的安卓系统了,现在国内的很多一键root软件还无法对这个手机进行root呢,今天在这里分享的是国外的 ...

  8. 三星android怎么获取root,三星S8如何获取ROOT权限_三星S8ROOT获取教程

    三星S8如何获取ROOT权限,三星S8ROOT获取教程.今天在这里分享一下咱们的三星Galaxy S8+手机的root教程,这个root教程也是采用卡刷的方式进行root,操作上也简单,因为之前给大家 ...

  9. 红米4A手机刷开发版rom并且获取root权限

    1 bl解锁 Critical partition flashing is not allowed就是因为没有bl解锁. 注册小米账号并且关联手机. 下载bl解锁工具 http://www.miui. ...

最新文章

  1. MySQL登陆时加-U选项
  2. java hdfs 新建目录_如何用java在hdfs中创建一个新目录?
  3. Zookeeper的Windows安装
  4. 华科硕士,一个优秀的数据分析师
  5. 自我分析colly的robots源码
  6. for循环优化_for 循环和while循环区别
  7. 图像正确(相当于这个小项目就要结尾了)
  8. Robots.txt 协议详解及使用说明
  9. Ubuntu12.10 下搭建基于KVM-QEMU的虚拟机环境(十五)
  10. 这些科技大佬们的星座你知道吗
  11. python编程语言继承_如何使用Python继承机制(子类化内置类型)
  12. Hibernate标准查询
  13. 给出问题一个临时解决方案(110224)
  14. 100923G-Por Costel and the Orchard
  15. 向武 清华大学 计算机,哥哥保送弟弟全市第二 双胞胎同时上清华(组图)
  16. C#中导出Excel的单元格属性设置
  17. 使用插入排序、归并排序对链表进行排序
  18. 一种高效的q+1准均匀量化(quasi-uniform quantization)方法及MATLAB实现
  19. word段落每行首字怎么对齐_Word段落首行左右缩进及五种对齐方式
  20. 树莓派pico w点灯

热门文章

  1. 目标检测、图像分割的专业名词
  2. DTI-ATS入门(3):DTI-ATS信息纵览(PART_1)
  3. Quartus||在Ubuntu内安装Quartus Prime Lite版20.1
  4. 今日力推: Android 高仿哔哩哔哩动画客户端 / Android MD版的花瓣网App
  5. 微信小程序做塔防类游戏
  6. 荣耀路由3 简约外观下不简单的千兆路由器
  7. 大数据培训技术之ClickHouse
  8. 大数据技术怎么自学?
  9. 11、软件工程基知识
  10. android动画--小飞机旋转效果