热更新Tinker研究(二):结合源码学习Dex格式

热更新Tinker研究(一):运行tinker-sample-android
热更新Tinker研究(二):结合源码学习Dex格式
热更新Tinker研究(三):加载补丁
热更新Tinker研究(四):TinkerLoader
热更新Tinker研究(五):Application的隔离
热更新Tinker研究(六):TinkerPatchPlugin
热更新Tinker研究(七):Dex的patch文件生成
热更新Tinker研究(八):res和so的patch文件生成
热更新Tinker研究(九):Dex文件的patch
热更新Tinker研究(十):Res文件的patch
热更新Tinker研究(十一):so文件的patch

热更新Tinker研究(二):结合源码学习Dex格式

一、Dex文件中的数据类型

NameDescription
byte1字节有符号数
ubyte1字节无符号数
short2字节有符号数, 低字节序
ushort2字节无符号数, 低字节序
int4字节有符号数, 低字节序
uint4字节无符号数, 低字节序
long8字节有符号数, 低字节序
ulong8字节无符号数, 低字节序
sleb128有符号 LEB128,可变长度 1~5 字节
uleb128无符号 LEB128,可变长度 1~5 字节
uleb128p1无符号 LEB128 值加1,可变长 1~5 字节

LEB128

这种编码是变长的,诞生于DWARF3 标准。在dex中,LEB128只能编码成32位的数字,而长度是1-5字节。每个字节上保留第一位,剩余7位是有效位。如下图。

这里写图片描述

由于采用低字节序,第一个字节表示低位。

uleb128 存储方式类似,只不过表示无符号数字。uleb128p表示在此基础上+1。

这里写图片描述

二、Dex数据结构

结合这张图来分析
这里写图片描述
整个dex文件可以分为三大部分,

  • header头文件
  • 索引区
  • 数据区

header头文件主要包含Dex Header,主要是dex文件中整个区块的索引和偏移,以确定各个区块在什么位置。索引区*ids是identifiers的缩写,主要指向数据区的位置和偏移。数据区*主要是存放类结构的定义,包含权限、类id、父类、字段、方法等。

以下是一段HelloWorld代码

public class HelloWorld{
    public static void main(String[] args){
        System.out.println("HelloWorld");
    }
}

生成的HelloWorld.dex文件在010 Editor中的显示
这里写图片描述

这里写图片描述

magic

固定值,用来标识dex文件,值为dex 035,
二进制表示

{0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00} = "dex\n035\0"
checksum

文件校验码,使用 alder32 算法校验文件除去 maigc、checksum 外余下的所有文件区域,用于检查文件错误。

signature

使用 SHA-1 算法 hash 除去 magic、checksum 和 signature 外余下的所有文件区域, 用于唯一识别本文件 。

file_size

文件大小

endian_tag

字节顺序,其中12345678h表示低字节序,78563412h表示高字节序。

其他

其他内容分别表示各个数据区域的偏移。

string_ids

这里写图片描述
其中string_id_item下,

string_data_off

string数据的偏移

struct uleb128 utf16_size

UTF-16编码下字符串长度

string data[]

MUTF-8编码构成的字符串,MUTF-8(Modified UTF-8)其实是对UTF-16字符编码的再编码。

type_ids

这里写图片描述

uint descriptor_idx

uint类型的id,0x3表示在string_ids[]中的游标

proto_ids

这里写图片描述
proto 的意思是 method prototype 代表 java 语言里的一个 method 的原型

uint shorty_idx

简短的方法描述符,它的值是string_ids的index。

uint return_type_idx

返回类型,它的值是type_ids的id。

uint parameters_off

参数列表的偏移

struct type_item_list parameters

参数列表,其中uint size表示参数个数,struct type_item list[]表示具体的参数列表。

field_ids

这里写图片描述

ushort class_idx

表示字段属于的class类,它的值是class_def的index。

ushort type_idx

表示该字段的类型,它的值是type_ids的index。

uint name_idx

表示字段名称,它的值是string_ids的index。

method_ids

这里写图片描述

ushort class_idx

表示方法属于的class类,它的值是class_def的id。由于这里是ushort类型,所以最大的方法数不能超过2^16个。

ushort proto_idx

表示方法的类型,它的值proto_ids的index。

uint name_idx

表示方法的名称,它的值string_ids的index。

class_defs

这里写图片描述
由于这个结构比较复杂,主要包括class_def_item和class_data_item,下面分别来分析。

class_def_item

class_idx

描述具体的 class 类型,值是 type_ids 的一个 index 。值必须是一个 class 类型,不能是数组类型或者基本类型。

access_flags

描述 class 的访问类型,比如 public, enum, final, static等。access_flags definitions会有描述。

superclass_idx

描述 supperclass 的类型,值的形式跟 class_idx 一样 。

interfaces_off

值为偏移地址,指向 class 的 interfaces,被指向的数据结构为 type_list 。class 若没有 interfaces 值为 0。

source_file_idx

表示源代码文件的信息,值是 string_ids 的一个 index。若此项信息缺失,此项值赋值为 NO_INDEX=0xffff ffff。

annotions_off

值是一个偏移地址,指向的内容是该 class 的注释,位置在 data 区,格式为 annotations_direcotry_item。若没有此项内容,值为 0 。

class_data_off

值是一个偏移地址,指向的内容是该 class 的使用到的数据,位置在 data 区,格式为 class_data_item。若没有此项内容值为 0。该结构里有很多内容,详细描述该 class 的 field、method, method 里的执行代码等信息,后面会介绍class_data_item

static_value_off

值是一个偏移地址 ,指向 data 区里的一个列表 (list),格式为 encoded_array_item。若没有此项内容值为 0。

class_data_item

struct uleb128 static_fields_size

描述静态方法数目

struct uleb128 instance_fields_size

描述实例方法数目

struct uleb128 direct_methods_size

描述直接方法数目

struct uleb128 virtual_methods_size

描述虚方法数目

struct encoded_method_list direct_methods

描述已有方法列表

code_item

描述方法的详细构成(省略)

map_list

这里写图片描述
map_list内容和header类似,包含各个区域的大小和偏移,但是内容更为丰富和详细,
这里写图片描述

三、结合tinker源码

com.tencent.tinker.android.dex.Dex

dex数据结构的抽象

public final class Dex {
    ...
    private static final int CHECKSUM_OFFSET = 8;   //checksum偏移位置
    private static final int SIGNATURE_OFFSET = CHECKSUM_OFFSET + SizeOf.CHECKSUM;  //signature偏移位置
    private final TableOfContents tableOfContents = new TableOfContents();  //对应header 和 map
    private final StringTable strings = new StringTable();  //对应string_ids
    private final TypeIndexToDescriptorIndexTable typeIds = new TypeIndexToDescriptorIndexTable();  //对应type_ids
    private final TypeIndexToDescriptorTable typeNames = new TypeIndexToDescriptorTable();  //抽象出type_name的数据结构
    private final ProtoIdTable protoIds = new ProtoIdTable();   //对应proto_ids
    private final FieldIdTable fieldIds = new FieldIdTable();   //对应field_ids
    private final MethodIdTable methodIds = new MethodIdTable();    //对应method_ids
    private final ClassDefTable classDefs = new ClassDefTable();    //对应class_defs
    ...
}

com.tencent.tinker.android.dex.TableOfContents

描述header和map

public final class TableOfContents {
    ...
    public static final short SECTION_TYPE_HEADER = 0x0000;
    public static final short SECTION_TYPE_STRINGIDS = 0x0001;
    public static final short SECTION_TYPE_TYPEIDS = 0x0002;
    public static final short SECTION_TYPE_PROTOIDS = 0x0003;
    public static final short SECTION_TYPE_FIELDIDS = 0x0004;
    public static final short SECTION_TYPE_METHODIDS = 0x0005;
    public static final short SECTION_TYPE_CLASSDEFS = 0x0006;
    public static final short SECTION_TYPE_MAPLIST = 0x1000;
    public static final short SECTION_TYPE_TYPELISTS = 0x1001;
    public static final short SECTION_TYPE_ANNOTATIONSETREFLISTS = 0x1002;
    public static final short SECTION_TYPE_ANNOTATIONSETS = 0x1003;
    public static final short SECTION_TYPE_CLASSDATA = 0x2000;
    public static final short SECTION_TYPE_CODES = 0x2001;
    public static final short SECTION_TYPE_STRINGDATAS = 0x2002;
    public static final short SECTION_TYPE_DEBUGINFOS = 0x2003;
    public static final short SECTION_TYPE_ANNOTATIONS = 0x2004;
    public static final short SECTION_TYPE_ENCODEDARRAYS = 0x2005;
    public static final short SECTION_TYPE_ANNOTATIONSDIRECTORIES = 0x2006;

    //各类Section 索引区
    public final Section header = new Section(SECTION_TYPE_HEADER, true);   
    public final Section stringIds = new Section(SECTION_TYPE_STRINGIDS, true);
    public final Section typeIds = new Section(SECTION_TYPE_TYPEIDS, true);
    public final Section protoIds = new Section(SECTION_TYPE_PROTOIDS, true);
    public final Section fieldIds = new Section(SECTION_TYPE_FIELDIDS, true);
    public final Section methodIds = new Section(SECTION_TYPE_METHODIDS, true);
    public final Section classDefs = new Section(SECTION_TYPE_CLASSDEFS, true);
    public final Section mapList = new Section(SECTION_TYPE_MAPLIST, true);
    public final Section typeLists = new Section(SECTION_TYPE_TYPELISTS, true);
    public final Section annotationSetRefLists = new Section(SECTION_TYPE_ANNOTATIONSETREFLISTS, true);
    public final Section annotationSets = new Section(SECTION_TYPE_ANNOTATIONSETS, true);
    public final Section classDatas = new Section(SECTION_TYPE_CLASSDATA, false);
    public final Section codes = new Section(SECTION_TYPE_CODES, true);
    public final Section stringDatas = new Section(SECTION_TYPE_STRINGDATAS, false);
    public final Section debugInfos = new Section(SECTION_TYPE_DEBUGINFOS, false);
    public final Section annotations = new Section(SECTION_TYPE_ANNOTATIONS, false);
    public final Section encodedArrays = new Section(SECTION_TYPE_ENCODEDARRAYS, false);
    public final Section annotationsDirectories = new Section(SECTION_TYPE_ANNOTATIONSDIRECTORIES, true);
    public final Section[] sections = {
            header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList,
            typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas,
            debugInfos, annotations, encodedArrays, annotationsDirectories
    };

    public int checksum;    //checksum校验值
    public byte[] signature;    //signatur识别签名
    public int fileSize;    
    public int linkSize;
    public int linkOff;
    public int dataSize;
    public int dataOff;
    ...
}

com.tencent.tinker.android.dex.TableOfContents.Section

描述选区大小和偏移量

public static class Section implements Comparable<Section> {
        public static final int UNDEF_INDEX = -1;
        public static final int UNDEF_OFFSET = -1;
        public final short type;
        public boolean isElementFourByteAligned;
        public int size = 0;
        public int off = UNDEF_OFFSET;
        public int byteCount = 0;
        ...
 }

com.tencent.tinker.android.dex.Dex.StringTable

string表描述

    private final class StringTable extends AbstractList<String> implements RandomAccess {
        @Override public String get(int index) {
            checkBounds(index, tableOfContents.stringIds.size);
            int stringOff = openSection(tableOfContents.stringIds.off + (index * SizeOf.STRING_ID_ITEM)).readInt();
            return openSection(stringOff).readStringData().value;
        }
        @Override public int size() {
            return tableOfContents.stringIds.size;
        }
    }

整个数据结构是基于Selection进行构建,只需要保存一些大小和偏移,需要取值时操作data即可,data也就是dex文件的二进制数组。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值