1. 程式人生 > >android的 root許可權

android的 root許可權

本文將從幾個方面,由淺至深地講述ROOT到底是什麼東西?

一. ROOT許可權簡單介紹

二.為什麼需要ROOT

三. ADBD的ROOT許可權

四.深入原始碼

ROOT許可權簡介:

ROOT許可權是Linux核心中的最高許可權,如果你的身份是ROOT,那麼你就具有了ROOT許可權。有了最高許可權,你就可以為所欲為,換句話說,如果惡意程式獲取到了ROOT許可權,那麼就可以肆意地破壞你的手機,獲取的隱私...所以廠商一般在生產手機的時候,不會提供給使用者ROOT許可權,官方宣稱是為了保護使用者手機的安全,然後裝了一堆開機自動啟動,而使用者這輩子也用不到也解除安裝不了垃圾軟體(相信使用安卓的同學們都懂我的意思),而蘋果所說的越獄,也就是獲取ROOT許可權。

為什麼需要獲取ROOT許可權?

蘋果使用者獲取ROOT許可權,是為了可以免費安裝各種軟體,以及為了獲取更加靈活的操作體驗,蘋果不會安裝一堆噁心的軟體;而安卓普通使用者獲取ROOT許可權,最大的目的就是為了解除安裝這些噁心的自帶軟體,安卓極客使用者則是為了各種折騰安卓手機,安卓開發人員是為了得到日誌檔案,分析BUG。

ADBD的ROOT許可權:

ADBD是什麼?相信大家都看過,IT男把手機連上電腦,然後不知怎麼的彈出一個黑乎乎的視窗,上面有一排排白色的英文字母,然後帥氣的敲擊著鍵盤(一般敲擊 adb shell,然後進入手機),啪啪幾下,可以幫你解決一些手機的問題。這個可以和手機互動的程序就是ADBD。這就好比,你去朋友家玩,然後你朋友家比較高階,進大門之前有個可視對講機,那麼你首先通過對講機呼叫朋友(敲擊adb shell),看看在不在家(手機是否相應adb shell這個命令),如果朋友在家,那麼就會通過對講機為你開門(成功進入手機中),提供這個服務的可視對講機就相當於ADBD。說白了,ADBD就是可以為你提供一種進入手機內部的服務通道。

那麼為什麼需要ADBD具有ROOT許可權?這就涉及到另一個與ROOT息息相關的東西——su。

我們開機之後,使用手機的身份就是一個普通使用者(user),如果執行su,那麼就可以直接切換到ROOT身份。就像仙劍奇俠傳三裡面的景天,是個凡人,法力有限,但是大家都知道,他的前世是飛蓬將軍,法力高強,天界無人能敵,只有魔界至尊重樓可以與他一較高下,當他們來到天庭的時候,玉帝施法,讓景天直接切換成飛蓬將軍,於是他就有了飛蓬將軍的記憶和法力,與重樓又一次大戰。su就是這樣一個神奇的命令。

高通平臺上,su的相關程式碼位於: LINUX/android/system/extras/su/su.c中

其實我們所說的越獄或者ROOT,就是把su安裝到手機裡面 /system/bin目錄下,並把它的許可權設定為4755,那麼某些程式需要ROOT許可權的時候,就可以通過su切換到ROOT身份,然後去執行,因此一般被ROOT的手機都會安裝一個“超級使用者”這樣的應用,就是用於管理哪些軟體可以切換到ROOT身份,哪些不可以,保證使用者安全,這個想法是很好很天真,對於正規的軟體,人家按照你的路子來,不正規的軟體,你一個小小的超級使用者的應用,豈能擋我獲取ROOT,虐不死你。

那麼為什麼需要ADBD獲取ROOT許可權呢?經過上面的講解大家應該能猜到,如果ADBD有ROOT許可權,就可以通過adb 工具,為所欲為,而又不留下痕跡(不安裝su),可以刪除自帶垃圾軟體,獲取任意目錄的檔案,安全性較高。筆者就是通過這種方式獲取一些系統級別檔案。

深入程式碼:

看了上面的文字,也許很多人會有這樣的想法,原來ROOT一個手機這麼簡單?錯!一點也不簡單。

首先:/system分割槽是隻讀的檔案系統,即你無法往system分割槽中寫入任何東西,其次就算你僥倖把su安裝到/system/bin下,你也沒法修改它的許可權為4755,再者,即使你僥倖把su的許可權設為4755,你也逃不過有些手機的反root機制(即檢測到有檔案的許可權為4755,就刪除)。

首先我們來看看su的部分程式碼:

 /* Until we have something better, only root and the shell can use su. */
   myuid = getuid();
    if (myuid != AID_ROOT && myuid != AID_SHELL) {
        fprintf(stderr,"su: uid %d not allowed to su\n", myuid);
        return 1;
    }
這句話告訴我們,如果執行su的不是ROOT或者SHELL使用者,那就直接退出,說明切換身份時候,你必須是這兩個使用者,還好我們用adb shell進入,是SHELL使用者,好險啊。
if(argc < 2) {
        uid = gid = 0;
    } else {
        int gids_count = sizeof(gids)/sizeof(gids[0]);
        extract_uidgids(argv[1], &uid, &gid, gids, &gids_count);
        if(gids_count) {
            if(setgroups(gids_count, gids)) {
                fprintf(stderr, "su: failed to set groups\n");
                return 1;
            }
        }
    }
判斷執行su的時候,有沒有其它引數,我們只是執行su,即argc < 2成立,su也可以切換成其它使用者(argc > 2,詳細看su命令的使用),這時候,uid 和 gid 都被設定為0 。即ROOT使用者的ID號和組號
if(setgid(gid) || setuid(uid)) {
        fprintf(stderr,"su: permission denied\n");
        return 1;
   }
來了來了,就是它,這就是我們最終的目的,把我變身成飛蓬將軍,如果一切順利,你就可以變身成功了(切換ROOT成功)
/* Default exec shell. */
    execlp("/system/bin/sh", "sh", NULL);
每次su之後,就會變成root#,然後就可以通過控制檯繼續敲命令,但是不同的是,你已經是ROOT了,許可權已經很大了。

Android系統啟動的時候,ADBD是ROOT許可權,只是後來被降級了,判斷是否降級的函式是should_drop_privileges()這個函式:

static int should_drop_privileges() {
#ifndef ALLOW_ADBD_ROOT
    return 1;
#else /* ALLOW_ADBD_ROOT */
首先判斷是否定義了ALLOW_ADBD_ROOT,如果系統都不允許你ROOT,就直接返回1,下面什麼都不看了,就像找工作的時候,不管你多麼厲害,如果你第一條要求都不符合,就直接把你pass了,說什麼都沒用,夠狠的。
    int secure = 0;
    char value[PROPERTY_VALUE_MAX];

  /* run adbd in secure mode if ro.secure is set and
    ** we are not in the emulator
    */
   property_get("ro.kernel.qemu", value, "");
    if (strcmp(value, "1") != 0) {
        property_get("ro.secure", value, "1");
        if (strcmp(value, "1") == 0) {
            // don't run as root if ro.secure is set...
            secure = 1;
下面就是獲取系統的屬性,判斷是否開啟ROOT許可權,可以看到如果ro.kernel.qumu 這個屬性被置為了,沒關係再給你一次機會,判斷ro.secure是否也是1,如果是,對不起你無法獲得root許可權,我要把secure置為1了(secure為1意味著要降級,後面會講解),接著:
           // ... except we allow running as root in userdebug builds if the
            // service.adb.root property has been set by the "adb root" command
            property_get("ro.debuggable", value, "");
            if (strcmp(value, "1") == 0) {
                property_get("service.adb.root", value, "");
                if (strcmp(value, "1") == 0) {
		    secure = 0;
            	}
            }
        }
    }
哈哈,又給了你一次機會,我再來判斷ro.debuggable是不是1,如果不是,對不起,我必須要降級,否則再給你一次機會,判斷service.adb.root這個屬性的值,如果也是1,那麼就不降級,一般在編譯ROM版本的時候,會同事編譯兩個版本,一個是工程版本,是具有ROOT許可權的ADBD,這樣方便開發者除錯系統,另一個就是我們使用者用的版本,叫user版本,這個是沒有ROOT許可權的,可以看到,就是通過property_get一系列屬性來判斷是否降級。
    return secure;
#endif /* ALLOW_ADBD_ROOT */
}
最後,返回secure即可。可以看到最後決定是否降級的變數就是secure,所有如果有原始碼的話,只有最後將secure賦值為0,不管什麼版本,最後都是ROOT許可權。
if (should_drop_privileges()) {
        drop_capabilities_bounding_set_if_needed();

        gid_t groups[] = { AID_ADB, AID_LOG, AID_INPUT, AID_INET,
                           AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
                           AID_NET_BW_STATS };
        if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) {
            exit(1);

        /* then switch user and group to "shell" */
        if (setgid(AID_SHELL) != 0) {<span style="white-space:pre">			</span>//曾經的漏洞,被封住了
            exit(1);
        }
        if (setuid(AID_SHELL) != 0) {<span style="white-space:pre">			</span>//曾經的漏洞
            exit(1);
        }

        D("Local port disabled\n");
    } 
從這段程式碼可以看到,如果should_drop_privileges返回1,那麼就可以降級了,降級函式為setgid(AID_SHELL)和setuid(AID_SHELL),曾經有黑客利用一些方法,使得setgid和setuid執行失敗,即降級失敗,以前的程式碼中是沒有exit的,那麼當setuid和setgid執行失敗之後,就不會降級。

ROOT不是那麼輕易的,現在有很多已知的漏洞,但是都被封鎖了,所有不是每個root工具都能root成功的,要看它採用的是什麼方法來ROOT。