其他分享
首页 > 其他分享> > 015 Android之可执行文件dex

015 Android之可执行文件dex

作者:互联网

文章目录

从一个hello world开始

smali代码和dex之间有着千丝万缕的联系,先从一个最简单的dex文件hello world开始,学习整个dex文件的结构

.class public LHelloWorld;
.super Ljava/lang/Object;

.method public static main([Ljava/lang/String;)V
    .registers 2

    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string	v1, "Hello World!"

    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    return-void
.end method

以上面这段smali代码为例

D:\Android\tools>java -jar smali.jar -o classes.dex HelloWorld.smali

然后将smali代码转成dex文件,并打成压缩包,命名为HelloWorld.zip

在这里插入图片描述

确保adb和模拟器连接成功

在这里插入图片描述

再将打包好的zip文件上传到模拟器
在这里插入图片描述

接着执行dex文件,可以看到成功打印了Hello World

Dex文件结构

Dex文件主要可以分为三大块:

  1. Dex文件头
  2. 各种数据的数组,包括字符串 类型 方法原型 字段 方法
  3. 类数据

在这里插入图片描述

文件头

首先来看文件头的部分

在这里插入图片描述

总共占0x70个字节大小,比较重要的字段有四个

  1. dex_magic:表示dex文件的文件标识,特征字符串
  2. checksum:校验和,对文件求了32位的哈希值(从字段3开始到文件末尾)
  3. signature:表示sha1,对文件求哈希值(从字段4开始到文件末尾)
  4. file_size:表示文件大小

除了这四个字段以外,文件头部还有其他一些字段

  1. header_size:dex文件头大小
  2. endian_tag:数据排列方式–小端方式

在这里插入图片描述

各种表的大小及偏移

  1. string_ids_size和string_ids_off,字符串表的大小和偏移
  2. type_ids_size和type_ids_off,类型表的大小和偏移
  3. proto_ids_size和proto_ids_off,字段表的大小和偏移
  4. class_defs_size和class_defs_off,类数据表的大小和偏移

各种数据的数组

dex文件第二部分是各种数据的数组,包括字符串 类型 方法原型 字段 方法

字符串表

在这里插入图片描述

字符串表项,是一个字符串数据的偏移,偏移指向的是一个string_data结构。string_data结构中有两个字段

字段1:字符串长度,数据类型是uleb128,安卓中特有的变长的数据类型

字段2:存储数据,字符串以0结尾

类型表

在这里插入图片描述

类型表,保存的是一个索引值,指向的是字符串表

在这里插入图片描述

例如:索引值为3表示的是字符串表的下标为3的位置指向的是L/java/lang/Object这个字符串。

原型表

在这里插入图片描述

原型表中存储的是函数原型的各部分描述信息。包括短类型(shorty_idx),返回类型(return_type_idx),参数的类型(parameters_off),最终还是一个指向字符串表的数组下标。

注意:字段为返回类型(return_type_idx)的值,是类型表中的索引

字段表

在这里插入图片描述

存储的是字段信息,包括字段所在类(class_idx),字段的类型(type_idx),字段的名称(name_idx)。

class_idx是类型表中的索引,type_idx是类型表中的索引,字段名称的索引是字符串的数组下标

方法表

在这里插入图片描述

方法表中存储的是方法的信息,包括方法所在的类(class_dex),方法的原型(proto_idx),方法的名称(name_idx)。

其中class_idx是类型表的索引,proto_idx是原型表的索引,方法名称的索引(name_idx)是字符串表的数组下标

类数据

类数据也是一个数组,每一个元素就是一个类的相关信息。现在所分析的这个文件因为只有一个类,所以只有一个类的信息。

在这里插入图片描述

在表项中的class_data中存储的是类数据,包括类名索引,访问属性,父类索引,接口偏移,源码索引,注解偏移,类数据偏移

在这里插入图片描述

其中,整个类的数据在class_data_item这个结构中。

在这里插入图片描述

method_list是类内所有方法的列表,因为当前这个文件只有一个Main方法,所以列表内只有一个结构体。结构体中有方法的基本信息,包括方法索引,访问标志,代码偏移,代码信息

在这里插入图片描述

其中code_item是整个代码的信息,里面有两个字段特别重要

ins_size:指令长度

ushort insns[8]:指令数组

在这里插入图片描述

这个数组存放的就是被翻译成smali代码的虚拟机指令,也就是OpCode

手工解析Smali代码

62 00 00 00 1A 01 00 00 6E 20 01 00 10 00 0E 00

接下来复制这一段十六进制,手工将这段代码解析成Smali代码。

在这里插入图片描述

这里还需要借助一个文档《(中文)Dalvik操作码》,里面有所有的Opcode和对应的操作码以及示例。

在这里插入图片描述

首先找到62,62代表的指令含义是根据字段ID读取静态对象引用字段到vx,接着,还需要看懂旁边的示例

6201 0C00

解析为smali代码是

sget‐object v1, Test3.os1:Lja va/lang/Object; // field@000c

读取 Object 的静态对象引用字段 os1(字段表#CH 条目)到 v1。

也就说这条指令一共4个字节

62 代表操作码 sget‐object
01 代表的是序号为1的寄存器v1
000C 代表字段表索引为0xC的字段

接着再来看我们要解析的Opcode

62 00 00 00 1A 01 00 00 6E 20 01 00 10 00 0E 00

先解析前四个字节

62 00 00 00

具体含义如下

62 代表操作码 sget‐object
00 代表的是序号为0的寄存器v0
0000 代表字段表索引为00的字段

接下来在字段表中找到第0个字段

在这里插入图片描述

java.io.PrintStream java.lang.System.out

第0个字段就是out这个对象,将这个字段翻译为smali代码

Ljava.lang.System;->out:java.io.PrintStream

那么前四个字节

62 00 00 00

解析为smali代码就是

sget‐object v0, Ljava.lang.System;->out:java.io.PrintStream

接着来看1A

在这里插入图片描述

1A08 0000

解析为

 const‐string v8, ""  // string @0000  

存入 string@0000(字符串表#0 条目)的引用到 v 8

需要解析的Opcode

1A 01 00 00

接着找到字符串表索引为0的字符串

在这里插入图片描述

那么这条指令解析为Smali代码就是

const‐string v1, "Hello World!"

接下来查找6E

在这里插入图片描述

直接看例子

6E53 0600 0421 ‐ invoke‐virtual { v4, v0,  v1, v2, v3}, Test2.method5:(IIII)V // me thod@0006  

这条指令比较复杂,一共6个字节,其中

6E---> invoke‐virtual
5---> 参数数量
3---> v3
0600--->method@0006 
0421--->v4, v0,  v1, v2

调用参数表的编译比较诡异,如果参数的数量大于4,第5个参数将编译在指令字节的下一个字节的4个最低位

那么我们要解析的Opcode

6E 20 01 00 10 00

就可以解析为

6E---> invoke‐virtual
2---> 参数数量
01 00--->method@0001
10 00--->v1 v0

翻译出来就是

invoke‐virtual {v0,v1},method@01

在这里插入图片描述

找到函数表下标为1的方法

void java.io.PrintStream.println(java.lang.String)

转为Smali代码

Ljava/io/PrintStream;->println(Ljava/lang/String;)V

完整的smali代码就是

invoke‐virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

最后看0E

在这里插入图片描述

表示返回值为空

62 00 00 00 1A 01 00 00 6E 20 01 00 10 00 0E 00

解析出来完整的smali代码就是

sget‐object v0, Ljava.lang.System;->out:java.io.PrintStream;

const‐string v1, "Hello World!"

invoke‐virtual {v0,v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

return‐void 

和HelloWorld.smali的源码没有区别

标签:dex,Ljava,00,lang,idx,v1,015,字符串,Android
来源: https://blog.csdn.net/qq_38474570/article/details/118546498