Android检查手机是否Root以及应用是否获取Root权限
在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权限相关推荐
- 4x root 红米_红米Note 4X root教程 红米Note4X获取root权限的方法
2017-09-13 11:53:45 红米Note 4X root教程 红米Note4X获取root权限的方法 来源:刷机之家 标签:红米Note 4X,root教程,获取root 刷机之家小编来说 ...
- 【转】Android检查手机是否被root
目前来说Android平台并没有提供能够root检查的工具.但是我们可以通过两种方式来判断 手机里面是否有su文件 这个su文件是不是能够执行 但是这两种检查方式都存在缺点. 第一种存在误测和漏测的情 ...
- check root android,Android检查手机是否被root
目前来说Android平台并没有提供能够root检查的工具.但是我们可以通过两种方式来判断 手机里面是否有su文件 这个su文件是不是能够执行 但是这两种检查方式都存在缺点. 第一种存在误测和漏测的情 ...
- 三星 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工具选用 ...
- 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 ...
- android 最新漏洞 root,新漏洞可获取root权限 所有安卓机躺枪
原标题:新漏洞可获取root权限 所有安卓机躺枪 [中关村在线软件资讯]10月25日消息:Android系统的安全问题可以用源源不断来形容,最近一个新的严重漏洞被发现,该漏洞甚至可以获取root权限. ...
- nexus6 android 6.0 root,Nexus6 root教程_Nexus6一键获取root权限教程
这一节主要是来说说有关Nexus6的root教程,这个手机还是真不好root呢,因为这个手机是5.0的安卓系统了,现在国内的很多一键root软件还无法对这个手机进行root呢,今天在这里分享的是国外的 ...
- 三星android怎么获取root,三星S8如何获取ROOT权限_三星S8ROOT获取教程
三星S8如何获取ROOT权限,三星S8ROOT获取教程.今天在这里分享一下咱们的三星Galaxy S8+手机的root教程,这个root教程也是采用卡刷的方式进行root,操作上也简单,因为之前给大家 ...
- 红米4A手机刷开发版rom并且获取root权限
1 bl解锁 Critical partition flashing is not allowed就是因为没有bl解锁. 注册小米账号并且关联手机. 下载bl解锁工具 http://www.miui. ...
最新文章
- MySQL登陆时加-U选项
- java hdfs 新建目录_如何用java在hdfs中创建一个新目录?
- Zookeeper的Windows安装
- 华科硕士,一个优秀的数据分析师
- 自我分析colly的robots源码
- for循环优化_for 循环和while循环区别
- 图像正确(相当于这个小项目就要结尾了)
- Robots.txt 协议详解及使用说明
- Ubuntu12.10 下搭建基于KVM-QEMU的虚拟机环境(十五)
- 这些科技大佬们的星座你知道吗
- python编程语言继承_如何使用Python继承机制(子类化内置类型)
- Hibernate标准查询
- 给出问题一个临时解决方案(110224)
- 100923G-Por Costel and the Orchard
- 向武 清华大学 计算机,哥哥保送弟弟全市第二 双胞胎同时上清华(组图)
- C#中导出Excel的单元格属性设置
- 使用插入排序、归并排序对链表进行排序
- 一种高效的q+1准均匀量化(quasi-uniform quantization)方法及MATLAB实现
- word段落每行首字怎么对齐_Word段落首行左右缩进及五种对齐方式
- 树莓派pico w点灯