1. 程式人生 > >[日更-2019.5.10、11、12] Android 系統的安全性分析(三)--Linux層面上的安全措施

[日更-2019.5.10、11、12] Android 系統的安全性分析(三)--Linux層面上的安全措施

宣告

  1. 最近工作上涉及到對Android系統安全性的改造,在改造之前先分析整理下目前Android系統自身的安全性;
  2. 參考了一些文章及書籍,在這裡大部分是對別人描述的提煉,我挑出一些對我有用的內容整理;
  3. 本文使用的程式碼是LineageOS的cm-14.1,對應Android 7.1.2,可以參考我的另一篇部落格:如何下載Nexus5的LineageOS14.1(cm-14.1)系統原始碼並編譯、刷機

0 寫在前面的

    Android在Linux的基礎上構建出了一個富框架(rich framework),但在它最核心的層面上,還要靠Linux來完成所有操作。它沿用了Linux提供的安全特性:許可權(permission)、權能(capability)、SELinux和其它一些底層安全保護措施。

1 Android繼承了Linux的安全模型

    Linux的安全模型的基本規定如下:

  • 每個使用者都有一個數字形式表示的UID:具體的使用者名稱是什麼對系統來說無關緊要,因為使用者名稱是給使用者看的(這些使用者名稱是專門分配給配置檔案和它們所屬的目錄的所有者使用的)。注意:兩個使用者名稱可能會共享同一個UID,但出現這種情況,從系統的角度講,實際上只是意味著一個UID有兩套使用者名稱/密碼而己。

  • 每個使用者都有一個數字形式表示的主組(primary group) ID: 和使用者名稱一樣,使用者組的名稱也是無關緊要的。注意:有些GID也是系統保留的。

  • 使用者還可以加入其他的組: 傳統上,使用者加入其他組後,它是這些組中的成員的這一層關係是記錄在/etc/group檔案中的。在該檔案中會列出:所有組的名稱、GID和所有加入了某個組,但又不是把它作為“主組”的組成員。

  • 檔案的訪問許可權由特定使用者(owner)、組(group)和“其他人員”(other)三部分組成:不論是檔案還是目錄,它們的訪問許可權都是以這種表達能力非常有限的模型表示的。由於這一模型的侷限性,只能通過建立專門的使用者組的方式來規定檔案的訪問許可權。

maxingrong@soc02:~/LineageOS$ ls -al
drwxrwxr-x  37 maxingrong maxingrong  4096 Feb 14 14:47 .
drwxr-xr-x  28 maxingrong maxingrong  4096 Jun  6 10:35 ..
drwxrwxr-x  12 maxingrong maxingrong  4096 Feb 14 11:26 cts
drwxrwxr-x  10 maxingrong maxingrong  4096 May  6 06:59 dalvik
drwxrwxr-x  15 maxingrong maxingrong  4096 Feb 14 11:29 hardware
drwxrwxr-x   3 maxingrong maxingrong  4096 Feb 14 13:26 kernel
drwxrwxr-x  18 maxingrong maxingrong  4096 Feb 14 11:29 libcore
drwxrwxr-x   5 maxingrong maxingrong  4096 May 27 00:17 libnativehelper
drwxrwxr-x  12 maxingrong maxingrong  4096 Feb 14 11:29 lineage
....
  • 在Linux中,幾乎所有的東西都是以檔案的形式來訪問的: 所以,系統資源的訪問許可權也是遵循檔案訪問許可權約定的,表示相應裝置的檔案的訪問許可權的表示方式和普通檔案是完全一樣的,而且使用者也可以像修改普通檔案的訪問許可權一樣,用chown/chgrp/chmod命令修改這些檔案的訪問許可權。

  • UID 0是無所不能的,即root使用者:由於檢查檔案訪問許可權的具體實現方式,UID 0實際上能夠在任何情況下通過檢查,root 使用者在系統中擁有絕對的權力。

  • 二進位制可執行檔案chomd能讓程式以另一個使用者的許可權(或者加入另一個使用者組)執行,也就是Linux中的set-user-ID和set-group-ID: 利用二進位制可執行檔案chmod修改的程式會被自動授予指定的許可權。這一機制乍一看好像是個設計缺陷,但它實際上是個讓普通使用者能夠臨時切換執行特權操作[比如,切換某個使用者的UID(su 命令)或者口令(password 命令)]的系統特性。注意:為了預防經過修改的二進位制可執行檔案被濫用,如果這些(己經被授權了的)二進位制可執行檔案被複制或移動到了別處,複製/移動過去的檔案中的setuid 和setgid許可權標誌位會被清零(也就是不再擁有特權了)。

給檔案加SUID和SUID的命令如下:
chmod u+s filename 設定SUID位
chmod u-s filename 去掉SUID設定
chmod g+s filename 設定SGID位
chmod g-s filename 去掉SGID設定

    Android 也繼承了這一經典模型只不過在用法上有一點區別: 在Android 中,“使用者”這個概念是分配給各個應用,而不是分配給使用計算機的人的。突然之間,原本用來區分共享同一個UNIX 伺服器的人類使用者的方法,也被用在了應用身上,並且也用相同的隔離措施限定了它們可以使用的許可權。一個“使用者”不能訪問另一個“使用者”使用者的檔案,目錄或是程序一一這種嚴密的隔離,使得可以同時併發地執行多個應用,並且使這些應用不會相互影響。

    當一個應用初次安裝時,PackageManager會把一個唯一的UID分配給它,這個ID也被稱為“應用ID ”。這個ID的取值範圍在10000~9000內,Android 的C執行時庫bionic會自動把這個ID對映為app_XXX或u_XXX這種更方便人類閱讀的格式。

1.1 系統定義的AID

    Android系統保留了數字較小的UID (1000-9999),專供系統使用。這部分UID中只有一部分被使用了,相關定義寫在原始碼目錄:~/LineageOS/system/core/include/private/android_filesystem_config.h檔案中:

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* This file is used to define the properties of the filesystem
** images generated by build tools (mkbootfs and mkyaffs2image) and
** by the device side of adb.
*/

#ifndef _ANDROID_FILESYSTEM_CONFIG_H_
#define _ANDROID_FILESYSTEM_CONFIG_H_

#include <sys/cdefs.h>
#include <sys/types.h>
#include <stdint.h>

#if defined(__ANDROID__)
#include <linux/capability.h>
#else
#include "android_filesystem_capability.h"
#endif

#define CAP_MASK_LONG(cap_name)  (1ULL << (cap_name))

/* 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_QCOM_DIAG          2950  /* access to QTI diagnostic resources */
#define AID_RFS                2951  /* Remote Filesystem for peripheral processors */
#define AID_RFS_SHARED         2952  /* Shared files for Remote Filesystem for peripheral processors  */

#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 */

#define AID_RFS_OLD          3012  /* DEPRECATED OLD ID FOR RFS, DO NOT USE */
#define AID_RFS_SHARED_OLD   3013  /* DEPRECATED OLD ID FOR RFS-SHARED  */

/* 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_SENSORS       3011 /* access to /dev/socket/sensor_ctl_socket & QCCI/QCSI */


#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 */

#if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
/*
 * Used in:
 *  bionic/libc/bionic/stubs.cpp
 *  external/libselinux/src/android.c
 *  system/core/logd/LogStatistics.cpp
 *  system/core/init/ueventd.cpp
 *  system/core/init/util.cpp
 */
struct android_id_info {
    const char *name;
    unsigned aid;
};

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, },

    { "qcom_diag",     AID_QCOM_DIAG, },

    { "rfs",           AID_RFS, },
    { "rfs_shared",    AID_RFS_SHARED, },

    { "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, },
    { "sensors",       AID_SENSORS, },

    { "rfs_old",           AID_RFS_OLD, },
    { "rfs_shared_old",    AID_RFS_SHARED_OLD, },

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

#define android_id_count \
    (sizeof(android_ids) / sizeof(android_ids[0]))

struct fs_path_config {
    unsigned mode;
    unsigned uid;
    unsigned gid;
    uint64_t capabilities;
    const char *prefix;
};

/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */

__BEGIN_DECLS

/*
 * Used in:
 *  build/tools/fs_config/fs_config.c
 *  build/tools/fs_get_stats/fs_get_stats.c
 *  system/extras/ext4_utils/make_ext4fs_main.c
 *  external/squashfs-tools/squashfs-tools/android.c
 *  system/core/cpio/mkbootfs.c
 *  system/core/adb/file_sync_service.cpp
 *  system/extras/ext4_utils/canned_fs_config.c
 */
void fs_config(const char *path, int dir, const char *target_out_path,
               unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities);

ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc);

__END_DECLS

#endif
#endif

下表中給出部分UID的巨集定義以及它們在Android中的作用,這些UID大多也同時被用作GID:通過加入這些次級組,system_server、adb、installd及其他一些系統程序就能獲得這些組對應的系統檔案或裝置的訪問許可權,這樣是相當簡單有效的策略

        

        

        

        

    Android的系統屬性也依賴於UID進行訪問控制一一init的property_service socket 是通過各種系統屬性的名稱空間(namespace)來進行訪問限制的。同樣的方法也被用來保證servicemanager(程序間通)賴以實現的關鍵服務)的安全性。儘管Binder最終是通過uid/pid模型提供安全保障的,但servicemanager也能夠把眾所周知的服務的名稱的查詢限制在指定uid範圍內一一儘管uid O 或SYSTEM 使用者總是例外的。

1.2 執行嚴格網路許可權控制時使用的Android GID

    當CONFIG_PARANOID_ANDROID被置為l時,Android 核心還能認出在3000到3999這個區間裡的GID。這時,通過在核心中的套接字處理程式碼中強制新增GID檢查的方式,使得與網路訪問相關的所有功能都只能被這些GID或其組成員使用。注意,netd可以撤銷這些設定,因為它是以root身份執行的。下表中是己知的與網路相關的AID。

        

1.3 服務分離

    從Android 4開始,Android引入了“服務分離”(isolated service)這一概念。它使應用能把它的服務分離到一個完全隔離的區域(即在另一個擁有獨立UID的程序裡)執行。被分離出來的服務使用的是99000到99999(AID_ISOLATED_START從AID_ISOLATED_END)這個區間裡的UID,servicemanager會拒絕所有來自這些程序的請求,其結果就是:這些程序不能使用任何系統服務,只能完成一些處理記憶體中資料的操作。這些服務主要是在Web瀏覽器之類的應用中使用,比如:Chrome。

1.4 擁有者為root 使用者的程序

    就像在Linux系統中一樣,root使用者(UID為0的使用者)是無所不能的,但Android一直都在限制具有root許可權的可執行檔案的存在,為的是貫徹最小許可權原則。之前有一些Android系統的root工具就是利用了root所擁有的程序中的漏洞(特別是vold,這個程序常被用作這類跳板)來root手機/平板電腦的,所以Google希望通過減少root所擁有的程序的數目的方式,縮小Android系統的受攻擊面。比如installd,這個程序以前是擁有root許可權的,不過從Android4.4開始,它的root許可權就被去掉了。

    不過把全部root所擁有的程序的root許可權都去掉好像也是不可能的: 至少init程序、Zygote程序必須具有root許可權。你可以用下面這條命令來列出你的手機中所有root 所擁有的程序:

ps | grep "root" | grep -v "2"

    其中,“ grep -v " 命令的作用是忽略掉所有PPID 為2 的核心執行緒。下表列出的是,在cm14-1 Android系統中預設以root許可權執行的服務。

        

        

    以上這些root許可權的程序都是AOSP中所必須的,還有些具有root許可權二進位制可執行檔案是有裝置商內建進來的,它們在很大程度上擴大Android系統的受攻擊面。畢竟AOSP的root許可權可執行檔案是開源的,可以根據程式碼分析它的安全性,但廠商塞進來的這些二進位制可執行檔案都是閉源的,而且為了實現某些功能,有些廠商還會犧牲安全性。

    我們可以預見到,最終Android只會讓那些必須擁有root許可權的服務保有root許可權,而其它的服務都會像installd一樣逐漸移除它的root許可權。為了實現這一點,Android就需要採用Linux另外一個安全特性——Linux能力

2 Linux能力

    如果一個set-user-ID的二進位制可執行檔案是可信的,那麼至少在理論上這一模型還是能有效工作的。不過在實踐中, SetUID 天生就有一些安全隱患: 如果一個set-user-ID的二進位制可執行檔案中被發現存在安全漏洞,攻擊者就能借助它取得root許可權。常見的漏洞利用方式包括: 利用符號連結、競爭條件(藉助存在漏洞的程式覆蓋系統的配置檔案)和程式碼注入(導致存在漏洞的程式執行一個擁有root許可權的shell)。

    Linux能力提供了一個能解決這一問題的解決方案一一把root許可權拆分成若干種各不相交的子許可權,每種許可權用bitmask中的一個bit位來表示。通過對這個bitmask中的各個標誌位進行設定,可以讓相關程式能夠執行與指定許可權對應的特權操作,同時又將該程式的許可權限制在被授予的許可權範圍內,使之不能執行其他型別的特權操作。這使得Linux能力成了“最小許可權原則”的一種實現方式。下圖可以解釋這一點:

        

    把許可權限制在一個子集中一一隻賦予那些絕對必須的許可權,同時保留其他的許可權,極大地增強了安全性。即便給定的應用程式或使用者最終是惡意的(或者因為程式碼注入而執行了不該執行的操作),危害的範圍也會被限制在一定的範圍之內。權能就像是一個沙箱,允許應用執行設計所需的操作一一同時又防止它為所欲為,危害系統安全。事實上,能力還有個很好的副作用:我們能在root 賬號的使用者並不是完全值得信任時,使用能力限制root 使用者本身的行為。

    init程序仍然會以root許可權啟動大部分Android 服務,在剛開始啟動時這些服務程序的能力標誌位確實是全部為true 的(bitmask為 Oxffffffffffffffff)。但是在這些服務程序開始真正做些什麼之前, 它們會把自己的能力降下來,只保留它們必須使用的那部分能力。installd就是這樣一個堅持最小許可權原則的很好的例子:它會確保把除了安裝包(package)所必需的許可權之外的所有許可權都交出去,其所在原始碼目錄:~/LineageOS/frameworks/native/cmds/installd/commands.cpp

static void drop_capabilities(uid_t uid) {
    if (setgid(uid) != 0) {
        ALOGE("setgid(%d) failed in installd during dexopt\n", uid);
        exit(64);
    }
    if (setuid(uid) != 0) {
        ALOGE("setuid(%d) failed in installd during dexopt\n", uid);
        exit(65);
    }
    // drop capabilities
    struct __user_cap_header_struct capheader;
    struct __user_cap_data_struct capdata[2];
    memset(&capheader, 0, sizeof(capheader));
    memset(&capdata, 0, sizeof(capdata));
    capheader.version = _LINUX_CAPABILITY_VERSION_3;
    if (capset(&capheader, &capdata[0]) < 0) {
        ALOGE("capset failed: %s\n", strerror(errno));
        exit(66);
    }
}

    使用能力最多的使用者無疑就是system_server 了。因為儘管這個程序的所有者是system ,但在執行許多它的普通操作時,還是需要root許可權的。下表列出了各種Linux的能力,以及使用這些能力的Android程序。

        

    我可以進入手機檔案系統中檢視我的手機中system_server的程序狀態(因為我此時手機執行的system_server程序的PID為820,所以檢視其中的status檔案,從中可以看出四個關於Linux能力的bitmask):

hammerhead:/proc/820 $ cat status
Name:	system_server
State:	S (sleeping)
Tgid:	820
Pid:	820
PPid:	214
TracerPid:	0
Uid:	1000	1000	1000	1000
Gid:	1000	1000	1000	1000
FDSize:	512
Groups:	1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1018 1021 1032 3001 3002 3003 3006 3007 3009 3010    #所屬的組
VmPeak:	 1864196 kB
VmSize:	 1810292 kB
VmLck:	       0 kB
VmPin:	       0 kB
VmHWM:	  138948 kB
VmRSS:	  129900 kB
VmData:	  268800 kB
VmStk:	    8192 kB
VmExe:	      20 kB
VmLib:	   87020 kB
VmPTE:	     478 kB
VmSwap:	       0 kB
Threads:	123
SigQ:	2/12268
SigPnd:	0000000000000000
ShdPnd:	0000000000000000
SigBlk:	0000000000001204
SigIgn:	0000000000000000
SigCgt:	2000000200008cf8
CapInh:	0000000000000000		#可以被子程序繼承的能力
CapPrm:	0000001006897c20		#被授予該程序的能力
CapEff:	0000001006897c20		#活躍有效的能力那些被投予該程序, 而且也是該程序明確需要的能力)
CapBnd:	fffffff000000000		#權能範圍
Cpus_allowed:	f
Cpus_allowed_list:	0-3
voluntary_ctxt_switches:	7797
nonvoluntary_ctxt_switches:	2519

    從Android4.4開始,Zygote會去呼叫prctl(PR_CAPSET_DROP)和prctl(PR_SET_NO_NEW_PRIVS),以確保不會有其他能力新增給它的子程序(也就是使用者開啟的各種應用)。可以預見,vold和netd將來也都會主動降低自己的許可權,只獲取部分必須的權能,而不是像現在這樣擁有root許可權。

    關於Linux的能力可以參考一本書《Linux/UNIX系統程式設計手冊(下冊)-第39章 能力》

3 SElinux (Security Enhanced Linux:安全加固的Linux)

    SELinux最初由NSA開發時,只是Linux的一個補丁集,後來被合併進了核心主線,以提供一個能夠根據預先定義的策略規則,對相關操作進行審查限制的強制訪問控制(MAC,Mandatory Access Control)框架類似Linux能力一樣,SELinux也實現了最小許可權原則,只是粒度更細。通過嚴格地防止(程序的)操作超出預定義的操作邊界,SELinux極大地增強了系統的安全態勢。只要程序的行為良好,就不會有任何問題。但如果程序行為不端(在大多數情況下,這可能是因為程序本身就是惡意的,也可能是因為程序被注入了惡意程式碼而在正常程序中出現惡意操作),SELinux 就會阻斷所有這些越界操作。

    SELinux一直到Android 4.4才被引入。現在SELinux預設被設為:對一些Android 服務(特別是對installd 、netd 、void 和zygote)啟用了強制(Enforcing)模式,而對所有其他程序仍使用寬容模式。一般而言,對每個域(domain)先使用寬容模式,以便在把它設為強制模式之前,對相關策略規則進行測試,一般在不知道怎麼配te檔案時會先這樣,根據SElinux的審計提示進行配置te檔案

3.1 策略規則

    SELinux所使用的基本方法是“打標籤”(大多數MAC框架所使用的基本方法)。所謂“標籤”(label)就是為資源[客體(object)]分配一個型別(type),或為程序[主體(subject )]分配一個安全域(security domain)。SELinux可以據此強制讓只有在同一個域中(打了同樣的標籤)的程序才能訪問對應的資源。根據策略規則,域也可以是有限的(confined),這時,除了被允許訪問的資源之外,程序就不能訪問任何其他資源了。

    策略也可以允許在某些情況下,給一些己經打了標籤的物件重新打新的標籤[relabelto和relabelfrom,也被稱為域切換(domain transition)],在一個可信程序(比如,Zygote) fork()出一個不可信的程序(實際上就是使用者安裝的應用)時,這一操作是絕對必要的。

    一個SELinux標籤實際上就是一個4元組,也就是一個user: role: type: level格式的字串。被打了同樣標籤(也就是處於同一個域中)的所有程序相互之間是等價的。目前SEAndroid中只定義了type,標籤應該總是u:r:domain:s 這種形式的。在Android 5中,SEAndroid策略為每個守護程序都定義了一個獨立的域(即每個守護程序都會有它自己的許可權和安全profile設定),而Android應用中的類也有對應的域, 下表中列出的就是這些域:

    

    

    所有的xxx_app域都是繼承自最基本的domainpp這個域的。這個域只允許執行一些最基本的行為,其中包括使用Binder、與Zygote通訊、surfraceflinger等。在AOSP的extemal/sepolicy目錄下,可以找到記錄所有域的詳細定義的各個.te(type enforcement)檔案。這些檔案中使用的是一種混合了關鍵宇(keyword)和巨集(來自temacros)的語法,定義了所有在這個域中允許或不允許執行的操作。

    extemal/sepolicy 目錄中的這些檔案形成了一個基線,所有裝置中使用的SELinux策略規則設定都是繼承自它的。Google不鼓勵廠商修改exteernal/sepolicy目錄中的這些檔案,而是鼓勵在它們的BoardConfig.mk檔案中通過新增四個特定變數的方式,自定義相關的SELinux策略規則。在這四個變數中:

  • BOARD_SEPOLICY_REPLACE、BOARD_SEPOLICY_UNION、BOARD_SEPOLICY_IGNORE分別是用來替代、新增和忽略策略規則中的指定.te檔案的;
  • BOARD_SEPOLICY_DIRS則是用來新增搜尋路徑,使應用能夠訪問存有應用自己的 .t e 檔案的目錄的。這樣做能夠減少因為檔案中出現錯誤,而在無意間改動了SELinux策略規則,進而產生安全漏洞的風險。

    移動裝置上存放的SELinux策略規則是將上述檔案編譯整合之後得到的二進位制資料檔案/sepolicy檔案。這一做法進一步提升了安全性, 因為根檔案系統是由initramfs mount上來的,而initramfs則是有數字簽名保護的(因而也就可以被認為是不可修改的)系統映象的一部分。

3.2 SELinux如何為資源打標籤、設定上下文

3.2.1 應用的上下文

3.2.2 檔案上下文

3.2.3 屬性上下文

4 真它值得注意的特性

    Android中也啟用了Linux中的其他一些特性,以增強安全性,特別是對於其他一些不安全的預設設定進行加固,包括:AT SECURE、地址空間佈局隨機化(ASLR)、核心加固、棧保護、資料執行保護、編譯時防護、seccomp-bpf等。

    這些太專業了,我也搞不懂