1. 程式人生 > >全方位理解Android許可權之底層實現概覽

全方位理解Android許可權之底層實現概覽

0000

這個階段搞了很多和Android檔案許可權相關的問題,雖然一知半解,但也算是對Android許可權機制有一些自己的理解。遂將這些內容整理出來。因為許可權這部分涉及到的內容很多,故將知識分為幾塊內容分別去整理。目前能想到的概要如下:

  1. Android 許可權底層實現原理概述
  2. Android uid,gid的生成與許可權機制的聯絡
  3. Android packageManagerService與許可權的千絲萬縷(原始碼解析)
  4. Android 從recovery模式下的OTA升級理解許可權
  5. Android ROOT 原理
  6. Android 簽名
  7. Android 許可權大殺器 — Selinux的策略

這是第一篇。2,3,4已經有一些草稿了,但離發出來還有一些時間。5,6,7還在規劃中。可以關注新弄的公眾號softard,後面比較完善的文章都會在這個號上及時更新。

這裡再推薦一本書 ,這也是查資料找到的一本寶貝,非常牛逼。

0001 Overview

Linux File Permission

眾所周知,Android的核心是linux的核心。對於linux來說,系統的一切都是檔案。同時,linux為檔案系統設計了一套完善的許可權機制。下面簡單提一下linux檔案許可權的核心:

比如在Android手機adb shell下檢視一個目錄

# ls -ld data/data/
drwxrwx--x 113 system system 4096 1970-01-01 15:08 data/data/

其中,drwxrwx--x這10位代表檔案許可權,第一位檔案型別可以忽略(這裡型別是資料夾),後面每三位代表檔案擁有的許可權,包括rwx(可讀,可寫,可執行)。system system表示檔案屬於system使用者和屬於system組。

翻譯過來是,system使用者對該檔案擁有讀寫執行許可權,system組對該檔案擁有讀寫執行許可權,其他使用者對該檔案擁有執行許可權。簡單來說就是771許可權。

Android Permission

Permission許可權是Android系統定義的一套許可權機制,用於控制APP訪問某個硬體裝置或某個Android系統的元件。
舉兩個常見的例子:

  1. 如果你的App想要訪問儲存卡,你需要在你的AndroidManifest檔案中使用對應的permission用於向系統請求許可權 。
  2. 你可以給你的Activity元件加個自定義的訪問許可權,這樣任何想啟動該Activity的程式必須在它的AndroidManifest中進行許可權的請求。見Android自定義許可權

那麼為什麼你在AndroidManifest檔案請求storage許可權你就可以訪問裝置檔案?linux檔案屬性的許可權和Permission到底是怎麼聯絡起來的呢?下面我們來具體來講。

0010 packages.list & packages.xml

Android開機階段會掃描所有App,從Manifest檔案中把App資訊和許可權存到packages.xmlpackages.list檔案中,具體的處理過程會在後面第三篇去分析。

因為檔案包含所有已安裝應用的資訊,所以我們嘗試安裝一個App(com.softard.test),並且檢視其資訊:

packages.list

com.softard.test 10052 1 /data/user/0/com.softard.test default none

這裡10052是uid,至於其怎麼生成的後面第二篇再詳談。

packages.xml

<package name="com.softard.test" codePath="/data/app/com.softard.test-1" nativeLibraryPath="/data/app/com.softard.test-1/lib" publicFlags="944291654" privateFlags="0" ft="8bbd70" it="89023c" ut="8bc04d" version="1" userId="10052">
        <sigs count="1">
            <cert index="10" key="308..../>
        </sigs>
        <proper-signing-keyset identifier="11" />
    </package>

然後對App做個修改,將其改成系統App並簽名放到system/app/Test下,再看這兩個檔案:

com.softard.test 1000 0 /data/user/0/com.softard.test platform 3009,3002,1023,1015,3003,3001,1021,1000,2002,2950,1010,1007

看到uid變成了1000,selinux從default變成platform,許可權組從none變成3009,3002,1023,1015,3003,3001,1021,1000,2002,2950,1010,1007。而且應用一下子增加了一堆許可權:

 <package name="com.softard.test" codePath="/system/app/Test" nativeLibraryPath="/system/app/Test/lib" primaryCpuAbi="armeabi-v7a" publicFlags="944291397" privateFlags="0" ft="1683b80c790" it="1683b80c790" ut="1683b80c790" version="1" sharedUserId="1000" isOrphaned="true">
        <sigs count="1">
            <cert index="0" />
        </sigs>
        <perms>
            <item name="android.permission.BIND_INCALL_SERVICE" granted="true" flags="0" />
            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.CONFIGURE_WIFI_DISPLAY" granted="true" flags="0" />
            <item name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE" granted="true" flags="0" />
            <item name="android.permission.ACCESS_WIMAX_STATE" granted="true" flags="0" />
            <item name="android.permission.RECOVERY" granted="true" flags="0" />
            <item name="com.qualcomm.permission.READPROC" granted="true" flags="0" />
            <item name="android.permission.USE_CREDENTIALS" granted="true" flags="0" />
            <item name="android.permission.MODIFY_AUDIO_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.ACCESS_CHECKIN_PROPERTIES" granted="true" flags="0" />
            <item name="com.zqautomotive.oris.wechat.provider.WRITE" granted="true" flags="0" />
            <item name="android.permission.INSTALL_LOCATION_PROVIDER" granted="true" flags="0" />
            <item name="android.permission.MANAGE_ACCOUNTS" granted="true" flags="0" />
            <item name="android.permission.SYSTEM_ALERT_WINDOW" granted="true" flags="0" />
            <item name="android.permission.BROADCAST_PHONE_ACCOUNT_REGISTRATION" granted="true" flags="0" />
            <item name="android.permission.CONTROL_LOCATION_UPDATES" granted="true" flags="0" />
            <item name="android.permission.CLEAR_APP_USER_DATA" granted="true" flags="0" />
            <item name="com.zqautomotive.oris.wechat.provider.READ" granted="true" flags="0" />
            <item name="android.permission.BROADCAST_CALLLOG_INFO" granted="true" flags="0" />
            <item name="android.permission.NFC" granted="true" flags="0" />
            <item name="android.permission.CALL_PRIVILEGED" granted="true" flags="0" />
            <item name="android.permission.CHANGE_NETWORK_STATE" granted="true" flags="0" />
            <item name="android.permission.MASTER_CLEAR" granted="true" flags="0" />
            <item name="android.permission.WRITE_SYNC_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />
            <item name="android.permission.PEERS_MAC_ADDRESS" granted="true" flags="0" />
            <item name="android.permission.DEVICE_POWER" granted="true" flags="0" />
            <item name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" granted="true" flags="0" />
            <item name="android.permission.READ_PROFILE" granted="true" flags="0" />
            <item name="android.permission.BLUETOOTH" granted="true" flags="0" />
            <item name="android.permission.WRITE_MEDIA_STORAGE" granted="true" flags="0" />
            <item name="android.permission.WRITE_BLOCKED_NUMBERS" granted="true" flags="0" />
            <item name="com.qualcomm.permission.USE_QCRIL_MSG_TUNNEL" granted="true" flags="0" />
            <item name="android.permission.ACCESS_SURFACE_FLINGER" granted="true" flags="0" />
            <item name="com.zqautomotive.voice_controller.GET_SPEECH_RESULT" granted="true" flags="0" />
            <item name="android.permission.AUTHENTICATE_ACCOUNTS" granted="true" flags="0" />
            <item name="android.permission.INTERNET" granted="true" flags="0" />
            <item name="android.permission.REORDER_TASKS" granted="true" flags="0" />
            <item name="zq.permissio.CONNECTION_MQTT_SERVICE" granted="true" flags="0" />
            <item name="android.permission.BLUETOOTH_ADMIN" granted="true" flags="0" />
            <item name="android.permission.CONTROL_VPN" granted="true" flags="0" />
            <item name="android.permission.UPDATE_DEVICE_STATS" granted="true" flags="0" />
            <item name="android.permission.MANAGE_FINGERPRINT" granted="true" flags="0" />
            <item name="com.qualcomm.permission.IZAT" granted="true" flags="0" />
            <item name="android.permission.BIND_CONNECTION_SERVICE" granted="true" flags="0" />
            <item name="android.permission.MANAGE_USB" granted="true" flags="0" />
            <item name="android.permission.INTERACT_ACROSS_USERS_FULL" granted="true" flags="0" />
            <item name="android.permission.STOP_APP_SWITCHES" granted="true" flags="0" />
            <item name="android.permission.BATTERY_STATS" granted="true" flags="0" />
            <item name="android.permission.PACKAGE_USAGE_STATS" granted="true" flags="0" />
            <item name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" granted="true" flags="0" />
            <item name="android.permission.TETHER_PRIVILEGED" granted="true" flags="0" />
            <item name="android.permission.WRITE_SECURE_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.MOVE_PACKAGE" granted="true" flags="0" />
            <item name="android.permission.READ_BLOCKED_NUMBERS" granted="true" flags="0" />
            <item name="com.qualcomm.permission.wfd.QC_WFD" granted="true" flags="0" />
            <item name="android.permission.READ_SEARCH_INDEXABLES" granted="true" flags="0" />
            <item name="android.permission.ACCESS_IMS_CALL_SERVICE" granted="true" flags="0" />
            <item name="android.permission.BLUETOOTH_PRIVILEGED" granted="true" flags="0" />
            <item name="android.permission.HARDWARE_TEST" granted="true" flags="0" />
            <item name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE" granted="true" flags="0" />
            <item name="android.permission.BIND_JOB_SERVICE" granted="true" flags="0" />
            <item name="android.permission.CONFIRM_FULL_BACKUP" granted="true" flags="0" />
            <item name="android.permission.SET_TIME" granted="true" flags="0" />
            <item name="android.permission.WRITE_APN_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.CHANGE_WIFI_STATE" granted="true" flags="0" />
            <item name="android.permission.MANAGE_USERS" granted="true" flags="0" />
            <item name="android.permission.SET_PREFERRED_APPLICATIONS" granted="true" flags="0" />
            <item name="android.permission.DELETE_CACHE_FILES" granted="true" flags="0" />
            <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />
            <item name="zq.permissio.CONNECTION_ACTIVATION_SERVICE" granted="true" flags="0" />
            <item name="android.permission.DISABLE_KEYGUARD" granted="true" flags="0" />
            <item name="android.permission.BACKUP" granted="true" flags="0" />
            <item name="android.permission.CHANGE_CONFIGURATION" granted="true" flags="0" />
            <item name="android.permission.USER_ACTIVITY" granted="true" flags="0" />
            <item name="android.permission.READ_LOGS" granted="true" flags="0" />
            <item name="android.permission.COPY_PROTECTED_DATA" granted="true" flags="0" />
            <item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />
            <item name="android.permission.SET_KEYBOARD_LAYOUT" granted="true" flags="0" />
            <item name="android.permission.USE_FINGERPRINT" granted="true" flags="0" />
            <item name="android.permission.WRITE_USER_DICTIONARY" granted="true" flags="0" />
            <item name="android.permission.READ_SYNC_STATS" granted="true" flags="0" />
            <item name="android.permission.REBOOT" granted="true" flags="0" />
            <item name="android.permission.OEM_UNLOCK_STATE" granted="true" flags="0" />
            <item name="android.permission.MANAGE_DEVICE_ADMINS" granted="true" flags="0" />
            <item name="android.permission.CHANGE_APP_IDLE_STATE" granted="true" flags="0" />
            <item name="android.permission.SET_POINTER_SPEED" granted="true" flags="0" />
            <item name="android.permission.MANAGE_NOTIFICATIONS" granted="true" flags="0" />
            <item name="android.permission.READ_SYNC_SETTINGS" granted="true" flags="0" />
            <item name="android.permission.OVERRIDE_WIFI_CONFIG" granted="true" flags="0" />
            <item name="android.permission.FORCE_STOP_PACKAGES" granted="true" flags="0" />
            <item name="android.permission.ACCESS_NOTIFICATIONS" granted="true" flags="0" />
            <item name="android.permission.VIBRATE" granted="true" flags="0" />
            <item name="com.android.certinstaller.INSTALL_AS_USER" granted="true" flags="0" />
            <item name="android.permission.READ_USER_DICTIONARY" granted="true" flags="0" />
            <item name="android.permission.ACCESS_WIFI_STATE" granted="true" flags="0" />
            <item name="android.permission.CHANGE_WIMAX_STATE" granted="true" flags="0" />
            <item name="android.permission.MODIFY_PHONE_STATE" granted="true" flags="0" />
            <item name="android.permission.STATUS_BAR" granted="true" flags="0" />
            <item name="android.permission.READ_FRAME_BUFFER" granted="true" flags="0" />
            <item name="android.permission.LOCATION_HARDWARE" granted="true" flags="0" />
            <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />
            <item name="android.permission.INJECT_EVENTS" granted="true" flags="0" />
            <item name="android.permission.DELETE_PACKAGES" granted="true" flags="0" />
        </perms>
        <proper-signing-keyset identifier="1" />
    </package>

所以問題來了,改成系統應用後uid為什麼變成1000?後面代表權限組的一串數字又都是什麼?

0011 Android File Permission

android_filesystem_config.h

在Android中,所有許可權的定義都在:system/core/include/private/android_filesystem_config.h

在這個標頭檔案中定義了Android系統的一些使用者,包含root使用者,system使用者,shell使用者所對應的值等等。

/* This is the master Users and Groups config for the platform.
 * DO NOT EVER RENUMBER
 */

#define AID_ROOT             0  /* traditional unix root user */

#define AID_SYSTEM        1000  /* system server */

#define AID_RADIO         1001  /* telephony subsystem, RIL */
#define AID_BLUETOOTH     1002  /* bluetooth subsystem */
#define AID_GRAPHICS      1003  /* graphics devices */
#define AID_INPUT         1004  /* input devices */
#define AID_AUDIO         1005  /* audio devices */
#define AID_CAMERA        1006  /* camera devices */
#define AID_LOG           1007  /* log devices */
#define AID_COMPASS       1008  /* compass device */
#define AID_MOUNT         1009  /* mountd socket */
#define AID_WIFI          1010  /* wifi subsystem */
#define AID_ADB           1011  /* android debug bridge (adbd) */
#define AID_INSTALL       1012  /* group for installing packages */
#define AID_MEDIA         1013  /* mediaserver process */
#define AID_DHCP          1014  /* dhcp client */
#define AID_SDCARD_RW     1015  /* external storage write access */
#define AID_VPN           1016  /* vpn system */
#define AID_KEYSTORE      1017  /* keystore subsystem */
#define AID_USB           1018  /* USB devices */
#define AID_DRM           1019  /* DRM server */
#define AID_MDNSR         1020  /* MulticastDNSResponder (service discovery) */
#define AID_GPS           1021  /* GPS daemon */
#define AID_UNUSED1       1022  /* deprecated, DO NOT USE */
#define AID_MEDIA_RW      1023  /* internal media storage write access */
#define AID_MTP           1024  /* MTP USB driver access */
#define AID_UNUSED2       1025  /* deprecated, DO NOT USE */
#define AID_DRMRPC        1026  /* group for drm rpc */
#define AID_NFC           1027  /* nfc subsystem */
#define AID_SDCARD_R      1028  /* external storage read access */
#define AID_CLAT          1029  /* clat part of nat464 */
#define AID_LOOP_RADIO    1030  /* loop radio devices */
#define AID_MEDIA_DRM     1031  /* MediaDrm plugins */
#define AID_PACKAGE_INFO  1032  /* access to installed package details */
#define AID_SDCARD_PICS   1033  /* external storage photos access */
#define AID_SDCARD_AV     1034  /* external storage audio/video access */
#define AID_SDCARD_ALL    1035  /* access all users external storage */
#define AID_LOGD          1036  /* log daemon */
#define AID_SHARED_RELRO  1037  /* creator of shared GNU RELRO files */
#define AID_DBUS          1038  /* dbus-daemon IPC broker process */
#define AID_TLSDATE       1039  /* tlsdate unprivileged user */
#define AID_MEDIA_EX      1040  /* mediaextractor process */
#define AID_AUDIOSERVER   1041  /* audioserver process */
#define AID_METRICS_COLL  1042  /* metrics_collector process */
#define AID_METRICSD      1043  /* metricsd process */
#define AID_WEBSERV       1044  /* webservd process */
#define AID_DEBUGGERD     1045  /* debuggerd unprivileged user */
#define AID_MEDIA_CODEC   1046  /* mediacodec process */
#define AID_CAMERASERVER  1047  /* cameraserver process */
#define AID_FIREWALL      1048  /* firewalld process */
#define AID_TRUNKS        1049  /* trunksd process (TPM daemon) */
#define AID_NVRAM         1050  /* Access-controlled NVRAM */
#define AID_DNS           1051  /* DNS resolution daemon (system: netd) */
#define AID_DNS_TETHER    1052  /* DNS resolution daemon (tether: dnsmasq) */
/* Changes to this file must be made in AOSP, *not* in internal branches. */

#define AID_SHELL         2000  /* adb and debug shell user */
#define AID_CACHE         2001  /* cache access */
#define AID_DIAG          2002  /* access to diagnostic resources */

/* The range 2900-2999 is reserved for OEM, and must never be
 * used here */
#define AID_OEM_RESERVED_START 2900
#define AID_OEM_RESERVED_END   2999

/* The 3000 series are intended for use as supplemental group id's only.
 * They indicate special Android capabilities that the kernel is aware of. */
#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 */
#define AID_NET_BW_ACCT   3007  /* change bandwidth statistics accounting */
#define AID_NET_BT_STACK  3008  /* bluetooth: access config files */
#define AID_READPROC      3009  /* Allow /proc read access */
#define AID_WAKELOCK      3010  /* Allow system wakelock read/write access */

/* The range 5000-5999 is also reserved for OEM, and must never be used here. */
#define AID_OEM_RESERVED_2_START 5000
#define AID_OEM_RESERVED_2_END   5999

#define AID_EVERYBODY     9997  /* shared between all apps in the same profile */
#define AID_MISC          9998  /* access to misc storage */
#define AID_NOBODY        9999

#define AID_APP          10000  /* first app user */

#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
#define AID_ISOLATED_END   99999 /* end of uids for fully isolated sandboxed processes */

#define AID_USER        100000  /* offset for uid ranges for each user */

#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
#define AID_SHARED_GID_END   59999 /* start of gids for apps in each user to share */

從標頭檔案定義,就知道每個許可權組都是由一串數字代表的。除了數字,該檔案還定義了一個結構體陣列,對映數字對應的字串:

static const struct android_id_info android_ids[] = {
    { "root",          AID_ROOT, },

    { "system",        AID_SYSTEM, },

    { "radio",         AID_RADIO, },
    { "bluetooth",     AID_BLUETOOTH, },
    { "graphics",      AID_GRAPHICS, },
    { "input",         AID_INPUT, },
    { "audio",         AID_AUDIO, },
    { "camera",        AID_CAMERA, },
    { "log",           AID_LOG, },
    { "compass",       AID_COMPASS, },
    { "mount",         AID_MOUNT, },
    { "wifi",          AID_WIFI, },
    { "adb",           AID_ADB, },
    { "install",       AID_INSTALL, },
    { "media",         AID_MEDIA, },
    { "dhcp",          AID_DHCP, },
    { "sdcard_rw",     AID_SDCARD_RW, },
    { "vpn",           AID_VPN, },
    { "keystore",      AID_KEYSTORE, },
    { "usb",           AID_USB, },
    { "drm",           AID_DRM, },
    { "mdnsr",         AID_MDNSR, },
    { "gps",           AID_GPS, },
    // AID_UNUSED1
    { "media_rw",      AID_MEDIA_RW, },
    { "mtp",           AID_MTP, },
    // AID_UNUSED2
    { "drmrpc",        AID_DRMRPC, },
    { "nfc",           AID_NFC, },
    { "sdcard_r",      AID_SDCARD_R, },
    { "clat",          AID_CLAT, },
    { "loop_radio",    AID_LOOP_RADIO, },
    { "mediadrm",      AID_MEDIA_DRM, },
    { "package_info",  AID_PACKAGE_INFO, },
    { "sdcard_pics",   AID_SDCARD_PICS, },
    { "sdcard_av",     AID_SDCARD_AV, },
    { "sdcard_all",    AID_SDCARD_ALL, },
    { "logd",          AID_LOGD, },
    { "shared_relro",  AID_SHARED_RELRO, },
    { "dbus",          AID_DBUS, },
    { "tlsdate",       AID_TLSDATE, },
    { "mediaex",       AID_MEDIA_EX, },
    { "audioserver",   AID_AUDIOSERVER, },
    { "metrics_coll",  AID_METRICS_COLL },
    { "metricsd",      AID_METRICSD },
    { "webserv",       AID_WEBSERV },
    { "debuggerd",     AID_DEBUGGERD, },
    { "mediacodec",    AID_MEDIA_CODEC, },
    { "cameraserver",  AID_CAMERASERVER, },
    { "firewall",      AID_FIREWALL, },
    { "trunks",        AID_TRUNKS, },
    { "nvram",         AID_NVRAM, },
    { "dns",           AID_DNS, },
    { "dns_tether",    AID_DNS_TETHER, },

    { "shell",         AID_SHELL, },
    { "cache",         AID_CACHE, },
    { "diag",          AID_DIAG, },

    { "net_bt_admin",  AID_NET_BT_ADMIN, },
    { "net_bt",        AID_NET_BT, },
    { "inet",          AID_INET, },
    { "net_raw",       AID_NET_RAW, },
    { "net_admin",     AID_NET_ADMIN, },
    { "net_bw_stats",  AID_NET_BW_STATS, },
    { "net_bw_acct",   AID_NET_BW_ACCT, },
    { "net_bt_stack",  AID_NET_BT_STACK, },
    { "readproc",      AID_READPROC, },
    { "wakelock",      AID_WAKELOCK, },

    { "everybody",     AID_EVERYBODY, },
    { "misc",          AID_MISC, },
    { "nobody",        AID_NOBODY, },
};

好了,瞭解這個檔案我們再來看我們應用的資訊:

com.softard.test 1000 0 /data/user/0/com.softard.test platform 3009,3002,1023,1015,3003,3001,1021,1000,2002,1010,1007

我們把數字對應的資訊擷取下來:

#define AID_SYSTEM        1000  "system"    /* system server */     
#define AID_LOG           1007  "log"       /* log devices */       
#define AID_WIFI          1010  "wifi"      /* wifi subsystem */    
#define AID_SDCARD_RW     1015  "sdcard_rw" /* external storage write access */
#define AID_GPS           1021  "gps"       /* GPS daemon */
#define AID_MEDIA_RW      1023  "media_rw"  /* internal media storage write access */
#define AID_DIAG          2002  "diag"      /* access to diagnostic resources */
#define AID_NET_BT_ADMIN  3001  "net_bt_admin"/* bluetooth: create any socket */
#define AID_NET_BT        3002  "net_bt"    /* bluetooth: create sco, rfcomm or l2cap sockets */
#define AID_INET          3003  "inet"      /* can create AF_INET and AF_INET6 sockets */
#define AID_READPROC      3009  "readproc"  /* Allow /proc read access */

所以,當我指定應用為系統應用時,就將uid指定為了1000。並且擁有了各種屬於系統的許可權組。

那麼,指定系統應用後是怎麼獲得這一系列的gid呢?這一塊原始碼流程很多,放到後面集中分析。

好了,現在我們瞭解system/core/include/private/android_filesystem_config.h檔案定義以後,其實就前進了一大步。注意一下程式碼所在原始碼位置,位於system/core下,其實它已經是Android核心部分了,所以後面涉及到許可權內容,都會匯入這個標頭檔案。

現在來看一段原始碼再來熟悉一下這部分內容:

FileUtils.setPermissions(fstr.getFD(), 0640, SYSTEM_UID, PACKAGE_INFO_GID);

這是位於frameworks/base/services/core/java/com/android/server/pm/Settings.java的一段程式碼,功能就是建立packages.list檔案。從函式名稱和引數可以知道,它是給檔案新增許可權的,通過這段程式碼可以推測出:它給packages.list檔案賦予了0640的許可權,許可權隸屬於system,許可權組為package_info。

然後我們進入系統看一看是不是這樣:

/data/system # ls -l packages.list
-rw-r----- 1 system package_info 13627 1970-01-01 11:27 packages.list

有沒有發現原來Android底層許可權其實也不是很難理解嘛?

這時,又有一個問題了,建立檔案你可以這樣設定許可權,但系統檔案/資料夾的預設許可權又是從哪來的?這就要引入另外一個檔案了。

fs_config.c

寫過Android App的你肯定知道,App的一些資料都放在/data/data目錄下。正常情況下這個目錄是不可以訪問的:

/data/data $ ls
ls: .: Permission denied

我們看一下這個目錄的許可權:

/data/data $ ls -ld
drwxrwx--x 310 system system 12288 2019-01-11 09:51 .

我們作為shell使用者,只有一個x許可權, 當然訪問不了。那如果我要給shell賦予訪問許可權改怎麼改呢?

這就需要了解一下system/core/libcutils/fs_config.c檔案了。系統目錄和檔案的使用者組以及許可權都是在這個檔案裡定義的:

/* Rules for directories.
** These rules are applied based on "first match", so they
** should start with the most specific path and work their
** way up to the root.
*/
static const struct fs_path_config android_dirs[] = {
    { 00770, AID_SYSTEM, AID_CACHE,  0, "cache" },
    { 00500, AID_ROOT,   AID_ROOT,   0, "config" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-ephemeral" },
    { 00771, AID_ROOT,   AID_ROOT,   0, "data/dalvik-cache" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },
    { 00771, AID_SHELL,  AID_SHELL,  0, "data/local/tmp" },
    { 00771, AID_SHELL,  AID_SHELL,  0, "data/local" },
    { 01771, AID_SYSTEM, AID_MISC,   0, "data/misc" },
    { 00770, AID_DHCP,   AID_DHCP,   0, "data/misc/dhcp" },
    { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, "data/misc/shared_relro" },
    { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
    { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
    { 00750, AID_ROOT,   AID_SHELL,  0, "data/nativetest" },
    { 00750, AID_ROOT,   AID_SHELL,  0, "data/nativetest64" },
    { 00775, AID_ROOT,   AID_ROOT,   0, "data/preloads" },
    { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
    { 00755, AID_ROOT,   AID_SYSTEM, 0, "mnt" },
    { 00755, AID_ROOT,   AID_ROOT,   0, "root" },
    { 00750, AID_ROOT,   AID_SHELL,  0, "sbin" },
    { 00751, AID_ROOT,   AID_SDCARD_R, 0, "storage" },
    { 00755, AID_ROOT,   AID_SHELL,  0, "system/bin" },
    { 00755, AID_ROOT,   AID_SHELL,  0, "system/vendor" },
    { 00755, AID_ROOT,   AID_SHELL,  0, "system/xbin" },
    { 00755, AID_ROOT,   AID_ROOT,   0, "system/etc/ppp" },
    { 00755, AID_ROOT,   AID_SHELL,  0, "vendor" },
    { 00777, AID_ROOT,   AID_ROOT,   0, "sdcard" },
    { 00755, AID_ROOT,   AID_ROOT,   0, 0 },
};

/* Rules for files.
** These rules are applied based on "first match", so they
** should start with the most specific path and work their
** way up to the root. Prefixes ending in * denotes wildcard
** and will allow partial matches.
*/
static const char conf_dir[] = "/system/etc/fs_config_dirs";
static const char conf_file[] = "/system/etc/fs_config_files";

static const struct fs_path_config android_files[] = {
    { 00440, AID_ROOT,      AID_SHELL,     0, "system/etc/init.goldfish.rc" },
    { 00550, AID_ROOT,      AID_SHELL,     0, "system/etc/init.goldfish.sh" },
    { 00550, AID_ROOT,      AID_SHELL,     0, "system/etc/init.ril" },
    { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/ppp/*" },
    { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/rc.*" },
    { 00440, AID_ROOT,      AID_ROOT,      0, "system/etc/recovery.img" },
    { 00444, AID_ROOT,      AID_ROOT,      0, conf_dir + 1 },
    { 00444, AID_ROOT,      AID_ROOT,      0, conf_file + 1 },
    { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app/*" },
    { 00644, AID_MEDIA_RW,  AID_MEDIA_RW,  0, "data/media/*" },
    { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-private/*" },
    { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-ephemeral/*" },
    { 00644, AID_APP,       AID_APP,       0, "data/data/*" },
    { 00640, AID_ROOT,      AID_SHELL,     0, "data/nativetest/tests.txt" },
    { 00640, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/tests.txt" },
    { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest/*" },
    { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/*" },

    /* the following two files are INTENTIONALLY set-uid, but they
     * are NOT included on user builds. */
    { 04750, AID_ROOT,      AID_SHELL,     0, "system/xbin/su" },
    { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/procmem" },

    /* the following files have enhanced capabilities and ARE included in user builds. */
    { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) | CAP_MASK_LONG(CAP_SETGID), "system/bin/run-as" },
    { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND), "system/bin/inputflinger" },

    /* Support FIFO scheduling mode in SurfaceFlinger. */
    { 00755, AID_SYSTEM,    AID_GRAPHICS,     CAP_MASK_LONG(CAP_SYS_NICE), "system/bin/surfaceflinger" },

    { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
    { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
    { 00755, AID_ROOT,      AID_SHELL,     0, "system/bin/*" },
    { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib/valgrind/*" },
    { 00755, AID_ROOT,      AID_ROOT,      0, "system/lib64/valgrind/*" },
    { 00755, AID_ROOT,      AID_SHELL,     0, "system/xbin/*" },
    { 00755, AID_ROOT,      AID_SHELL,     0, "system/vendor/bin/*" },
    { 00755, AID_ROOT,      AID_SHELL,     0, "system/vendor/xbin/*" },
    { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/bin/*" },
    { 00755, AID_ROOT,      AID_SHELL,     0, "vendor/xbin/*" },
    { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/*" },
    { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
    { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
    { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
    { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
    { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
};

感覺也不用多說,android_dirs[]負責資料夾的許可權配置,android_files[]負責檔案的許可權配置。

從裡面定義找找剛提到的/data/data目錄

{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/data" },

你看,就是這麼回事兒而已。如果要改成shell許可權的話,只需要這樣改就可以了:

{ 00771, AID_SYSTEM, AID_SHELL, 0, "data/data" },

當然,如果系統裡還要新增其他目錄、檔案需要指定許可權,只需要在這個檔案裡新增一行即可。

0100 Android App Permission

講到這裡,還遺留一個開頭提出的問題:

為什麼你在AndroidManifest檔案請求storage許可權你就可以訪問裝置檔案?

0010裡提到過,PackageManagerService在啟動後會掃描所有已經安裝的App,然後載入和解析他們的Androidmanifest檔案,生成packages.listpackages.xml等檔案。解析過程就包括了permission的解析與攔截。

frameworks/base/core/res/AndroidManifest.xml 這個檔案中定義了Permission攔截規則,下面列舉幾個:

<!-- Allows an application to read from external storage.
     <p>Any app that declares the {@link #WRITE_EXTERNAL_STORAGE} permission is implicitly
     granted this permission.</p>
     <p>This permission is enforced starting in API level 19.  Before API level 19, this
     permission is not enforced and all apps still have access to read from external storage.
     You can test your app with the permission enforced by enabling <em>Protect USB
     storage</em> under Developer options in the Settings app on a device running Android 4.1 or
     higher.</p>
     <p>Also starting in API level 19, this permission is <em>not</em> required to
     read/write files in your application-specific directories returned by
     {@link android.content.Context#getExternalFilesDir} and
     {@link android.content.Context#getExternalCacheDir}.
     <p class="note"><strong>Note:</strong> If <em>both</em> your <a
     href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
     minSdkVersion}</a> and <a
     href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
     targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
     grants your app this permission. If you don't need this permission, be sure your <a
     href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
     targetSdkVersion}</a> is 4 or higher.
     <p>Protection level: dangerous
-->
<permission android:name="android.permission.READ_EXTERNAL_STORAGE"
            android:permissionGroup="android.permission-group.STORAGE"
            android:label="@string/permlab_sdcardRead"
            android:description="@string/permdesc_sdcardRead"
            android:protectionLevel="dangerous" />

<!-- Allows applications to access information about Wi-Fi networks.
         <p>Protection level: normal
-->
<permission android:name="android.permission.ACCESS_WIFI_STATE"
            android:description="@string/permdesc_accessWifiState"
            android:label="@string/permlab_accessWifiState"
            android:protectionLevel="normal" />

<!-- @SystemApi Allows applications to set the system time.
    <p>Not for use by third-party applications. -->
<permission android:name="android.permission.SET_TIME"
            android:protectionLevel="signature|privileged" />

這裡看一下protectionLevel,normal是一般許可權,即不需要動態申請,直接在Manifest裡註冊即可獲得的許可權。dangerous是敏感許可權,需要動態申請告知使用者才能獲取的許可權。signature|privileged一般是系統priv-app才擁有的許可權,也就是擁有系統簽名的系統應用。

他的攔截規則大概是,如果App申請了signature|privileged許可權,但他是普通開發者的三方App,PMS就會將其從申請許可權的列表裡將該許可權刪除。這樣你的App實際上就沒有獲得對應的許可權了。

那麼,檔案屬性的許可權是怎麼和Permission聯絡起來的?不出意外,系統也有一個關聯檔案的定義frameworks/base/data/etc/platform.xml

<!-- This file is used to define the mappings between lower-level system
     user and group IDs and the higher-level permission names managed
     by the platform.

     Be VERY careful when editing this file!  Mistakes made here can open
     big security holes.
-->
<permissions>

    <!-- ================================================================== -->
    <!-- ================================================================== -->
    <!-- ================================================================== -->

    <!-- The following tags are associating low-level group IDs with
         permission names.  By specifying such a mapping, you are saying
         that any application process granted the given permission will
         also be running with the given group ID attached to its process,
         so it can perform any filesystem (read, write, execute) operations
         allowed for that group. -->

    <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="net_bt_stack" />
        <group gid="wakelock" />
    </permission>

    <permission name="android.permission.NET_TUNNELING" >
        <group gid="vpn" />
    </permission>

    <permission name="android.permission.INTERNET" >
        <group gid="inet" />
    </permission>

    <permission name="android.permission.READ_LOGS" >
        <group gid="log" />
    </permission>

    <permission name="android.permission.WRITE_MEDIA_STORAGE" >
        <group gid="media_rw" />
        <group gid="sdcard_rw" />
    </permission>

    <permission name="android.permission.ACCESS_MTP" >
        <group gid="mtp" />
    </permission>

    <permission name="android.permission.NET_ADMIN" >
        <group gid="net_admin" />
    </permission>

    <!-- The group that /cache belongs to, linked to the permission
         set on the applications that can access /cache -->
    <permission name="android.permission.ACCESS_CACHE_FILESYSTEM" >
        <group gid="cache" />
    </permission>

    <!-- RW permissions to any system resources owned by group 'diag'.
         This is for carrier and manufacture diagnostics tools that must be
         installable from the framework. Be careful. -->
    <permission name="android.permission.DIAGNOSTIC" >
        <group gid="input" />
        <group gid="diag" />
    </permission>

    <!-- Group that can read detailed network usage statistics -->
    <permission name="android.permission.READ_NETWORK_USAGE_HISTORY">
        <group gid="net_bw_stats" />
    </permission>

    <!-- Group that can modify how network statistics are accounted -->
    <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING">
        <group gid="net_bw_acct" />
    </permission>

    <permission name="android.permission.LOOP_RADIO" >
        <group gid="loop_radio" />
    </permission>

    <!-- Hotword training apps sometimes need a GID to talk with low-level
         hardware; give them audio for now until full HAL support is added. -->
    <permission name="android.permission.MANAGE_VOICE_KEYPHRASES">
        <group gid="audio" />
    </permission>

    <permission name="android.permission.ACCESS_FM_RADIO" >
        <!-- /dev/fm is gid media, not audio -->
        <group gid="media" />
    </permission>

    <!-- These are permissions that were mapped to gids but we need
         to keep them here until an upgrade from L to the current
         version is to be supported. These permissions are built-in
         and in L were not stored in packages.xml as a result if they
         are not defined here while parsing packages.xml we would
         ignore these permissions being granted to apps and not
         propagate the granted state. From N we are storing the
         built-in permissions in packages.xml as the saved storage
         is negligible (one tag with the permission) compared to
         the fragility as one can remove a built-in permission which
         no longer needs to be mapped to gids and break grant propagation. -->
    <permission name="android.permission.READ_EXTERNAL_STORAGE" />
    <permission name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <!-- ================================================================== -->
    <!-- ================================================================== -->
    <!-- ================================================================== -->

    <!-- The following tags are assigning high-level permissions to specific
         user IDs.  These are used to allow specific core system users to
         perform the given operations with the higher-level framework.  For
         example, we give a wide variety of permissions to the shell user
         since that is the user the adb shell runs under and developers and
         others should have a fairly open environment in which to
         interact with the system. -->

    <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />
    <assign-permission name="android.permission.WAKE_LOCK" uid="media" />
    <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />
    <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />
    <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" />

    <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="audioserver" />
    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="audioserver" />
    <assign-permission name="android.permission.WAKE_LOCK" uid="audioserver" />
    <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="audioserver" />
    <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="audioserver" />

    <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="cameraserver" />
    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="cameraserver" />
    <assign-permission name="android.permission.WAKE_LOCK" uid="cameraserver" />
    <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="cameraserver" />
    <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" />

    <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />

    <!-- This is a list of all the libraries available for application
         code to link against. -->

    <library name="android.test.runner"
            file="/system/framework/android.test.runner.jar" />
    <library name="javax.obex"
            file="/system/framework/javax.obex.jar" />
    <library name="org.apache.http.legacy"
            file="/system/framework/org.apache.http.legacy.jar" />

    <!-- These are the standard packages that are white-listed to always have internet
         access while in power save mode, even if they aren't in the foreground. -->
    <allow-in-power-save package="com.android.providers.downloads" />

    <!-- These are the standard packages that are white-listed to always have internet
         access while in data mode, even if they aren't in the foreground. -->
    <allow-in-data-usage-save package="com.android.providers.downloads" />

    <!-- These are the packages that are white-listed to be able to run as system user -->
    <system-user-whitelisted-app package="com.android.settings" />

    <!-- These are the packages that shouldn't run as system user -->
    <system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
</permissions>

這個檔案定義了所有許可權所屬的gid。從裡面找一下READ_EXTERNAL_STORAGE許可權,emmmm,什麼都沒做。這是因為6.0之後儲存許可權變成動態,需要使用者確認才可以獲取許可權,所以這裡不作處理。動態許可權這部分程式碼先不分析了,來看一下老版本的檔案:

<permission name="android.permission.READ_EXTERNAL_STORAGE" >
    <group gid="sdcard_r" />
</permission>

那明確了,READ_EXTERNAL_STORAGE許可權獲取的gid是sdcard_r,然後檢視上面的檔案定義,對應AID_SDCARD_R,數值是1028。實際上在7.0上得到的是sdcard_rw,即1015。

PMS在解析每個Permission時會根據這個檔案將Permission關聯的gid 加入到一個gid的陣列中去,從而硬體裝置所對應的裝置檔案就能被該應用程式訪問。這塊具體程式碼流程放到下一篇去分析。想自己跟程式碼的話可以從該函式看起:

private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
                                              String[] grantedPermissions) {
    for (int userId : userIds) {
        grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
    }

    // We could have touched GID membership, so flush out packages.list
    synchronized (mPackages) {
        mSettings.writePackageListLPr();
    }
}

回到儲存許可權,既然系統會根據permission給App新增合適的gid,那麼我們在看下內建儲存的許可權為

/sdcard # ls -ld
drwxrwx--x 27 root sdcard_rw 4096 1970-01-01 18:30 .

所以獲得sdcard_rw許可權組的應用才可以訪問內建儲存。

到此Android許可權的底層實現原理簡單介紹完了,不過目前這裡還留有一個坑,那就是我在測試App裡新增STORAGE許可權後,安裝到裝置裡,通過檢視程序屬性,發現:

# ps | grep softard
u0_a53    3530  638   969076 30720 binder_thr b07244fc S com.softard.test
# cat /proc/3530/status
Name:   com.softard.test
State:  S (sleeping)
Tgid:   3530
Pid:    3530
PPid:   638
TracerPid:  0
Uid:    10053   10053   10053   10053
Gid:    10053   10053   10053   10053
Ngid:   0
FDSize: 256
Groups: 9997 50053

Groups沒有對應的gid,但是程式的確可以訪問/sdcard

然後我又給App系統簽名,作為系統應用放進去再看,

# ps | grep soft
system    3560  629   985352 49184 SyS_epoll_ abea53b8 S com.softard.test
# cat /proc/3560/status
Name:   com.softard.test
State:  S (sleeping)
Tgid:   3560
Pid:    3560
PPid:   629
TracerPid:  0
Uid:    1000    1000    1000    1000
Gid:    1000    1000    1000    1000
Ngid:   0
FDSize: 256
Groups: 1000 1007 1010 1015 1021 1023 2002 2950 3001 3002 3003 3009 9997 41000

這時候Groups有sdcard_rw許可權了。然後從設定裡手動關掉儲存許可權,App無法讀取檔案,再次檢查gid發現這個1015依舊存在。

WTF,7.0的表現跟5.0完全不一樣?又是一個坑…後面抽空再填吧...

行吧,這篇概覽就這樣了,後面就開始從眼花繚亂的原始碼角度去看這一切的實現。

0101 Reference

Android檔案屬性的許可權和Permission的聯絡