在博文《Android程式的安全系統》中提到兩種讓root權限的辦法。本文將會以一個例子實現來演示怎樣讓一個Android應用程式獲得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系統安全。
arrow
arrow
    全站熱搜

    戮克 發表在 痞客邦 留言(0) 人氣()