前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >dex文件格式

dex文件格式

作者头像
全栈程序员站长
发布2022-09-13 15:01:27
1.7K0
发布2022-09-13 15:01:27
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

dex文件格式

Android 4.0源码Dalvik/docs目录下文档dex-format.html有详细介绍dex文件格式

1.dex文件中的数据结构

dex文件使用到的数据类型

  • u1~u8表示1到8字节的无符号数,而sleb128、uleb128与uleb128p1则是dex文件中特有的LEB128数据类型。
  • 每个LEB128由1~5个字节组成,所有的字节组合在一起表示一个32位的数据。
  • LEB128,每个字节只有7位为有效位,如果第1个字节的最高位为1,表示LEB128需要使用到第2个字节,如果第2个字节的最高位为1,表示会使用到第3个字节,以此类推,直到最后的字节最高位为0,当然,LEB128最多只会使用到5个字节,如果读取5个字节后下一个字节最高位仍为1,则表示该dex文件无效,Dalvik虚拟机在验证dex时会失败返回。

Android源码dalvik\libdex\Leb128.h文件中可以找到LEB128的实现,读取无符号LEB128(uleb128)数据的代码:

代码语言:javascript
复制
DEX_INLINE int readUnsignedLeb128(const ul** pStream){
    const ul* ptr=*pStream;
    int result=*(ptr++);
    if(result>0x7f){                //大于0x7f表示第1个字节最高位为1
        int cur=*(ptr++);           //第2个字节
        result=(result&0x7f)|((cur&0x7f)<<7);//前2个字节组合
        if(cur>0x7f){               //大于0x7f表示第2个字节最高位为1
            cur=*(ptr++);           //第3个字节
            result|=(cur&0x7f)<<14; //前3个字节的组合
            if(cur>0x7f){
                cur=*(ptr++);       //第4个字节
                result|=(cur&0x7f)<<21;//前4个字节的组合
                if(cur>0x7f){
                    cur=*(ptr++);   //第5个字节
                    result|=cur<<28;//前5个字节的组合
                }
            }
        }
    }
    *pStream=ptr;
    return result;
}

对于有符号的LEB128(sleb128)来说,计算方法与无符号的LEB128是一样的,只是对无符号LEB128最后一个字节的最高有效位进行了符号扩展。读取有符号LEB128数据的代码如下:

代码语言:javascript
复制
DEX_INLINE int readSignedLeb128(const ul** pStream){
    const ul* ptr=*pStream;
    int result=*(ptr++);
    if(result<=0x7f){               //小于0x7f表示第1个字节的最高位不为1
        result=(result<<25)>>25;    //对第1个字节的最高有效位进行符号扩展
    }else{
        int cur=*(ptr++);           //第2个字节
        result=(result&0x7f)|((cur&0x7f)<<7);//前2个字节组合
        if(cur<=0x7f){  
            result=(result<<18)>>18;//对结果进行符号位扩展
        }else{
            cur=*(ptr++);           //第3个字节
            result|=(cur&0x7f)<<14; //前3个字节的组合
            if(cur<=0x7f){
                result=(result<<11)>>11;//对结果进行符号位扩展
            }else{
                cur=*(ptr++);       //第4个字节
                result|=(cur&0x7f)<<21;//前4个字节的组合
                if(cur<=0x7f){
                    result=(result<<4)>>4;//对结果进行符号位扩展
                }else{
                    cur=*(ptr++);   //第5个字节
                    result|=cur<<28;//前5个字节的组合
                }
            }
        }
    }
    *pStream=ptr;
    return result;
}

uleb128p1类型很简单,值为uleb128的值加1。

c0 83 92 25计算uleb128值:

第1个字节0xc0大于0x7f,表示需要用到第2个字节。result=0xc0&0x7f

第2个字节0x83大于0x7f,表示需要用到第3个字节。result2 = result1 + (0x83 & 0x7f) << 7

第3个字节0x92大于0x7f,表示需要用到第4个字节。result3 = result2 + (0x92 & 0x7f) << 14

第4个字节0x25小于0x7f,表示到了结尾。result4 = result3 + (0x25 & 0x7f) << 21

最后计算结果为0x40 + 0x180 + 0x48000 + 0x4a00000 = 0x4a481c0

2.dex文件整体结构

dex文件由多个结构体组合而成。

– dex header:dex文件头,它指定了dex文件的一些属性,并记录了其它6部分数据结构在dex文件中的物理偏移 – string_ids到class_def结构为“索引结构区”,真实的数据存放在data数据区 – link_data:静态链接数据区

未经过优化的dex文件结构:

代码语言:javascript
复制
struct DexFile{
    DexHeader   Header;
    DexStringId StringIds[stringIdsSize];
    DexTypeId   TypeIds[typeIdsSzie];
    DexProtoId  ProtoIds[protoIdsSize];
    DexFieldId  FieldIds[fieldIdsSize];
    DexMethodId MethodIds[methodIdsSize];
    DexData     Data[];
    DexLink     LinkData;
};

(DexFile结构的声明在Android系统源码dalvik\libdex\DexFile.h文件中的,是dex文件被映射到内存中的结构,保存的是各个结构的指针,其中还包括了DexOptHeader与DexFile尾部的附加数据)

  • DexHeader结构占用0x70个字节
  • 70个字节解释如下:
    • magic字段标识了一个有效的dex文件,值固定为64 65 78 0a 30 33 35 00,转换为字符串为dex.035
    • checksum段为dex文件的校验和,通过它来判断dex文件是否被损坏或篡改。
    • signature字段用来识别最佳化之前的dex文件
    • fileSize字段记录了包括DexHeader在内的整个dex文件的大小
    • headerSize字段记录了DexHeader结构本身占用的字节数,目前它的值为0x70。
    • endianTag字段指定了dex运行环境的cpu字节序,预设值ENDIAN_CONSTANT等于0x12345678,表示默认采用Little-Endian字节序
    • linkSize字段与linkOff字段指定链接段的大小与文件偏移,大多数情况下它们的值都为0。
    • mapOff字段指定了DexMapList结构的文件偏移
    • 接下来的字段分别表示DexStringId、DexTypeId、DexProtoId、DexFieldId、DexMethodId、DexClassDef以及数据段的大小与文件偏移。
  • DexHeader结构下面的数据为”索引结构区“与”数据区“
    • “索引结构区”中各数据结构的偏移地址都是从DexHeader结构的stringIdsOff~classDefsOff字段的值指定的,它们并非真正的类数据,而是指向dex文件的data数据区(DexData字段,实际上是ubyte字节数组,包含了程序所有使用到的数据)的偏移或数据结构索引。
3.dex文件结构分析

Dalvik虚拟机解析dex文件的内容,最终将其映射成DexMapList数据结构。

  • DexHeader结构的mapOff字段指明了DexMapList结构在dex文件中的偏移:
代码语言:javascript
复制
struct DexMapList{
    u4  size;               //DexMapItem的个数
    DexMapItem  list[1];    //DexMapItem结构
};
//size字段表示接下来有多少个DexMapItem结构
//DexMapItem的结构如下:
struct DexMapItem{
    u2  type;               //kDexType开头的类型
    u2  unused;             //未使用,用于字节对齐
    u4  size;               //指定类型的个数
    u4  offset;             //指令类型数据的文件偏移
};

type字段为一个枚举常量,通过类型名称很容易判断具体类型:

分析整体结构:

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/153503.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • dex文件格式
    • 1.dex文件中的数据结构
      • 2.dex文件整体结构
        • 3.dex文件结构分析
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档