美文网首页开卷有益python热爱者码农的世界
用python一步步解剖dex文件(二)

用python一步步解剖dex文件(二)

作者: 虎七 | 来源:发表于2018-02-05 20:15 被阅读206次

    请勿转载,谢谢!!! 


    连续半个月重感冒和咳嗽,这个时候才倍感身体的重要。

    所以身体才是革命的本钱啊。。。

    言归正传。


    编码格式uleb128

    先介绍下dex中用到的一种编码方式uleb128。

    简单来说,它是对int(4字节)数据的一个可变长度编码,使得原先固定长度(4字节)的数据,可以通过更少的字节表示出来;而且这个可变长度不超过5。

    假定有数据序列:a b c d e,我们把它们用二进制表示出来:

    aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee

    如果每字节的高位开头是1,那么继续往后;如果高位开头是0,那么终止在这里;然后把所有的高位都去除,反序拼出来的二进制,就是最后的结果。

    比如这样的:

    1aaaaaaa 1bbbbbbb 1ccccccc 0ddddddd eeeeeeee

    一直到第4个,它是以0为高位开头的,那么这个有效数据就是前四个字节组成,并且反序如下:

    ddddddd ccccccc bbbbbbb aaaaaaa

    如上,第一是反序,第二是每个字节只有7位。

    这个就是简单的uleb128的示范。

    解码程序如下:

    uleb128解码

    数据结构

    类信息

    其中classDataOff是指向类数据信息区域的一个偏移数值。

    类数据信息

    从这里可以看到,类数据其实包含了四种信息,静态属性,实例属性,直接方法和虚方法。

    字段和方法

    对于DexMethod来说,它代表一个类的方法,方法中当然是包含代码的,那么代码(字节码)所在的位置,通过codeOff来表示。

    另外加一个说明,这里的fieldIdx和methodIdx其实是一个diff,也就是说,在field列表和method列表中,第一个Idx是真实的Idx,后面的Idx都是在这个基础上做的diff。

    字节码信息

    数据追踪

    按照上面的数据结构,我们走一遍真实的dex数据。

    头部区域中的类区域信息

    如上一篇所讲,在dex的头部区域中,有两个字段分别表示类信息的偏移值和类的个数;上图中,第一个四字节(07)表示类的总个数,第二个四字节(2ac)就表示类信息的偏移值。

    我们找到2ac处: (按照类定义,每个类信息占据4*8=32个字节)

    类信息数据区

    我们把这7个类的信息,按着类的结构解析并且打印出来:

    打印类信息

    结果是这样的:

    类信息

    也就是说,我写的这个Demo程序中,在dex里包含了7个类的信息,如上所示;其中MainActivity就是主Activity。

    我们现在只看这个MainActivity的数据:

    MainActivity类信息

    其中类数据的偏移数值,是第7个四字节,也就是5cc (cc05 0000)。

    MainActivity类数据信息

    这里使用到了前面锁说的uleb128格式:00 00 01 01(四个字节)

    按上面的解释方法,每个字节都是以0开头的,所以就是最后的结果了,那么定义出来的:

    static_field_size = 0

    instance_field_size = 0

    direct_method_size = 1

    virtual_method_size = 1

    也就是说,MainActivity中没有属性信息,但是有一个直接方法和一个虚方法。

    接下来的10个字节,就是直接方法和虚方法的信息(属性为空忽略),它们也是用uleb128进行编码的。

    解码信息

    这两个方法分别是:

    A: method_idx = 5, code_off = 4f0

    B: method_idx = 6, code_off = 508

    根据上一篇文章中介绍的,我们查找到方法信息如下:

    method-idx = 5 method-idx = 6

    其中第一个是构造函数<init>,第二个就是我们很熟悉的android方法,onCreate。

    继续,我们跟踪onCreate方法的信息,它的偏移是508(十六进制):

    onCreate方法体数据

    对照结构如下:

    onCreate方法体信息

    其中从6f20开始,连续的32个字节,就是该方法体的字节码信息。

    接下来的任务,就是把这32个字节,翻译成可以识别的指令信息,就是反汇编过程了。

    在这之前,我们总结下类信息的打印程序:

    读取类信息 读取类数据(一) 读取类数据(二)

    用到的一些方法:

    读取属性信息 读取方法信息 读取注解信息 读取DexTypeList类型 读取指令信息

    反汇编

    前面提到,onCreate方法中的指令字节序列,就是下面的32个字节数据。

    指令集数据

    我们知道指令一般是指令符+指令数组成的,第一个字节一般标识一个特定的指令。

    在dex格式中,这个指令既表示具体的操作(字节码格式),又对应一个数据解析格式(说明格式)。

    字节码格式文档:

    https://source.android.google.cn/devices/tech/dalvik/dalvik-bytecode

    说明格式文档:

    https://source.android.google.cn/devices/tech/dalvik/instruction-formats

    比如第一个字节是6f,对照文档如下:

    字节码格式

    字节码格式

    6e..72是数字范围,下面的35c就代表着数据说明格式。

    说明格式

    对于上面的35c来说,3表示本次指令共计3个字(2个字节),5表示寄存器数目,而c是一个助记符标识常量:

    助记符

    这3个字是怎么排列呢?

    字节排列

    把6f开始的3个字,如上面格式依次排开:

    206f 0001 0032

    对照格式得出:

    A = 2, G = 0, op = 6f, BBBB = 1, C = 2, D = 3, E = 0, F = 0

    这里的A = 2,那么对应格式:

    格式A = 2

    也就是op {v2, v3}, kind@0001。

    再对照字节码格式文档,其实就是:

    invoke-super {v2, v3}, meth@0001

    根据method的信息,查询出: meth@0001是

    meth@0001

    那么前3个字的反汇编代码就是:

    invoke-super {v2, v3}, Landroid/app/Activity; --> V onCreate(Landroid/os/Bundle;)

    这个不就是调用父类Activity的onCreate方法吗?

    对照源代码:

    源代码

    同样的方式,可以把32个字节的数据,全部反汇编出来。

    我们是为了学习Dex的结构,所以会这么大费周折;更加方便的方式,是直接用现有的反编译工具,将dex反编译成smali,可以更加直观的看到信息。


    在面对一个未知的应用时,我们没有源码,如果去修改它的信息呢?(假定没有加固)

    最简单的方式,还是反编译出smali,通过修改smali再重新打包。

    如果是,我们直接在dex上修改呢? 后面继续研究和探讨。

    【待续】

    相关文章

      网友评论

      • 虎七:给自己顶下!
        如果要了解android应用的本质,dex就是一个很好的通道!

      本文标题:用python一步步解剖dex文件(二)

      本文链接:https://www.haomeiwen.com/subject/topizxtx.html