与iOS相比。Android最被人诟病的是其流畅性和安全性。

然而,从4.0開始,Android不遗余力地改善其流畅性。

特别是在即将公布的L版本号中,用ART替换了Dalvik,相信会越来越流畅。至于安全性,Android也没有遗忘。

从4.3開始,Android引入了一套基于SELinux的安全机制,称为SEAndroid,来加强系统安全性。接下来我们就对SEAndroid进行简要介绍和制定学习计划。

老罗的新浪微博:http://weibo.com/shengyangluo。欢迎关注!

在介绍SEAndroid安全机制之前,我们要先了解一下Android当前所採用的安全机制是什么。实际上,在前面从NDK在非Root手机上的调试原理探讨Android的安全机制一文中,我们已经介绍过Android系统的安全机制了。

因此。这里主要是进行一下总结。

在引进SEAndroid安全机制之前,Android系统的安全机制分为应用程序和内核两个级别。应用程序级别的安全机制就是我们通常说的Permission机制。一个应用假设须要訪问一些系统敏感或者特权资源,那么就必须要在AndroidManifest.xml配置文件里进行申请,而且在安装的时候由用户决定是否赋予对应的权限。

应用安装过后。通常是通过系统服务来间接使用系统敏感或者特权资源的。这样系统服务在代表应用使用这些资源之前。就会先检查应用之前是否已经申请过对应的权限。

假设已经申请过,那么就直接放行。否则的话,就拒绝运行。

内核级别的安全机制就是传统的Linux UID/GID机制。在Linux中,每个用户都拥有一个用户ID,而且也有一个用户组ID,分别简称为UID和GID。

此外,Linux系统的进程和文件也有UID和GID的概念。Linux就是通过用户、进程、文件的UID/GID属性来进行权限管理的。

我们知道,当Linux内核启动完毕之后,启动的第一个进程称为init进程。Init进程接着会启动一个login进程,等待用户输入username和password登录。一旦登录成功。就会启动一个shell进程。之后这个shell进程就负责运行用户的命令。注意。在上述启动过程中init进程是以root用户身份启动的,也就是init进程的UID是0,即root。

在默认情况下,由init进程启动的全部进程,也就是fork出来的全部子进程,也相同是以root身份运行的。因此。

负责登录的login进程的UID也是0。

可是,当用户输入username和password,而且登录成功后,所启动的shell进程就不再是root了,而是成功登入系统的用户。这是怎样做到的呢?原来,具有root权限的进程能够通过系统接口setuid来改变自己身份。也就是说。由login进程启动的用户shell进程在開始的时候的身份其实也是root的,只是在它能够运行用户的命令之前,它已经通过setuid将自己的UID改动为登录用户对应的UID了。这样就能够限制每个成功登入系统的用户的权限。

用户之后在shell进程中运行的各种命令,要么是在本进程中运行,要么在启动的子进程中运行。注意,依据我们上面的分析,由用户shell进程启动的子进程相同是以登录用户的身份运行的,而且这些进程在运行的过程中所创建的文件的默认UID和GID也是和登录用户的UID和GID一致的,而且这些文件仅仅能够被用户自己訪问。

假设一个用户想将一些自己创建的文件交给另外一个用户訪问,那么应该怎么办呢?Linux将文件的权限划分为读、写和运行三种。分别用字母r、w和x表示。

每个文件有三组读、写和运行权限,各自是针对文件的全部者、文件全部者所属的组以及除了全部者以及在全部者所属组的用户之外全部其他用户。

这样,假设一个用户想要将一个自己创建的文件交给另外一个用户訪问。那么仅仅须要对应地设置一下这个文件的其他用户权限位就能够了。

我们知道,Android是一个基于Linux内核的系统。可是它不像传统的Linux系统。须要用户登录之后才干使用。然而,Android系统又像传统的Linux系统一样实用户的概念。仅仅只是这些用户不须要登录,也能够使用Android系统。

具体来说。就是Android系统将每个安装在系统的APK都映射为一个不同的Linux用户。

也就是说,每个APK都有一个对应的UID和GID。这些UID和GID是在APK安装的时候由系统安装服务PackageManagerService分配的。

我们知道,APK所运行在的进程是由另外一个系统服务ActivityManagerService负责启动的。

ActivityManagerService在启动APK进程之前。会先向PackageManagerService查询APK安装时分配到的UID和GID。

有了APK的UID和GID后。ActivityManagerService就向另外一个以root身份运行的zygote进程发出创建APK进程的请求。

Zygote进程收到请求之后。就会fork出一个子进程来作为请求创建的APK进程。APK进程的创建过程的具体分析能够參考Android应用程序进程启动过程的源代码分析一文。

注意,我们上面提到。zygote进程是以root身份运行的。因此,它fork出来的子进程,也就是APK进程,在一開始的时候也是以root身份运行的。只是,APK进程在能够运行APK代码之前,会通过系统接口setuid将自己的UID设置为APK安装时分配到的UID。这个过程与传统的Linux系统通过login进程启动用户shell进程的过程非常相似。通过这样的方式,就能够保证每个APK进程都以不同的身份来运行,从而保证了相互之间不会受到干扰。这就是所谓的沙箱了,这全然是建立在Linux的UID和GID基础上的。

以上分析的UID/GID机制能够通过图1来描写叙述:

图1 Android系统基于UID/GID的安全机制

这样的基于Linux UID/GID的安全机制存在什么样的问题呢?注意我们前面提到的。当一个用户想将授予另外一个用户訪问自己创建的文件的时候,它仅仅须要改动一下该文件的訪问权限位即可了。也就是说,在Linux系统中,文件的权限控制在全部者的手中。

因此,这样的权限控制方式就称为自主式的,正式的英文名称为Discretionary Access Control,简称为DAC。

在理想情况下,DAC机制是没有问题的。然而,在现实中。会产生严重的安全问题。

比如,一个用户可能会不小心将自己创建的文件的权限位错误地改动为同意其他用户訪问。

假设这个用户是一个特权用户。而且它错误操作的文件是一个敏感的文件,那么就会产生严重的安全问题。

这样的误操作的产生方式有三种:

1. 用户运行了错误的命令

2. 负责运行用户命令的程序有BUG

3. 负责运行用户命令的程序受到攻击

由此可见,DAC机制仅仅能在理想情况下没有问题,可是在现实中是防不胜防!比如,GingerBread漏洞就是通过攻击以root身份运行Android磁盘管理守护进程vold来获得root权限,从而实现对设备进行root的。

注意,上面我们说的DAC问题尽管是针对内核级别的Linux UID/GID机制。然而相同适用于应用级别的Permission机制。这个问题通过MasterKey漏洞表现得淋漓尽致。我们知道,MasterKey漏洞能够在不改变签名的情况下对APK进行篡改。

这会导致什么后果呢?假如被篡改的APK申请有特殊的Permission,那么就意味着嵌入的恶意代码能够随意地使用这些特殊的Permission。

更为严重的是被篡改的APK是一个系统APK。Android系统的Permission分为两种,一种是全部APK都能够申请的,另一种是系统APK才干够申请的。仅仅有系统APK才干够申请的Permission更为敏感。比如用来安装APK的Permission--android.permission.INSTALL_PACKAGES。这意味着一个具有android.permission.INSTALL_PACKAGES的系统APK被篡改后。恶意代码就能够在设备上安装随意的恶意APK了。

其实,应用级别的Permission机制也是建立在Linux UID/GID基础上的。

当我们在APK的AndroidManfest.xml配置文件里申请某一个Permission的时候,Android系统的安装服务PackageManagerService除了会记录它申请有对应的Permission之外(以便APK调用须要权限的API接口时进行验证),还会将APK增加到对应的某个Linux用户组去。这是由于在Android系统中,并非全部的特权操作都是间接地通过系统服务来运行的,比如网络訪问。

一旦一个APK申请网络訪问的Permission,那么它就会增加到Linux的网络用户组去。这时候APK就能够通过创建socket来訪问网络了。

这样,在Android系统中,不管是应用级别的Permission机制,还是内核级别的Linux UID/GID机制,都相同会受到DAC问题的困扰。

这时候我们就须要一种更为强有力的安全机制来保证系统的安全。

在訪问控制模型中,与DAC机制相对的是MAC机制。MAC的全称是Mandatory Access Control,翻译为强制訪问控制。

在MAC机制中,用户、进程或者文件的权限是由管理策略决定的,而不是由它们自主决定的。比如。我们能够设定这样的一个管理策略,不同意用户A将它创建的文件F授予用户B訪问。这样不管用户A怎样改动文件F的权限位。用户B都是无法訪问文件F的。这样的安全訪问模型能够强有力地保护系统的安全。我们在这个系列的文件里要介绍的SEAndroid就是一种MAC机制。

在SEAndroid中,每个进程和文件都会关联有一个安全上下文。

这个安全上下文由用户、角色、类型、安全级别四个部分组成,每一部分通过一个冒号来分隔。比如,u:r:t:s0描写叙述的就是一个SEAndroid安全上下文。当每个进程和文件都关联上一个安全上下文之后。系统管理员就能够基于这些安全上下文制定一个安全訪问策略,用来规定什么样的进程能够訪问什么样的文件。

上面描写叙述的SEAndroid安全机制如图2所看到的:

图2 SEAndroid安全机制

在图2中,红色标注的即为SEAndroid安全上下文。当中。u:r:unstructed_app:s0描写叙述的是用户安装的APK所运行在的进程的安全上下文,而u:object_r:app_data_file:s0描写叙述的是用户安装的APK在运行过程中生成的数据文件的安全上下文。

从图2还能够看到,SEAndroid安全机制与传统的Linux UID/GID安全机制是并存关系的。也就是说。它们同一时候用来约束进程的权限。

当一个进程訪问一个文件的时候,首先要通过基于UID/GID的DAC安全检查,接着才有资格进入到基于SEAndroid的MAC安全检查。仅仅要当中的一个检查不通过,那么进程訪问文件的请求就会被拒绝。上述的安全检查过程如图3所看到的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTHVvc2hlbmd5YW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" />

图3 基于Linux UID/GID和SEAndroid的安全訪问流程

我们通过一个样例来说明上述的安全訪问流程。

当我们想从手机上下载一个文件到电脑上时,我们使用adb pull命令来实现。当我们运行adb pull命令的时候。实际上是由手机上的守护进程adbd来读出指定的文件。而且将读出来的内容发送给在电脑上运行的adb进程的。

接下来,我们就依照下面步骤尝试从启用了SEAndroid的三星Note II上下载文件/system/bin/gpsd到电脑上来。

1. 运行ls -l命令检查手机上存在/system/bin/gpsd文件。以及它基于传统的Linux UID/GID的权限位:

$ ./adb shell ls -l /system/bin/gpsd
-rwxr-xr-x root shell 2822268 2014-02-11 03:27 gpsd

从命令的输出能够看到,假设仅仅考虑传统的Linux UID/GID安全机制。手机上的/system/bin/gpsd文件是全部用户均能够读取的。

2. 运行adb pull命令下载手机上的/system/bin/gpsd文件:

$ ./adb pull /system/bin/gpsd ./gpsd
failed to copy '/system/bin/gpsd' to './gpsd': Permission denied

从命令的输出能够看到,我们没有依照预期那样将手机上的/system/bin/gpsd文件下载电脑上来,原因是“Permission denied”,也就是权限不够。

3. 分别通过ls -Z和ps -Z命令检查文件/system/bin/gpsd和进程adbd的安全上下文:

./adb shell ls -Z /system/bin/gpsd
-rwxr-xr-x root shell u:object_r:gpsd_exec:s0 gpsd
$ ./adb shell ps -Z | grep 'adbd'
u:r:adbd:s0 shell 1978 1 /sbin/adbd

从命令的输出能够看到。文件/system/bin/gpsd的安全上下文为u:object_r:gpsd_exec:s0。而进程adbd的安全上下文为u:r;adbd:s0。因此我们能够断定。在三星Note II运行的系统上。一定存在一个訪问策略不同意安全上下文为u:r;adbd:s0的进程訪问安全上下文为u:object_r:gpsd_exec:s0的文件。

从上面这个样例就能够看出,在启用SEAndroid之前。原本能够訪问的文件。到启用SEAndroid之后,就变得不能够訪问了!

假设我们确实是须要訪问这些文件,比如我们须要将这些文件打包在我们自己制作ROM里面。那么有没有其他办法訪问呢?当读完SEAndroid安全机制这个系列的文章之后。你就会发现答案是肯定的。而且是在遵循SEAndroid安全策略的前提下实现的!

关于SEAndroid安全机制,另一个关键点是值得提及的。那就是SEAndroid安全机制的目的不是为了全然杜绝别人攻击我们的设备。而是为了保证我们的设备受到攻击时。受到的损害降低到最少的程度。比如。SEAndroid安全机制并不能全然阻止我们的设备被root。可是它能保证我们的设备被root之后。一些敏感的文件仍然是不可訪问,这样就能够最大程度地保护我们的设备。这是由于仅仅要程序是由人类来编写的。就或多或少地存在BUG。或者说漏洞,特别是复杂的程序。进而就会被黑客利用,而且成功地侵入到我们的系统中来。

这是防不胜防的。

当然,我们并非说SEAndroid对阻止设备被侵入毫无用处,它在一定程度上还是能加大侵入的技术难度的。

由于SEAndroid的内容非常多,足够写一本非常厚非常厚的书来描写叙述,可是在接下来的文章中,老罗并不打算逐一逐一地介绍。而是主要抓住关键部分进行具体的分析,因此希望同学们在继续学习接下来的文章之前。能够读读下面的一本书以及一篇论文:

1. SELinux by Example - Using Security Enhanced Linux

2. Security Enhanced (SE) Android: Bringing Flexible MAC to Android

其实,接下来介绍SEAndroid的文章都是基于上面的论文来Security Enhanced (SE) Android: Bringing Flexible MAC to Android展开的,目的是从Android系统源代码分析的角度来阐述该论文的内容。

好了。依照惯例,接下来我们依照下面的情景来深入学习SEAndroid安全机制:

1. SEAndroid安全机制框架分析

2. SEAndroid安全机制的文件安全上下文分析

3. SEAndroid安全机制的进程安全上下文分析

4. SEAndroid安全机制的Binde IPC保护支持分析

5. SEAndroid安全机制的Property保护支持分析

希望通过这五个情景的分析,使得我们对Android系统的安全机制有一个深刻的认识。以帮助我们更好地保护手机上的隐私和数据,敬请关注!

很多其他信息能够关注老罗的新浪微博:http://weibo.com/shengyangluo

最新文章

  1. JS高程5.引用类型(3)Array类型-检测数组
  2. VGA, QVGA, HVGA, WVGA, FWVGA和iPhone显示分辨率
  3. 54. Android中adb常用命令及应用常用目录
  4. zju3547
  5. poj 2777
  6. JavaScript If...Else、Switch、For、While、Break、Continue语句
  7. CSS纯样式实现箭头、对话框等形状
  8. unity中的欧拉角
  9. web.xml配置bug之提示The content of element type "web-app" must match "(icon?,display- name?,description?,distributable?,
  10. JedisPool使用原理和源代码
  11. javascript 关闭页面提示
  12. UVA1366-----Martian Mining------DP
  13. centos7下部署nginx与php
  14. bash的快捷键、特殊参数、历史命令、相关文件
  15. Azure 国内版 如何用powershell修改linux系统的密码
  16. C语言基础四(敲打键盘、寻找资料,循环语句)请一个个字读,助于您的学会机率
  17. Opening Ceremony(贪心)
  18. 【LeetCode】224. Basic Calculator
  19. nginx AIO机制与sendfile机制
  20. a标签的四个伪类

热门文章

  1. mysql 操作时间戳
  2. AssetBundle——外部加载资源Asset
  3. BootStrap的table表格的基本写法
  4. mysql cursor游标的使用,实例
  5. POJ 2431 Expedition (贪心 + 优先队列)
  6. java中Property类的基本用法
  7. Oracle表空间不足ORA-01654
  8. 【Java】 奇偶数的判断
  9. 【WPF】奇怪的INotifyPropertyChanged的实现
  10. Python类总结-ClassMethod, StaticMethod