携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
安全报告
评测目的:检测 App 程序是否可以在被 Root 的手机设备中运行
危险等级:低
危险:为了获取更大的手机自主使用功能,如卸载应用、禁用自启动程序等,不少用户会将手机进行 Root 处理以获取 Root 权限。Root 权限包括:超越任何用户和用户组来对文件或目 录进行读取、修改或删除;对可执行程序的执行、终止;对硬件设备的添加、创建和移除 等;也可以对文件和目录进行属主和权限进行修改,以适合系统管理的需要。获取 Android 的 Root 权限通常是通过系统漏洞,替换或添加可绕过用户验证的可执行 SU 程序。在给手 机用户赋予 Root 权限的同时,也给了其他应用获取 Root 权限的可能性。恶意程序可能在 用户不知情的情况下申请 Root 权限,读取到其他应用的文件或者进程中的敏感信息,例 如支付宝、手机银行等应用的账号和密码等;或者任意读取手机中的短信记录和联系人信 息;也可能获取到系统权限的重启功能,恶意重启或关闭设备
解决方案
su
是 Linux 下切换用户的命令,在使用时不带参数,就是切换到超级用户。通常我们获取 root 权限,就是使用 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;
}
当 CheckRootPathSU
返回值为 true 时,禁止应用启动应用
这个方法是检测常用目录,那么就有可能漏过不常用的目录。所以就有了第二个方法,直接使用 shell 下的命令来查找
2)使用 which 命令查看是否存在 su
which
是 linux 下的一个命令,可以在系统 PATH 变量指定的路径中搜索某个系统命令的位置并且返回第一个搜索结果,这里,我们就用它来查找 su
//使用which命令查看是否存在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;
}
}
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;
}
CheckRootWhichSU
返回值为 true 时,禁止应用启动
所以综合 1 和 2,我们可以这样判断
//判断设备是否root
public static boolean isDeviceRooted() {
//find su in some path
if (checkRootPathSU()) {
return true;
}
//find su use 'which'
if (checkRootWhichSU()) {
return true;
}
return false;
}
在启动页 SplashActivity.java 的 onCreate
时,判断,如果是 root 设备弹出提示即可
//Root设备下不能运行
if(Utils.isDeviceRooted()){
showNormalDialog();
return;
}
private void showNormalDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("运行提示");
builder.setMessage("检测到当前运行环境存在安全风险,无法使用。");
builder.setPositiveButton("确定", (dialogInterface, i) -> finish());
builder.setCancelable(false);
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
效果如下: