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整数类说明需要对象引用的污点传播