抽絲剝繭:理解Android權限機制


  前一段時間面試官問我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的進程,可以訪問相同的系統資源,還可以對統一資源的組件進行特殊訪問控制。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM