现今,智能手机操作系统不能有效的提供给用户足够的控制权并且很清楚的了解到第三方的应用程序是如何使用其的隐私数据。我们使用了TaintDroid来阐明这个缺点,其是一个高效的,全系统动态污点跟踪和分析系统可以同时跟踪对个敏感数据来源。TaintDroid利用了Android虚拟机的执行环境提供了一个实时的分析。TaintDroid仅需消耗绑定的CPU为基准14%的性能开销对于第三方的应用程序的开销完全可以忽略不计。通过使用TaintDroid监控了Android第三方比较流行的30款软件的行为,我们发现在其中的20款软件中就有68个例子是误用用户隐私信息的。使用TaintDroid监控敏感信息有助于手机用户在使用第三方应用时获得很多的信息对于智能手机有价值的输入对于安全公司也可以有效的寻找出有恶意行为的软件。
现代智能手机平台的一个主要特征就是一个集中式的服务下载多数来自第三方应用市场。这样的一个“App stores”不仅方便了用户和开发者,而且已经让手机设备更加的有趣以及有用,因此使得智能手机平台有了一个爆发式的增长。苹果的APP Store仅仅18个月就提供了将近3亿的应用程序。大多数的这些应用程序混合数据时来自本地的传感器像GPS接收器,照相机,摄像头和加速器然后上传到云端。应用程序通常都具有合法的原因来获取用户的隐私数据,但是用户往往希望自己的数据是被正确使用的。程序的开发者中继利用了上传到云端的数据并且这些隐私数据是通过看似无辜的传感器产生的比如说加速计。
解决这种用户体验乐趣和使用第三方的应用软件而带来的隐私冲突在智能手机平台是一个极大的挑战。目前,手机的操作系统提供的仅仅是一个粗粒度的控制访问信息是否一个应用程序可以访问用户的隐私信息,但是没有提供隐私数据是怎样被使用的。例如,如果用户允许应用程序获取到她的地理位置信息,但是她无法得知是否这个应用程序将会送她的应用程序到地理位置服务系统,广告商,应用开发者或者是其他的应用实体。作为这个结果,用户盲目的相信了应用程序将会正确的使用他们的隐私数据。
这篇文章讲了TaintDroid,是针对Android手机平台的一个扩展,通过第三方的应用程序用于跟踪敏感数据流。TaintDroid假设下载的第三方运用程序是不被信任的,通过实时的检测这些应用程序是怎样获取和控制用户的隐私数据的。我们的首要目标是检测当敏感数据通过不信任程序离开我们的系统时通过手机用户或者是外部安全服务来促进对应用程序的分析。
分析应用程序行为要求有足够的上下文信息关于数据将要离开一个设备以及数据将会被送往哪里。因此,TaintDroid自动标记数据的来源从隐私敏感来源以及物地应用标签作为敏感数据传播通过程序的变量,文件,进程间的消息。当污点数据通过网络传播或者是其他的途径离开了系统,TaintDroid将会对这个数据进行标签,应用程序负责传送数据和数据的目的地。这样的实时反馈给用户还有安全服务商更深入的了解到移动的运用程序在做什么,有可能识别出行为不端的应用程序。
对于实用性来说,TaintDroid的性能开销必须要最小。相比于现在的重量级的全系统仿真,我们利用了Android的虚拟化架构整合了四个粒度级的污点传播:变量级,方法级,信息级和文件级。尽管现有的技术都不是新的,我们的贡献在于合并了这些技术并且找到了一个由于智能手机对资源约束在性能和精度之间的平衡点。实验表明我们的原型系统显示跟踪而引起的CPU的运行时候的开销通过微基准测试还不到14%。更为重要的就是,被检测的第三方软件可以被监控并且产生很少的感知延迟。
为了评估TaintDroid的准确性,我们使用了30款随机选取的运用程序,这些流行的Android运用程序会涉及到使用定位用户的地理位置信息,使用摄像头,录音数据等等。TaintDroid正确的标记了这些应用程序中的105例这样的应用程序传输污染路径;在这105例中我们检测到其中的37例是合法的。TaintDroid同样显示了在这30款软件中的15例提供了用户的地理位置信息给远程的广告服务商。其中的7款软件收集了手机的相关信息的ID,例如手机的号码和SIM卡号。总的来说,在我们研究的三分之二的应用程序中都有涉及到使用敏感数据的可以行为。我们的发现揭示了TaintDroid能够帮助解决可能存在威胁的第三方应用程序的敏感行为。
类似于信息流跟踪系统,TaintDroid的一个基本限制可以通过隐式流绕过泄露。通过隐式流避免了污点检测,就这种行为而言,就可以作为恶意软件的一项指标,并且可以通过其他的技术例如自动化的静态代码分析,这一技术我们将在第8章中讨论。
剩余文章的组织如下:第二部分提供更高层次的对TaintDroid的介绍,第三部分会对Android平台的背景做一个介绍,第四部分将会介绍TaintDroid的设计,第五部分描述了TaintDroid对污染源的追踪,第六部分描述了对Android应用程序的研究,第7部分描述了我们原型系统的性能特征,第八部分描述了我们方法的局限性,第9部分介绍了相关工作,第十部分进行相关的总结。
我们试图寻找一个框架允许用户监控第三方智能手机应用程序让他们实时处理他们的隐私数据。许多的智能手机都是闭源的,因此,静态代码分析是不可能的。即使源代码是可用的,运行时事件和配置常常规定了信息使用;实时监控在特殊的环境了占了很重要的比重。
对于网络的监控披露隐私敏感信息存在以下几个挑战:
l 智能手机的资源是受限的。由于资源的限制阻碍了像Panorama这样重量级信息跟踪系统。
l 第三方应用程序委托了几种类型的隐私数据信息。这个监控系统必须区分各种各样的信息,这就产生了额外的计算和存储。
l 基于上下文的敏感信息是动态的,难以识别甚至是当发送的时候才会清晰。例如,地理位置是一系列的浮点数可以很频繁的改变并且很难预测。
l 应用程序可以分享信息。如果将监控系统限制在单独的APP里面的话不能够很好的检测文件之间的信息流和应用程序之间的进程通信,包括核心系统的设计到敏感信息的传递。
我们使用动态的污点分析来监控智能手机里的敏感隐私信息。敏感信息是首先用来标记污点来源的,一个污点标示指示信息是被分配好的。动态污点分析标记数据标签是怎样影响其他数据的可能方式是原始的敏感信息泄露。这种追踪方式常常是在指令执行水平上的。最后,受影响的数据将会被标示在他离开系统之前存放到污点池(常常是网络接口)中。
存在的污点跟踪方法通常有几个限制。首先也是最重要的,方法依靠指令级的动态污点分析来做整个系统的仿真将会产生很高的性能消耗。指令级的检测将会是产生2-20倍的性能消耗,除此之外也会招致仿真的性能消耗,因此指令级的动态分析并不适合实时的动态分析。第二,发展中的精确污点传播逻辑已经证明对于X86的指令级是一个很大的挑战。指令级的污点检测可能导致污点爆发如果出现堆栈指针错误或者是污点丢失例如像CMPXCHG,REF MOV这样复杂的指令。然而大多数的智能手机都是使用ARM指令集,类似很多假阳性和假阴极的现象都可能出现。
表一呈现出了我们污点跟踪智能手机的方法。我们利用智能手机的体系架构来提升架构(e.g.,安卓,黑莓,J2ME架构的手机),系统范围的污染使用语义明确细粒度跟踪标签。
首先,我们执行VM解释器来提供各种等级的跟踪用以跟踪可疑的程序代码。使用各种语义是通过解释器提供的值得上下文来避免污点爆发在X86的指令级中。除此之外,通过追踪的变化,我们只是跟踪数据而并不跟踪代码。第二,我们在应用程序之间使用了信息级别的跟踪。跟踪消息之间的污点而不是数据在进程间通信的信息最小化开销以此来扩大分析的系统宽度。第三,对于系统提供的原生态库,我们使用了方法级别的跟踪。在这里,我们运行代码而不做测试,并且修复了污点追踪传播的返回值。这些方法伴随着系统和一直心系流动的语义。最后,我们使用了文件级别的跟踪以确保信息的持久性保留了污点标记。
我们充分利用应用程序定义好的接口来访问敏感数据,以此来分配标签。例如,所有的信息来自于GPS硬件的都是位置敏感信息,并且所有信息来自联系人数据库是涉及联系人的敏感信息。这避免了对标签的依赖试探或者手册规范。对于扩展的信息我们将在第5部分详述。
为了实现跟踪的多粒度,我们的方法依赖于固件的完整性。这个污点跟踪的可信计算基于包括虚拟机在内的执行用户空间和任何不可信的解释应用程序加载的系统库。然而,这部分代码是固件的一部分,因此是可信的。应用程序仅仅可以逃避虚拟机执行的原生代码。在我们的目标平台(Android),我们修改了本地库加载程序确保应用程序不仅可以只从固件加载本地库而不是从应用程序下载。值得注意的是,在2010年初对Android Market市场上的每一个类别前50的最流行的免费发行的应用程序都揭示出这些程序少于4%包含a.so文件。一份类似的调查报告在2010年中期揭示出这个部分增长到了5%,这个增长预示着使用第三方库的增长,但是这个数量对应用程序的影响还是很小的。
总的来说,我们提供了一个新颖的,高效的,款系统的,多标记的根据不同的信息进行多粒度结合的污点跟踪系统。虽然对于一些技术像通过解释器进行变量跟踪在之前曾经被提出过(看第九章),据我们所知,我们的方法是第一个扩充像系统级跟踪的,通过选择多粒度的方法,我们平衡了性能和精度之间的问题。此部分我们将在第6和第7节中提及,我们的系统方法即高效(对于同时跟踪每个数据单元的32个污点只占了14%的系统开销和4.4%的内存开销)
Android是一个基于Linux的开源的,移动手机平台。最核心的手机功能被执行作为一个应用程序运行在顶层自己定制的中间键上。中间件本身使用的是Java和C/C++代码。运用程序是用Java实现的并且被编译成一个自定义的字节码被命名为Dalvik可执行文件(DEX)的格式。每一个的可执行程序都是一个Dalvik虚拟机实例。每一个实例作为一个唯一的UNIX用户身份在Linux平台子系统上每个应用程序是隔离的互不干扰的。应用程序之间的通信通过Binder IPC机制。Binder通过一个基于包交换的信息处理系统。我们现在讨论一些必要的话题来是我们更好的理解我们的系统。
Dalvik 虚拟机解释器:Dex文件是一种基于寄存器的机器语言,和Java的字节码不同,其是基于栈的。对于每一个DEX方法都有它自己预定义的一定数量的虚拟寄存器(由于经常使用所以我们简单称为“寄存器”)。Dalvik虚拟机的解释器管理方法寄存器使用一个内部的可执行栈。当前的方法寄存器总是在顶部的堆栈框架。这些寄存器对应着Java方法的局部变量和原始的存储类型以及对象的引用。所有的这些计算都发生在寄存器中,因此在使用前和使用后类的字段都必须要加载和存储。值得注意的是DEX使用的类的字段必须是长时间存储的,不像硬件基于寄存器的语言(e.g.,X86)存储值的时候是随机的。
Native 方法:Android的中间件提供了访问到本地性能优化和第三方库像OpenGL和WebKit。Android同样使用了Apache Harmony Java架构,使其可以频繁的使用系统库(e.g.,math routines)。Native层的方法是用C/C++写的暴露出功能是基于Linux内核和服务的。他们同样提供了可以访问Java内部的接口,因此它是被包含在我们的可信计算部分。
图2 TaintDroid的体系结构
Android包含了两种类型的原生方法:内部虚拟机方法和JNI方法。内部虚拟机方法用于访问内部特殊的结构和APIs。JNI方法符合Java本地接口标准规范,这要求Dalvik对单独的java参数传进的变量使用JNI调用桥。相反的,内部的VM方法必须手动的解析从解释出的字节码数组参数。内部虚拟机的方法必须手动解析参数从解释器传来的字节数组的参数。
Binder IPC:所有的Android IPC都需要使用Binder 机制来进行通信。Binder是一个基于组件的进程并且IPC框架设计是为了BeOS,Palm Inc 进行扩展,Google为Android量身定制的。Binder的基础是基于包的,是活跃并且标准数据的序列化,允许应用程序框架层在进程之间管理分享对象。Binder 的内核模块在进程之间传递包的信息。
TaintDroid是一个多粒度Android的污点跟踪方法。TaintDroid使用变量级的跟踪VM解释器。多个污点标记被存储为一个污点标签。当应用程序执行本地方法的时候,各种的污点标记被作为返回值返回。最后,最后污点标记被分配给包通过Binder机制进行传播。注意在技术报告中对于这部分执行细节有更详细的解释。
图 2描述了TaintDroid的系统架构。信息被标记为可信任的应用程序当有充足的上下文时。污点接口调用了本地的方法和Dalvik虚拟机进行交互,存储精确的污点标记在虚拟的污点集中。Dalvik VM传播污点标签根据数据流规则作为可信任的应用程序使用污点信息。每一个的解释器实例同时传播污点标记。当可信任的APP使用污点信息在IPC中进行传播,修改后的Binder库确保了包裹有污点标记反射出合并后的污点标记包含了所有的数据。包裹传输完全通过内核并且被远程的不可信任程序接受。注意仅有解释代码是不可信的。修改后的Binder库检索来自包的污点标记并将它分配给所有的值从其中读取。Dalvik VM实例的远程传播将完全相同的污点标记为不可信应用程序。当不可信任的应用程序调用调用一个特定的程序作为一个污点池。e.g.网络发送数据,库来检索污点标记数据问题并且报告事件。
执行这个架构需要解决几个系统的挑战:
a) 污点的存储
b) 解释代码污点传播
c) 原生代码污点传播
d) IPC污点传播
e) 二级存储污染
剩余的部分将会描述我们的设计。
对于怎样选择存储标记直接影响了执行和存储的效率。动态污点跟踪系统通常存储每个字节或标签。跟踪内存是结构化的,没有内容的语义。常用的污点标记被存储在不相邻的单元和标记地图上。TaintDroid使用了解释器里的语义变量。我们存储相邻的污点标记在内存当中,提供空间位置。
Dalvik有五种类型的变量要求存储:局部方法变量,方法的参数,类的静态变量,类实例的字段值和数组。在所有的情况下,我们存储了一个32位的位向量对于每一个污点标记每个变量进行编码,允许存储32种不同的污点标记。
Dalvik存储方法的本地变量值还有参数在内部栈。当应用程序调用一个方法的时候,一个新的堆栈帧分配给所有的局部变量。方法参数的传递也通过内部栈。在调用方法之前,被调用的函数的参数的地方在堆栈的顶部,这样它们成为了高编号被登记在被调用的函数的堆栈帧。我们给污点存储分配增加一倍大小的存储空间。污点标签值之间的交叉例如寄存器Vi最初访问的变量是fp[i]当被修改之后访问的是fp[2·i]。注意Dalvik存储一个64位的变量是通过两个相邻的32位内部寄存器完成的。然而字节码解释这些相邻的寄存器作为一个单独的64位的值,解释器管理这些寄存器作为单独的值。因此,我们对栈的修改是透明的并且检索64位的值对于分开的寄存器。最后,本机方法的目标要求一个稍微不同的堆栈框架便是基于以上原因。
图3修改后的栈的格式
污点标记是交错在寄存器之间的来解释方法目标和附加的本地方法。深灰色的盒子代表的是污点标记。
详细的讨论在4.3节。修改的栈格式如图 3所示。
污点标记存储在相邻的类字段和数组在VM解释器的内部的数据结构。为了使存储开销最小化TaintDroid存储的仅仅是每个数组的一个污点标签。对于Java字符串对象来说每个污染值的存储是极其低效的。作为所有的特征都有相同的标签。不幸的是,对于每个数组只存储一个污点标签可能导致假阳性污染传播。例如,如果一个没有被标记污点的值u存储的数组A下标索引为0(A[0])的位置并且污点变量t存储在A[1],此时数组A是被标记为污染的。之后,如果变量v被分配给A[0],v将会被标记,即使u是未被标记的。不幸的是,Java频繁的使用对象,并且对象之间的引用是被频繁的标记的(见4.2节),因此编码实践将会呈现出更少的假阳性。
污点跟踪的粒度和流的语义直接影响了性能和准确性。TaintDroid执行变量级的污点跟踪使用了Dalvik VM解释器。变量提供有价值的语义污点传播,从标量值区分数据指针。TaintDroid最原始的跟踪是基本类型变量(e.g.int,float,etc);但是,在一些情况下对象的引用必须成为受污染的以确保污点传播的正确性;这一节描述了为什么这些例子是存在的。但是,首先我们呈现出污点跟踪在Dalvik机器语言是作为正常的逻辑。
Dalvik VM运行唯一的DEX机器语言指令集,因此我们必须设计一个合适的传播逻辑。我们使用了数据流逻辑,作为跟踪隐式流要求的静态分析和引起显著的
性能开销和高估的跟踪(见第8节)。我们开始定义了污点标记,污点标签,变量以及污点传播。我们呈现了我们对于DEX的逻辑。
设集合L是对一个特定集合的一般情况下的污点标记。一个污点标记是这个集合中的一个元素,t⊆ L。每一个变量都有一个相关联的污点标记。一个变量是在4.1节中描述的5个类型中的一种。对于每一种类型我们采用了不同的表示。局部变量和参数变量我们对应于相应的虚拟寄存器,使用VX 。类的字段变量我们使用fx来表示用X来表示类的下标。字段的实例要有一个对象的实例来指示被标记为Vy(fx),其中Vy是实例对象的引用(包括对象的直接引用和间接引用),静态字段也是用fx来标记,速记为S(fx),其中S()表示的是静态的取值范围。最终,Vx[·]代表的是一个数组,Vx代表的是数组变量的引用。
我们的污点函数集使用的是τ(·)。τ(v)返回的是污点标记变量。检索和复制是不同我们使用符号←来区分。当τ(v)出现在←的右边表示的是检索污点标记的变量值v。当τ(v)出现在←的左边表示的对变量值v赋值。例如,τ(v1)← τ (v2)将污点标记V2的值赋给V1。
表 1展示的是我们传播逻辑。该表列举了抽象的DEX字节码指令的详细说明。寄存器变量和类的字段分别使用Vx和fx代替。R表示的返回E表示的是异常。大写的A,B,C代表的是不变的字节码。表中没有列出清除污点寄存器的目的寄存器。
表1TaintDroid的传播逻辑
例如我们不考虑指令数组的长度对于返回一个污点值即使这个数组是受污染的。注意数组的长度有助于直接控制流的传播。
在表 1中的传播规则有两个例外。其一,污点传播逻辑包括污点标记数组的索引中查找处理转换表(e.g. ASCII/UNICODE的大小写转换)。例如,考虑从小写转换到大写字母的情况:如果一个污点值“a”是数组的一个索引,由此导致“A”也是被污点标记的即使“A”在数组中不是。因此,污点逻辑aget-op既使用数组和数组索引污点。第二,当这个数组包含对象索引(e.g.一个证型数组),这个污点索引标记传播的是对象的引用而不是对象的值。因此,我们包含对象引用污点标记在实例中得到了(iget-op)规则。
图 4列出的代码展示出了一个实际的实例对象引用污点的必要性。在这里,ValueOf()返回一个整数对象传递int。如果整型参数在-128到127之间,ValueOf()返回引用静态定义的整型对象。ValueOf()对象隐式的转换为一个对象。考虑下列方法的定义:
Object intProxy(int val) { return val; }
int out = (Integer) intProxy(tVal);
考虑一下这样一种情况tVal的值是int为1并且是被标记的。当intProxy()传递tVal的时候最后返回的值是val。在这种情之下,Integer.valueOf()获得一个整数实例对应val标量的变量。在这种情况下,Integer.valueOf()返回一个引用静态对象其值为1。这个变量字段(整型类)对应的对象标记为∅;然而,因为aget-op传播规则包括污点的索引寄存器,这个对象的索引有一个污点标记。因此,仅仅只包含对象的污点标记当字段读取来自整型,正确的污点标记将会被赋值。
图4摘自Android整数类说明需要对象引用的污点传播