Android手機Root受權原理細節全解析

首先關於Root的方式,這裏不作詳解,能夠有不少漏洞,好比利用uid溢出後歸爲0,獲得Root權限,而後操做文件系統等。
 
手機Root後,最重要的是,給手機安裝了su程序和superuser apk。 su通常被安裝在/system/xbin 或者 /system/bin 下面,su文件的權限以下:
 
# ls su -l
ls su -l
-rwsr-sr-x    1 root     root         26336 Aug  1  2008 su
 
這裏能夠看到,su的Owner和Group分別爲Root,Root。 Other用戶具備exeute權限,另外,su設置了suid和sgid,這個很是重要,後面會詳述,這個使得Su進程能夠提高自身的EUID。
 
咱們這裏以RootExplorer爲例,看是如何申請和提高權限的:
 
首先,ps一下,能夠看到root explorer的信息,這裏第一列是UID,更加準確的,應該是EUID。
app_73    1143  103   301620 39944 ffffffff 400194c4 S com.speedsoftware.rootexp
lorer
 
這裏能夠看到,Root Explorer的EUID=10073(App Base是從10000開始的)
 
而後,Root Explorer經過Runtime方式,使用shell 命令行運行了「su」, 因此,會有一個Root Explorer啓動的sh:
 
app_73    1159  1143  764    376   c003e454 4001cf94 S /system/bin/sh
 
default狀況下,進程的UID是繼承的,這裏sh的PPID是1143,說明是RootExplorer啓動的。
 
RootExplorer經過相似下面的代碼運行「su」:

p = Runtime.getRuntime().exec("su");     linux

因此,1159  PID的sh會啓動su,這裏須要注意的是,su進程的Real UID是10073,由於繼承自parent,可是,其EUID卻提高爲了ROOT,這就是因爲SUID被設置的緣由。 因爲su進程的EUID是ROOT,因此致使了su能夠作不少高權限的事情。 android

su會經過: sql

sprintf(sysCmd, "am start -a android.intent.action.MAIN -n com.koushikdutta.superuser/com.koushikdutta.superuser.SuperuserRequestActivity --ei uid %d --ei pid %d > /dev/null", g_puid, ppid);
  if (system(sysCmd))
   return executionFailure("am."); shell

啓動SuperUser Request Activity來詢問用戶是否受權當前應用,若是否的話,則su將return 結束, 不然su會繼續運行。 app

獲得用戶許可後(經過sqlite和SuperUser Request Activity交流用戶許可),su會將本身的Real UID也設置爲ROOT: ui

 if(setgid(0) || setuid(0)) 
  return permissionDenied(); spa

因爲su的EUID是ROOT,因此su有權限執行以上代碼,執行完後su進程的Real UID,effective UID都變成了ROOT。 這裏爲何要同時提高Real UID的權限,後面會說明。 .net

而後,su會啓動一個額外的sh來運行用戶的指令: 命令行

char *exec_args[argc + 1];
 exec_args[argc] = NULL;
 exec_args[0] = "sh";
 int i;
 for (i = 1; i < argc; i++)
 {
  exec_args[i] = argv[i]; orm

 }
 execv("/system/bin/sh", exec_args);
 return executionFailure("sh");


 這裏很是關鍵的代碼是execv! 這裏沒有使用fork,而是直接使用execv,這將致使不會建立新的進程運行sh image,而是直接在當前su的進程空間load並執行sh image。 因此,return executionFailure("sh"); 正常狀況下是永遠不會被執行的,執行完execv後,就開始直接sh的代碼了。

這裏能夠看到,因爲複用su當前進程來運行sh,使得sh運行的進程的EUID,Real UID都是ROOT,那麼,任何操做均可以執行了。 若是不提高Real UID爲Root的話,那麼只要sh運行中經過啓動外部程序的方式來完成操做的話,就會出現權限問題,由於新啓動的進程只會繼承父進程的Real UID,而不是EUID,那麼新起的進程的Real UID=EUID= parent RealUID != ROOT,那麼某些操做可能有問題。 因此,su把本身的Real UID也提高爲ROOT後,則無論啓動後的任何代後的子進程執行操做,都是以ROOT的權限運行。

RootExplorer經過獲得sh流後,就能夠經過該流執行任何shell指令了:

  • try {   
  •    // Preform su to get root privledges   
  •    p = Runtime.getRuntime().exec("su");    
  •   
  •    // Attempt to write a file to a root-only   
  •    DataOutputStream os = new DataOutputStream(p.getOutputStream());   
  •    os.writeBytes("echo \"Do I have root?\" >/system/sd/temporary.txt\n");   
  •   
  •    // Close the terminal   
  •    os.writeBytes("exit\n");   
  •    os.flush();   

     

    經過ps也能夠明顯看到su變成了sh,以下,

    app_73    1143  103   301620 39944 ffffffff 400194c4 S com.speedsoftware.rootexp
    lorer
    app_73    1159  1143  764    376   c003e454 4001cf94 S /system/bin/sh
    root      1161  1159  772    388   c012e760 4007c578 S sh

    這裏的PID=1161的進程實際上是su(能夠經過打log驗證),當執行execv("/system/bin/sh", exec_args);後,進程名就變成了sh。固然進程EUID不變,仍然是ROOT。

    經過cat 進程的proc信息,咱們也能夠看到其實RootExplorer自身的UID或者權限根本沒有被提高(這是和linux上執行su不同的地方),

    root@android :/proc/1143 # cat status
    cat status
    Name:   re.rootexplorer
    State:  S (sleeping)
    Tgid:   1143
    Pid:    1143
    PPid:   103
    TracerPid:      0
    Uid:    10073(Real)   10073(Effecttive)   10073(saved)   10073
    Gid:    10073   10073   10073   10073

    而su演變成的sh,倒是具備全部的ROOT UID:

    root@android :/proc/1161 # cat status
    cat status
    Name:   sh
    State:  S (sleeping)
    Tgid:   1161
    Pid:    1161
    PPid:   1159
    TracerPid:      0
    Uid:    0       0       0       0
    Gid:    0       0       0       0

     

    總結: Android中App受權獲取Root權限,其實不是App自身的權限提高了,而是經過具備ROOT權限的sh流來執行shell命令

    相關文章
    相關標籤/搜索