【轉】Android root檢測方法總結


一 為什么要進行root檢測?
出於安全原因,我們的應用程序不建議在已經root的設備上運行,所以需要檢測是否設備已經root,以提示用戶若繼續使用會存在風險。

二 root了會有什么風險?
在Linux操作系統中,root的權限是最高的,也被稱為超級權限的擁有者。 
在系統中,每個文件、目錄和進程,都歸屬於某一個用戶,沒有用戶許可其它普通用戶是無法操作的,但對root除外。

root用戶的特權性還表現在:root可以超越任何用戶和用戶組來對文件或目錄進行讀取、修改或刪除(在系統正常的許可范圍內);對可執行程序的執行、終止;對硬件設備的添加、創建和移除等;也可以對文件和目錄進行屬主和權限進行修改,以適合系統管理的需要(因為root是系統中權限最高的特權用戶);root是超越任何用戶和用戶組的,基於用戶ID的權限機制的沙盒是隔離不了它的。

三 不root為什么就安全了呢?
Android安全架構是基於Linux多用戶機制的訪問控制。應用程序在默認的情況下不可以執行其他應用程序,包括讀或寫用戶的私有數據(如聯系人數據或email數據),讀或寫另一個應用程序的文件。

一個應用程序的進程就是一個安全的沙盒(在受限的安全環境中運行應用程序,在沙盒中的所有改動對操作系統不會造成任何危害)。它不能干擾其它應用程序,除非顯式地聲明了“permissions”,以便它能夠獲取基本沙盒所不具備的額外的能力。 
每一個Android應用程序都會在安裝時就分配一個獨有的Linux用戶ID,這就為它建立了一個沙盒,使其不能與其他應用程序進行接觸。這個用戶ID會在安裝時分配給它,並在該設備上一直保持同一個數值。

 所有的Android應用程序必須用證書進行簽名認證,而這個證書的私鑰是由開發者保有的。該證書可以用以識別應用程序的作者。簽名影響安全性的最重要的方式是通過決定誰可以進入基於簽名的permisssions,以及誰可以share 用戶IDs。通過這樣的機制,在不考慮root用戶的情況下,每個應用都是相互隔離的,實現了一定的安全。

四 root的方式有哪些?
通常可以分為2種,不完全Root和完全Root 

目前獲取Android root 權限常用方法是通過各種系統漏洞,替換或添加SU程序到設備,獲取Root權限,而在獲取root權限以后,會裝一個程序用以提醒用戶是否給予程序最高權限,可以一定程度上防止惡意軟件,通常會使用Superuser或者 SuperSU ,這種方法通常叫做“不完全Root”。

而 “完全ROOT”是指,替換設備原有的ROM,以實現取消secure設置。

五 檢測Android設備是否root有哪些方法?
1、查看系統是否是測試版

我們可以查看發布的系統版本,是test-keys(測試版),還是release-keys(正式版)。 
可以先在adb shell中運行下命令查看:

root@android:/ # cat /system/build.prop | grep ro.build.tags
ro.build.tags=release-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;
    }

 


若是非官方發布版,很可能是完全root的版本,存在使用風險。 
可是在實際情況下,我遇到過某些廠家的正式發布版本,也是test-keys,可能大家對這個標識也不是特別注意吧。所以具體是否使用,還要多考慮考慮呢。也許能解決問題,也許會給自己帶來些麻煩。

2、檢查是否存在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;
    }

 


3、檢查su命令

su是Linux下切換用戶的命令,在使用時不帶參數,就是切換到超級用戶。通常我們獲取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();
            }
        }
    }

 


這種檢測su的方法,應該是最靠譜的,不過,也有個問題,就是在已經root的設備上,會彈出提示框,請求給app開啟root權限。這個提示不太友好,可能用戶會不喜歡。 
如果想安靜的檢測,可以用上兩種方法的組合;如果需要盡量安全的檢測到,還是執行su吧。

4、執行busybox

Android是基於Linux系統的,可是在終端Terminal中操作,會發現一些基本的命令都找不到。這是由於Android系統為了安全,將可能帶來風險的命令都去掉了,最典型的,例如su,還有find、mount等。對於一個已經獲取了超級權限的人來講,這是很不爽的事情,所以,便要想辦法加上自己需要的命令了。一個個添加命令也麻煩,有一個很方便的方法,就是使用被稱為“嵌入式Linux中的瑞士軍刀”的Busybox。簡單的說BusyBox就好像是個大工具箱,它集成壓縮了 Linux 的許多工具和命令。 
所以若設備root了,很可能Busybox也被安裝上了。這樣我們運行busybox測試也是一個好的檢測方法。
 

public static synchronized boolean checkBusybox() {
        try {
            Log.i(LOG_TAG, "to exec busybox df");
            String[] strCmd = new String[]{"busybox", "df"};
            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;
            }
        } catch (Exception e) {
            Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
                    + e.getMessage());
            return false;
        }
    }

 

5、訪問/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;
        }
    }

 


六、具體如何使用?
只需調用以下代碼即可

CheckRoot.isDeviceRooted()

 


其中CheckRoot.java的完整代碼如下:

import android.util.Log;
 
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
 
public class CheckRoot {
    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;
    }
 
    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;
    }
 
    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;
    }
 
    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();
            }
        }
    }
 
    public static synchronized boolean checkBusybox() {
        try {
            Log.i(LOG_TAG, "to exec busybox df");
            String[] strCmd = new String[]{"busybox", "df"};
            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;
            }
        } catch (Exception e) {
            Log.i(LOG_TAG, "Unexpected error - Here is what I know: "
                    + e.getMessage());
            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;
    }
 
    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;
        }
    }
}

​​​​
from:https://blog.csdn.net/yonbor605/article/details/83748665


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM