用户
搜索
  • TA的每日心情
    慵懒
    7 天前
  • 签到天数: 117 天

    连续签到: 2 天

    [LV.6]常住居民II

    i春秋作家

    推荐小组成员

    Rank: 7Rank: 7Rank: 7

    109

    主题

    258

    帖子

    544

    魔法币
    收听
    0
    粉丝
    15
    注册时间
    2017-7-24

    幽默灌水王突出贡献春秋文阁i春秋签约作者i春秋推荐小组积极活跃奖春秋游侠秦

    HAI_ i春秋作家 推荐小组成员 幽默灌水王 突出贡献 春秋文阁 i春秋签约作者 i春秋推荐小组 积极活跃奖 春秋游侠 秦 楼主
    发表于 2018-3-5 16:44:05 424870
    本帖最后由 HAI_ 于 2018-3-20 01:15 编辑

    本文章首发于i春秋,未经允许,禁止转载。感谢蛋总栽培~

    0x00 前言

    这里是无价之宝

    资料在附件中。地址将在文章最后给出。(花时间整理,不容易,手动斜眼笑)

    说明

    相对于elf文件格式来说,dex文件就简单多了。映射什么的都会简单很多。而且dex文件格式是基于索引的一种格式。
    别人都是拿着文件格式去写分析程序的,我就不一样了,我不想写程序,我拿着人家写好的分析程序去分析文件格式,顺便学学人家是怎么写代码的。改改我不堪的代码。

    为什么要分析这个?

    不分析这个不行,之后的各种加固dump技术都要基于这个进行实现。所以必须要分析。
    你说有人分析过了?不好意思,那是别人的。看别人分析是一个学习的过程,自己分析又是另外一个学习过程。
    我告诉你说这个dex文件格式和elf文件格式不一样,是索引形式的,不进行分析,你知道为什么是索引分析吗?
    乖乖学习吧。

    内容

    1.简单介绍

    简单了解dex。

    2.header分析

    通过python程序进行header头部分析。

    3.索引区分析

    由于索引区对于动态方面不是很相关,所以只是做一个简单的介绍,并没有进行深入分析。

    4.class_defs(难点)

    这个是我们分析的重点,同时也是我们分析的难点。
    分析方式:
    010 editor 模板分析
    GDA 分析

    0x01 简单介绍

    先借用大佬们的图片。

    这里写图片描述

    这个是dex文件层次是不是比上一个图片好一点。

    这里写图片描述

    这个看看就好,反正现在也看不懂。我们还是来着手于分析吧。

    0x02 header分析

    因为拿到的程序有两个部分,一个是整体的,还有一个是header部分的,我们就先拿header部分进行分析吧。当然还是要结合010 editor这个神器来进行分析,当然还有其他的一些分析工具来进行分析。我们的最终目的就是为了学好dex文件格式。

    1.程序演示

    既然拿到了程序,那肯定要运行一下呀。具体的程序在附件中可能会有,或者在辅助文档中进行查找。

    这里写图片描述

    看了程序的输出,其实字段都已经出来了。

    2.其他header解析

    使用010 editor进行文件分析

    这里写图片描述

    使用dexdump进行dex文件分析

    dexdump classes.dex >demo.txt

    这里写图片描述

    3.程序分析 start

    3.1 主要函数

    if __name__ == "__main__":
        df = DexFile("classes.dex") 
        df.ReadDexHeader()
        df.CalSignature()
        df.CalChecksum()
        df.CloseDexFile()

    这里是程序入口,这里调用了五个函数。我们将挨个对其进行分析。

    首先df是一个实例化后的类。

    3.2 DexFile("classes.dex")

    def __init__(self,filepath):
            self.dex = open(filepath,"r")
            self.strings = []
            self.types = []
            self.protos = []
            self.fields = []
            self.method = []
            self.map_type = {0x0:"header_item",
                         0x1:"string_id_item",
                         0x2:"type_id_item",
                         0x3:"proto_id_item",
                         0x4:"field_id_item",
                         0x5:"method_id_item",
                         0x6:"class_def_item",
                         0x1000:"map_list",
                         0x1001:"type_list",
                         0x1002:"annotation_set_ref_list",
                         0x1003:"annotation_set_item",
                         0x2000:"class_data_item",
                         0x2001:"code_item",
                         0x2002:"string_data_item",
                         0x2003:"debug_info_item",
                         0x2004:"annotation_item",
                         0x2005:"encoded_array_item",
                         0x2006:"annotations_directory_item"}

    我们还是一点点的来。
    这里写图片描述
    这里的__init__是构造方法,self是对自身的一个引用。

    剩下的是对其他的一个定义,我们这里研究的是header,所以不进行过多分析,如果有兴趣可以自行研究。

    3.3ReadDexHeader()

    这个函数就是对header进行分析的主体部分,为了方便有一个对照。我去盗一张图回来。

    这里写图片描述

    来一点一点看程序吧。

    3.3.1 magic:

    magic就是用来标志这个文件是什么文件的一个字段。固定的信息:dex\n035

    print "----------------½âÎö DexHeader-----------------------------"
            self.magic = struct.unpack("4s",self.dex.read(4))[0]
            print "magic code:",self.magic,
            if self.magic != "dex\n":
                print "the file is not a dex file"
                self.CloseDexFile()
                exit(-1)
                return

    struct.unpack用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个元组。

    “4s”表示char[4]

    self.dex.read(4),读取4个字节

    然后输出。

    来看看version字段,默认是035

     self.version = struct.unpack("4s",self.dex.read(4))[0]
            print "version:",self.version

    看到这里,其实他的代码使用的技术已经很明了了。就是使用struct.unpack来进行读取,我之前写的使用的也是这个方法,不过我一个字节一个字节去读了。这个方法可能会简单一点。

    3.3.2 checksum字段

    用adler32算法对这个字段之后的所有数据做的检验码,来验证这个dex文件是否损坏

    self.checksum = struct.unpack("i",self.dex.read(4))[0]       
            print "checksum:",self.checksum

    010 editor
    这里写图片描述

    checksum字段占四个字节。

    怎么说呢,接下来就枯燥没有意思了。

    3.3.3 sig

    dex文件的签名值(是对这个字段之后的所有数据做的签名,用来唯一的标识这个dex文件)。

    这里需要读取20个字节。代码就不贴了。和上面相似,只是读取的大小不一样。

    这里写图片描述

    3.3.4 filesize

    整个文件大小。
    这里写图片描述

    3.3.5 headersize

    文件头的大小。一般头部的大小是固定的,0x70。都是这个。
    这里写图片描述

    3.3.6 endiantag

    高位在前,或者低位在前。涉及到汇编的知识。
    这里写图片描述

    接下来就是索引个数,以及偏移。

    链接段的大小,如果是0的话为静态连接。

    链接段的偏移,一般_off都是xxx的偏移。

    3.3.9 map_off

    map的偏移。

    3.3.10 string_num

    num就是数字的意思,也就是说这个就是字符串的个数。

    3.3.11 string_table_off

    string_table的偏移,string_table就是 string,字符串,table,表。合起来就是字符串表的偏移。

    3.3.12 type_num

    type的个数。就是类型的个数。

    3.3.13 type_table_off

    type_table的偏移

    3.3.14 proto_num

    proto的个数。 proto是个什么东西?这个就是方法声明。

    3.3.15  proto_off

    proto的偏移

    3.3.16 field_num

    field的个数,字段列表的个数

    3.3.17 field_off

    字段列表的偏移

    3.3.18 method_num

    方法列表的个数,(说来惭愧,今天刚好背了这个单词。。。你们有谁教教我英语?我半个小时才背了10个单词)

    3.3.19 method_off

    方法列表的偏移

    3.3.20 class_def_size

    类定义列表的个数

    3.3.21 class_def_off

    类定义列表的偏移

    3.3.22 data_size

    数据段的大小

    3.3.23 data_off

    数据段的偏移

    总结

    dex为什么说是索引呢,因为在头部表记录了很多的off,也就是偏移量,由此来进行文件格式的布局的。

    这里是程序源码:

    0x03 索引区

    说白了,这里就是存数据的地方。

    但是我们的数据又不存在这个地方。

    这个区域只是做一个对应的索引,并没有真正的存取数据。是从data中通过偏移来进行存取的。

    先来看下data域:

    这里写图片描述

    然后来看下分析结构怎么说?

    010 editor
    这里写图片描述

    dexdump
    这里写图片描述

    一共存在五个表,五个表分别存了五个内容。
    从字符串到类名等。

    这里需要注意的有一个数据类型就是,uleb128。
    uleb128就是一个1~5个字节来回浮动的数据类型,出现的作用就是为了避免内存浪费。
    这里用python进行实现。

    def get_uleb128(content):
            value = 0
            for i in xrange(0,5):
                    tmp = ord(content[i]) & 0x7f
                    value = tmp << (i * 7) | value //前n个字节组合,如i=1,为前2个字节的组合;当i=4时,共5个字节组合
                    if (ord(content[i]) & 0x80) != 0x80:
                            break
            if i == 4 and (tmp & 0xf0) != 0:
                    print "parse a error uleb128 number"
                    return -1
            return i+1, value

    总结

    这些字段,网上资料可以看一看就行了,和动态的相关性不是很强,这里就不再进行赘述了。

    0x04 class_defs

    class_defs 区段是对 class 的定义,这是一个很重要的区段,同时也是一个非常复杂的区段,当然分析起来也是非常复杂的。

    首先来一张 010 editor 的图。

    这里写图片描述

    如果想要进行深层次的分析的话,这里还有一个更加详细的图。

    这里写图片描述

    我还是乖乖分析我的字段。

    这里是一个结构体。

    struct class_def_item
    {
            uint class_idx;             //-->type_ids   
            uint access_flags;                
            uint superclass_idx;        //-->type_ids
            uint interface_off;     //-->type_list
            uint source_file_idx;        //-->string_ids
            uint annotations_off;        //-->annotation_directory_item
            uint class_data_off;        //-->class_data_item
            uint static_value_off;        //-->encoded_array_item
    }

    1.class_idx

    类的类型,指向DexTypeId列表的索引。
    这里写图片描述

    这里可以看到这个实例就是com.example.helloworld1.MainActivity。

    2.accessFlags

    访问标志,就是public啊,private呀这些。

    我们还是来用实例说话。
    这里写图片描述

    3.superclassIdx

    父类类型,指向DexTypeId列表的索引

    这里写图片描述
    这里的父类索引就是android.app.Activity

    4.interfacesoff

    接口,指向DexTypeList的偏移
    这里写图片描述
    这里的demo没有接口项,所以这里显示为0。

    5.sourceFileIdx

    源文件名,指向DexStringId列表的索引。

    这里写图片描述

    demo的源文件名称MainActivity.java

    6.annotationsOff

    注解,指向DexAnnotationsDirectory
    这里写图片描述
    demo没有注解所以为0。

    7.staticValuesOff

    指向DexEncodedArray结构的偏移。
    这里写图片描述

    8.classDataOff

    指向DexClassData结构的偏移。

    心细的小伙伴可能已经发现了,这个和上一个段的顺序进行了颠倒,为什么要这样做呢,因为我们要进行深入的研究了。

    classData是一个由五个部分组成的结构。
    (1)Header
    (2)staticFields
    (3)instanceFields
    (4)directMethods
    (5)virtualMethods

    8.1 Header

    头部信息,头部信息又可以向下延伸。
    Header包含四个部分。

    静态字段个数
    实例字段个数
    直接方法个数
    虚方法个数

    这里写图片描述

    这里可以看到我们的demo有两个直接方法,还有一个虚方法。

    8.2 directMethods

    直接方法字段

    8.3 virtualMethods

    虚方法字段

    我们要再次向下深入了。
    每一个Methods又有一个结构。
    我们用第一个direct为例子。

    8.2.1 dexMethod结构

    这个结构包含三个部分。

    methodIdx 索引字段
    acccessFlags 访问标志位
    codeOff指向DexCode结构偏移的字段

    首先来看看 methodldx
    这里写图片描述

    然后就是该method的访问标志。
    这里写图片描述
    这里我们就知道method的访问标志是public。

    codeOff就是索引到DexCode的一个标志。

    接下来我们就要继续深入到DexCode了。

    8.2.1.1 DexCode

    我们一层一层的深入终于到达了我们最重要的地方,这个地方有关于我们的smali代码。

    这里我们将结合010 以及GDA进行分析。

    DexCode包含了7个部分

    8.2.1.1.1 registersSize

    使用的寄存器个数。
    这里写图片描述

    demo中有一个寄存器

    8.2.1.1.2 insSize

    参数的个数
    这里写图片描述
    demo中有一个参数

    8.2.1.1.3 outSize

    调用其他方法时使用的寄存器个数
    这里写图片描述

    8.2.1.1.4 triesSize

    Try/Catch个数
    这里写图片描述
    demo中为0。

    8.2.1.1.5 debugInfoOff

    调用调试信息的偏移
    这里写图片描述

    8.2.1.1.6 insnsSize

    指令集个数,以2字节为单位。
    这里写图片描述

    8.2.1.1.7 insns[](重点部分)

    指令集
    我们终于到达了我们的目的地,指令部分。
    这里写图片描述

    我们来看看 70 是什么指令。

    这里写图片描述

    使用GDA进行分析查看。
    这里写图片描述
    这个是文件偏移
    这里写图片描述
    这个是之后的顺序。
    这里写图片描述

    其他分析都是类似的。

    至此,重点部分就分析完成了。

    0x05 结束语

    昨天晚上看了很久的资料,最后还是觉得没感觉,所以就自己拿出来进行分析一下。

    收获

    1.010 editor 分析 dex文件
    2.GDB 文件分析
    3.python 脚本编写
    4.dex文件格式分析get

    附件整理

    1.word一份。
    2.pdf一份。
    3.dex文件头部分析工具一个。
    4.Dalvik 文件一份。


    word.zip (3.18 MB, 下载次数: 7, 售价: 2 魔法币)

    本帖被以下淘专辑推荐:

    破解的目的是为了更好的开发
    发表于 2018-3-6 15:19:23
    谢谢,正好需要了解dex
    使用道具 举报 回复
    发表于 2018-3-6 22:28:02
    哇,好详细哦
    http://nico.lolimoe.cn/blog/
    使用道具 举报 回复
    发表于 2018-3-6 23:52:05
    丰富详细,了解dex格式帮助很大
    使用道具 举报 回复
    发表于 2018-3-16 11:09:45
    感谢分享 非常详细 很好的资料
    使用道具 举报 回复
    发新帖
    您需要登录后才可以回帖 登录 | 立即注册