Android權限獲取機制與常見權限不足問題分析

Android系統是運行在Linux內核上的,Android與Linux分別有自己的一套嚴格的安全及權限機制,

    很多像我這樣的新手,尤其是習慣了windows低安全限制的用戶,很容易在這方面弄混淆,下面是我總結的Android系統權限相關的內容,

    作爲這段時間對android權限學習的總結,也希望能對大家有所幫助,不正確之處請指出。

    首先分清兩個概念:

    要區分apk運行時的擁有的權限與在文件系統上被訪問(讀寫執行)的權限兩個概念。

    apk程序是運行在虛擬機上的,對應的是Android獨特的權限機制,只有體現到文件系統上時才使用linux的權限設置。

    (一)linux文件系統上的權限

    -rwxr-x--x system system 4156 2010-04-30 16:13 test.apk

    代表的是相應的用戶/用戶組及其他人對此文件的訪問權限,與此文件運行起來具有的權限完全不相關。

    比如上面的例子只能說明system用戶擁有對此文件的讀寫執行權限;system組的用戶對此文件擁有讀、執行權限;其他人對此文件只具有執行權限。

    而test.apk運行起來後可以幹哪些事情,跟這個就不相關了。

    千萬不要看apk文件系統上屬於system/system用戶及用戶組,或者root/root用戶及用戶組,就認爲apk具有system或root權限。

    (二)Android的權限規則

    (1)Android中的apk必須簽名

    這種簽名不是基於權威證書的,不會決定某個應用允不允許安裝,而是一種自簽名證書。

    重要的是,android系統有的權限是基於簽名的。比如:system等級的權限有專門對應的簽名,簽名不對,權限也就獲取不到。

    默認生成的APK文件是debug簽名的。

    獲取system權限時用到的簽名,見:如何使Android應用程序獲取系統權限

    (2)基於UserID的進程級別的安全機制

    大家都知道,進程有獨立的地址空間,進程與進程間默認是不能互相訪問的,是一種很可靠的保護機制。

    Android通過爲每一個安裝在設備上的包(apk)分配唯一的linux userID來實現,名稱爲"app_"加一個數字,比如app_43

    不同的UserID,運行在不同的進程,所以apk之間默認便不能相互訪問。

    Android提供瞭如下的一種機制,可以使兩個apk打破前面講的這種壁壘。

    在AndroidManifest.xml中利用sharedUserId屬性給不同的package分配相同的userID,通過這樣做,兩個package可以被當做同一個程序,

    系統會分配給兩個程序相同的UserID。當然,基於安全考慮,兩個package需要有相同的簽名,否則沒有驗證也就沒有意義了。

    (這裏補充一點:並不是說分配了同樣的UserID,兩程序就運行在同一進程, 下面爲PS指令摘取的,

    顯然,system、app_2分別對應的兩個進程的PID都不同,不知Android到底是怎樣實現它的機制的)

    User PID PPID

    system 953 883 ffffffff afe0cbcc S system_server

    app_2 1072 883 ffffffff afe0dcc4 S com.android.inputmethod.

    system 1083 883 ffffffff afe0dcc4 S android.process.omsservi

    app_2 1088 883 ffffffff afe0dcc4 S android.process.acore

    (3)默認apk生成的數據對外是不可見的

    實現方法是:Android會爲程序存儲的數據分配該程序的UserID。

    藉助於Linux嚴格的文件系統訪問權限,便實現了apk之間不能相互訪問似有數據的機制。

    例:我的應用創建的一個文件,默認權限如下,可以看到只有UserID爲app_21的程序才能讀寫該文件。

    -rw------- app_21 app_21 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.

    (4)AndroidManifest.xml中的顯式權限聲明

    Android默認應用是沒有任何權限去操作其他應用或系統相關特性的,應用在進行某些操作時都需要顯式地去申請相應的權限。

    一般以下動作時都需要申請相應的權限:

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

    在應用安裝的時候,package installer會檢測該應用請求的權限,根據該應用的簽名或者提示用戶來分配相應的權限。

    在程序運行期間是不檢測權限的。如果安裝時權限獲取失敗,那執行就會出錯,不會提示用戶權限不夠。

    大多數情況下,權限不足導致的失敗會引發一個 SecurityException, 會在系統log(system log)中有相關記錄。

    (5)權限繼承/UserID繼承

    當我們遇到apk權限不足時,我們有時會考慮寫一個linux程序,然後由apk調用它去完成某個它沒有權限完成的事情,很遺憾,這種方法是行不通的。

    前面講過,android權限是經營在進程層面的,也就是說一個apk應用啓動的子進程的權限不可能超越其父進程的權限(即apk的權限),

    即使單獨運行某個應用有權限做某事,但如果它是由一個apk調用的,那權限就會被限制。

    實際上,android是通過給子進程分配父進程的UserID實現這一機制的。

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

    首先要知道,普通apk程序是運行在非root、非system層級的,也就是說看要訪問的文件的權限時,看的是最後三位。

    另外,通過system/app安裝的apk的權限一般比直接安裝或adb install安裝的apk的權限要高一些。

    言歸正傳,運行一個android應用程序過程中遇到權限不足,一般分爲兩種情況:

    (1)Log中可明顯看到權限不足的提示。

    此種情況一般是AndroidManifest.xml中缺少相應的權限設置,好好查找一番權限列表,應該就可解決,是最易處理的情況。

    有時權限都加上了,但還是報權限不足,是什麼情況呢?

    Android系統有一些API及權限是需要apk具有一定的等級才能運行的。

    比如 SystemClock.setCurrentTimeMillis()修改系統時間,WRITE_SECURE_SETTINGS權限 好像都是需要有system級的權限才行。

    也就是說UserID是system.

    (2)Log裏沒有報權限不足,而是一些其他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 的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. 用壓縮軟件打開apk文件,刪掉META-INF目錄下的CERT.SF和CERT.RSA兩個文件。

    4. 使用目標系統的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

    實踐證明,第二種方法不需要刪掉META-INF目錄下的CERT.SF和CERT.RSA兩個文件,直接signapk就可以。

    編寫使用root權限的android應用程序這個是如何執行su命令的使用

    publicstaticboolean runRootCommand(String command){

      Process process =null;

      DataOutputStream os =null;

      try{

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

           os =newDataOutputStream(process.getOutputStream());

           os.writeBytes(command+"\n");

           os.writeBytes("exit\n");

          os.flush();

           process.waitFor();

      }catch(Exception e){

          Log.d("*** DEBUG ***", "Unexpected error - Here is what I know: "+e.getMessage());

          return false;

      }finally{

          try{

              if(os !=null){

                      os.close();

                    }

                    process.destroy();

                    }catch(Exception e){

                      // nothing

                    }

            }returntrue;

          }

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