一篇文章带你搞懂DEX文件的结构[通俗易懂]

一篇文章带你搞懂DEX文件的结构[通俗易懂]*本篇文章已授权微信公众号guolin_blog(郭霖)独家发布本文地址:一篇文章带你搞懂DEX文件的结构DEX文件就是AndroidDalvik虚拟机运行的程序,关于DEX文件的结构的重要性我就不多说了。下面,开练!建议:不要只看,跟着我做。看再多遍不如自己亲自实践一遍来的可靠,别问我为什么知道。泪崩ing…..首先,我们需要自己构造一个dex文件,因为自己构造的比…

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

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

本文地址:一篇文章带你搞懂DEX文件的结构

DEX文件就是Android Dalvik虚拟机运行的程序,关于DEX文件的结构的重要性我就不多说了。下面,开练!

建议:不要只看,跟着我做。看再多遍不如自己亲自实践一遍来的可靠,别问我为什么知道。泪崩ing…..

首先,我们需要自己构造一个dex文件,因为自己构造的比较简单,分析起来比较容易。等你简单的会了,难的自然也就懂了。

 

0x00■  构造DEX文件

首先,我们编写一个简单的Java程序,如下:

 

public class HelloWorld {  
    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;  
        HelloWorld helloWorld = new HelloWorld();  
        String sayNumber = String.valueOf(helloWorld.getNumber(c, d));  
        System.out.println("HelloDex!" + sayNumber);  
    }  
}  

然后将其编译成dex文件:打开命令行,进入HelloWorld.class所在文件夹下,执行命令:

 

 

 

javac HelloWorld.java

接下来会出现一个HelloWorld.class文件,然后继续执行命令:

dx –dex –output=HelloWorld.dex HelloWorld.class

就会出现HelloWorld.dex文件了。这时,我们需要下载一个十六进位文本编辑器,因为用它可以解析二进制文件,我们用它打开dex文件就会全部以十六进制的数进行展现了。这里推荐010Editor,下载地址:010Editor(收费软件,可以免费试用30天)。

下载完成之后,我们可以用它打开dex文件了,打开之后,你的界面应该是这样的:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

一下子看到这些东西,是不是立马懵逼了,正常,我刚开始看的时候也是,这什么玩意儿啊!其实,这就是二进制流文件中的内容,010Editor把它转化成了16进制的内容,以方便我们阅读的。

 

0x01■  DEX文件结构总览

不要慌,下面我跟你解释,这些东西我们虽然看了懵逼,但是Dalvik虚拟机不会,因为它就是解析这些东西的,这些东西虽然看起来头大,但是它是有自己的格式标准的。dex文件的结构如下图所示:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

这就是dex的文件格式了,下面我们从最上面的Header说起,Header中存储了什么内容呢?下面我们还得来一张图:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

 

0x02■  DEX文件结构解析

先看下就行,不用着急,下面我们一步一步来,首先点击你的010Editor的这里:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

对,就是箭头指的那里,点击之后,你会发现上面的有一片区域成了选中的颜色,这部分里面存储的就是Header中的数据了,下面我们根据Header的数据图以此来进行分析。

首先,我们看到DexHeader中每个数据前面有个u1或者u4,这个是什么意思呢?它们其实就是代表1个或者4个字节的无符号数。下面我们依次根据Header中的数据段进行解释。

1. 从第一个看起,magic[8];它代表dex中的文件标识,一般被称为魔数。是用来识别dex这种文件的,它可以判断当前的dex文件是否有效,可以看到它用了8个1字节的无符号数来表示,我们在010Editor中可以看到也就是“64 65 78 0A 30 33 35 00 ”这8个字节,这些字节都是用16进制表示的,用16进制表示的话,两个数代表一个字节(一个字节等于8位,一个16进制的数能表示4位)。这8个字节用ASCII码表转化一下可以转化为:dex.035(点击这里可以进行十六进制转ASCII,你可以试试:其中,’.’ 不是转化来的)。目前,dex的魔数固定为dex.035。

2. 第二个是,checksum;  它是dex文件的校验和,通过它可以判断dex文件是否被损坏或者被篡改。它占用4个字节,也就是“5D 9D F9 59”。这里提醒一下,在010Editor中,其实可以分别识别我们在DexHeader中看到的这些字段的,你可以点一下这里:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

你可以看到这个header列表展开了,其实我们分析下来就和它这个结构是一样的,你可以先看下,我们现在分析到了checksum中了,你可以看到后面对应的值是“59 F9 9D 5D”。咦?这好像和上面的字节不是一一对应的啊。对的,你可以发现它是反着写的。这是由于dex文件中采用的是小字节序的编码方式,也就是低位上存储的就是低字节内容,所以它们应该要反一下。

3. 第三个到了signature[kSHA1DigestLen]了,signature字段用于检验dex文件,其实就是把整个dex文件用SHA-1签名得到的一个值。这里占用20个字节,你可以自己点010Editor看一看。

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

5. 第五个headerSize;表示DexHeader头结构的大小,占用4个字节。这里可以看到它一共占用了112个字节,112对应的16进制数为70h,你可以选中头文件看看010Editor是不是真的占用了这么多:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

6. 第6个是endianTag;代表 字节序标记,用于指定dex运行环境的cpu,预设值为0x12345678,对应在101Editor中为“78 56 34 12”(小字节序)。

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

8. 再下来就是mapOff字段了,它指定了DexMapList的文件偏移,这里我们先不过多介绍它,你可以看一下它的值为“14 04 00 00”,它其实对应的16进制数就是414h(别忘了小字节序),我们可以在414h的位置看一下它在哪里:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

其实就是dex文件最后一部分内容。关于这部分内容里面是什么,我们先不说,继续往下看。

9. stringIdsSize stringIdsOff字段:这两个字段指定了dex文件中所有用到的字符串的个数和位置偏移,我们先看stringIdsSize,它的值为:“1C 00 00 00”,16进制的1C也就是十进制的28,也就是说我们这个dex文件中一共有28个字符串,然后stringIdsOff为:“70 00 00 00”,代表字符串的偏移位置为70h,这下我们找到70h的地方:   

一篇文章带你搞懂DEX文件的结构[通俗易懂]

这下我们就要先介绍一下DexStringId这个结构了,图中从70h开始,所有被选中的都是DexStringId这种数据结构的内容,DexStringId代表的是字符串的位置偏移,每个DexStringId占用4个字节,也就是说它里面存的还不是真正的字符串,它们只是存储了真正字符串的偏移位置。

下面我们先分析几个看看,

①取第一个“B2 02 00 00”,它代表的位置偏移是2B2h,我们先找到这个位置:    

一篇文章带你搞懂DEX文件的结构[通俗易懂]

可以发现我一共选中了10个字节,这10个字节就表示了一个字符串。下面我们看一下dex文件中的字符串是如何表示的。dex中的字符串采用了一种叫做MUTF-8这样的编码,它是经过传统的UTF-8编码修改的。在MTUF-8中,它的头部存放的是由uleb128编码的字符的个数。(至于uleb128编码是什么编码,这里我不详细展开说,有兴趣的可以搜索看看。)

也就是说在“08 3C 63 6C 69 6E 69 74 3E 00”这些字节中,第一个08指定的是后面需要用到的编码的个数,也就是8个,即“ 3C 63 6C 69 6E 69 74 3E”这8个,但是我们为什么一共选中了10个字节呢,因为最后一个空字符“0”表示的是字符串的结尾,字符个数没有把它算进去。下面我们来看看“ 3C 63 6C 69 6E 69 74 3E”这8个字符代表了什么字符串:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

依旧可以点这里查询ASCII 。(要说明的一点是,这里凑巧这几个uleb128编码的字符都用了1个字节,所以我们可以这样进行查询,uleb128编码标准用的是1~5个字节, 这里只是恰好都是一个字节)。也就是说上面的70h开始的第一个DexStringId指向的其实是字符串“<clinit>”(但是貌似我们的代码中没有用到这个字符串啊,先不用管,我们接着分析)。再看到这里:

 一篇文章带你搞懂DEX文件的结构[通俗易懂]

②刚刚我们分析到“B2 02 00 00”所指向的真实字符串了,下面我们接着再分析一个,我们直接分析第三个,不分析第二个了。第三个为“C4 02 00 00”,对应的位置也就是2C4h,我们找到它:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

看这里,这就是2C4h的位置了。我们首先看第一个字符,它的值为0Bh,也就是十进制的11,也就是说接下来的11个字符代表了它的字符串,我们依旧是查看接下来11个字符代表的是什么,经过查询整理:   

一篇文章带你搞懂DEX文件的结构[通俗易懂]

依旧可以在这里查询ASCII。上面就是“HelloDalvik”这个字符串,可以看看我们的代码,我们确实用了一个这样的字符串,bingo。

下面剩下的字符串就不分析了。经过整理,可以整理出我们一共用到的28个字符串为:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

ok,字符串这里告一段落,下面我们继续看DexHeader的下面的字段。头好晕~乎乎

 噢,读了,还不能结束呢,你现在可以看一下最开始发的那张dex结构图了:

一篇文章带你搞懂DEX文件的结构[通俗易懂]

看到了吧,我们这半天分析的stringIdsSize 和 stringIdsOff字段指向的位置就是上面那个箭头指向的位置,它们里面存储的是真实字符串的位置偏移,它们都存储在data区域。(先透露一下,后面我们要分析的几个也和stringIdsSize 与stringIdsOff字段类似,它们里面存储的基本都是位置偏移,并不是真正的数据,真正的数据都在data区域)

好,我们继续。

10. 继续看DexHeader图,我们现在该typeIdsSizetypeIdsOff了。它们代表什么呢?它们代表的是类的类型的数量和位置偏移,也是都占4个字节,下面我们看它们的值一篇文章带你搞懂DEX文件的结构[通俗易懂]

可以看到,typeIdsSize的值为9h,也就是我们dex文件中用到的类的类型一共有9个,位置偏移在E0h位置,下面我们找到这个位置

一篇文章带你搞懂DEX文件的结构[通俗易懂]

看到了吧,我选中的位置就是了。这里我们又得介绍一种数据结构了,因为这里的数据也是一种数据结构的数据组成的。那就是DexTypeId,也就是说选中的内容都是DexTypeId这种数据,这种数据结构中只有一个变量,如下所示:

 

struct DexTypeId{
	u4 descriptorIdx;	/*指向DexStringId列表的索引*/
}

看到了吧,这就是DexTypeId数据结构,它里面只有一个数据descriptorIdx,它的值的内容是DexStringId列表的索引。还记得DexStringId是什么吗?在上面我们分析字符串时,字符串的偏移位置就是由DexStringId这种数据结构描述的,也就是说descriptorIdx指向的是所有的DexStringId组成的列表的索引。上面我们整理出了所有的字符串,你可以翻上去看看图。然后我们看这里一共是9个类的类型代表的都是什么。先看第一个“05 00 00 00”,也就是05h,即十进位的5。然后我们在上面所有整理出的字符串看看5索引的是什么?翻上去可以看到是“I”。接下来我们依次整理这些类的类型,也可以得到类的类型的列表

 

一篇文章带你搞懂DEX文件的结构[通俗易懂]

看到了吧,这就是我们dex文件中所有用到的类的类型。比如“I”代表的就是int,LHelloWorld代表的就是HelloWorld,Ljava/io/PrintStream代表的就是java.io.PrintStream。后面的几个先就不说了。我们接着往下分析。

11. 这下到了protoIdsSizeprotoIdsOff了,它们代表的是dex文件中方法原型的个数和位置偏移。我们先看它们的值

一篇文章带你搞懂DEX文件的结构[通俗易懂]
如上图就是它们的值了,protoIdsSize的值为十进制的7,说明有7个方法原型,然后位置偏移为104h,我们找到这个位置

一篇文章带你搞懂DEX文件的结构[通俗易懂]

看到了吧,这里就是了。对,下面又有新的数据结构了。这下一个数据结构不能满足这块的内容了,我们先看第一个数据结构,DexProtoId

 

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

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

 

我们接着看看DexTypeList这个数据结构,看看参数列表是如何存储的。

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

看到了嘛,它有两个参数,其中第一个size说的是DexTypeItem的个数,那DexTypeItem又是啥咧?它又是一种数据结构。我们继续看看

 

 

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

恩,还好,里面就一个参数。也比较简单,就是一个指向DexTypeId列表的索引,也就是代表参数列表中某一个具体的参数的位置。

 

分析完这几个数据结构了,下面我们具体地分析一个类吧。别走神,我们该从上图的104h开始了。

在104h这里,由于 都是DexProtoId这种数据结构的数据,一个DexProtoId一共占用12个字节。所以,我们取前12个字节进行分析。“06 00 00 00,00 00 00 00,94 02 00 00”,这就是那12个字节了。首先“06 00 00 00”代表的是shortyIdx,它的值是指向DexStringId列表的索引,我们找到DexStringId列表中第6个对应的值,也就是III,说明这个方法中声明字符串为三个int。接着,“00 00 00 00”代表的是returnTypeIdx,它的值指向的是DexTypeId列表的索引,我们找到对应的值,也就是I,说明这个方法的返回值是int类型的。最后,我们看“94 02 00 00”,它代表的是DexTypeList的位置偏移,它的值为294h,我们找到这个位置

一篇文章带你搞懂DEX文件的结构[通俗易懂]
这里是DexTypeList结构,首先看前4个字节,代表的是DexTypeItem的个数,“02 00 00 00 ”也就是2,说明接下来有2个DexTypeItem的数据,每个DexTypeItem占用2个字节,也就是两个都是“00 00”,它们的值是DexTypeId列表的索引,我们去找一下,发现0对应的是I,也就是说它的两个参数都是int型的。因此这个方法的声明我们也就确定了。也就是int(int,int),可以看看我们的源代码,getNumber方法确实是这样的。好,第一个方法就这样分析完了,下面我们依旧是将这些方法的声明整理成列表,后面可能有数据会指向它们的索引。

一篇文章带你搞懂DEX文件的结构[通俗易懂]
 

终于又完了一个。我们准备继续下面的。累了就先去听听歌吧,歇一歇再看 -_-

12. fieldIdsSizefieldIdsOff字段。这两个字段指向的是dex文件中字段名的信息。我们看到这里

一篇文章带你搞懂DEX文件的结构[通俗易懂]

可以看到,fieldIdsSize为3h,说明共有3个字段。fieldIdsOff为158h,说明偏移为158h,我们继续看到158h这里

一篇文章带你搞懂DEX文件的结构[通俗易懂]

咳咳,又该新的数据结构了,再忍一忍,接下来的数据结构是DexFieldId,我们看下

 

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

可以看到,这三个数据都是指向的索引值,具体的就不说了,看后面的备注就是。我们依旧是分析一下第一个字段,“01 00 ,00 00,13 00 00 00”,类的类型为DexTypeId列表的索引1,也就是HelloWorld,字段的类型为DexTypeId列表中的索引0,也就是int,字段名为DexStringId列表中的索引13h,即十进制的19,找一下,是a,也就是说我们这个字段就确认了,即int HelloWorld.a。这不就是我们在HelloWorld.java文件里定义的变量a嘛。然后我们依次把我们所有的3个字段都列出来:

 

〇int HelloWorld.a , ①java.lang.String HelloWorld.b ,②java.io.PrintStream java.lang.System.out

ok,先告一段落。继续分析下一个

13. methodIdsSizemethodIdsOff字段。这俩字段指明了方法所在的类、方法的声明以及方法名。我们看看

一篇文章带你搞懂DEX文件的结构[通俗易懂]
 

先是,methodIdsSize,为Ah,即十进制的10,说明共有10个方法。methodIdsOff,为170h,说明它们的位置偏移在170h。我们看到这里

一篇文章带你搞懂DEX文件的结构[通俗易懂]

对对对,又是新的数据结构,不过这个和上个一样简单,请看DexMethodId

 

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

对吧,这个也简单,三个数据也都是指向对应的结构的索引值。我们直接分析一下第一个数据,“01 00, 04 00, 00 00 00 00”,首先,classIdx,为1,对应DexTypeId列表的索引1,也就是HelloWorld;其次,protoIdx,为4,对应DexProtoId列表中的索引4,也就是void();最后,nameIdx,为0,对应DexStringId列表中的索引0,也就是<clinit>。因此,第一个数据就出来了,即void HelloWorld.<clinit>() 。后面的不进行分析了,我们依旧是把其余的9个方法列出来
一篇文章带你搞懂DEX文件的结构[通俗易懂]

 

好了,这个就算分析完了。下面真正开始我们的重头戏了。先缓一缓再继续吧。

14. classDefsSizeclassDefsOff字段。这两个字段指明的是dex文件中类的定义的相关信息。我们先找到它们的位置。

一篇文章带你搞懂DEX文件的结构[通俗易懂]

classDefsSize字段,为1,也就是只有一个类定义,classDefsOff,为1C0h,我们找到它的偏移位置。

一篇文章带你搞懂DEX文件的结构[通俗易懂]

这里就是了,到了这里,你现在应该也知道又有新的数据结构了。对的,接下来的数据结构是DexClassDef,请看

 

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结构的偏移*/
}

不多说了,我们直接根据结构开始分析吧,反正就只有一个类定义。classIdx为1,对应DexTypeId列表的索引1,找到是HelloWorld,确实是我们源程序中的类的类型。accessFlags为1,它是类的访问标志,对应的值是一个以ACC_开头的枚举值,1对应的是 ACC_PUBLIC,你可以在010Editor中看一下,说明我们的类是public的。superclassIdx的值为3,找到DexTypeId列表中的索引3,对应的是java.lang.object,说明我们的类的父类类型是Object的。interfaceOff指向的是DexTypeList结构,我们这里是0说明没有接口。如果有接口的话直接对应到DexTypeList,就和之前我们分析的一样了,这里不多解释,有兴趣的可以写一个有接口的类验证下。再下来sourceFileIdx指向的是DexStringId列表的索引,代表源文件名,我们这里位4,找一下对应到了字符串”HelloWorld.java”,说明我们类程序的源文件名为HelloWorld.java。annotationsOff字段指向注解目录接口,根据类型不同会有注解类、注解方法、注解字段与注解参数,我们这里的值为0,说明没有注解,这里也不过多解释,有兴趣可以自己试试。

 

接下来是classDataOff了,它指向的是DexClassData结构的位置偏移,DexClassData中存储的是类的数据部分,我们开始详细分析一下它,首先,还是先找到偏移位置3F8h

一篇文章带你搞懂DEX文件的结构[通俗易懂]
接着,我们看看DexClassData数据结构

 

struct DexClassData{
	DexClassDataHeader	        header;			/*指定字段与方法的个数*/
	DexField* 			staticFields;		/*静态字段,DexField结构*/
	DexField*			instanceFields;	/*实例字段,DexField结构*/
	DexMethod*			directMethods;		/*直接方法,DexMethod结构*/
	DexMethod*			virtualMethods;		/*虚方法,DexMethod结构*/
}

可以看到,在DexClassData结构中又引入了三种结构,我们一起写出来看一下吧

 

 

struct DexClassDataHeader{
	u4 staticFieldsSize;	/*静态字段个数*/
	u4 instanceFieldsSize;	/*实例字段个数*/
	u4 directMethodsSize;	/*直接方法个数*/
	u4 virtualMethodsSize;  /*虚方法个数*/
}

struct DexField{
	u4 fieldIdx;		/*指向DexFieldId的索引*/
	u4 accessFlags;		/*访问标志*/
}

struct DexMethod{
	u4 methodIdx;		/*指向DexMethodId的索引*/
	u4 accessFlags;		/*访问标志*/
	u4 codeOff;		/*指向DexCode结构的偏移*/
}	/*指向DexFieldId的索引*/
	u4 accessFlags;		/*访问标志*/
}

struct DexMethod{
	u4 methodIdx;		/*指向DexMethodId的索引*/
	u4 accessFlags;		/*访问标志*/
	u4 codeOff;		/*指向DexCode结构的偏移*/
}

代码中的注释写的也都很清楚了,我们就不多说了。但是请注意,在这些结构中的u4不是指的占用4个字节,而是指它们是uleb128类型(占用1~5个字节)的数据。关于uleb128还是不多说,想了解的可以自己查查看。

 

好,接下来开始分析,对于DexClassData,第一个为DexClassDataHeader,我们找到相应的位置,第一个staticFieldsSize其实只占用了一个字节,即01h就是它的值,也就是说共有一个静态字段,接下来instanceFieldsSize,directMethodsSize,virtualMethodsSize也都是只占用了一个字节,即实例字段的个数为1,直接方法的个数为3,虚方法的个数为1。(这里只是凑巧它们几个都占用一个字节,并不一定是只占用一个字节,这关于到uleb128数据类型,具体可以自己了解下)。

然后接下来就是staticFields了,它对应的数据结构为DexField,可以看到,第一个fieldIdx,是指向DexFieldId的索引,值为1,找到对应的索引值为java.lang.String HelloWorld.b。第二个accessFlags,值为8,对应的ACC_开头的数据为ACC_STATIC(可以在010Editor中对应查看一下),说明我们这个静态字段为:static java.lang.String HelloWorld.b。可以对应我们的源代码看一下,我们确实定义了一个static的b变量。

接着看instanceFields,它和staticFields对应的数据结构是一样的,我们直接分析,第一个fieldIdx,值为0,对应的DexField的索引值为int HelloWorld.a。第二个accessFlags,值为0,对应的ACC_开头的数据为空,就是什么也没有。说明我们这个实例字段为:int HelloWorld.a。可以对应我们的源码 看看,我们确实定义了一个a实例变量。

再接着,根据directMethodsSize,有3个直接方法,我们先看第一个,它对应的数据结构是DexMethod,首先methodIdx指向的是DexMethodId的索引,值为0,找到对应的索引值为void HelloWorld.<clinit>()。然后accessFlages为……为……为….我的个天!我以为就这样能蒙混过关了,没想到还真碰到一个uleb128数据不是占用一个字节的,这个accessFlags对应的值占用了三个字节,“88 80 04”,为什么?因为是按照uleb128格式的数据读出来的(还是自己去查查吧,这个坑先不填了,其实这种数据也不麻烦,就是前面字节上的最高位指定了是否需要下一个字节上的内容)。“88 80 04”对应的ACC_开头的数据为 ACC_STATIC ACC_CONSTRUCTOR,表明这个方法是静态的,并且是构造方法。最后,看看codeOff,它对应了DexCode结构的偏移,DexCode中存放了方法的指令集等信息,也就是真正的代码了。我们暂且不分析DexCode,就先看看它的偏移位置为“E0 03”,这个等于多少呢?uleb128转化为16进制数结果为:1E0h。也就是DexCode存放在偏移位置1E0h的位置上。

一篇文章带你搞懂DEX文件的结构[通俗易懂]

具体的DexCode我们就先不分析了,因为它里面存放的一些指令局需要根据相关资料一一查找,有兴趣的自己可以找资料看看。剩下的两个直接方法我们也不分析了。

接下来,我们看根据virtualMethodsSize,有1个虚方法,我们直接看。首先methodIdx的值为2,对应的DexMethodId的索引值为int HelloWorld.getNumber(int, int)。然后accessFlags为1,对应的值为ACC_PUBLIC,表明这是一个public类。codeOff为“FC 04”,对应的位置为27Ch,这里就不上图了,自己找找吧。

好了,我们整个DEX文件的结构就这样从DexHeader开始基本分析完了,好累啊,不过这样分析一遍,对DEX文件的格式会有更深刻的认识。总是看别人的真不如自己来一遍来的实在!

 

0x03■  参考资料

参考资料:

《Android软件安全与逆向分析》.非虫

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

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/153504.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 【Android音视频开发】【034】WEBRTC之ICE服务器搭建

    【Android音视频开发】【034】WEBRTC之ICE服务器搭建前篇由于ICE服务器是作为一个中转服务器来使用的,因此它必须搭建在外网,我是搭建在阿里云服务器上面的搭建ICE服务器需要一定的Linux经验和排错能力,没接触过Linux的量力而为切换到root用户sudosuroot安装opensslapt-getinstallopenssllibssl-devmake安装libevent2wgethttps://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar

  • Docker(三)- 从镜像运行启动容器「建议收藏」

    Docker(三)- 从镜像运行启动容器「建议收藏」文章目录从镜像运行启动容器容器启动后运行的命令`ENTRYPOINT`和`CMD`启动容器时覆盖`ENTRYPOINT`和`CMD“-d`后台运行`dockerexec`进入容器,运行指定命令`–name`和`–restart=always“–rm`和`dockercp`从镜像运行启动容器从一个镜像可以运行启动一个或多个容器。所谓容器,我们可以理解为是一个虚拟的计算机,其中运行着操作系统,操作系统中运行着我们部署的应用。从tomcat镜像启动容器:do

  • mybatiscodehelperpro使用_IDEA插件

    mybatiscodehelperpro使用_IDEA插件IDEA集成MyBatisCodeHelperPro插件

  • 网络流量分析netflow

    网络流量分析netflow前言  随着宽带互联网在中国的迅速发展,全国各大电信运营商的网络规模都在不断扩张,网络结构日渐复杂,网络业务日趋丰富,网络流量高速增长。电信运营商需要通过可靠、有效的网络业务流量监测系统对其网络以及网络所承载的各类业务进行及时、准确的流量和流向分析,进而挖掘网络资源潜力,控制网络互联成本,并为网络规划、优化调整和业务发展提供基础依据。  目前国内电信运营商已建的网络管理系统所能实现

  • idea替换的快捷键是 ctrl +?_idea怎么查找替换

    idea替换的快捷键是 ctrl +?_idea怎么查找替换idea替换快捷键ctrl+r:当前文件内容替换,指的是在当前打开的文件中替换匹配的字符,只操作一个文件。如下图1所示ctrl+shift+r:在路径中替换,指的是在选定的目录下或者类包下,查找要被替换的字符,再在第二个输入框中输入要替换的字符,点击弹出框的右下角的replace或者replaceall即可。如下图2所示…

  • linux系统管理初学者指南 基于centos 7.6下载_centos查看运行的进程

    linux系统管理初学者指南 基于centos 7.6下载_centos查看运行的进程目录一、进程概念1.进程组成部分:2.僵尸进程:3.线程:4.上下文切换:5.中断处理:6.进程的属性二、ps查看进程工具1.-aux弹出消息及详解2、linux进程有5种基本状态三、free查看内存使用情况1.弹出消息及详解:三、uptime查看系统负载1.弹出消息含意如下:四、top命令动态性能分析工具1.每行信息2.top快捷键:五、lsof命令用于查看你进程调用、打开的文件六、kill关闭进程1)ki

发表回复

您的电子邮箱地址不会被公开。

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