Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android Root Check #53

Open
yunshuipiao opened this issue May 28, 2019 · 0 comments
Open

Android Root Check #53

yunshuipiao opened this issue May 28, 2019 · 0 comments
Labels

Comments

@yunshuipiao
Copy link
Owner

yunshuipiao commented May 28, 2019

Android Root Check

[TOC]

Android 安全机制

Android 是基于 Linux 多用户机制的访问控制。应用程序在默认的情况下不可以执行其他应用程序,包括读写用户的私有数据。一个应用程序的进程就是一个安全的沙盒(在受限的安全环境中运行应用程序,在沙盒中的所有改动对操作系统不会造成任何危害)。

每一个Android应用程序都会在安装时就分配一个独有的Linux用户ID,这就为它建立了一个沙盒,使其不能与其他应用程序进行接触。这个用户ID会在安装时分配给它,并在该设备上一直保持同一个数值。

所有的Android应用程序必须用证书进行签名认证,而这个证书的私钥是由开发者保有的。该证书可以用以识别应用程序的作者。签名影响安全性的最重要的方式是通过决定谁可以进入基于签名的permisssions,以及谁可以share 用户IDs。通过这样的机制,在不考虑root用户的情况下,每个应用都是相互隔离的,实现了一定的安全。

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

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

root的方式

  • 不完全 root
  • 完全 root

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

而 “完全ROOT”是指,替换设备原有的ROM,以实现取消secure设置。

root检测的方法

查看系统是否测试版

可以查看发布的系统版本,是test-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;

}

实际情况下,某些厂家的正式发布版本,也是test-keys,可能大家对这个标识也不是特别注意吧。所以具体是否使用,要多考虑考虑。

检查是否存在Superuser.apk

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命令

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

检测在常用目录下是否存在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;
    }

执行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();
            }
        }
    }

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

在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;
        }
    }
//写文件
    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;
        }
    }

总结

由于每种方法各有其特色与缺陷,所以综合起来检查。

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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant