《Android Security Internals》第一章安卓安全模型翻译

安卓架构以下图 java

1.1 linux内核

做为一个unix系统,android内核能驱动运行硬件、网络、文件系统、进程管理。可是不一样于一般的Linux内核,android有着low memory killer, wakelocks, anonymous shared memory (ashmem), alarms, paranoid networking, Binder机制。其中最重要的是Binder和paranoid networking。linux

  • Android使用文件系统权限和特定的内核补丁称为 paranoid networking,用来限制低级系统功能的访问,好比网络套接字、摄像机设备、外部存储器、日志读取能力等
  • Binder是Android系统中进程间通信(IPC)的一种方式

1.2 本地用户空间

内核之上是本地用户空间层,由_init_二进制文件(第一个启动的进程,用于启动全部其它进程),几个本地守护进程以及在整个系统中使用的几百个本地库组成android

1.3 Dalvik VM

大部分Android都是用java实现的,而且由java虚拟机(JVM)执行。Android当前JVM实现被称为Dalvik。Dalvik不能直接运行Java字节码(.class文件):其原生输入格式称为Dalvik Executable(DEX),而且被打包在.dex文件中。反过来,dex文件被打包在系统Java库(JAR文件)或者安装在Android应用程序(APK文件)中。shell

Dalvik和Oracle的JVM有不一样的体系结构——基于寄存器的Dalvik和基于堆栈的JVM,以及有着不一样的指令集。JVM会使用x条指令将x个参数加载到堆栈,而Dalvik则是使用寄存器,用指令进行运算。通常来讲,基于寄存器的Dalvik使用较少的指令,但代码大于基于堆栈的虚拟机中相应的代码。可是在大多数体系架构中,加载代码比指令调度成本要小,所以基于寄存器的虚拟机效率更高。数据库

在大多数生产设备中,系统库和预安装的应用程序不直接包含独立于设备的DEX代码。在性能优化上,DEX代码被转换为设备相关格式并存储在Optimized DEX(.odex)文件中,该文件一般位于与其父JAR或者APK文件相同目录中。在安装时为用户安装的应用程序执行相似的优化过程。编程

1.4 Java运行时库

Java语言的实现须要一组运行时库(runtime libraries),主要定义在java.*和javax.*包中。Android的核心Java库最初是从Apache Harmony project4中派生出来的。随着Android的发展,原来的Harmony代码已经发生的很大变化,在这个过程当中,一些功能已经彻底被替换(好比国际化支持,加密服务,和一些相关类),而其余功能则获得了扩展和改进。核心库主要以Java开发,但它们也具备一些本机代码依赖性。本机代码使用标准Java Native Interface(JNI)连接到Android的Java库中,该代码容许Java代码调用本机代码,反之亦然。Java运行时库直接从系统服务和应用程序访问。浏览器

1.5 系统服务

迄今为止引入的各类层构成了实现Android系统服务核心所需的管道。系统服务(从4.4版本开始)实现了大部分基本的Android功能,包括显示和触摸屏支持、电话和网络链接。大多数系统服务都是用Java实现的,一些基本的是用本机代码编写的。安全

除少数例外状况,每一个系统服务都定义了能够从其余服务和应用程序调用的远程接口。再加上Binder提供的服务发现、中介和IPC,系统服务在Linux之上有效地实现了一个面向对象操做系统。性能优化

1.6 进程间通讯(IPC)

如前所述,Binder是一个IPC机制。与任何类Unix系统同样,Android中的进程是具备独立的地址空间的,进程没法直接访问另外一进程的内存(进程隔离)。可是若是进程想为其余进程提供一些有用的服务,则须要提供一些机制,以容许其余进程发现这些服务并与之交互,这个机制被称为IPC。对于标准IPC机制的需求并不新鲜,在Android以前就有好几个选择,包括文件、信号、套接字(sockets)、管道(pipes)、信号量(semaphores)、共享内存(shared memory)、消息队列(message queues)等等。虽然Android使用了其中一些(如本地套接字),可是它不支持其余(即系统V IPC,好比信号量、共享内存和消息队列)服务器

1.7 Binder

因为标准IPC机制不够灵活可靠,所以为Android开发了一种名为Binder的新的IPC机制。虽然Android的Binder是一个新的实现,但它基于OpenBinder的体系结构和思想。

Binder实现了基于抽象接口的分布式组件结构,它相似于Unix上的Windows通用对象模型(COM)和通用对象代理请求结构(CORBA),但与这些框架不一样,它运行在单个设备上,不支持跨网络的远程调用(RPC)(尽管RPC支持能够在Binder上实现)。下面简要介绍Binder主要组件。

1.7.1 Binder实现

如前面所述,在类Unix系统上,进程没法访问另外一个进程的内存,可是内核能够控制因此进程,所以能够公开一个启用IPC的接口。在Binder中,此接口是/dev/binder,由Binder内核驱动程序实现。Binder driver是框架的中心,全部的IPC调用都会经过它。进程间通讯经过一个ioctl()调用实现,调用经过binder\_write\_read结构(由包含驱动程序命令的write_buffer和包含用户空间须要执行的命令的read_buffer组成)发送和接收数据。

可是数据如何在实际进程中传递的?Binder驱动程序管理每一个进程的部分地址空间。Binder驱动程序管理的内存块对进程是只读的,而且全部的写入都有内核模块执行。当进程向另外一进程发送消息的时候,内核会在目标进程的内存中分配一些空间,而后直接从发送进程中复制消息数据。而后它将一条短消息排队到接收过程,告诉它收到的消息在哪里。接受者能够直接访问该消息,由于它在本身的内存空间中。当一个进程结束时,它会通知Binder驱动程序将内存标记为空闲。下图是Binder IPC体系结构简化图

Android中更高级别的IPC抽象,例如Intents(带有相关数据的命令,这些数据经过进程传递给组件)、Messengers(支持跨进程的基于消息通讯的对象)和ContentProviders(公开跨进程数据管理接口的组件)创建在Binder之上。另外须要暴露给其它进程的服务接口可使用Android接口定义语言(AIDL)进行定义,AIDL使客户端能够调用远程服务,就好像它们是本地Java对象同样。关联的AIDL工具自动生成stubs存根(客户端表示的远程对象),和将接口方法映射到较低级别的transact()Binder方法并负责将参数转换为Binder能够传输的格式的proxies代理(这称为parameter marshalling/unmarshalling参数的编组/解组)。由于Binder本质上仍是无类型的,因此AIDL生成的存根和代理还经过在每一个Binder事务(在代理中)包含目标接口名称并在存根中验证它来提供类型安全性。

1.7.2 Binder安全

在更高级别上,能够经过Binder框架访问每一个对象实现的IBinder接口而且这被称为Binder对象。对Binder对象的调用在Binder事务中执行,该事务包括对目标对象的引用、要执行的方法的ID以及数据缓冲区。Binder驱动程序会自动将调用进程的进程标识(PID)和有效用户标识(EUID)添加到事务数据中。被调用进程(被调用者)能够检查PID和EUID,并根据其内部逻辑或关于调用应用程序的系统元数据来决定是否应该执行请求的方法。

因为PID和EUID由内核填充,所以调用者进程不能伪造其身份以得到比系统容许的更多特权(Binder阻止提权)。这是Android安全模型的核心部分之一,全部更高级别的抽象(好比权限)都是基于它的。调用者的EUID和PID能够经过android.os.Binder类的getCallingPid()getCallingUid()方法范围,该类是Android的公有API一部分。

注意: 若是多个应用程序在同一个UID下执行,则调用进程的EUID可能没法映射到单个应用程序,可是这不会影响安全决策,由于在相同的UID下运行的进程一般都会被授予相同的权限和特权(除非已定义特定于进程的SELinux规则)

1.7.3 Binder标识

Binder对象最重要的属性之一是它们在流程中保持惟一的身份。所以,若是进程A建立一个Binder对象并将其传递给进程B,而进程B又将其传递给进程C,则来自全部三个进程的调用将由同一个Binder对象进行处理。实际上,进程A将直接经过其内存地址引用Binder对象(由于它在进程A的内存空间中),而进程B和C只会接收到Binder对象的句柄。

内核维护其余进程中"实时"Binder对象及其句柄之间的映射。因为Binder对象的身份是惟一的,而且由内核维护,因此用户空间进程不可能建立Binder对象的副本或获取对其的引用,除非已经过IPC交给IPC。所以,一个Binder对象是一个独特的,不可伪造的,可传播的对象,能够充当安全令牌(token)。这使得在Android中使用Capability-Based Security(基于能力的安全性)成为可能。

1.7.4 Capability-Based Security

在基于能力的安全模型中,程序被授予对特定资源的访问权限,为其提供一种不可伪造的功能,该功能既引用目标对象又封装一组访问权限。因为功能是不可伪造的,程序拥有一项功能的事实足以让它可以访问目标资源,不须要维护与实际资源相关联的访问控制列表(ACL)或相似结构。

1.7.5 Binder Tokens

在Android中,Binder对象能够充当功能,并以这种方式使用时称为Binder token。Binder token既能够是能力也能够是目标资源。拥有一个Binder token赋予拥有进程彻底访问一个Binder对象的权利,使它可以对目标对象执行Binder事务。若是Binder对象实现多个动做(经过根据Binder事务的代码参数选择要执行的动做),则调用者能够在引用该Binder对象时执行任何动做。若是须要更细化的访问控制,则每一个操做的实施须要执行必要的权限检查,一般经过使用调用者进程的PID和EUID。

Android中的一种常见模式是运行对系统(UID 1000)和根(UID 0)运行的调用者执行全部操做。但对全部其余进程执行额外的权限检查。所以访问重要的Binder对象(如系统服务)的方式有两种:经过限制谁能够获取对该Binder对象的引用,以及在对Binder对象执行操做以前检查调用者身份。(若是须要,这个检查是可选的,而且由Binder对象自己实现)

另外,一个Binder对象只能做为一个功能使用,而不须要实现任何其余功能。在这种使用模式中,同一个Binder对象由两个(或更多)合做进程持有,而充当服务器(处理某种客户端请求)的Binder对象使用Binder token来验证其客户端,就像Web服务器使用session cookies同样。这种使用模式由Android框架在内部使用,而且大多数应用程序不可见。在公有API中可见的Binder token的一个值得注意的用例是窗口 token。每一个活动的顶层窗口都与一个Binder token(称为一个窗口token)相关联,该窗口token是Android的窗口管理器(负责管理应用程序窗口的系统服务)跟踪的。应用程序能够获取本身的窗口token,但没法访问其余应用程序的窗口token。一般您不但愿其余应用程序在您本身的顶部添加或者删除窗口,每一个请求都必须提供与应用程序关联的窗口标记,从而保证窗口请求来着您本身的应用程序或者系统。

1.7.6 Accessing Binder Object

虽然Android处于安全目的控制对Binder对象的访问,而且与Binder对象进行通讯的惟一方法是引用它,可是某些Binder对象(最显著的是系统服务),须要广泛可访问。然而,将全部系统服务的引用分发黑每一个进程是不切实际的,所以咱们须要一些机制来容许进程根据须要发现和获取对系统服务的引用。

为了启动服务发现,Binder框架具备单个上下文管理器,该管理器维护对Binder对象的引用。Android的上下文管理器实现是服务管理器本地守护进程。它在引导过程的早期就开始了,以便系统服务能够在启动时注册。经过将服务名称和Binder引用传递给服务管理器来注册服务。一旦注册了服务,任何客户端均可以使用其名称获取其Binder引用。可是,大多数系统服务都会执行额外的权限检查,所以获取引用不会自动保证访问其全部功能。由于任何人在向服务管理器注册时均可以访问Binder引用,全部只有一小部分被列入白名单的系统进程能够注册系统服务。例如,只有以UID 1002(AID_BLUETOOTH)执行的进程才能够注册蓝牙系统服务。

你可使用service list命令查看已注册服务的列表,该命令将返回每一个已注册服务的名称和已实现的Binder界面。

1.7.7 其它Binder功能

虽然和Android安全模型没有直接关系,可是另外两个著名的Binder功能是引用计数和死亡通知(也称为死亡连接)。引用计数可确保当没有人引用它们时,Binder对象会自动释放,并在内核驱动程序中使用BC_INCREFS,BC_ACQUIRE,BC_RELEASE和BC_DECREFS命令实现。引用计数集成在Android框架的各个级别,但不直接显示给应用程序。

死亡通知容许使用由其余进程托管的Binder对象的应用程序在内核终止这些进程并执行任何须要的清理时获得的通知。死亡通知是经过内核驱动程序中的BC_REQUEST_DEATH_NOTIFICATION和BC_CLEAR_DEATH_NOTIFICATION命令以及框架中的IBinder接口的linkToDeath()unlinkToDeath()方法实现的。(本地binder的死亡通知是不会发送的,由于没有托管过程死亡,本地binder也不会死亡)

1.8 Android框架库

有时候简称为"框架"(the framework)。框架包括全部不属于标准Java运行时(java.* , javax.*等)的Java库,而且适用于大多数部分托管在android顶层的包中。该框架包含构建Android应用程序的基本块,例如 活动,服务和内容提供者(在android.app.*包中),GUI小部件(在android.view.*和android.widget包中),文件和数据库访问类(主要在android.database.*和android.content.*包中)。它还包括容许您与设备硬件交互的类以及利用系统提供的更高级别服务的类。

尽管内核级以上的几乎全部Android操做系统功能都是做为系统服务实现的,但它并不直接暴露在框架中,而是经过称为管理器的外观类来访问。一般每一个管理器都要相应的系统服务支持,例如BluetoothManager是BluetoothManagerService的表面。

1.9 应用

最高层是应用程序(或app),它们是用户直接与之交互的程序。虽然全部应用程序都具备相同的结构,并构建在Android框架之上,但咱们区分了系统应用程序和用户安装的应用程序。

1.9.1 系统app

系统应用程序包含在操做系统映像(image)中,该映像在生产设备上是只读的(一般以/system身份登陆),而且不能由用户卸载或更改。所以,这些应用程序被认为是安全的,而且被赋予比用户安装的应用程序更多的特权。系统app能够是核心Android操做系统的一部分,也能够只是预先安装的用户应用程序,例如电子邮件客户端或浏览器。虽然安装在/system下的全部应用程序在早期版本的Android中平等对待(除了经过检查应用程序签名证书的操做系统功能除外),Android 4.4和更高版本将安装在/system/priv-app/中的应用程序视为特权应用程序,保护级别为signatureOrSystem的权限授予应用程序,而不是安装在/system下的全部应用程序。使用平台签名密钥签名的应用能够被授予具备签名保护级别的系统权限,所以能够得到操做系统级别的权限,即便它们未预安装在/system下。虽然没法卸载或更改系统应用程序,但只要更新使用相同的私钥签名,用户就能够更新系统应用程序,有些能够由用户安装应用。例如,用户能够选择用第三方应用程序替换预安装的应用程序启动器或输入法。

1.9.2 用户安装的app

用户安装的应用程序安装在专用的读写分区上(一般以/data的形式安装),能够托管用户数据并能够随意卸载。 每一个应用程序都位于专用安全沙箱中,一般不会影响其余应用程序或访问其数据。 此外,应用程序只能访问他们明确被授予使用权限的资源。 特权分离和最小特权原则是Android安全模型的核心。

1.10 Android应用程序组件

Android应用程序与传统应用程序不一样,能够有多个入口点。每一个组件均可以提供多个入口点,这些入口点能够基于同一应用程序或另外一个应用程序中的用户操做达到,或者由应用程序已注册要通知的系统事件触发。组件及其入口点以及其余元数据在应用程序的清单文件中定义,称为AndroidManifest.xml。与大多数Android资源文件同样,此文件在捆绑到应用程序包(APK)文件以前被编译为二进制XML格式(相似于ASN.1),以减少文件大小并加快解析速度。清单文件中定义的最重要的应用程序属性是应用程序包名称,它惟一标识系统中的每一个应用程序。包名称与Java包名称(反向域名表示法;例如com.google.email)格式相同。

AndroidManifest.xml文件在应用程序安装时解析,而且它定义的包和组件在系统中注册。 Android须要使用开发人员控制的密钥对每一个应用程序进行签名。这保证已安装的应用程序不能被声称具备相同程序包名称的另外一个应用程序替代(除非它使用相同的密钥签名,在这种状况下现有应用程序已更新)。下面列出了Android应用程序的主要组件。

1.10.1 Activities

activity是带有用户界面的单个屏幕。 activity是Android GUI应用程序的主要构建块。 一个应用程序能够有多个activity,虽然它们一般被设计为以特定顺序显示,但每一个activity均可以独立启动,可能由不一样的应用程序(若是容许)启动。

1.10.2 Services

service是一个在后台运行而且没有用户界面的组件。service一般用于执行一些长时间运行的操做,例以下载文件或播放音乐,而不会阻止用户界面。service还可使用AIDL定义远程接口并为其余应用程序提供一些功能。 可是,与做为操做系统一部分并始终运行的系统服务不一样,应用程序的service是按需启动和中止的。

1.10.3 Content providers

content provider提供应用程序数据的接口,一般存储在数据库或文件中。 content provider能够经过IPC访问,主要用于与其余应用程序共享应用程序的数据。 content provider对数据的哪些部分是可访问的进行了细粒度的控制,容许应用程序仅共享其数据的一个子集。

1.10.4 Broadcast receivers

broadcast receiver 是响应全系统事件的组件,称为广播。 广播能够源自系统(例如,宣布网络链接性的变化),或来自用户应用程序(例如,宣布后台数据更新已完成)。

1.11 Android's Security Model

与系统的其余部分同样,Android的安全模型也利用了Linux内核提供的安全功能。 Linux是一个多用户操做系统,内核能够隔离用户资源,就像隔离进程同样。 在Linux系统中,一个用户不能访问另外一个用户的文件(除非明确授予权限),而且每一个进程都以启动它的用户的标识(用户和组ID,一般称为UID和GID)运行,除非在相应的可执行文件上set-user-ID或者set-group-ID (SUID和SGID)。

Android利用了这种用户隔离,但与传统的Linux系统(桌面或服务器)对待用户不一样。 在传统的系统中,UID被赋予一个物理用户,该用户能够登陆系统并经过shell执行命令,或者在后台执行系统服务(守护进程)(由于系统守护进程一般能够经过网络访问 ,使用专用的UID运行每一个守护进程能够在受到攻击时限制其受到的损害)。 Android最初是为智能手机设计的,并且因为手机是我的设备,所以不须要在系统中注册不一样的物理用户。 物理用户是隐含的,UID用于区分应用程序。 这构成了Android应用沙盒的基础。

1.11.1 Application Sandboxing

Android会在安装时自动为每一个应用程序分配一个惟一的UID(一般称为应用程序ID),并在做为该UID运行的专用进程中执行该应用程序。 此外,每一个应用程序都有一个专用的数据目录,只有它有权读取和写入。 所以,应用程序在进程级别(经过使每一个进程在专用进程中运行)和在文件级别(经过具备专用数据目录)都是隔离的或者沙箱化的。 这将建立一个内核级应用程序沙箱,该沙箱应用于全部应用程序,而无论它们是在本机仍是虚拟机进程中执行。

系统守护进程和应用程序在定义好的常量UID下运行,不多的守护进程以root用户(UID 0)运行。 Android没有传统的/etc/password文件,它的系统UID是在android_filesystem_config.h头文件中静态定义的。系统服务的UID从1000开始,其中1000是系统(AID_SYSTEM)用户,具备特殊(但仍然有限)的权限。自动生成的应用程序的UID从10000开始(AID_APP),相应的用户名格式为app_XXX或uY_aXXX(在支持多个物理用户的Android版本上),其中XXX是来自AID_APP的偏移量,Y是Android用户ID(不是与UID相同)。例如,10037 UID对应于u0_a37用户名,可能会分配给Google电子邮件客户端应用程序(com.google.android.email包)。

电子邮件应用程序的数据目录以其包名称命名,并在单用户设备上的/data/data/下建立。数据目录内的全部文件均归专用Linux用户u0_a37全部。 应用程序能够选择使用MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE标志建立文件,以容许其余应用程序直接访问文件,这些应用程序分别有效地设置文件上的S_IROTH和S_IWOTH访问位。 可是,不鼓励直接共享文件,而且这些标志在Android 4.2及更高版本中已弃用。

应用程序UID与/data/system/packages.xml文件(规范源文件)中的其余程序包元数据一块儿管理,而且还写入/data/system/packages.list文件。

在这里,第一个字段是软件包名称,第二个是分配给应用程序的UID,第三个是可调试标志(1:可调试),第四个是应用程序的数据目录路径,第五个是seinfo标签(被SELinux使用的)。最后一个字段是应用程序启动的补充GID列表。每一个GID一般都与Android权限相关联,并根据授予应用程序的权限生成GID列表。

可使用相同的UID(称为共享用户ID)安装应用程序,在这种状况下,它们能够共享文件,甚至能够在相同的进程中运行。 共享用户标识被系统应用程序普遍使用,这些应用程序一般须要在不一样的程序包中使用相同的资源以实现模块化。例如,在Android 4.4中,系统UI和keyguard(锁屏实现)共享UID 10012。

尽管不建议将非共享用户标识设施用于非系统应用程序,但它也可用于第三方应用程序。 为了共享相同的UID,应用程序须要使用相同的代码签名密钥进行签名。 此外,因为将共享用户ID添加到已安装应用的新版本会致使其更改其UID,所以系统不容许这样作。 所以,没法追溯添加共享用户标识,而且须要将应用程序设计为从一开始就使用共享标识。

1.11.2 Permissions

因为Android应用程序是沙盒式的,所以它们只能访问本身的文件和设备上的任何世界可访问的资源。 这样一个有限的应用程序虽然不会颇有趣,而且Android能够为应用程序授予额外的细粒度访问权限,以便实现更丰富的功能。 这些访问权被称为权限,而且它们能够控制对硬件设备,互联网链接,数据或操做系统服务的访问。

应用程序能够经过在AndroidManifest.xml文件中定义它们来请求权限。 在应用程序安装时,Android会检查请求的权限列表并决定是否授予它们。 一旦被授予,权限不能被撤销,而且它们能够在没有任何额外确认的状况下被应用程序使用。 此外,即便请求应用程序已被授予相应权限,对于诸如私钥或用户账户访问等功能,每一个访问对象都须要明确的用户确认。 某些权限只能授予属于Android操做系统的应用程序,或者是由于它们是预安装的或使用与操做系统相同的密钥签名的。 第三方应用程序能够定义自定义权限并定义称为权限保护级别的相似限制,从而限制对同一做者建立的应用程序访问应用程序的服务和资源。

权限能够在不一样的级别执行。 请求更低级别的系统资源(如设备文件)由Linux内核经过检查调用进程的UID或GID(针对资源的全部者和访问位)来强制执行。 当访问更高级别的Android组件时,强制执行由Android OS或每一个组件(或二者)执行。

1.11.3 IPC

Android使用内核驱动程序和用户空间库的组合来实现IPC。 “Binder”中所述,Binder内核驱动程序保证不能伪造调用者的UID和PID,而且许多系统服务依赖Binder提供的UID和PID来动态控制对经过IPC公开的敏感API的访问。

更多粗粒度的权限影响经过IPC公开的服务的全部方法的粗粒度权限能够经过在服务声明中指定权限由系统自动执行。 与请求的权限同样,所需的权限在AndroidManifest.xml文件中声明。 与上例中的动态权限检查同样,每一个组件的权限也能够经过查询从Binder得到的调用者UID来实现。 系统使用包数据库来肯定被调用者组件所需的权限,而后将调用者UID映射到包名称并检索授予调用者的权限集。 若是所需的权限在该组中,则调用成功。 若是不是,则失败而且系统抛出SecurityException。

1.11.4 Code Signing and Platform Keys

全部Android应用程序必须由其开发人员签名,包括系统应用程序。因为Android APK文件是Java JAR包格式的扩展,所以所使用的代码签名方法也基于JAR签名。 Android使用APK签名来确保应用程序的更新来自同一做者(这称为相同的来源策略),并在应用程序之间创建信任关系。这两种安全功能都是经过将当前安装的目标应用程序的签名证书与更新证书或相关应用程序进行比较来实现的。系统应用程序由多个平台密钥签名。不一样的系统组件能够共享资源,并在使用相同平台密钥签名时在同一进程内运行。平台密钥由维护安装在特定设备上的Android版本生成和控制:设备制造商,运营商,Google for Nexus设备或用户自建的开源Android版本。

1.11.5 Multi-User Support

因为Android最初是为具备单个物理用户的手机(智能手机)设备设计的,所以它为每一个已安装的应用程序分配不一样的Linux UID,而且传统上没有物理用户的概念。 Android在4.2版中得到了对多个物理用户的支持,但只有平板电脑才支持多用户支持,而平板电脑更有可能被共享。经过将用户的最大数量设置为1,手机设备上的多用户支持被禁用。每一个用户被分配惟一的用户ID,从0开始,而且用户被赋予其本身的专用数据目录在/data/system/users/<用户ID>/,这被称为用户的系统目录。此目录托管用户特定的设置,例如主屏幕参数,账户数据以及当前安装的应用程序的列表。虽然应用程序二进制文件在用户之间共享,但每一个用户都会得到应用程序数据目录的副本。为了区分为每一个用户安装的应用程序,安卓为特定用户安装时,Android会为每一个应用程序分配一个新的有效UID。这个有效的UID基于目标物理用户的用户ID和应用程序在单用户系统中的UID(应用程序ID)。授予的UID的这种组合结构保证即便两个不一样用户安装了相同的应用程序,两个应用程序实例也均可以得到他们本身的沙箱。此外,Android还为每一个物理用户保证专用的共享存储(托管在旧设备的SD卡上),这些存储是世界可读的。 用户首先初始化设备称为设备全部者,只有他们能够管理其余用户或执行影响整个设备的管理任务(如出厂重置)。

1.11.6 SELinux

传统的Android安全模型在很大程度上依赖于授予应用程序的UID和GID。尽管这些内核是由内核保证的,而且默认状况下每一个应用程序的文件都是私有的,可是没有任何东西阻止应用程序授予世界对其文件的访问权限(不管是有意仍是因为编程错误)。一样,没有任何措施能够防止恶意应用程序利用系统文件或本地套接字的过分宽容访问位。实际上,分配给应用程序或系统文件的不适当权限已成为多个Android漏洞的来源。这些漏洞在Linux使用的默认访问控制模型中是不可避免的,称为自主访问控制(DAC)。这里的自由选择意味着一旦用户访问特定的资源,他们能够自行决定将其传递给另外一个用户,例如经过将其中一个文件的访问模式设置为世界可读。相反,强制访问控制(MAC)确保对资源的访问符合系统范围内称为策略的一组受权规则。该策略只能由管理员进行更改,而且用户没法覆盖或绕过该策略,例如,授予每一个人访问本身的文件的权限。安全加强型Linux(SELinux)是Linux内核的MAC实现,已经在主线内核中集成了10多年。从版本4.3开始,Android集成了来自Android安全加强(SEAndroid)项目的一个修改过的SELinux版本,该版本已被加强以支持Android特定的功能,例如Binder。在Android中,SELinux用于隔离不一样安全域中的核心系统守护进程和用户应用程序,并为每一个域定义不一样的访问策略。从版本4.4开始,SELinux以强制模式进行部署(违反系统策略生成运行时错误),但策略强制只应用于核心系统守护进程。应用程序仍然以宽容模式运行,而且会记录违规,但不会致使运行时错误。

1.11.7 System Updates

Android设备能够经过空中传送(OTA)或经过将设备链接到PC并使用标准Android调试桥(ADB)客户端或某些供应商提供的具备相似功能的应用程序推送更新映像来进行更新。因为除系统文件以外,Android更新可能须要修改基带(调制解调器)固件,引导加载程序以及没法从Android直接访问的设备的其余部分,所以更新过程一般使用特殊用途的最小操做系统独家访问全部设备硬件。这被称为恢复操做系统或简单恢复。 OTA更新经过下载OTA包文件(一般是带有附加代码签名的ZIP文件)来执行,其中包含一个由恢复解释的小脚本文件,并在恢复模式下从新启动设备。或者,用户能够在引导设备时使用设备特定组合键进入恢复模式,并经过使用恢复的菜单界面手动应用更新,恢复菜单界面一般使用硬件按钮(音量增大/减少,电源和等等)的设备。在生产设备上,恢复只接受设备制造商签名的更新。更新文件经过扩展ZIP文件格式进行签名,以在注释部分的整个文件中包含签名,在安装更新以前,恢复将提取并验证这些签名。在某些设备(包括全部Nexus设备,专用开发人员设备和某些供应商设备)上,设备全部者能够替换恢复操做系统并禁用系统更新签名验证,从而容许他们经过第三方安装更新。将设备引导加载程序切换到容许替换恢复和系统映像的模式称为引导加载程序解锁(不要与SIM解锁混淆,它容许设备在任何移动网络上使用),而且一般须要擦除全部用户数据(工厂重置),以确保潜在的恶意第三方系统映像没法访问现有的用户数据。在大多数消费设备上,解锁引导加载程序会致使设备保修失效。

1.11.8 Verified Boot

从版本4.4开始,Android支持使用Linux Device-Mapper的verity target进行验证启动。 Verity使用加密哈希树提供对块设备的透明完整性检查。树中的每一个节点都是加密哈希,叶节点包含物理数据块的哈希值,中间节点包含其子节点的哈希值。因为根节点中的散列基于全部其余节点的值,所以只须要信任根散列以验证树的其他部分。使用引导分区中包含的RSA公钥执行验证。设备块在运行时经过计算块读取时的散列值并将其与哈希树中记录的值进行比较来检查。若是这些值不匹配,则读取操做会致使I/O错误,指示文件系统已损坏。由于全部检查都是由内核执行的,因此引导过程须要验证内核的完整性,以便验证的引导起做用。此过程是特定于设备的,一般经过使用不可更改的硬件特定的密钥来实现,该密钥被“烧”(写入只写存储器)到设备中。该密钥用于验证每一个引导加载程序级别和最终内核的完整性。

总结

Android是基于Linux内核的权限分离操做系统。 更高级别的系统功能是做为一套协做系统服务实现的,它们使用称为Binder的IPC机制进行通讯。 Android经过运行具备不一样系统标识(Linux UID)的应用程序来隔离彼此的应用程序。 默认状况下,应用程序被赋予极少的权限,而且必须请求精细的权限才能与系统服务,硬件设备或其余应用程序交互。 权限在每一个应用程序的清单文件中定义,并在安装时授予。 系统使用每一个应用程序的UID来找出它已授予的权限并在运行时强制执行它们。 在最近的版本中,系统进程隔离利用SELinux来进一步限制给予每一个进程的权限。

相关文章
相关标签/搜索