Android程序的安全系統 apk獲得root權限

轉自:http://my.unix-center.net/~Simon_fu/?p=531

最近在移植Android過程中遇到了Android程序(apk)權限的問題。最近也對這方面進行了一些瞭解,在此和大家分享。

     Android框架是基於Linux內核構建,所以Android安全系統也是基於Linux的安全架構建立的。在Linux安全系統中,用戶和組起着重要的作用,Linux中所有的資源給不同的用戶和用戶組設置了不同的訪問屬性。如果你對Linux下面用戶和組的概念不熟悉,請先補習一下Linux基礎知識。

    在Android系統中,系統爲每一個應用程序(apk)創建了一個用戶和組。這個用戶和組都是受限用戶,不能訪問系統的數據,只能訪問自己的文件和目錄,當然它也不能訪問其他應用程序的數據。這樣設計可以儘可能地保護應用程序的私有數據,增強系統的安全性和健壯性。

     但是有一些應用程序是需要訪問一些系統資源的。比如Setting程序,他就需要訪問wiffi,在系統中創建刪除文件等等操作。怎樣做到這一點兒呢?Android通過一定途徑可以獲得system權限。獲得system用戶權限,需要以下步驟:

1. 在應用程序的AndroidManifest.xml中的manifest節點中加入android:sharedUserId="android.uid.system"這個屬性。 
2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform這一行 
3. 使用mm命令來編譯,生成的apk就有修改系統時間的權限了。

     一般情況下system用戶權限就已經夠用了,system用戶可以在系統中創建和刪除文件,訪問設備等等。但是有些情況下system權限還是不夠的。比如:設置網卡IP地址,ifconfig命令是需要root權限的。我可以很肯定的說,在Android下面應用程序是沒有可能拿到root權限的。但是如果我的應用程序需要root權限怎麼辦呢?只能想辦法繞般過去。就以我的問題爲例,設置網卡IP地址,root權限下面命令爲:

ifconfig eth0 192.168.1.188

在普通用戶或者system用戶權限下面這條命令是不起作用的,但是不會返回失敗和異常,這個我個人認爲是Android的bug。那麼怎樣實現這個功能呢?我想出了兩個辦法。

1、系統啓動的時候init進程創建一個後臺進程,該進程處於root用戶權限下面。用來監聽系統中應用程序的請求(可以用socket實現),並代其完成。這樣應用程序就可以執行root用戶權限的任務了。

2、實現一個虛擬的設備,該設備的功能就是在內核態幫應用程序執行相應的命令。Linux內核態沒有權限的問題了。肯定可以執行成功。

     我解決設置網卡IP地址問題時,選擇是後者。相對來說設計比較簡單。

     如果你到網上去搜一下,你會發現很多文章說怎樣讓Android應用程序獲得root權限。如果你不想浪費時間就不要相信他們,因爲那些途徑是根本不可能獲得root權限的。


問題

    我遇到的問題是我想在Java應用程序中動態mount一個NFS的系統,但是執行mount命令必須要要root權限纔可以。一般情況下,在Android的Java層是不能獲得root權限的。

思路

   我在博文《Android程序的安全系統》中提到兩種思路:

1、實現一個init實現一個Service,來幫助Android應用程序執行root權限的命令。 
2、實現一個虛擬設備,這個設備幫助Android應用程序執行root權限的命令。

   本文將會選擇第一種來解決Android應用程序mount NFS文件系統的問題。

Init.rc Service

   在Android系統init.rc中定義很多Service,具體定義格式可以參考《Android Platform Developer’s Guide》中的“Android Init Language”。Init.rc中定義的Service將會被Init進程創建,這樣將可以獲得root權限。

   現在問題是Android應用程序怎樣啓動讓init進程知道我們想運行那個進程呢?答案是設置系統屬性“ctl.start”,把“ctl.start”設置爲你要運行的Service,假設爲“xxx”,Android系統將會幫你運行“ctl.start”系統屬性中指定的Service。那麼運行結果init進程將會將會寫入命名爲“init.svc.+Service名稱”的屬性中,也就是“init.svc.xxx”屬性,應用程序可以參考查閱這個值來確定Service執行的情況。想更深入瞭解Android property系統可以參考博文《(翻譯)Android屬性系統》。

Android property權限

    難道Android屬性“ctl.start”是所有進程都可以設置的嗎?那世界不就亂套了,誰都可以可以執行init.rc中Service了,查看property_service.c中的源碼,設置Android系統屬性的函數爲handle_property_set_fd:

1: void handle_property_set_fd(int fd)

2: {

3: ......

4: switch(msg.cmd) {

5: case PROP_MSG_SETPROP:

6: msg.name[PROP_NAME_MAX-1] = 0;

7: msg.value[PROP_VALUE_MAX-1] = 0;

8: 

9: if(memcmp(msg.name,"ctl.",4) == 0) {

10: if (check_control_perms(msg.value, cr.uid, cr.gid)) {

11: handle_control_message((char*) msg.name + 4, (char*) msg.value);

12: } else {

13: ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",

14: msg.name + 4, msg.value, cr.uid, cr.pid);

15: }

16: }

17: ......

18: }

19: }

    從源碼中我們發現如果設置“ctl.”開頭的Android系統property,將會調用check_control_perms函數來檢查調用者的權限,其定義如下:

1: static int check_control_perms(const char *name, int uid, int gid) {

2: int i;

3: if (uid == AID_SYSTEM || uid == AID_ROOT)

4: return 1;

5: 

6: /* Search the ACL */

7: for (i = 0; control_perms[i].service; i++) {

8: if (strcmp(control_perms[i].service, name) == 0) {

9: if ((uid && control_perms[i].uid == uid) ||

10: (gid && control_perms[i].gid == gid)) {

11: return 1;

12: }

13: }

14: }

15: return 0;

16: }

    我們發現root權限和system權限的應用程序將會授權修改“ctl.”開頭的Android系統屬性。否則將會檢查control_perms全局變量中的定義權限和Service。

    如果想更深入的瞭解Android Init進程和Android Property的權限控制,請參考《Android Permission》。

實例

    通過上面的介紹我們基本已經有思路了,下面以上面提出的mount nfs文件系統爲例說明:

1、首先定義一個執行mount的腳本,我把它位於/system/etc/mount_nfs.sh,定義如下:

1: #!/system/bin/sh

2: 

3: /system/bin/busybox mount -o rw,nolock -t nfs 192.168.1.6:/nfs_srv /data/mnt

不要忘了把它加上可執行權限。

2、在init.rc中加入一個Service定義,定義如下:

1: service mount_nfs /system/etc/mount_nfs.sh

2: oneshot

3: disabled

3、讓自己的應用程序獲得system權限,博文《Android程序的安全系統》中提到了怎樣獲得system權限,請參考,這裏就不贅述了。

4、在自己應用程序中設置System系統屬性“ctl.start”爲“mount_nfs”,這樣Android系統將會幫我們運行mount_nfs系統屬性了。這裏需要強調的是不能夠調用System.getProperty,這個函數只是修改JVM中的系統屬性。而不能修改Android的系統屬性。可以調用android.os.SystemProperties(Android 2.1 Eclair系統可以調用這個API),如果你的Android版本不能調用這個類,只能通過JNI,調用C/C++層的API property_get和property_set函數了。如果想詳細瞭解請參考(翻譯)Android屬性系統。代碼如下:

1: SystemProperties.set("ctl.start""mount_nfs");

5、最後在自己應用程序中,讀取“init.svc.mount_nfs”Android系統Property,檢查執行結果。代碼如下:

1: while(true)

2: {

3: mount_rt = SystemProperties.get("init.svc.mount_nfs""");

4: if(mount_rt != null && mount_rt.equals("stopped"))

5: {

6: return true;

7: }

8:

9: try

10: {

11: Thread.sleep(1000);

12: }catch(Exception ex){

13: Log.e(TAG, "Exception: " + ex.getMessage());

14: }

15: }

    init進程維護一個service的隊列,所以我們需要輪訓來查詢service的執行結果。

    通過上面的這些步驟,Android應用程序就能夠調用init.rc中定義的Service了。這樣你的Android應用程序也就獲得了root權限。

總結

   通過上文可以看出,在Android獲得root權限還是需要一些前提的,比如:

1、必須是Android系統開發人員,否則你無法修改init.rc等文件。 2、你的應用程序必須要獲得system權限。

    這樣可以防止root權限被應用程序無限制的使用,最終危及Android系統安全。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章