详解Android 安全机制
public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message)
public void enforceUriPermission(Uri uri, String readPermission, String writePermission,int pid, int uid, int modeFlags, String message)
2.2.2 实现分析
ContextImpl.java 中提供的 API ,其实都是由 ActivityManagerService 中的如下几个接口进行的封装。
public int checkPermission(String permission, int pid, int uid) throws RemoteException; // 主要用于一般的 permission 检查
public int checkUriPermission(Uri uri, int pid, int uid, int mode) throws RemoteException; // 主要用于 Content Uri 的 permission 检查
n checkPermission 的实现分析
1. 如果传入的 permission 名称为 null ,那么返回 PackageManager.PERMISSION_DENIED 。
2. 判断调用者 uid 是否符合要求 。
1 ) 如果 uid 为 0 ,说明是 root 权限的进程,对权限不作控制。
2 ) 如果 uid 为 system server 进程的 uid ,说明是 system server ,对权限不作控制。
3 ) 如果是 ActivityManager 进程本身,对权限不作控制。
4 )如果调用者 uid 与参数传入的 req uid 不一致,那么返回 PackageManager.PERMISSION_DENIED 。
3. 如果通过 2 的检查后,再 调用 PackageManagerService.checkUidPermission ,判断 这个 uid 是否拥有相应的权限,分析如下 。
1 ) 首先它通过调用 getUserIdLP ,去 PackageManagerService.Setting.mUserIds 数组中,根据 uid 查找 uid (也就是 package )的权限列表。一旦找到,就表示有相应的权限。
2 ) 如果没有找到,那么再去 PackageManagerService.mSystemPermissions 中找。这些信息是启动时,从/system/etc/permissions/platform.xml 中读取的。这里记录了一些系统级的应用的 uid 对应的 permission 。
3 )返回结果 。
n 同样 checkUriPermission 的实现 主要在 ActivityManagerService 中,分析如下:
1. 如果 uid 为 0 ,说明是 root 用户,那么不控制权限。
2. 否则,在 ActivityManagerService 维护的 mGrantedUriPermissions 这个表中查找这个 uid 是否含有这个权限,如果有再检查其请求的是读还是写权限。
关于签名机制,其实分两个阶段。
包扫描阶段需要进行完整性和证书的验证。普通 package 的签名和证书是必须要先经过验证的。具体做法是对 manifest 下面的几个文件进行完整性检查。完整性检查包括这个 jar 包中的所有文件。如果是系统 package 的话,只需要使用 AndroidMenifest.xml 这个文件去提取签名和验证信息就可以了。
在权限创建阶段。如果该 package 来自 system img (系统 app ),那么 trust it ,而且使用新的签名信息去替换就的信息。前提是如果这个 package 与其它 package 共享一个 uid ,那么这个共享 uid 对应的 sharedUser 中保存的签名与之不一致,那么签名验证失败。有些时候系卸载一个 app ,但是不删除数据,那么其 PackageSettings 信息会保留,其中会保存签名信息。这样再安装是就会出现不一致。
3.1 Android Package 签名原理
android 中系统和 app 都是需要签名的。可以自己通过 development/tools/make_key 来生成公钥和私钥。
android 源代码中提供了工具 。/out/host/linux-x86/framework/signapk.jar 来进行手动签名。签名的主要作用在于限制对于程序的修改仅限于同一来源。系统中主要有两个地方会检查。如果是程序升级的安装,则要检查新旧程序的签名证书是否一致,如果不一致则会安装失败;对于申请权限的 protectedlevel 为 signature 或者 signatureorsystem 的,会检查权限申请者和权限声明者的证书是否是一致的。签名相关文件可以从 apk 包中的 META-INF 目录下找到。
signapk.jar 的源代码在 build/tools/signapk ,签名主要有以下几步:
l 将除去 CERT.RSA , CERT.SF , MANIFEST.MF 的所有文件生成 SHA1 签名
首先将除了 CERT.RSA , CERT.SF , MANIFEST.MF 之外的所有非目录文件分别用 SHA-1 计算摘要信息,然后使用 base64 进行编码,存入MANIFEST.MF 中。 如果 MANIFEST.MF 不存在,则需要创建。存放格式是 entry name 以及对应的摘要
l 根据 之前计算的 SHA1 摘要信息,以及 私钥生成 一系列的 signature 并写入 CERT.SF
对 整个 MANIFEST.MF 进行 SHA1 计算,并将摘要信息存入 CERT.SF 中 。然后对之前计算的所有摘要信息使用 SHA1 再次计算数字签名,并写入 CERT.SF 中。
l 把公钥和签名信息写入 CERT.RST
把之前整个的签名输出文件 使用私有密钥计算签名。同时将签名结果,以及之前声称的公钥信息写入 CERT.RSA 中保存。
3.2 Package 的签名验证
安装时对一个 package 的签名验证的主要逻辑在 JarVerifier.java 文件的 verifyCertificate 函数中实现。 其主要的思路是通过提取 cert.rsa 中的证书和签名信息,获取签名算法等信息,然后按照之前对 apk 签名的方法进行计算,比较得到的签名和摘要信息与 apk 中保存的匹配。
第一步。提取证书信息,并对 cert.sf 进行完整性验证。
1. 先找到是否有 DSA 和 RSA 文件 ,如果找到则对其进行 decode ,然后读取其中的所有的证书列表(这些证书会被保存在 Package 信息中,供后续使用)。
2. 读取这个文件中的签名数据信息块列表,只取第一个签名数据块。读取其中的发布者和证书序列号。
3. 根据证书序列号,去匹配之前得到的所有证书,找到与之匹配的证书。
4. 从之前得到的签名数据块中读取签名算法和编码方式等信息
5. 读取 cert.sf 文件,并计算整个的签名,与数据块中的签名(编码格式的)进行比较,如果相同则完整性校验成功。
第二步。使用 cert.sf 中的摘要信息,验证 MANIFEST.MF 的完整性。
在 cert.sf 中提取 SHA1-Digest-Manifest 或者 SHA1-Digest 开头的签名 数据块 ( -Digest-Manifest 这个是整个 MANIFEST.MF 的摘要 信息,其它的是 jar 包中其它文件的摘要信息 ), 并逐个对这些数据块 进行验证。验证的方法是,现将 cert.sf 看做是很多的 entries ,每个entries 包含了一些基本信息,如这个 entry 中使用的摘要算法( SHA1 等),对 jar 包中的哪个文件计算了摘要,摘要结果是什么。 处理时先找到每个摘要数据开中的文件信息,然后从 jar 包中读取,然后使用 -Digest 之前的摘要算法进行计算,如果计算结果与摘要数据块中保存的信息的相匹配,那么就完成验证。
pid控制相关文章:pid控制原理
评论