android .dex文件探究

android .dex文件探究在我们写Java代码的时候,生成的文件是.java文件,但是JVM并不识别这个,所以会先转成class文件,而在Android端,Android上的Davlik虚拟机能运行.dex。所以dex文件中包含了所有的app代码,可利用反编译工具获取java代码。即dex文件就是AndroidDalvik虚拟机运行的程序。为了能够加深印象,我们先构造一个dex文件。publicclassDEX…

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

在我们写Java代码的时候,生成的文件是.java文件,但是JVM并不识别这个,所以会先转成class文件,而在Android端,Android上的Davlik虚拟机能运行.dex。所以dex文件中包含了所有的app代码,可利用反编译工具获取java代码。

即dex文件就是Android Dalvik虚拟机运行的程序。

为了能够加深印象,我们先构造一个dex文件。

public class DEX { 
   

    int a = 0;
    static String b = "HelloDalvik";

    public int getNumber(int i,int j){ 
   
        int e = 3;
        return e+i+j;
    }

    public static void main(String[] args){ 
   
        int c= 1;
        int d = 2;
        DEX dex = new DEX();
        String sayNumber = String.valueOf(dex.getNumber(c,d));
        System.out.println("HelloDalVik "+sayNumber);
    }
}

先执行 javac DEX.java
这样会生成一个.class文件,然后在SDK中找到dx.bat,
执行 xx\xxx\xxx\dx –dex –output=DEX.java DEX.class
这样就会生成DEX.dex文件,如果出现does not match path 的错误,就,这样执行:

D:\SDK\build-tools\27.0.1\dx --dex --output=DEX.dex com\example\asus1\rexiufu\DEX.class

然后我们使用010 Editor来打开这个DEX.dex文件

在这里插入图片描述

DEX文件结构

在这里插入图片描述

我们先看Header:
在这里插入图片描述

文件头包含了dex文件的信息,所有数据的大致分布情况

然后我们看看Header的数据图:在Editor中查看:
在这里插入图片描述

可以看到第一个,magic[8],它代表dex文件中的文件标识,一般被称为魔数。是用来识别dex这种文件的,它可以判断当前的dex文件是否有效,可以看到它用来8个1字节的无符号数来表示,我们在Editor中可以看到是“64 65 78 0A 30 33 35 00”这8个字节,这些字节都是16进制表示的。这8个字节用ASCII码转换为dex.035(“.”不是转换来的)。目前,dex的魔数固定为dex.035

然后第二个,checksum,它是dex文件的校验和,通过它可以判断dex文件是否被损坏或者被篡改。它占用4个字节
在这里插入图片描述

我们可以看到它的值和它对应的四个字节,刚好是反着的。这是由于dex文件中采用的是小字节序的编码方法,也就是低位上存储的就是低字节的内容。

第三个是SHA1 signature[20],signature字段用于检验dex文件,其实就是把整个dex文件用SHA-1签名得到的一个值。这里占用了20个字节。

第四个是fileSize,表示整个文件的大小,占用4个字节

第五个是headerSize,表示DexHeader头结构的大小,占用4个字节。

第六个是endianTag,代表字节序标记,用于执行dex运行环境的cpu,预设值为0x12345678,对应在101Editor中为“78 56 34 12”

接下来两个分别是linkSizelinkOff,这连个字段,分别指定了链接段的大小和文件偏移,通常情况下它们都为0,。linkSize为0的话表示静态连接。

再下来就是mapOff字段,它指定了DexMapList的文件偏移,就是dex文件结构图中的最后一层。

接下来的两个StringIdsSizeStringIdsOff:这两个字段指定了dex文件中所有用到的字符串的个数和位置偏移。我们先看StringIdsSize,现在它的是十进制值为28,也就是说我们这个dex文件一共有28个字符串,StringIdsOff的值为“70 00 00 00”,表示字符串的偏移位置为70h,然后我们找到70h的地方:
在这里插入图片描述

在第4个中,显示的字符串是“HelloDalVik”,
然后我们再看看dex的结构图:
在这里插入图片描述

然后我们继续看
接下来是typeIdsSizetypeIdsOff,它们代表类的类型的数量和位置偏移,都占4个字节,typeIdsSize的的值为9,表示dex文件中用到的类的类型一共有9个,typeIdsOff的值为“E0 00 00 00 ”,即偏移位置在E0:
在这里插入图片描述

接下来两个是protoIdsSizeprotoIdsOff,它们表示dex文件中方法原型的个数和位置偏移,现在protoIdsSize的值为7,说明有7个方法原型,然后位置偏移是104h
在这里插入图片描述

这里涉及到一个数据结构:


struct DexProtoId{ 
   
	u4 shortyIdx;			/*指向DexStringId列表的索引*/
	u4 returnTypeIdx;		/*指向DexTypeId列表的索引*/
	u4 parametersOff;		/*指向DexTypeList的位置偏移*/
}

可以看到,这个数据结构由三个变量组成。第一个shortyIdx,它指向我们上面分析的DexStringId列表的索引,代表的是方法声明字符串。第二个returnTypeIdx它指向的是我们上班分析的DexTypeId列表索引,代表的是方法返回类型字符串。第三个parameterOff指向的是DexTypeList的位置索引,这又是一个新的数据结构,这里面存储的是方法的参数类别。
可以看到这三个参数,方法声明字符串,返回类型,参数列表,这基本上确定了我们一个方法的大体内容。


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


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

这样的话,我们可以看到,第一个方法为 int(int,int)
在这里插入图片描述

我们继续看下面的两个,fieldIdsSizefieldIdsOff,指向的是dex文件中字段名的信息。这里fieldIdsSize的大小为3,fieldIdsOff的值为“58 01 00 00 ”:

在这里插入图片描述

又涉及到有个数据结构:

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

我们可以看到第一个DexFieldId是Dex中的a

继续继续,methodIdsSizemethodIdsOff,指明了方法所在的类,方法的声明以及方法名,现在methodIdsSize的值为10,methodIdsOff的值为“70 01 00 00”,
在这里插入图片描述

它涉及到的数据结构:

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

分析可以得到,第一个方法为 void DEX.<clinit>()

接下来是classDefsSizeclassDefsOff,这两个字段指明的是dex文件中类的定义的相关信息,在这里,classDefsSize的值是1,classDefsOff的是“C0 01 00 00”,

在这里插入图片描述

这里涉及的数据结构:

struct DexClassDef{ 
   
	u4 classIdx;		/*类的类型,指向DexTypeId列表的索引*/
	u4 accessFlags;		/*访问标志*/
	u4 superclassIdx;	/*父类类型,指向DexTypeId列表的索引*/
	u4 interfacesOff;	/*接口,指向DexTypeList的偏移*/
	u4 sourceFileIdx;	/*源文件名,指向DexStringId列表的索引*/
	u4 annotationsOff;	/*注解,指向DexAnnotationsDirectoryItem结构*/
	u4 classDataOff;	/*指向DexClassData结构的偏移*/
	u4 staticValuesOff;	/*指向DexEncodedArray结构的偏移*/
}

我们可以看到,这个找到的类是DEX

接下来是DataSizeDataOff,这里DataOff的值是“E0 01 00 00 ”,找到01E0h,这里存放的是DexCode。

dex文件的加载流程

https://blog.csdn.net/jsqfengbao/article/details/52103439
https://www.jianshu.com/p/c9fd64e0b934

参考:
https://blog.csdn.net/sinat_18268881/article/details/55832757

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • c语言qq聊天刷屏代码大全,QQ聊天刷屏脚本 达人分享技巧

    教大家自己编写一个QQ聊天刷屏的脚本,几步就可以搞定哦。操作方法01点击电脑左下角的开始菜单,选择记事本,新建一个记事本文件。02在记事本中输入以下代码:SetWshShell=WScript.CreateObject(“WScript.Shell”)WshShell.AppActivate”wendy”fori=1to10WScript.Sleep500WshShell.SendK…

    2022年4月9日
    775
  • tensorflow实现DCGAN

    tensorflow实现DCGAN1 DCGAN 的简单总结 Paper nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp http arxiv org abs 1511 06434 github nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp https github com Newmu dcgan code nbsp nbsp theano nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp nbsp https github com carpedm20 DCGAN tensorflow nbsp nbsp tensorflow nbsp nbsp

    2025年10月22日
    2
  • pip源配置

    pip源配置pip源配置1、使用配置文件配置文件[global]trusted-host=mirrors.aliyun.comindex-url=http://mirrors.aliyun.com/pypi/simple/配置文件放置位置Linux下:放在~/.pip/pip.confwindows下:用户文件夹下\pip\pip.ini2、使用命令行临时改变pip源pipinstall-i–trusted-hostpackage例如pipinstall-ihttp://

    2022年5月4日
    170
  • linux 编译安装GCC4.9.3(完整版)「建议收藏」

    linux 编译安装GCC4.9.3(完整版)「建议收藏」第一步首先下载gcc源码包wgethttp://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-4.9.3/gcc-4.9.3.tar.bz2第二步将下载好的文件放在非root用户也有读权限的地方,例如/home/myuser或者/usr/gcc-build/下面第四步我会讲为什么要这么做.第三步解压文件,做一些准备工作tar

    2022年5月25日
    35
  • fbx文件导入3dmax_3d中z轴的值没办法输入

    fbx文件导入3dmax_3d中z轴的值没办法输入本文通过参考网上资源做的一个例子。本程序的功能就是通过xna将3d图像显示到winfrom对他进行旋转操作。首先我们先准备好两个文件夹model文件夹放fbx文件,textures放渲染文件,操作步骤都是添加现有项,准备好资源文件后,先检查下是否有以下引用下面将定义Ga…

    2022年10月9日
    2
  • Oracle触发器写法和举例[通俗易懂]

    Oracle触发器写法和举例[通俗易懂]1、declare声明变量后的每个变量都要加分号;2、所有的语句结束和sql语句结尾,都要加分号;3、变量赋值variable:=’1234’如:recordId:=’1234′;4、插入列数据获取:new.colname如:new.exec_record_id5、sql查询写入变量selectcolnameintovariable如:sel…

    2022年7月15日
    11

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号