转载自公众号“嵌入式Linux技术分享 ”
我们的重点不是分析Android的DEX格式,因为DEX格式部分可以直接通过010Editor解析出来的,这部分直接贴出自动解析图,我们的重点是根据指令集内容通过查询手册分析出具体的汇编指令。
首先编写简单的HelloWorld程序,Java代码如下
使用Java8编译器编译生成Hello.class,然后使用Android SDK29.0.2的dx将class文件转换成DEX文件,注意Java版本与SDK版本需要匹配,可使用javap -verbose Hello命令查看major version字段确定Java编译器的版本。
下面使用010 Editor软件分析DEX文件,选择DEX.bt脚本,点击Templates->Run Template对DEX文件进行分析。
先列出DEX文件的格式,如下
header |
String_ids |
Type_ids |
Proto_ids |
Field_ids |
Method_ids |
Class_def |
data |
Link_data |
直接根据010Editor软件显示,如下是string_ids内容,
据此,再贴出type_ids内容如下,
然后贴出proto_ids
下面贴出的是field_ids
如下是method_ids
下面部分是重点,首先我们寻找指令,如下图所示该DEX有两个method,我们先分析第一个method的指令。
参考指令文档链接https://source.android.google.cn/devices/tech/dalvik/dalvik-bytecode
该指令格式,查询“字节码格式”表,开头“70”的Opcode为invoke-dirct,格式为0x35c,如下表所示。
查询“说明格式”表,知道0X35c的指令格式如下图所示,为A|G|op BBBB F|E|D|C。
根据指令内容是“7010 0300 0000 0E00”,在指令“7010”(按照小端字节排序是“1070”)中,A=1,所以对应”[A=1] op {vC}, kind@BBBB”格式,解析得到BBBB=0X0003,C=D=E=F=0,因为BBBB属于kind@类型,查method_ids表,得Java.lang.Object.
所以第一条指令是invoke-direct {v0}, Ljava/lang/Object;->
指令最后的0E00,先根据0E查“字节码格式”表,得到其opcode为return-void,如下结果。
再根据上图中10X,查“说明格式”表,如下所示。
所以指令指令最后的0E00,对应的是“return-void”
下面解析第二个method方法,如下是指令截图,指令内容为“6200 0000 1A01 0100 6E20 0200 1000 0E00”,
根据下图可以解析“6200 0000”,查询field_ids,得到汇编为sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
根据下图可以解析“1A01 0100”,查询string_ids,得到const-string v1, "Hello, world!"
根据下图可以解析“6E20 0200 1000”,其中A=2,BBBB=2, F=E=C=0 D=1,查询module_ids表,得到invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
最后一个“0E00”指令,前面已经分析过,解析得到return-void。
下面贴出Hello.java对应的Smali反汇编结果,证明了我们的分析是正确的。
为了贴近实战,我们分析一个实际的Android APK,众所周知,Android Studio创建新的工程,自动生成默认输出“Hello World!”的代码,其MainActivity.java源码如下,
1 | 将其编译成的APK转成对应的DEX文件,利用010Editor进行分析,如下所示。 |
1 |
1 |
我们只分析MainActivity.java中的类,该类包含一个虚拟方法,一个直接方法,我们分析指令最多的虚拟方法,我们只分析最核心的指令部分,其对应的指令集是“6F20 620D 2100 1400 1C00 097F 6E20 633C 0100 0E00”。
下面是指令“6F20 620D 2100”解析过程,其中A=2,BBBB=0X0D62=3426,F=E=0,D=2,C=1。
1 |
1 | 查询method_ids表,如下 |
最终得到,
invole-super {v1,v2}
Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
下面是指令“1400 1C00 097F”解析过程,其中AA=0,BBBBBBBB=0X7F09001C,
最终得到const v0, 0x7f09001c
下面是指令“6E20 633C 0100”解析过程,A=2,BBBB=0X3C63=15459,F=E=0,D=0,C=1,
最终得到invoke-virtual {v1, v0}, Lcom/example/prj/MainActivity;->setContentView(I)V
最后贴下MainActivity类的Smali代码,论证了上面分析的正确性。
Android之DEX文件指令解析
我们的重点不是分析Android的DEX格式,因为DEX格式部分可以直接通过010Editor解析出来的,这部分直接贴出自动解析图,我们的重点是根据指令集内容通过查询手册分析出具体的汇编指令。
首先编写简单的HelloWorld程序,Java代码如下
使用Java8编译器编译生成Hello.class,然后使用Android SDK29.0.2的dx将class文件转换成DEX文件,注意Java版本与SDK版本需要匹配,可使用javap -verbose Hello命令查看major version字段确定Java编译器的版本。
下面使用010 Editor软件分析DEX文件,选择DEX.bt脚本,点击Templates->Run Template对DEX文件进行分析。
先列出DEX文件的格式,如下
header |
String_ids |
Type_ids |
Proto_ids |
Field_ids |
Method_ids |
Class_def |
data |
Link_data |
直接根据010Editor软件显示,如下是string_ids内容,
据此,再贴出type_ids内容如下,
然后贴出proto_ids
下面贴出的是field_ids
如下是method_ids
下面部分是重点,首先我们寻找指令,如下图所示该DEX有两个method,我们先分析第一个method的指令。
参考指令文档链接https://source.android.google.cn/devices/tech/dalvik/dalvik-bytecode
该指令格式,查询“字节码格式”表,开头“70”的Opcode为invoke-dirct,格式为0x35c,如下表所示。
查询“说明格式”表,知道0X35c的指令格式如下图所示,为A|G|op BBBB F|E|D|C。
根据指令内容是“7010 0300 0000 0E00”,在指令“7010”(按照小端字节排序是“1070”)中,A=1,所以对应”[A=1] op {vC}, kind@BBBB”格式,解析得到BBBB=0X0003,C=D=E=F=0,因为BBBB属于kind@类型,查method_ids表,得Java.lang.Object.
所以第一条指令是invoke-direct {v0}, Ljava/lang/Object;->
指令最后的0E00,先根据0E查“字节码格式”表,得到其opcode为return-void,如下结果。
再根据上图中10X,查“说明格式”表,如下所示。
所以指令指令最后的0E00,对应的是“return-void”
下面解析第二个method方法,如下是指令截图,指令内容为“6200 0000 1A01 0100 6E20 0200 1000 0E00”,
根据下图可以解析“6200 0000”,查询field_ids,得到汇编为sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
根据下图可以解析“1A01 0100”,查询string_ids,得到const-string v1, "Hello, world!"
根据下图可以解析“6E20 0200 1000”,其中A=2,BBBB=2, F=E=C=0 D=1,查询module_ids表,得到invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
最后一个“0E00”指令,前面已经分析过,解析得到return-void。
下面贴出Hello.java对应的Smali反汇编结果,证明了我们的分析是正确的。
为了贴近实战,我们分析一个实际的Android APK,众所周知,Android Studio创建新的工程,自动生成默认输出“Hello World!”的代码,其MainActivity.java源码如下,
1 | 将其编译成的APK转成对应的DEX文件,利用010Editor进行分析,如下所示。 |
1 |
1 |
我们只分析MainActivity.java中的类,该类包含一个虚拟方法,一个直接方法,我们分析指令最多的虚拟方法,我们只分析最核心的指令部分,其对应的指令集是“6F20 620D 2100 1400 1C00 097F 6E20 633C 0100 0E00”。
下面是指令“6F20 620D 2100”解析过程,其中A=2,BBBB=0X0D62=3426,F=E=0,D=2,C=1。
1 |
1 | 查询method_ids表,如下 |
最终得到,
invole-super {v1,v2}
Landroidx/appcompat/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V
下面是指令“1400 1C00 097F”解析过程,其中AA=0,BBBBBBBB=0X7F09001C,
最终得到const v0, 0x7f09001c
下面是指令“6E20 633C 0100”解析过程,A=2,BBBB=0X3C63=15459,F=E=0,D=0,C=1,
最终得到invoke-virtual {v1, v0}, Lcom/example/prj/MainActivity;->setContentView(I)V
最后贴下MainActivity类的Smali代码,论证了上面分析的正确性。