Android系统由许多承担安全检查与策略执行任务的机制构成。与任何现代操作系统一样,Android中的这些安全机制互相交互,交换关于主体(应用、用户)、客体(其他应用、文件和设备)以及将要执行操作(读、写、删除等)的各种信息。安全策略执行通常不会发生故障,但偶尔也会出现一些裂缝,为滥用提供了机会。本章将讨论Android系统的安全设计与架构,为分析Android平台的整体攻击面打好基础。 2.1 理解Android系统架构Android的总体架构有时被描述为“运行在Linux上的Java”,然而这种说法不够准确,并不能完全体现出这一平台的复杂性和架构。Android的总体架构由5个主要层次上的组件构成,这5层是:Android应用层、Android框架层、Dalvik虚拟机层、用户空间原生代码层和Linux内核层。图2-1显示了这些层是如何构成Android软件栈的。 图 2-1 Android系统的总体架构 Android应用层允许开发者无须修改底层代码就对设备的功能进行扩展和提升,而Android框架层则为开发者提供了大量的用来访问Android设备各种必需设备的API,也就是充当应用层与Dalvik虚拟机(DalvikVM)层之间的“粘合剂”。API中包含各种构件(building block)以允许开发者执行通用任务,比如管理UI元素、访问共享数据存储,以及在应用组件间传递信息等。 Android应用和Android框架都是用Java语言开发的,并在DalvikVM中运行。DalvikVM的作用主要是为底层操作系统提供一个高效的抽象层。DalvikVM是一种基于寄存器的虚拟机,能够解释执行Dalvik可执行格式(DEX)的字节码;另一方面,DalvikVM依赖于一些由支持性原生代码程序库所提供的功能。 Android系统中的用户空间原生代码组件包括系统服务(如vold和DBus)、网络服务(如dhcpd和wpa_supplicant)和程序库(如bionic libc、WebKit和OpenSSL)。其中一些服务和程序库会与内核级的服务与驱动进行交互,而其他的则只是便利底层原生操作管理代码。 Android的底层基础是Linux内核,Android对内核源码树作了大量的增加与修改,其中有些代码存在一些独特的安全后果。我们会在第3章、第10章和第12章中更加详细地讨论这些话题。内核级驱动也提供了额外的功能,比如访问照相机、Wi-Fi以及其他网络设备。需要特别注意Binder驱动,它实现了进程间通信(IPC)机制。 2.3节将详细介绍每一层上的关键组件。 2.2 理解安全边界和安全策略执行安全边界,有时也会称为信任边界,是系统中分隔不同信任级别的特殊区域。一个最直接的例子就是内核空间与用户空间之间的边界。内核空间中的代码可以对硬件执行一些底层操作并访问所有的虚拟和物理内存,而用户空间中的代码则由于CPU的安全边界控制,无法访问所有内存。 Android操作系统应用了两套独立但又相互配合的权限模型。在底层,Linux内核使用用户和用户组来实施权限控制,这套权限模型是从Linux继承过来的,用于对文件系统实体进行访问控制,也可以对其他Android特定资源进行控制。这一模型通常被称为Android沙箱。以DalvikVM和Android框架形式存在的Android运行时实施了第二套权限模型。这套模型在用户安装应用时是向用户公开的,定义了应用拥有的权限,从而限制Android应用的能力。事实上,第二套权限模型中的某些权限直接映射到底层操作系统上的特定用户、用户组和权能(Capability)。 2.2.1 Android沙箱Android从其根基Linux继承了已经深入人心的类Unix进程隔离机制与最小权限原则。具体而言,进程以隔离的用户环境运行,不能相互干扰,比如发送信号或者访问其他进程的内存空间。因此,Android沙箱的核心机制基于以下几个概念:标准的Linux进程隔离、大多数进程拥有唯一的用户ID(UID),以及严格限制文件系统权限。 Android系统沿用了Linux的UID/GID(用户组ID)权限模型,但并没有使用传统的passwd和group文件来存储用户与用户组的认证凭据,作为替代,Android定义了从名称到独特标识符Android ID(AID)的映射表。初始的AID映射表包含了一些与特权用户及系统关键用户(如system用户/用户组)对应的静态保留条目。Android还保留了一段AID范围,用于提供原生应用的UID。Android 4.1之后的版本为多用户资料档案和隔离进程用户增加了额外的AID范围段(如Chrome沙箱)。你可以从AOSP树的system/core/include/private/android_filesystem_config.h文件中找到AID的定义。以下是一个简化过的示例。 #define AID_ROOT 0 /*传统的unix根用户*/#define AID_SYSTEM 1000 /*系统服务器*/#define AID_RADIO 1001 /*通话功能子系统,RIL*/#define AID_BLUETOOTH 1002 /*蓝牙子系统*/...#define AID_SHELL 2000 /*adb shell与debug shell用户*/#define AID_CACHE 2001 /*缓存访问*/#define AID_DIAG 2002 /*访问诊断资源*//*编号3000系列只用于辅助用户组们,表示出了内核所支持的Android权能*/#define AID_NET_BT_ADMIN 3001 /*蓝牙:创建套接字*/#define AID_NET_BT 3002 /*蓝牙:创建sco、rfcomm或l2cap套接字*/#define AID_INET 3003 /*能够创建AF_INET和AF_INET6套接字*/#define AID_NET_RAW 3004 /*能够创建原始的INET套接字*/...#define AID_APP 10000 /*第一个应用用户*/#define AID_ISOLATED_START 99000 /*完全隔绝的沙箱进程中UID的开始编号 */#define AID_ISOLATED_END 99999 /*完全隔绝的沙箱进程中UID的末尾编号*/#define AID_USER 100000 /*每一用户的UID编号范围偏移*/ 除了AID,Android还使用了辅助用户组机制,以允许进程访问共享或受保护的资源。例如,
除了用来实施文件系统访问,辅助用户组还会被用于向进程授予额外的权限。例如, 在4.3及之后的版本中,Android提升了对Linux权能的使用,比如Android 4.3将二进制程序/system/bin/run-as从原先设置成set-UID root权限,修改为使用Linux权能来访问特权资源。在这里,这一权能方便了对packages.list文件的访问。
在应用执行时,它们的UID、GID和辅助用户组都会被分配给新创建的进程。在一个独特UID和GID环境下运行,使得操作系统可以在内核中实施底层的限制措施,也让运行环境能够控制应用之间的交互。这就是Android沙箱的关键所在。 下面的代码给出了在一台HTC One V手机上运行 app_16 4089 1451 304080 31724 ... S com.htc.bgpapp_35 4119 1451 309712 30164 ... S com.google.android.calendarapp_155 4145 1451 318276 39096 ... S com.google.android.apps.plusapp_24 4159 1451 307736 32920 ... S android.process.mediaapp_151 4247 1451 303172 28032 ... S com.htc.lockscreenapp_49 4260 1451 303696 28132 ... S com.htc.weather.bgapp_13 4277 1451 453248 68260 ... S com.android.browser 通过使用应用包中的一种特殊指令,应用也可以共享UID,这一点我们会在2.3.1节详细讨论。 实际上,进程显示的用户与用户组名称是由一种 345 passwd* getpwuid(uid_t uid) { // NOLINT:实现不良函数346 stubs_state_t* state = __stubs_state();347 if (state == NULL) {348 return NULL;349 }350351 passwd* pw = android_id_to_passwd(state, uid);352 if (pw != NULL) {353 return pw;354 }355 return app_id_to_passwd(uid, state);356 } 与它的同胞函数一样, static passwd* android_iinfo_to_passwd(stubs_state_t* state, const android_id_info* iinfo) { snprintf(state->dir_buffer_, sizeof(state->dir_buffer_), "/"); snprintf(state->sh_buffer_, sizeof(state->sh_buffer_),"/system/bin/sh"); passwd* pw = &state->passwd_; pw->pw_name= (char*) iinfo->name; pw->pw_uid = iinfo->aid; pw->pw_gid = iinfo->aid; pw->pw_dir = state->dir_buffer_; pw->pw_shell = state->sh_buffer_; return pw; } 2.2.2 Android权限Android的权限模型是多方面的,有API权限、文件系统权限和IPC权限。在很多情况下,这些权限都会交织在一起。正如前面提到的,一些高级权限会后退映射到低级别的操作系统权能,这可能包括打开套接字、蓝牙设备和文件系统路径等。 要确定应用用户的权限和辅助用户组,Android系统会处理在应用包的AndroidManifest.xml文件中指定的高级权限(Manifest文件和权限会在2.3.1节详细描述)。应用的权限由PackageManager在安装时从应用的Manifest文件中提取,并存储在/data/system/packages.xml文件中。这些条目然后会在应用进程的实例化阶段用于向进程授予适当的权限(比如设置辅助用户组GID)。下面的代码片段显示了packages.xml文件中的Chrome浏览器条目,包括这个应用的唯一UID以及它所申请的权限。 <package name="com.android.chrome"codePath="/data/app/com.android.chrome-1.apk"nativeLibraryPath="/data/data/com.android.chrome/lib"flags="0" ft="1422a161aa8" it="1422a163b1a"ut="1422a163b1a" version="1599092" userId="10082"installer="com.android.vending"> <sigs count="1"> <cert index="0" /> </sigs> <perms> <item name="com.android.launcher.permission.INSTALL_SHORTCUT" /> <item name="android.permission.NFC" /> ... <item name="android.permission.WRITE_EXTERNAL_STORAGE" /> <item name="android.permission.ACCESS_COARSE_LOCATION" /> ... <item name="android.permission.CAMERA" /> <item name="android.permission.INTERNET" /> ... </perms> </package> 权限至用户组的映射表存储在/etc/permissions/platform.xml文件中。它被用来确定应用设置的辅助用户组GID。下面的代码片段显示了一些映射。 ... <permission name="android.permission.INTERNET" > <group gid="inet" /> </permission> <permission name="android.permission.CAMERA" > <group gid="camera" /> </permission> <permission name="android.permission.READ_LOGS" > <group gid="log" /> </permission> <permission name="android.permission.WRITE_EXTERNAL_STORAGE" > <group gid="sdcard_rw" /> </permission>... 在应用包条目中定义的权限后面会通过两种方式实施检查:一种检查在调用给定方法时进行,由运行环境实施;另一种检查在操作系统底层进行,由库或内核实施。 1. API权限 API权限用于控制访问高层次的功能,这些功能存在于Android API、框架层,以及某种情况下的第三方框架中。一个使用API权限的常见例子是 前面提到过,一些API权限与内核级的安全实施机制相对应。例如,被授予 在第4章中,我们还将讨论了API权限及实施检查机制中的一些疏忽和问题。 2. 文件系统权限 Android的应用沙箱严重依赖于严格的Unix文件系统权限模型。默认情况下,应用的唯一UID和GID都只能访问文件系统上相应的数据存储路径。注意,以下代码清单中的UID和GID(分别在第2列和第3列)对于目录都是唯一的,它们的权限被设置为只有这些UID和GID才能访问这些目录。 root@android:/ # ls -l /data/datadrwxr-x--x u0_a3 u0_a3 ... com.android.browser drwxr-x--x u0_a4 u0_a4 ... com.android.calculator2 drwxr-x--x u0_a5 u0_a5 ... com.android.calendar drwxr-x--x u0_a24 u0_a24 ... com.android.camera...drwxr-x--x u0_a55 u0_a55 ... com.twitter.android drwxr-x--x u0_a56 u0_a56 ... com.ubercab drwxr-x--x u0_a53 u0_a53 ... com.yougetitback.androidapplication.virgin. mobile drwxr-x--x u0_a31 u0_a31 ... jp.co.omronsoft.openwnn 相应地,由这些应用创建的文件也会拥有相应的权限设置。以下代码清单中显示了某个应用的数据目录,子目录和文件的属主和权限都被只设置给该应用的UID和GID。 root@android:/data/data/com.twitter.android # ls -lR .: drwxrwx--x u0_a55 u0_a55 2013-10-17 00:07 cache drwxrwx--x u0_a55 u0_a55 2013-10-17 00:07 databases drwxrwx--x u0_a55 u0_a55 2013-10-17 00:07 files lrwxrwxrwx install install 2013-10-22 18:16 lib -> /data/app-lib/com.twitter.android-1drwxrwx--x u0_a55 u0_a55 2013-10-17 00:07 shared_prefs ./cache: drwx------ u0_a55 u0_a55 2013-10-17 00:07 com.android.renderscript.cache ./cache/com.android.renderscript.cache: ./databases: -rw-rw---- u0_a55 u0_a55 184320 2013-10-17 06:47 0-3.db -rw------- u0_a55 u0_a55 8720 2013-10-17 06:47 0-3.db-journal -rw-rw---- u0_a55 u0_a55 61440 2013-10-22 18:17 global.db -rw------- u0_a55 u0_a55 16928 2013-10-22 18:17 global.db-journal ./files: drwx------ u0_a55 u0_a55 2013-10-22 18:18 com.crashlytics.sdk.android ./files/com.crashlytics.sdk.android: -rw------- u0_a55 u0_a55 80 2013-10-22 18:18 5266C1300180-0001-0334-EDCC05CFF3D7BeginSession.cls ./shared_prefs : -rw-rw---- u0_a55 u0_a55 155 2013-10-17 00:07 com.crashlytics.prefs. xml -rw-rw---- u0_a55 u0_a55 143 2013-10-17 00:07 com.twitter.android_preferences.xml 正如前面所提到的,特定的辅助用户组GID用于访问共享资源,如SD卡或其他外部存储器。作为一个例子,注意在HTC One V手机上运行 root@android:/ # mount.../dev/block/dm-2 /mnt/sdcard vfat rw,dirsync,nosuid,nodev,noexec,relatime, uid=1000,gid=1015,fmask=0702,dmask=0702,allow_utime=0020,codepage=cp437, iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0...root@android:/ # ls -l /mnt...d---rwxr-x system sdcard_rw 1969-12-31 19:00 sdcard 这里你可以看到SD卡被使用GID 1015进行挂载,对应为 3. IPC权限 IPC权限直接涉及应用组件(以及一些系统的IPC设施)之间的通信,虽然与API权限也有一些重叠。这些权限的声明和检查实施可能发生在不同层次上,包括运行环境、库函数,或直接在应用上。具体来说,这个权限集合应用于一些在Android Binder IPC机制之上建立的主要Android应用组件。关于这些组件和Binder的详细信息,本章后面会详细描述。 2.3 深入理解各个层次本节将详细介绍Android软件栈中与安全最相关的组件,包括应用层、Android框架层、DalvikVM、用户空间的支持性原生代码与相关服务,以及Linux内核层。这将为我们理解后续章节对这些组件的详细介绍打下基础,并为我们攻击这些组件提供必要的知识。 2.3.1 Android应用层为了了解如何评估和攻击Android应用层的安全性,你首先需要了解它们是如何工作的。本节讨论了Android应用、应用运行时和支持性IPC机制的安全相关部分。这也会为理解第4章奠定基础。 应用通常被分为两类:预装应用与用户安装的应用。预装应用包括谷歌、原始设备制造商(OEM)或移动运营商提供的应用,如日历、电子邮件、浏览器和联系人管理应用等。这些应用的程序包保存在/system/app目录中。其中有些应用可能拥有提升的权限或权能,因此人们会特别感兴趣。用户安装的应用是指那些由用户自己安装的应用,无论是通过Google
Play商店等应用市场直接下载,还是通过 Android在与应用相关的多种用途中使用公共密钥加密算法。首先,Android使用一个特殊的平台密钥来签署预安装的应用包。使用这个密钥签署的应用的特殊之处它们拥有 主要的应用组件 尽管Android应用由无数个组件组成,但本节将重点介绍那些与Android系统版本无关,在大多数应用中都值得关注的组件。这些组件包括AndroidManifest、Intent、Activity、BroadcastReceiver、Service和Content Provider。后面4类组件代表IPC通信端点(endpoint),它们有一些非常有趣的安全属性。
2.3.2 Android框架层作为应用和运行时之间的连接纽带,Android框架层为开发者提供了执行通用任务的部件——程序包及其类。这些任务可能包括管理UI元素、访问共享数据存储,以及在应用组件中传递消息等。也就是说,框架层中包含任何仍然在DalvikVM中执行的非应用特定代码。 通用的框架层程序包位于 表 2-1 框架层中的管理器
使用 root@generic:/ # ps -t -p 376USER PID PPID ... NAMEsystem 376 52 ... system_server ...system 389 376 ... SensorServicesystem 390 376 ... WindowManagersystem 391 376 ... ActivityManager ...system 399 376 ... PackageManager 2.3.3 DalvikVMDalvikVM是基于寄存器而不是栈的。虽然有人说Dalvik是基于Java的,但它并不是Java,因为谷歌并不使用Java的Logo,而且Android的应用模型也与JSR(Java标准规范要求)没有关系。Android应用开发者要记住,DalvikVM虽然看起来和感觉上都像Java,但实际上并不是。整体的开发流程大致如下: (1) 开发者以类似Java的语法进行编码; (2) 源代码被编译成.class文件(也类似于Java); (3) 得到的类文件被翻译成Dalvik字节码; (4) 所有类文件被合并为一个Dalvik可执行文件(DEX)文件; (5) 字节码被DalvikVM加载并解释执行。 作为一个基于寄存器的虚拟机,Dalvik拥有大约64 000个虚拟寄存器。不过通常只会用到最前16个,偶尔会用到前256个。这些寄存器被指定为虚拟机内存的存储位置,用于模拟微处理器的寄存器功能。就像实际的微处理器一样,DalvikVM在执行字节码时,使用这些寄存器来保持运行状态,并跟踪一些值。 DalvikVM是专门针对嵌入式系统的约束(如内存小和处理器速度慢)而设计的。因此,在DalvikVM设计时考虑到了速度和运行效率。但虚拟机毕竟只是对底层CPU寄存器机的一个抽象,本质上就意味着在运行效率上有所损失,而这也正是谷歌力求减轻这些副作用的原因。 为了在这些约束中发挥更大的能力,DEX文件在被虚拟机解释执行之前会进行优化处理。对于从一个Android应用中启动的DEX文件,这种优化通常只在应用第一次启动时进行一次。优化过程的结果是一个优化后的DEX文件(ODEX)。需要注意,ODEX文件是无法在不同版本的DalvikVM之间或是不同设备之间进行移植的。 与Java虚拟机类似,DalvikVM使用Java Native Interface(JNI)与底层原生代码进行交互。这一功能允许在Dalvik代码和原生代码之间相互调用。欲了解DalvikVM、DEX文件格式以及JNI on Android的更详细信息,可查阅Dalvik官方文档,网址为http:///kodebase/dalvik-docs-mirror/docs/。 Zygote Android设备启动时,Zygote进程是最先运行的进程之一。接下来,Zygote负责启动其他服务以及加载Android框架所使用的程序库。然后,Zygote进程作为每个Dalvik进程的加载器,通过复制自身进程副本(也被称为forking,分支)来创建进程。这种优化方案可以避免重复那些不必要且消耗大量资源的加载过程,即启动Dalvik进程(包括应用)时加载Android框架及其依赖库。作为优化结果,核心库、核心类和对应的堆结构会在DalvikVM的所有实例之间共享。这也给攻击带来了一些有趣的可能性,你会在第12章中阅读到更详细的内容。 Zygote的第二大功能是启动
在初始启动后,Zygote通过RPC和IPC机制为其他Dalvik进程提供程序库访问,这是承载Android应用组件的进程实际启动的机制。 2.3.4 用户空间原生代码层操作系统用户空间内的原生代码构成了Android系统的一大部分,这一层主要由两大类组件构成:程序库和核心系统服务。本节将讨论这两大类组件,并详述一些属于这两大类的单独组件。 1. 程序库 Android框架层中的较高层次类所依赖的许多底层功能都是通过共享程序库的方式来实现,并通过JNI进行访问的。在这其中,许多程序库都也是在其他类Unix系统中所使用的知名开源项目。比如,SQLite提供了本地数据存储功能,Webkit提供了可嵌入的Web浏览器引擎,FreeType提供了位图和矢量字体渲染功能。 供应商特定的程序库,即那些为某一设备型号提供硬件支持的代码库,保存在/vendor/lib(或/system/vendor/lib)路径。其中包括对图形显示设备、GPS收发器或蜂窝式无线电的底层支持库等。非厂商特定的程序库则保存在/system/lib路径中,通常会包括一些外部项目,比如像下面这些库。
这些只是Android的大量程序库中的一小部分,一个运行Android 4.3的设备中包含了超过200个共享程序库。 然而,并非所有的底层程序库都是标准的,Bionic就是一个值得注意的特例。Bionic是BSD C运行时库的一个变种,旨在提供更小的内存使用空间,更好的优化,同时避免产生GNU公共许可证(GPL)授权问题。这些差异也带来了少许代价。Bionic的 这些库是使用原生代码开发的,因而很容易出现内存破坏漏洞,这使得该层成为探索Android安全性时的一个特别有趣的部分。 2. 核心服务 核心服务是指建立基本操作系统环境的服务与Android原生组件。这些服务包括初始化用户空间的服务(如init)、提供关键调试功能的服务(如adbd和debugggerd)等。注意,某些核心服务可能是硬件或版本特定的,本节当然不能详尽描述所有的用户空间服务。
2.3.5 内核尽管Android的根基——Linux内核文档相当完备而且已经被深入理解,但是Linux内核和Android使用的内核还是有很多显著的差异。本节将介绍其中的一些变化,特别是那些和Android安全相关的。 1. Android分支 在早期,谷歌创建了Linux内核的一个Android分支,因为许多修改和添加已经不再与Linux内核主代码树相互兼容。总体而言,这其中包括了大约250个补丁,涉及文件系统支持、网络处理调整,以及进程和内存管理功能等。根据一位内核工程师的说法,绝大部分的补丁“代表着Android开发者在Linux内核中发现的一些局限性”。2012年3月,Linux内核维护者将Android特有的内核修改合并到了主代码树。表2-3显示了一些对主代码树的添加与修改,本节将详细介绍其中的一部分。 表 2-3 Android对Linux内核的主要修改
2. Binder 对Android的Linux内核最为重要的一个添加也许是Binder驱动。Binder是一个基于OpenBinder修改版本的IPC机制,OpenBinder最初由Be公司开发,后来又由Palm公司开发和维护。Android的Binder代码量相对较小(大约有4000行源码,存在于2个文件中),但是对于大部分的Android功能都是非常关键的。 概括地说,Binder内核驱动是整个Binder架构的粘合剂。Binder作为一个架构,以客户端—服务器模型运行,允许一个进程同时调用多个“远程”进程中的多个方法。Binder架构将底层细节进行了抽象,使得这些方法调用看起来就像是本地函数调用。图2-3显示了Binder的通信流图。 图 2-3 Binder的通信流 Binder也使用进程ID(PID)和UID信息作为一种标识调用进程的手段,允许被调用方作出访问控制的决策。通常会调用 在实际情况中会遇到的一个例子是 const int pid = ipc->getCallingPid();const int uid = ipc->getCallingUid(); if ((uid != AID_GRAPHICS) && !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { ALOGE("Permission Denial: " "can't read framebuffer pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; } 在更高的层次上所暴露的IPC方法,如那些由绑定服务所提供的IPC方法,通常会通过Android接口定义语言(AIDL)提炼成一个抽象接口。AIDL允许两个应用使用“协商确定”或者标准化的接口,来发送和接收数据,使得接口独立于具体的实现。AIDL类似于其他的接口定义语言文件,比如C/C++中的头文件。以下是一个AIDL代码片段的示例。 // IRemoteService.aidl package com.example.android;//在此声明任何非默认类型导入声明 /**范例服务接口*/ interface IRemoteService { /**请求这一服务的进程ID,做点“有趣”的事情*/ int getPid(); /**显示一些用作AIDL参数和返回值的基本类型*/ void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString); } 这个AIDL的例子定义了一个简单的接口——IRemoteService,包含两个方法: 3. ashmem 匿名共享内存服务,简称ashmem,是另一个在Linux内核Android分支中添加的代码模块。ashmem驱动基本上提供了基于文件、通过引用计数的共享内存接口。它广泛应用于大多数Android核心组件中,包括Surface Flinger、Audio Flinger、系统服务器和DalvikVM等。ashmem能够自动收缩内存缓存,并在全局可用内存较低时回收内存区域,因而非常适用于低内存环境。 在底层使用ashmem很简单,只需调用 int fd = ashmem_create_region("SomeAshmem", size); if(fd == 0) { data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ... 在较高层次上,Android框架层中提供了 4. pmem 另一个Android特有的自定义驱动是pmem,用来管理1~16MB(或更多,取决于具体实现)的大块物理上连续的内存区块。这些区块是特殊的,可以在用户空间进程和其他内核驱动(比如GPU驱动)之间共享。与ashmem不同的是,pmem驱动需要一个分配进程,为pmem的内存堆保留一个文件描述符,直到所有其他索引都关闭。 5. 日志记录器 虽然Android内核仍然维护自己基于Linux内核的日志机制,但它也使用另一个日志记录子系统,即俗称的“日志记录器”(logger)。作为 图 2-4 Android日志记录系统架构 主缓冲区通常是日志数量最大的,并且是应用相关事件的日志源。应用通常从 系统缓冲区也是许多信息的来源,即由系统进程生成的系统级事件。这些进程利用 日志消息可以使用 $ adb -d logcat --------- beginning of /dev/log/system D/MobileDataStateTracker( 1600): null: Broadcast received : ACTION_ANY_DATA_CONNECTION_STATE_CHANGEDmApnType=null != received apnType=internet D/MobileDataStateTracker( 1600): null: Broadcast received: ACTION_ANY_DATA_CONNECTION_STATE_CHANGEDmApnType=null != received apnType=internet D/MobileDataStateTracker( 1600): httpproxy: Broadcast received: ACTION_ANY_DATA_CONNECTION_STATE_CHANGEDmApnType=httpproxy != received apnType=internet D/MobileDataStateTracker( 1600): null: Broadcast received: ACTION_ANY_DATA_CONNECTION_STATE_CHANGEDmApnType=null != received apnType=internet ... --------- beginning of /dev/log/main ... D/memalloc( 1743): /dev/pmem: Unmapping buffer base:0x5396a000size:12820480 offset:11284480D/memalloc( 1743): /dev/pmem: Unmapping buffer base:0x532f8000size:1536000 offset:0D/memalloc( 1743): /dev/pmem: Unmapping buffer base:0x546e7000size:3072000 offset:1536000D/libEGL ( 4887): loaded /system/lib/egl/libGLESv1_CM_adreno200.so D/libEGL ( 4887): loaded /system/lib/egl/libGLESv2_adreno200.so I/Adreno200-EGLSUB( 4887): <ConfigWindowMatch:2078>: Format RGBA_8888. D/OpenGLRenderer( 4887): Enabling debug mode 0V/chromium( 4887): external/chromium/net/host_resolver_helper/host_ resolver_helper.cc:66: [0204/172737:INFO:host_resolver_helper.cc(66)] DNSPreResolver::Init got hostprovider:0x5281d220 V/chromium( 4887): external/chromium/net/base/host_resolver_impl.cc:1515: [0204/172737:INFO:host_resolver_impl.cc(1515)] HostResolverImpl::SetPreresolver preresolver:0x013974d8 V/WebRequest( 4887): WebRequest::WebRequest, setPriority = 0I/InputManagerService( 1600): [unbindCurrentClientLocked] Disable input method client. I/InputManagerService( 1600): [startInputLocked] Enable input method client. V/chromium( 4887): external/chromium/net/disk_cache/ hostres_plugin_bridge.cc:52: [0204/172737:INFO:hostres_ plugin_bridge.cc(52)] StatHubCreateHostResPlugin initializing.. . ... 这个 6. Paranoid Networking Android内核基于一个调用进程的辅助用户组来限制网络操作,而这个调用进程就是被称为Paranoid
Networking的内核修改模块。在高层次上,这个模块将一个AID(以及随后的GID)映射到应用层的权限声明或请求上。例如,Manifest文件中的权限 表 2-4 根据用户组定义的网络权能
你可以从AOSP代码库中的system/core/include/private/android_filesystem_config.h文件中找到其他Android特有的GID。 2.4 复杂的安全性,复杂的漏洞利用在仔细观察了Android的设计与架构之后,我们已经清楚地了解到,Android操作系统是一种非常复杂的系统。设计者坚持了最低权限原则,也就是说任何特定组件都应该只能访问它真正所需要访问的东西。在本书中,你将看到他们使用这一原则的大量证据。不过,这虽然有助于提高安全性,却也增加了复杂性。 进程隔离和减少特权往往是安全系统设计中的基石。无论对于开发者还是攻击者,这些技术的复杂性也让系统都变得更加复杂,从而增加两方的开发成本。当攻击者在打磨他的攻击工具时,他必须花时间去充分了解问题的复杂性。对于像Android这样一个系统,单单攻击一个安全漏洞,可能不足以获取到系统的完全控制权。攻击者可能需要利用多个安全漏洞才能达到目的。总之,要成功地攻击一个复杂系统,需要一个复杂的漏洞利用。 一个能够很好地说明这一点的真实例子是,用于root HTC J Butterfly手机的“diaggetroot”漏洞利用。为了获取root访问控制权,它利用了多个互为补充的安全问题。这个特殊的漏洞利用会在第3章中详细讨论。 2.5 小结本章概述了Android安全设计和架构。我们引入了Android沙箱及Android使用的权限模型,包括Android对Unix系统UID/GID映射关系的特殊实现AID,以及在整个系统中实施的限制和权能。 我们也深入介绍了Android的逻辑层次,包括应用层、Android框架层、DalvikVM、用户空间原生代码和Linux内核。对于每个层次,我们都讨论了主要组件,特别是那些与安全相关的组件。我们强调了Android开发者对Linux内核所作出的重要添加与修改。 |
|