TaintDroid剖析之DVM变量级污点跟踪(下篇)

TaintDroid剖析之DVM变量级污点跟踪(下篇)

简行、走位@阿里聚安全




回顾

上一章节中我们详细分析了TaintDroid对DVM方法参数和方法变量的变量级污点跟踪机制,现在我们将继续分析TaintDroid对类的静态域、实例域以及数组的污点跟踪。



了解DVM中类的数据结构

由于DVM师从JVM,所以DVM中所有类的祖先也是Object类,该类定义在dalvik/vm/oo/Object.h中。其实不仅仅是Object类,DVM所有的基本类都定义在Object.h文件中。

众所周知,Object类共分三种类型:

1)Class Objects,它是java.lang.Class的实例,此类object的公共基类是ClassObject;

2)Array Objects,由new Array指令创建,此类object的公共基类是ArrayObject;

3)Data Objects,除了上面两种Object之外的所有object,公共基类是DataObject。

这里有一个特例需要注意,那就是String Objects!String Objects当前等同于Data Objects,但鉴于该类在DVM中大量使用,因此DVM单独定义了一个类StringObject,它直接继承至Object。

了解了类的数据结构,再去分析TaintDroid对类的静态域、实例域和数组的污点跟踪就不会觉得无从下手了。



对各种数据结构的修改

要想实现对类的实例域和静态域的污点跟踪,最简单粗暴的方式就是对类中相关的数据结构进行修改。TaintDroid就是这么做的。

1)首先,修改了ClassObject::Object:


struct ClassObject : Object {

    /* leave space for instance data; we could access fields directly if we freeze the definition of java/lang/Class */

#ifdef WITH_TAINT_TRACKING

    // x2 space for interleaved taint tags

    u4              instanceData[CLASS_FIELD_SLOTS*2];

#else

    u4              instanceData[CLASS_FIELD_SLOTS];

#endif /*WITH_TAINT_TRACKING*/

TaintDroid将其中的u4 instanceData[CLASS_FILED_SLOTS]改为u4 instanceData[CLASS_FILED_SLOTS * 2]。这里CLASS_FILED_SLOTS默认为4。倍增的空间用于交叉存储各个实例域的污点。联想到类的实例域有两种类型:1)诸如int之类的基本类型;2)类对象的引用。所以我们可以知道,TaintDroid为每个引用也分配了一个tag,用于表示该引用的污点信息。充分理解这一点,对我们后续分析复杂污点传播逻辑很有帮助。

2)其次,修改了静态域StaticField:Field:


struct StaticField : Field {

    JValue          value;          /* initially set from DEX for primitives */

#ifdef WITH_TAINT_TRACKING

    Taint           taint;

#endif

};

在JValue之后添加了Taint tiant成员。Taint成员定义在vm/interp/Taint.h文件中定义如下:

typedef struct Taint{ u4 tag}Taint;

通过这样的修改,再对涉及到操作这些数据结构的方法进行修复就能实现类的实例域和静态域的污点跟踪了。这里以computeFieldOffsets函数为例,此函数定义在dalvik/vm/oo/Class.cpp中,由于代码较多,仅截取部分修复相关部分:


……

if (clazz->super != NULL)

        fieldOffset = clazz->super->objectSize;

    else

        fieldOffset = OFFSETOF_MEMBER(DataObject, instanceData);

……

/*Start by moving all reference fields to the front */

for (i = 0; i < clazz->ifieldCount; i++) {

        InstField* pField = &clazz->ifields[i];

        char c = pField->signature[0];

 

        if (c != '[' && c != 'L') {

            while (j > i) {

                InstField* refField = &clazz->ifields[j--];

                char rc = refField->signature[0];

                if (rc == '[' || rc == 'L'] {

                    swapField(pField, refField);

                    c = rc;

                    clazz->ifieldRefCount++;

                    break;

                }

            }

            /* We may or may not have swapped a field.*/

        } else {

            /* This is a reference field.*/

            clazz->ifieldRefCount++;

        }

        /*If we've hit the end of the reference fields, break.*/

        if (c != '[' && c != 'L')

            break;

 

        pField->byteOffset = fieldOffset;

#ifdef WITH_TAINT_TRACKING

        fieldOffset += sizeof(u4) + sizeof(u4); /* interleaved tag */

#else

        fieldOffset += sizeof(u4);

#endif

        LOGVV("  --- offset1 '%s'=%d", pField->name,pField->byteOffset);

}

……

 

/* Alignment is good, shuffle any double-wide fields forward, and finish assigning field offsets to all fields.*/

for ( ; i < clazz->ifieldCount; i++) {

        InstField* pField = &clazz->ifields[i];

        char c = pField->signature[0];

 

        if (c != 'D' && c != 'J') {

            while (j > i) {

                InstField* doubleField = &clazz->ifields[j--];

                char rc = doubleField->signature[0];

                if (rc == 'D' || rc == 'J') {

                    swapField(pField, doubleField);

                    c = rc;

                    break;

                }

            }

        } else {

        }

        pField->byteOffset = fieldOffset;

#ifdef WITH_TAINT_TRACKING

        fieldOffset += sizeof(u4) + sizeof(u4); /* room for tag */

        if (c == 'J' || c == 'D')

            fieldOffset += sizeof(u4) + sizeof(u4); /* keep 64-bit aligned */

#else

        fieldOffset += sizeof(u4);

        if (c == 'J' || c == 'D')

            fieldOffset += sizeof(u4);

#endif /* ndef WITH_TAINT_TRACKING */

    }

显然,在计算类中各个实例域的偏移值的时候,由于TaintDroid对实例域的空间进行了倍增(交叉存储污点),所以这里应该加上2*sizeof(u4)。另外需要注意的是对于Double和Long类型的数据,要加上4*sizeof(u4)!

至此类的实例域和静态域的污点跟踪分析完毕,下一步轮到数组了。

3)对数组对象ArrayObject:Object的修改:


struct ArrayObject : Object {

    /* number of elements; immutable after init */

    u4              length;

#ifdef WITH_TAINT_TRACKING

    Taint           taint;

#endif

    u8              contents[1];

};

在length成员之后添加Taint tiant成员。之所以这样做,是因为出于性能的考虑:如果数组中每个成员都存储一个tag的话,对性能的影响就太大了,所以TaintDroid对每个ArrayObject对象只分配一个tag。

同样的,修改了ArrayObject的结构体,就必须同步修改涉及到对ArrayObject进行操作的函数。这里以oo/Array.cpp中的allocArray函数为例:


static ArrayObject* allocArray(ClassObject* arrayClass, size_t length,

    size_t elemWidth, int allocFlags)

{

    ……

    ArrayObject* newArray = (ArrayObject*)dvmMalloc(totalSize, allocFlags);

    if (newArray != NULL) {

        DVM_OBJECT_INIT(newArray, arrayClass);

        newArray->length = length;

#ifdef WITH_TAINT_TRACKING

        newArray->taint.tag = TAINT_CLEAR;

#endif

        dvmTrackAllocation(arrayClass, totalSize);

    }

}

在分配一个新的数组的时候,TaintDroid将它的taint成员赋值为TAINT_CLEAR(即清空污点信息)。

4)特殊类StringObject的结构分析。它的结构体如下:


struct StringObject : Object {

    /* variable #of u4 slots; u8 uses 2 slots */

    u4              instanceData[1];

    /** Returns this string's length in characters. */

    int length() const;

    /**

     * Returns this string's length in bytes when encoded as modified UTF-8.

     * Does not include a terminating NUL byte.

     */

    int utfLength() const;

    /** Returns this string's char[] as an ArrayObject. */

    ArrayObject* array() const;

    /** Returns this string's char[] as a u2*. */

    const u2* chars() const;

};

由于StringObject提供了一个方法array(),此方法返回一个ArrayObject型指针,所以在获取和设置StringObject的污点信息的时候,需要通过StringObject.array()->taint.tag进行操作。



进一步分析DVM污点传播逻辑

在前一章节中,我们分析了两参数相加的DVM opcode(OP_ADD_INT_2ADDR),这是因为我们当时对类的静态域、实例域以及数组的污点存储并不熟悉,所以也就仅仅能捏一捏这类软柿子而已,现在我们挑战一下更高难度的数组操作相关的opcode——OP_AGET_OBJECT(即aget-obj)。该opcode的汇编实现在dalvik/vm/mterp/armv*te_taint/OP_AGET_OBJECT.S文件中:


%verify "executed"

%include "armv5te_taint/OP_AGET.S"

转到OP_AGET.S:


%default { "load":"ldr", "shift":"2" }   //表示移位基准为2位,即乘以4

%verify "executed"

    /*

     * Array get, 32 bits or less.  vAA <- vBB[vCC].

     *

     * Note: using the usual FETCH/and/shift stuff, this fits in exactly 17

     * instructions.  We use a pair of FETCH_Bs instead.

     *

     * for: aget, aget-object, aget-boolean, aget-byte, aget-char, aget-short

     */

    /* op vAA, vBB, vCC */

    FETCH_B(r2, 1, 0)                   @ r2<- BB

    mov     r9, rINST, lsr #8           @ r9<- AA

    FETCH_B(r3, 1, 1)                   @ r3<- CC

    GET_VREG(r0, r2)                    @ r0<- vBB (array object)

    GET_VREG(r1, r3)                    @ r1<- vCC (requested index)

    cmp     r0, #0                      @ null array object?

    beq     common_errNullObject        @ yes, bail

// begin WITH_TAINT_TRACKING

    bl                .L${opcode}_taint_prop_1

// end WITH_TAINT_TRACKING

    ldr     r3, [r0, #offArrayObject_length]    @ r3<- arrayObj->length

    add     r0, r0, r1, lsl #$shift     @ r0<- arrayObj + index*width

    cmp     r1, r3                      @ compare unsigned index, length

// begin WITH_TAINT_TRACKING

//    bcs     common_errArrayIndex        @ index >= length, bail        // in subroutine

//    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST // in subroutine

    bl                .L${opcode}_taint_prop_2

// end WITH_TAINT_TRACKING

    $load   r2, [r0, #offArrayObject_contents]  @ r2<- vBB[vCC]

    GET_INST_OPCODE(ip)                 @ extract opcode from rINST

    SET_VREG(r2, r9)                    @ vAA<- r2

    GOTO_OPCODE(ip)                     @ jump to next instruction

 

%break

 

.L${opcode}_taint_prop_1:

    ldr            r2, [r0, #offArrayObject_taint]   @获取数组对象vBB的taint,赋给r2

    SET_TAINT_FP(r10)

    GET_VREG_TAINT(r3, r3, r10)                  @获取索引数据vCC的taint,赋给r3

    orr            r2, r3, r2                  @ r2<- r2 | r1

    bx            lr

 

.L${opcode}_taint_prop_2:

    bcs     common_errArrayIndex        @ index >= length, bail

    FETCH_ADVANCE_INST(2)               @ advance rPC, load rINST

    SET_TAINT_FP(r3)

    SET_VREG_TAINT(r2, r9, r3)            @将r2(即此时的污点信息)赋值给vAA的taint tag

    bx      lr

显然重点在*_taint_prop_1*_taint_prop_2两个代码段。简要概括它们的功能:

1)taint_prop_1首先取得数组对象vBBtaint注意这里offArrayObject_taint定义在dalvik/vm/common/asm-constants.h中:


#ifdef WITH_TAINT_TRACKING

MTERP_OFFSET(offArrayObject_taint,        ArrayObject, taint, 12) //结合ArrayObject数据结构,不难理解此代码

#endif

 

MTERP_OFFSET宏的定义如下:

# define MTERP_OFFSET(_name, _type, _field, _offset)                        \

    if (OFFSETOF_MEMBER(_type, _field) != _offset) {                        \

        ALOGE("Bad asm offset %s (%d), should be %d",                        \

            #_name, _offset, OFFSETOF_MEMBER(_type, _field));               \

        failed = true;                                                      \

    }

获取了vBB的taint tag之后,再获取索引vCC的taint tag,然后将两者相或,最终结果赋给r2寄存器;

2)taint_prop_2再将此时的r2寄存器中的tag信息赋值给vAAtaint tag这样就完成了aget-object的污点传播了。

至此整个DVM的变量级污点跟踪机制我们都已经分析完毕,下一步就是分析Native层的方法级污点跟踪,这里给各位读者预留一个问题:为什么在DVM中可以实现变量街污点跟踪,但是native层却只能实现方法级污点跟踪呢?

作者:简行、走位@阿里聚安全,更多技术文章,请点击阿里聚安全博客


阿里聚安全由阿里巴巴移动安全部出品,面向企业和开发者提供企业安全解决方案,全面覆盖移动安全、数据风控、内容安全、实人认证等维度,并在业界率先提出“以业务为中心的安全”,赋能生态,与行业共享阿里巴巴集团多年沉淀的专业安全能力。

时间: 2024-09-10 16:52:31

TaintDroid剖析之DVM变量级污点跟踪(下篇)的相关文章

TaintDroid剖析之Native方法级污点跟踪分析

TaintDroid剖析之Native方法级污点跟踪分析 简行.走位@阿里聚安全 1.Native方法的污点传播 在前两篇文章中我们详细分析了TaintDroid对DVM栈帧的修改,以及它是如何在修改之后的栈帧中实现DVM变量级污点跟踪的.现在我们继续分析其第二个粒度的污点跟踪--Native方法级跟踪. 回顾前文,我们知道Native方法执行在Native栈帧中,且Native栈帧由dvmPushJNIFrame函数分配栈空间,再由dvmCallMethodV/A或者dvmInvokeMeth

TaintDroid剖析之IPC级污点传播

TaintDroid剖析之IPC级污点传播       前言 在前三篇文章中我们详细分析了TaintDroid对DVM栈帧的修改,以及它是如何在修改之后的栈帧中实现DVM变量级污点跟踪.Native方法级跟踪.本篇文章我们来分析下IPC级污点传播 TaintDroid深入剖析系列目录: TaintDroid深入剖析之启动篇 TaintDroid剖析之DVM变量级污点跟踪(下篇) TaintDroid剖析之Native方法级污点跟踪分析 具体实现 这里我以情景为上下进行跟进,每个情景会涉及多个源文

TaintDroid剖析之File &amp; Memiry &amp; Socket级污点传播

TaintDroid剖析之File & Memiry & Socket级污点传播 简行.走位@阿里聚安全 1.涉及到的代码文件 TaintDroid在File, Memory以及Socket三方面的污点传播主要涉及到如下一些文件: /libcore/luni/src/main/java/libcore/io/Posix.java  /libcore/luni/src/main/native/libcore_io_Posix.cpp  /libcore/luni/src/main/java/

WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[下篇]

WCF客户端和服务端的框架体系相互协作,使得开发人员可以按照我们熟悉的方式进行异常的处理:在服务操作执行过程中抛出异常(FaultException),在调用服务时捕获异常,完全感觉不到"分布式"的存在,如同典型的"本地"操作一般.为了实现这样的效果,WCF在内部为我们作了很多. 消息交换是WCF进行通信的唯一手段,消息不仅仅是正常服务调用请求和回复的载体,服务端抛出的异常,甚至是服务的元数据都是通过消息的形式传向客户端的.所以,实现异常与消息之间的转换是整个异常处

TaintDroid深入剖析之启动篇

TaintDroid深入剖析之启动篇 简行.走位@阿里聚安全 1 背景知识 1.1   Android平台软件动态分析现状 众所周知,在计算机领域中所有的软件分析方法都可以归为静态分析和动态分析两大类,在Android平台也不例外.而随着软件加固.混淆技术的不断改进,静态分析越来越难以满足安全人员的分析要求,因此天生对软件加固.混淆免疫的动态分析技术应运而生.虽然动态分析技术本身有很多局限性,诸如:代码覆盖率低,执行效率低下等等,但是瑕不掩瑜,个人认为熟悉各种动态分析技术的核心原理也应当是安全从

代码-请问 有人做过污点追踪的吗

问题描述 请问 有人做过污点追踪的吗 以前从来没做过污点跟踪,百度的资料很少,google了一下,发现大致是分析代码,追踪有污点的变量和语句,不让危险语句得以执行.但是还是没很深入的了解,麻烦做过大神和前辈帮忙介绍一下这方面的经验好吗?谢谢

《Photoshop混合模式深度剖析》—第2章绘画工具中的混合模式

绘画工具中的混合模式Photoshop混合模式深度剖析画笔工具及其变体也能够使用混合模式.新建一个空白图层,将混合模式设置为正常,在工具箱中选择画笔工具,用户可以单独地控制画笔的混合模式和图层的不透明度. 并不是所有的画笔工具都有相同的混合模式选项,也不是所有选项都能以相同的方式进行应用.许多工具(比如仿制图章)也有一些混合模式选项,但是它们的使用范围有限,一般只适用于当前活动图层. 画笔工具通常用于绘制.除了工具的混合模式,只有"不透明度"设置会影响当前活动图层下面的图层.例如,打开

安卓性能优化手册

本手册适合至少有初级经验的开发者查阅或复习相关知识使用,新手可能会看不懂. 1.java代码优化 1.1安卓如何执行代码 dvm:.java->.class->.dex->.apk 优化斐波那契数列: 斐波那契数列的递推公式是f(n)=f(n-1)+f(n-2),特征方程为:x2=x+1,解该方程得(1+sqrt(5))/2,(1-sqrt(5))/2.所以f(n)=Ax1n+Bx2n,带入f(0)=0,f(1)=1得A=sqrt(5)/5,B=-sqrt(5)/5.则f(n)求出. B

DroidBox简介

目前Android系统最大的问题是什么?安全,这也是Android总是进不了企业的主要原因.如果问主要的安全问题是什么?我想应该是吸费.流量消耗和隐私泄漏.针对于隐私泄漏的侦测,目前有一个开源软件叫做DroidBox,算是做的不错的了.今天我们就来分析一下DroidBox是如何侦测隐私泄漏的. DroidBox的核心技术称作TaintDroid,从字面上理解就是污染机器.我们都知道标识隐私数据并不难,只要牢牢卡住相关的API调用就可以了,问题是如何知道这些隐私数据被泄漏出去了?恶意程序拿到隐私数