dex文件格式笔记

Dex是Dalvik Executable的简称
Dex文件包含了编译java生成的.class文件,由dx工具将java字节码转化为smali字节码
apk安装后,对于dalvik第一次运行系统完成dex优化,转化为odex文件,存放在/data/dalvik-cache目录下,执行时加载odex文件到内存,art虚拟机是转成oat文件,都是在前后加些
odex优化仅在dex文件之上添加头和数据

整体结构:
文件头,字符串列表,类型列表,原型列表,字段列表,方法列表,类定义列表,数据

文件头

struct DexHeader{	
u1 magic[8];	//dex\n035
u4 checksum;	//adler32校验
u1 signature[kSHA1DigestLen];	//JAVA的SHA-1签名
u4 fileSize;			//文件大小,整个文件多大。要对齐么?
u4 headerSize;			//DexHeader结构大小
u4 endianTag;        		//字节序标记
u4 linkSize;	u4 linkOff;        	//链接段大小和偏移
u4 mapOff;			//DexMapList文件偏移
u4 stringIdsSize;	u4 stringIdsOff;//这些表的大小和在文件中的偏移
u4 typeIdsSize;	u4 typeIdsOff;
u4 protoIdsSize;	u4 protoIdsOff;
u4 fieldIdsSize;	u4 fieldIdsOff;
u4 methodIdsSize;	u4 methodIdsOff;
u4 classDefsSize;	u4 classDefsOff;
u4 dataSize;	u4 dataOff;
};

 字符串和类型列表

struct DexStringId{	
u4 stringDataOff;	//指向字符串数据起始地址,每一项指向字符串的起始地址
};

struct DexTypeId{
	u4 descriptorIdx;	//指向DexStringId列表的索引,就是有几种类型,就几个变量
};

 

struct DexProtoId{
   u4 shortyIdx;	// 指向DexStringId列表的索引,是方法声明字符串,就是参数返回值的缩写
   u4 returnTypeIdx;     //  指向DexTypeId列表的索引,返回值的类型,所以肯定也是指向typeid中的一项	
   u4 parametersOff;	// 指向DexTypeList的偏移,参数
};

struct DexTypeList{
	u4 size;		// DexTypeItem的个数
	DexTypeItem list[1];
};

struct DexTypeItem{			 
	u2 typeIdx;	// 指向DexTypeId列表的索引
};

字段和方法列表

struct DexFieldId{
	u2 classIdx;		//字段类类型,指向DexTypeId列表索引,字段属于哪个类
	u2 typeIdx;		//字段类型,指向DexTypeId列表索引,字段的类型    
	u4 nameIdx;		//字段名,指向DexStringId列表索引,字段的名字
};

struct DexMethodId{
	u2 classIdx;	//类类型,指向DexTypeId列表索引
	u2 protoIdx;	//声明类型,指向DexTypeId列表索引
	u4 nameIdx;	//方法名,指向DexStringId列表索引
};

 类列表

struct DexClassDef{
	u4 classIdx;		//类类型,指向DexTypeId列表索引
	u4 accessFlags;		//访问标志,ACC_为前缀的枚举值,就是public那些东西
	u4 superclassIdx;		//父类类型,指向DexTypeId列表索引
	u4 interfacesOff;		//接口,指向DexTypeList
	u4 sourceFileIdx;		//源文件名,指向DexStringId列表索引
	u4 annotationsOff;		//注解,指向DexAnnotationsDirectoryItem
	u4 classDataOff;		//指向DexClassData结构偏移
	u4 staticValuesOff;		//指向DexEncodedArray结构偏移
};
struct DexClassData{
	DexClassDataHeader header;		//字段和方法个数的描述
	DexField*		   staticFields;	//静态字段
	DexField*		   instanceFields;	//实例字段
	DexMethod*	   directMethods;	//直接方法
	DexMethod*	   virtualMethods;	//虚方法
}
struct DexClassDataHeader{
	u4 staticFieldsSize;		//静态字段个数
	u4 instanceFieldsSize;	//实例字段个数
	u4 directMethodsSize;	//直接方法个数
	u4 virtualMethodsSize;	//虚方法个数
};
struct DexField{
	u4 fieldIdx;		//DexFieldId列表索引
	u4 accessFlags;		//访问标志
};
struct DexMethod{
	u4 methodIdx;		//方法列表索引
	u4 accessFlags;		//访问标志
	u4 codeOff;		//指向DexCode偏移	
};

然后对于native没有dexcode因为不在这里,在native层,如果是普通java方法,如下

struct DexCode{
	u2 registersSize;		//使用寄存器个数
	u2 insSize;		//参数个数
	u2 outsSize;		//调用其他方法使用的寄存器个数
	u2 triesSize;		//try catch个数,java里的trycatch
	u4 debugInfoOff;		//调试信息偏移
	u4 insnsSize;		//指令个数
	u2 insns[1];		//指令
    // u2 padding		//结构体对齐
    // try_item[triesSize]		//DexTry结构
    // uleb128 handlersSize	//Try hanlder个数
    // catch_handler_item[handlersSize]  //DexCatchHanlder
};

做成这种结构,原因是想把这个dex文件压缩,比如有些字符串比如类型反复使用,其实只要一份就好。

Demo

比如打开010editor,观察class

这里classidx指向tpyeid是4,指向字符串表第四项。

注意前面的u4,是一个变长的代表1个或者4个字节的无符号数。算法如下

// get the actual uint value of the uleb128
uint uleb128_value(uleb128 &u) {
    local uint result;
    local ubyte cur;

    result = u.val[0];
    if(result > 0x7f) {
        cur = u.val[1];
        result = (result & 0x7f) | (uint)((cur & 0x7f) << 7);
        if(cur > 0x7f) {
            cur = u.val[2];
            result |= (uint)(cur & 0x7f) << 14;
            if(cur > 0x7f) {
                cur = u.val[3];
                result |= (uint)(cur & 0x7f) << 21;
                if(cur > 0x7f) {
                    cur = u.val[4];
                    result |= (uint)cur << 28;
                }
            }
        }
    }

    return result;
}

大致就是看一个字节最高位如果是1,就拼接起来。

 再看下函数,比如main函数

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值