轉載目的,以前主要應用這裏的原理解決了,手機被某個APP檢測爲root過的手機的問題,記錄後續可能參考。html
出於安全緣由,咱們的應用程序不建議在已經root的設備上運行,因此須要檢測是否設備已經root,以提示用戶若繼續使用會存在風險。java
那麼root了會有什麼風險呢,爲何不root就沒有風險,又怎麼來檢查手機是否root了?linux
Android安全架構是基於Linux多用戶機制的訪問控制。應用程序在默認的狀況下不能夠執行其餘應用程序,包括讀或寫用戶的私有數據(如聯繫人數據或email數據),讀或寫另外一個應用程序的文件。
一個應用程序的進程就是一個安全的沙盒(在受限的安全環境中運行應用程序,在沙盒中的全部改動對操做系統不會形成任何危害)。它不能干擾其它應用程序,除非顯式地聲明瞭「permissions」,以便它可以獲取基本沙盒所不具有的額外的能力。
每個Android應用程序都會在安裝時就分配一個獨有的Linux用戶ID,這就爲它創建了一個沙盒,使其不能與其餘應用程序進行接觸。這個用戶ID會在安裝時分配給它,並在該設備上一直保持同一個數值。
全部的Android應用程序必須用證書進行簽名認證,而這個證書的私鑰是由開發者保有的。該證書能夠用以識別應用程序的做者。簽名影響安全性的最重要的方式是經過決定誰能夠進入基於簽名的permisssions,以及誰能夠share 用戶IDs。經過這樣的機制,在不考慮root用戶的狀況下,每一個應用都是相互隔離的,實現了必定的安全。android
爲何要把root排除在外,才能說應用的隔離是安全的呢?shell
在Linux操做系統中,root的權限是最高的,也被稱爲超級權限的擁有者。
在系統中,每一個文件、目錄和進程,都歸屬於某一個用戶,沒有用戶許可其它普通用戶是沒法操做的,但對root除外。安全
root用戶的特權性還表如今:root能夠超越任何用戶和用戶組來對文件或目錄進行讀取、修改或刪除(在系統正常的許可範圍內);對可執行程序的執行、終止;對硬件設備的添加、建立和移除等;也能夠對文件和目錄進行屬主和權限進行修改,以適合系統管理的須要(由於root是系統中權限最高的特權用戶);root是超越任何用戶和用戶組的,基於用戶ID的權限機制的沙盒是隔離不了它的。ruby
一般能夠分爲2種:
1,不徹底Root
2,徹底Root
目前獲取Android root 權限經常使用方法是經過各類系統漏洞,替換或添加SU程序到設備,獲取Root權限,而在獲取root權限之後,會裝一個程序用以提醒用戶是否給予程序最高權限,能夠必定程度上防止惡意軟件,一般會使用Superuser或者 SuperSU ,這種方法一般叫作「不徹底Root」。
而 「徹底ROOT」是指,替換設備原有的ROM,以實現取消secure設置。markdown
下面介紹下root檢測的各類方法:架構
咱們能夠查看發佈的系統版本,是test-keys(測試版),仍是release-keys(發佈版)。
能夠先在adb shell中運行下命令查看:app
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,可能你們對這個標識也不是特別注意吧。因此具體是否使用,還要多考慮考慮呢。也許能解決問題,也許會給本身帶來些麻煩。
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是Linux下切換用戶的命令,在使用時不帶參數,就是切換到超級用戶。一般咱們獲取root權限,就是使用su命令來實現的,因此能夠檢查這個命令是否存在。
有三個方法來測試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; }
這個方法是檢測經常使用目錄,那麼就有可能漏過不經常使用的目錄。
因此就有了第二個方法,直接使用shell下的命令來查找。
2)使用which命令查看是否存在su
which是linux下的一個命令,能夠在系統PATH變量指定的路徑中搜索某個系統命令的位置而且返回第一個搜索結果。
這裏,咱們就用它來查找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; } }
其中調用了一個函數 executeCommand(),是執行linux下的shell命令。具體實現以下:
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; }
然而,這個方法也存在一個缺陷,就是須要系統中存在which這個命令。我在測試過程當中,就遇到有的Android系統中沒有這個命令,因此,這也不是一個徹底有保障的方法,卻是能夠和上一個方法(在經常使用路徑下查找)進行組合,能提高成功率。
這種查找命令的方式,還有一種缺陷,就是可能系統中存在su,可是已經失效的狀況。例如,我曾經root過,後來又取消了,就可能出現這種狀況:有su這個文件,可是當前設備不是root的。
3)執行su,看可否獲取到root權限
因爲上面兩種查找方法都存在可能查不到的狀況,以及有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(); } } }
這種檢測su的方法,應該是最靠譜的,不過,也有個問題,就是在已經root的設備上,會彈出提示框,請求給app開啓root權限。這個提示不太友好,可能用戶會不喜歡。
若是想安靜的檢測,能夠用上兩種方法的組合;若是須要儘可能安全的檢測到,仍是執行su吧。
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; } }
在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; } }
上面的代碼,調用了兩個函數:writeFile()寫文件,readFile()讀文件,下面是具體實現:
//寫文件 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; } }
這裏說句題外話,我最初是想使用shell命令來寫文件:
echo "test_ok" > /data/su_test
但是使用executeCommand()來調用執行這個命令,結果連文件都沒有建立出來。在屢次失敗後纔想到:應該是這個shell命令涉及到了重定向(例如本例中,將原本應該屏幕輸出的信息轉而寫入文件中),才致使的失敗。這個重定向應該是須要寫代碼獲取數據流來本身實現。不過,既然要寫代碼使用數據流,那麼我能夠更簡單的直接寫文件,就沒有去嘗試用代碼來實現重定向了。
因爲每種方法各有其特點與缺陷,因此我最終將這些方法加起來了。注意,檢查su的3種方法,沒必要都使用上,能夠選第一二種查找的方法,或者選第三種執行的方法。
組合調用的代碼以下:
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; }
http://blog.csdn.net/quanshui540/article/details/48242459
https://blog.netspi.com/android-root-detection-techniques/
http://blog.csdn.net/hudashi/article/details/8091543
http://blog.csdn.net/jia635/article/details/38514101
http://bobao.360.cn/learning/detail/144.html