Android将安全设计贯穿系统架构的各个层面,覆盖系统内核、虚拟机、应用程序框架层以及应用层各个环节,力求在开放的同时,也最大程度地保护用户的数据、应用程序和设备的安全。Android安全模型主要提供以下几种安全机制:



android应用内安装其他权限_android引导用户打开位置权限

从技术架构角度来看,Android安全模型基于Linux操作系统内核安全性,有基本的用户和文件访问隔离,在这个基础上辅以内存管理技术和进程间通信机制,来适应移动端处理器性能与内存容量的限制。在应用层面,使用显式定义且经用户授权的权限控制机制,系统化地规范并强制各类应用程序的行为准则与权限许可,可以看到权限机制在安全模型中所处的位置。

那么为什么有权限机制?

我们知道 Android 应用程序是沙箱隔离的,每个应用都有一个只有自己具有读写权限的专用数据目录,应用只能访问自己的文件和一些设备上全局可访问的资源。

那如果我需要访问系统服务呢?这就有了 Android 的权限机制。所以根本原因一是沙箱隔离,二是服务支持的需求。

在本文开始之前,先抛出几个问题思考,什么是权限?权限是怎么进行赋予的?怎么判断一个组件是否拥有特定的权限?权限维护在哪?Android 6.0 运行时权限是什么原理?权限赋予后还能再更改吗?

带着问题和结构图,我们一一解开疑惑。



android应用内安装其他权限_android引导用户打开位置权限_02

权限的本质

什么是权限?在 Android 中,一个权限,本质上是一个字符串,一个可以表示执行特定操作的能力的字符串。比如说:访问 SD 卡的能力,访问通讯录的能力,启动或访问一个第三方应用中的组件的能力。

使用 pm list permissions -f 命令可以详细查看 Android 所有预定义的权限。我们挑一个权限出来:

+ permission:android.permission.DELETE_PACKAGES
package:android
label:
description:
protectionLevel:signature|privileged

可以看到,一个权限的信息包括:定义的包名、标签、描述和保护级别,保护级别,嗯,这个我们需要详细讲讲。因为不是应用声明了权限,就一定会全部被自动赋予的,保护级别决定了包管理器是否应该赋予组件所申请的权限。

权限的级别

normal 级别。权限保护级别的默认值,无须用户确认,只要声明了,就自动默默授权。如:ACCESS_NETWORK_STATE。

dangerous 级别。赋予权限前,会弹出对话框,显式请求权限。如:READ_SMS。因为 Android 需要在安装时赋予权限,所以安装的确认对话框,也会显示列出权限清单。

signature 级别。signature 级别的权限是最严格的权限,只会赋予与声明权限使用相同证书的应用程序。

以系统内置 signature 级别权限为例,Android 系统应用的签名由平台密钥签发,默认情况下源码树里有 4 个不同的密钥文件:platform、shared、media 和 testkey。所有核心平台的包(如:设置、电话、蓝牙)均使用 platform 密钥签发;搜索和通讯录相关的包使用 shared 签发;图库和媒体相关的包使用 media 密钥签发;其他的应用使用 testkey 签发。定义系统内置权限的 framework-res.apk 文件是使用平台密钥签发的,因此任何试图请求 signature 级别内置权限的应用程序,需要使用与框架资源包相同的密钥进行签名。

signatureOrSystem 级别。可以看做是一种折中的级别,可被赋予与声明权限具有相同签名证书密钥的应用程序(同 signature 级别)或者系统镜像的部分应用,也就是说这允许厂商无须共享签名密钥。Android 4.3 之前,安装在 system 分区下的应用会被自动赋予该保护级别的权限,而 Android 4.4 之后,只允许安装在 system/priv-app/ 目录下的应用才能被主动赋予。

权限的管理

那么,系统内置权限,自定义权限,是怎么维护和管理的?

在每个应用安装时,权限就已经赋予了,系统使用包管理服务来管理权限。打开我们系统目录下的 /data/system/packages.xml,可以看到文件包含了所有已定义的权限列表和所有 apk 的包信息,这可以看做是包管理服务维护的一个已安装程序的核心数据库,这个数据库,随着每次应用安装、升级或卸载而进行更新。

主要属性如图:



android应用内安装其他权限_android应用内安装其他权限_03

标签内,定义了目前系统中的所有权限,分为系统内置的(package 属性为 android 的)和 apk 自定义的(package 属性为 apk 的包名)。根元素 含有每个 apk 的核心属性,以一个应用程序的条目为例:

Android 6.0 以下 packages.xml

name="com.feelschaotic.demo"
codePath="/data/app/com.feelschaotic.demo-1"
nativeLibraryPath="/data/app/com.feelschaotic.demo-1/lib"
primaryCpuAbi="x86"
flags="5783366"
ft="16349bcc4d0"
it="16349bcc752"
ut="16349bcc752"
version="8220"
userId="10097">

Android 6.0 及以上 packages.xml

name="com.feelschaotic.demo"
codePath="/data/app/com.feelschaotic.demo-Gi5ksdF6mUDLakfOugCcwQ=="
nativeLibraryPath="/data/app/com.feelschaotic.demo-Gi5ksdF6mUDLakfOugCcwQ==/lib"
primaryCpuAbi="x86"
publicFlags="945307462"
privateFlags="0"
ft="16348dc3870"
it="16343f1d6aa"
ut="16348dc4c4d"
version="8220"
userId="10102">

可以发现,其他信息没有太大差异,但是权限列表中,部分在 Android 6.0 被标记为高危的权限都不在 里了,如:READ_SMS、READ_EXTERNAL_STORAGE、CALL_PHONE 等。这是怎么一回事呢?

Android 6.0 之前,权限都是在安装时自动赋予的,不卸载应用的情况下,不能更改或撤销(实际上有些厂商打开了appOps,使得 6.0 以下也能更改权限,这种特殊情况我们不讨论)。而 Android 6.0 版本对 permission 的管理做了部分改动,针对 dangerous 级别,不再安装的时候赋予权限,而是在运行时动态申请。

我们大胆地推断下,packages.xml 里保留的是不会再变更的权限,运行时权限一定是另外单独地维护。我们在 data/system 目录下来了个全盘搜索,找到了 /data/system/users/0/runtime-permissions.xml。

...

没错了,就是这个文件。里面就记录着运行时权限的授予和拒绝状态。申请时,申请的结果会动态修改 granted 值。管理权限的仍然是 PMS,只不过 Android 6.0 之后新增了 runtime-permissions.xml 数据库。

权限的赋予

我们知道,Android 应用安装时,会被分配一个唯一的 UID,应用启动时,包管理器会设置新建进程的 UID 和 GID 为应用程序的 UID。如果应用已经被赋予了额外的权限,就把这些权限映射成一组 GID,作为补充 GID 分配给进程。低层就可以依赖于进程的 UID、GID 和补充 GID 来决定是否赋予权限了。

那么,上面流程的重点:

  1. 权限是如何映射到 OS 层的 UID、GID 上的呢?
  2. 映射完是怎么分配给进程的?
  3. 低层是怎么判断是否赋予权限的?

我们一个个来看,首先第一个,权限是如何映射的?内置权限到 GID 的映射是定义在 /etc/permission/platform.xml 中。

···
···

值得注意的是:READ_EXTERNAL_STORAGE 这种运行时权限,在 Android 6.0 之后已经不会映射到 gid 了。动态赋予,动态申请,也就不需要映射了。



包管理器在启动时读取 platform.xml,并维护「权限-GID」对应的列表。当它给安装中的包授权时,会把权限对应的 GID 加入到该应用进程的补充 GID 中。我们举个例子,看下进程的属性:adb shell ps 拿到想要查找的进程的 PID。

USER PID PPID VSZ RSS WCHAN ADDR S NAME
...
u0_a88 2149 1624 1929916 112340 SyS_epoll_wait 0 S com.feelschaotic.demo

接着 adb shell -> cd proc -> cd 2149 (2149 为进程 PID,不固定) -> cat status



android应用内安装其他权限_android引导用户打开位置权限_04

我们关注如下两行:

Gid: 10050 10050 10050 10050Groups: 1006 1015 1028 3002 3003 50050

这里我们便看到了系统进程的权限配置信息,这里的数字具体代表意义,可以在Androidsystemcoreincludeprivateandroid_filesystem_config.h 里面看到,其部分内容如下:

#define AID_CAMERA 1006 /* camera devices */ 
#define AID_SDCARD_RW 1015 /* external storage write access */
#define AID_SDCARD_R 1028 /* external storage read access */
···
static const struct android_id_info android_ids = {
{ "camera