前一段時間面試官問我Android在Linux的基礎上,權限做了哪些改變。霹靂呱啦說了一堆,但是說着說着,始終感覺自己說的缺了點東西,自己理解還是不夠到位
,而且網上的很多文章在原理上基本都是大同小異,很多地方都是語焉不詳,所以,自己半看源碼半看文章的總結了一下。
一:Android權限是什么
這個是老生長談的東西了,說到底,權限就是告訴系統我需要干什么,從訪問物理數據,到訪問第三方組件均是。
二:權限賦予
權限的賦予可分為兩類,一類是高層的組件,例如應用和系統服務,這一部分一般采用包管理器依賴進行管理,查詢。而另一部分則是低層的組件,這一部分則是利用了傳統了Linux DAC機制進行管理,一般不直接訪問包管理器。
1:低層權限管理
這里包括但不限於設備文件,UNIX套接字,網絡套接字。Android進程主要通過UID,GID以及一組補充的GID實現的。眾所周知,Android沙箱是以UID為基礎實現的,每個進程擁有自己獨特的UID(先不考慮共享UID)。進程的UID和GID會由包管理器映射到應用程序的UID。而補充gid則為額外的權限。值得一提的是,內置權限到組的映射是靜態的。部分源碼如下
<permission name="android.permission.BLUETOOTH_ADMIN" > <group gid="net_bt_admin" /> </permission> <permission name="android.permission.BLUETOOTH" > <group gid="net_bt" /> </permission> <permission name="android.permission.BLUETOOTH_STACK" > <group gid="bluetooth" /> <group gid="wakelock" /> </permission>
如上,android.permission.BLUETOOTH_ADMIN,android.permission.BLUETOOTH 和GID net_bt_admin組是關聯的.而在 android_filesystem_config.h中,組和GID是想映射的,如下
static struct android_id_info android_ids[] = {
....
{ "shell", AID_SHELL, },
{ "cache", AID_CACHE, },
{ "net_bt_admin", AID_NET_BT_ADMIN, },
....
}
#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */ #define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */ #define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ #define AID_NET_RAW 3004 /* can create raw INET sockets */ #define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ #define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
可得android.permission.BLUETOOTH_ADMIN映射的GID的3001。總而言之,包管理器在讀取platfrom.xml時,並維護一個權限到GID的列表。在對一個安裝中的包進行授權時,包管理器會檢查每個權限是否有對應的GID。如果有,則加入在補充GID列表。當然,到這里只是確定了進程需要賦予哪些額外的gid。
並沒有說怎么賦權的,這里要談到一個叫zygote的進程,顧名思義,當Android啟動新進程的時候,為了減少程序所需內存以及加快啟動時間,Android會直接fork()zygote進程,並執行Android特有的函數進行分化而不執行固有的exec函數。簡化代碼如下(android / platform / dalvik / 7033bed / . / vm / native / dalvik_system_Zygote.cpp forkAndSpecializeCommon()):
pid = fork(); if(pid ==0 ){ err = setgroupsIntarray(gids); //設置補充gid err = setrlimitsFromArray(rlimits); //設置資源限制 err = setresgid(gid, gid, gid); //設置實際用戶/組id err = setresuid(uid, uid, uid); //設置有效用戶/組id err = setCapabilities(permittedCapabilities, effectiveCapabilities); //設置進程權能 err = set_sched_policy(0, SP_DEFAULT); //設置調度策略 err = setSELinuxContext(uid, isSystemServer, seInfo, niceName); //SElinux }
2.高層權限管理
在仔細講解之前,先來看看包管理器所維護的安裝程序包核心數據庫,這個數據庫以xml文件的形式放進了/data/system/packages.xml里,先來看看里面究竟有些什么。
<package name="com.android.protips" codePath="/system/app/Protips" nativeLibraryPath="/system/app/Protips/lib" flags="572997" ft="1560a280490" it="1560a280490" ut="1560a280490" version="1" userId="10040"> <sigs count="1"> <cert index="0" /> </sigs> <proper-signing-keyset identifier="2" /> <signing-keyset identifier="2" /> </package> <package name="com.android.launcher" codePath="/system/priv-app/Launcher2" nativeLibraryPath="/system/priv-app/Launcher2/lib" flags="1078509125" ft="1560a29ae58" it="1560a29ae58" ut="1560a29ae58" version="22" userId="10007"> <sigs count="1"> <cert index="3" key="308204a1406035504071302fc58d017971bd0f6b52c262d70819d191967e158dfd3a2c7f1b30fa1eaafc2a556f84" /> </sigs> <proper-signing-keyset identifier="3" /> <signing-keyset identifier="3" /> </package>
<package name="com.android.widgetpreview" codePath="/data/app/WidgetPreview" nativeLibraryPath="/data/app/WidgetPreview/lib" flags="572996" ft="15bbc858e28" it="1560a27f8d8" ut="1560a27f8d8" version="22" userId="10052">
<sigs count="1">
<cert index="0" />
</sigs>
<perms>
<item name="android.permission.READ_EXTERNAL_STORAGE" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" />
</perms>
<proper-signing-keyset identifier="2" />
<signing-keyset identifier="2" />
</package>
從上面可以發現,這里包括了安裝路徑,版本號,簽名證書,每個包的權限。上層的管理都是通過和包管理器和這個數據庫進行交互的。由於組件不能在運行時改變權限,所以權限執行檢查都是靜態的。但它的執行一般分為兩類,一類是靜態,另一類是動態的。靜態執行和動態執行流程大致相同:Binder.getCallingUid()和Binder.getCallingPid()獲取調用者的UID和PID,然后利用UID映射包名,再獲得相關權限。如果權限集合中含有所需權限即啟動,否則拋出SecurityException異常。
三:共享UID(補充)
使用相同的密匙簽發的Android應用可以使用共同的UID運行 ,並且可以運行在同一進程中。這個屬性可以簡單通過在AndroidManifest.xml的根元素中添加shareUserId屬性即可開啟,但是不能在已安裝的應用里添加該屬性,這只會導致它修改自身uid,以至於失去對自身文件的訪問權限。
shareid內置了以下幾種:
android.uid.system(SYSTEM_UID,1000)
android.uid.phone(PHONE_UID,1001)
android.uid.bluetooth(BLUETOOH_UID,1002)
android.uid.log(LOG_UID,1007)
android.uid.nfc(NFC_UID,1027)
它們在系統引導時自動添加。具有相同userid的進程,可以訪問相同的系統資源,還可以對統一資源的組件進行特殊訪問控制。