mirror of
https://github.com/nganhkhoa/CTF-All-In-One.git
synced 2024-12-25 11:41:16 +07:00
516 lines
19 KiB
Markdown
516 lines
19 KiB
Markdown
# 1.7.2 Dalvik 指令集
|
||
|
||
- [Dalvik 虚拟机](#dalvik-虚拟机)
|
||
- [Dalvik 指令集](#dalvik-指令集)
|
||
- [指令格式](#指令格式)
|
||
- [寄存器](#寄存器)
|
||
- [类型、方法和字段](#类型方法和字段)
|
||
- [空操作指令](#空操作指令)
|
||
- [数据操作指令](#数据操作指令)
|
||
- [返回指令](#返回指令)
|
||
- [数据定义指令](#数据定义指令)
|
||
- [锁指令](#锁指令)
|
||
- [实例操作指令](#实例操作指令)
|
||
- [数组操作指令](#数组操作指令)
|
||
- [异常指令](#异常指令)
|
||
- [跳转指令](#跳转指令)
|
||
- [比较指令](#比较指令)
|
||
- [字段操作指令](#字段操作指令)
|
||
- [方法调用指令](#方法调用指令)
|
||
- [数据转换指令](#数据转换指令)
|
||
- [数据运算指令](#数据运算指令)
|
||
- [smali 语法](#smali-语法)
|
||
- [循环语句](#循环语句)
|
||
- [switch 语句](#switch-语句)
|
||
- [try-catch 语句](#trycatch-语句)
|
||
- [更多资料](#更多资料)
|
||
|
||
|
||
## Dalvik 虚拟机
|
||
Android 程序运行在 Dalvik 虚拟机中,它与传统的 Java 虚拟机不同,完全基于寄存器架构,数据通过直接通过寄存器传递,大大提高了效率。Dalvik 虚拟机属于 Android 运行时环境,它与一些核心库共同承担 Android 应用程序的运行工作。Dalvik 虚拟机有自己的指令集,即 smali 代码,下面会详细介绍它们。
|
||
|
||
|
||
## Dalvik 指令集
|
||
#### 指令格式
|
||
Dalvik 指令语法由指令的**位描述**与指令**格式标识**来决定。
|
||
|
||
位描述约定如下:
|
||
- 每 16 位使用空格分隔。
|
||
- 每个字母占 4 位,按照顺序从高字节到低字节排列。
|
||
- 顺序采用 A~Z 的单个大写字母作为一个 4 位的操作码,op 表示一个 8 位的操作码。
|
||
- ”∅“来表示这字段所有位为0值。
|
||
|
||
指令格式约定如下:
|
||
- 指令格式标识大多由三个字符组成,前两个是数字,最后一个是字母。
|
||
- 第一个数字表示指令有多少个 16 位的字组成。
|
||
- 第二个数字表示指令最多使用寄存器的个数。
|
||
- 第三个字母为类型码,表示指令用到的额外数据的类型。
|
||
|
||
#### 寄存器
|
||
Dalvik 寄存器都是 32 位的,如果是 64 位的数据,则使用相邻的两个寄存器来表示。
|
||
|
||
寄存器有两种命名法:v 命名法和 p 命名法。如果一个函数使用到 M 个寄存器,其中有 N 个参数,那么参数会使用最后的 N 个寄存器,而局部变量使用从 v0 开始的前 M-N 个寄存器。在 v 命名法中,不管寄存器中是参数还是局部变量,都以 v 开头。而 p 命名法中,参数命名从 p0 开始,依次递增,在代码比较复杂的时候,使用 p 命名法可以清楚地区分开参数和局部变量,大多数工具使用的也是 p 命名法。
|
||
|
||
#### 类型、方法和字段
|
||
Dalvik 字节码只有基本类型和引用类型两种。除了对象类型和数组类型是引用类型外,其余的都是基本类型:
|
||
|
||
|语法 | 含义 |
|
||
| --- | --- |
|
||
| V | void |
|
||
| Z | boolean |
|
||
| B | byte |
|
||
| S | short |
|
||
| C | char |
|
||
| I | int |
|
||
| J | long |
|
||
| F | float |
|
||
| D | double |
|
||
| L | 对象类型 |
|
||
| [ | 数组类型 |
|
||
|
||
- 对象类型格式是 `L<包名>/<类名>;`,如 String 表示为 `Ljava/lang/String;`。
|
||
- 数组类型格式是 `[` 加上类型,如 `int[]` 表示为 `[I`,`int[][]` 表示为 `[[I`。
|
||
|
||
Dalvik 使用方法名、类型参数和返回值来描述一个方法。方法格式如下:
|
||
```
|
||
Lpackage/name/ObjectName;->MethodName(III)Z
|
||
```
|
||
例如把下面的 Java 代码转换成 smali:
|
||
```
|
||
# Java
|
||
String method(int, int [][], int, String, Object[])
|
||
|
||
# smali
|
||
.method method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
|
||
.end method
|
||
```
|
||
|
||
字段格式如下:
|
||
```
|
||
Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;
|
||
```
|
||
|
||
#### 空操作指令
|
||
空操作指令的助记符为 `nop`,值为 00,通常用于对齐代码。
|
||
|
||
#### 数据操作指令
|
||
数据操作指令为 `move`,原型为 `move destination, source`。
|
||
- `move vA, vB`:vB -> vA,都是 4 位
|
||
- `move/from16 vAA, vBBBB`:vBBBB -> vAA,源寄存器 16 位,目的寄存器 8 位
|
||
- `move/16 vAAAA, vBBBB`:vBBBB -> vAAAA,都是 16 位
|
||
- `move-wide vA, vB`:4 位的寄存器对赋值,都是 4 位
|
||
- `move-wide/from16vAA, vBBBB`、`move-wide/16 vAAAA, vBBBB`:与 move-wide 相同
|
||
- `move-object vA, vB`:对象赋值,都是 4 位
|
||
- `move-object/from16 vAA, vBBBB`:对象赋值,源寄存器 16 位,目的寄存器 8 位
|
||
- `move-object/16 vAAAA, vBBBB`:对象赋值,都是 16 位
|
||
- `move-result vAA`:将上一个 invoke 类型指令操作的单字非对象结果赋值给 vAA 寄存器
|
||
- `move-result-wide vAA`:将上一个 invoke 类型指令操作的双字非对象结果赋值给 vAA 寄存器
|
||
- `move-result-object vAA`:将上一个 invoke 类型指令操作的对象结果赋值给 vAA 寄存器
|
||
- `move-exception vAA`:保存一个运行时发生的异常到 vAA 寄存器
|
||
|
||
#### 返回指令
|
||
基础字节码为 `return`。
|
||
- `return-void`:从一个 void 方法返回
|
||
- `return vAA`:返回一个 32 位非对象类型的值,返回值寄存器位 8 位的寄存器 vAA
|
||
- `return-wide vAA`:返回一个 64 位非对象类型的值,返回值寄存器为 8 位的 vAA
|
||
- `return-object vAA`:返回一个对象类型的值,返回值寄存器为 8 位的 vAA
|
||
|
||
#### 数据定义指令
|
||
基础字节码为 `const`。
|
||
- `const/4 vA, #+B`:将数值符号扩展为 32 位后赋值给寄存器 vA
|
||
- `const/16 vAA, #+BBBB`:将数值符号扩展为 32 位后赋值给寄存器 vAA
|
||
- `const vAA, #+BBBBBBBB`:将数值赋值给寄存器 vAA
|
||
- `const/high16 vAA, #+BBBB0000`:将数值右边零扩展为 32 位后赋值给寄存器 vAA
|
||
- `const-wide/16 vAA, #+BBBB`:将数值符号扩展为 64 位后赋值给寄存器 vAA
|
||
- `const-wide/32 vAA, #+BBBBBBBB`:将数值符号扩展为 64 位后赋值给寄存器 vAA
|
||
- `const-wide vAA, #+BBBBBBBBBBBBBBBB`:将数值赋给寄存器对 vAA
|
||
- `const-wide/high16 vAA, #+BBBB000000000000`:将数值右边零扩展为 64 位后赋值给寄存器对 vAA
|
||
- `const-string vAA, string@BBBB`:通过字符串索引构造一个字符串并赋值给寄存器 vAA
|
||
- `const-string/jumbo vAA, string@BBBBBBBB`:通过字符串索(较大)引构造一个字符串并赋值给寄存器 vAA
|
||
- `const-class vAA, type@BBBB`:通过类型索引获取一个类型引用并赋值给寄存器 vAA
|
||
- `const-class/jumbo vAAAA, type@BBBBBBBB`:通过给定的类型索引获取一个类引用并赋值给寄存器 vAAAA。这条指令占用两个字节,值为 0x00ff
|
||
|
||
#### 锁指令
|
||
用在多线程程序中对同一对象操作。
|
||
- `monitor-enter vAA`:为指定的对象获取锁
|
||
- `monitor-exit vAA`:释放指定的对象的锁
|
||
|
||
#### 实例操作指令
|
||
- `check-cast vAA, type@BBBB`
|
||
- `check-cast/jumbo vAAAA, type@BBBBBBBB`:将 vAA 寄存器中的对象引用转换成指定的类型,如果失败会抛出 ClassCastException 异常。如果类型 B 指定的是基本类型,对于非基本类型的 A 来说,运行始终会失败
|
||
- `instance-of vA, vB, type@CCCC`
|
||
- `instance-of vAAAA, vBBBB, type@CCCCCCCC`:判断 vB 寄存器中的对象引用是否可以转换成指定的类型,如果可以 vA 寄存器赋值为 1,否则 vA 寄存器赋值为 0
|
||
- `new-instance vAA, type@BBBB`
|
||
- `new-instance vAAAA, type@BBBBBBBB `:构造一个指定类型对象的新实例,并将对象引用赋值给 vAA 寄存器,类型符 type 指定的类型不能是数组类
|
||
|
||
#### 数组操作指令
|
||
- `array-length vA, vB`:获取vB寄存器中数组的长度并将值赋给vA寄存器。
|
||
- `new-array vA, vB, type@CCCC`
|
||
- `new-array/jumbo vAAAA, vBBBB, type@CCCCCCCC`:构造指定类型(type@CCCCCCCC)与大小(vBBBB)的数组,并将值赋给 vAAAA 寄存器
|
||
- `filled-new-array {vC, vD, vE, vF, vG}, type@BBBB`:构造指定类型(type@BBBB)和大小(vA)的数组并填充数组内容。vA 寄存器是隐含使用的,处理指定数组的大小外还指定了参数的个数,vC~vG 是使用的参数寄存器列表。
|
||
- `filled-new-array/range {vCCCC .. vNNNN}, type@BBBB`:同上,只是参数寄存器使用 range 字节码后缀指定了取值范围,vC 是第一个参数寄存器,N=A+C-1。
|
||
- `fill-array-data vAA, +BBBBBBBB`:用指定的数据来填充数组,vAA 寄存器为数组引用,引用必须为基础类型的数组,在指令后面紧跟一个数据表。
|
||
- `arrayop vAA, vBB, vCC`:对 vBB 寄存器指定的数组元素进行取值和赋值。vCC 寄存器指定数组元素索引,vAA 寄存器用来存放读取的或需要设置的数组元素的值。读取元素使用 aget 类指令,元素赋值使用 aput 类指令。
|
||
|
||
#### 异常指令
|
||
- `throw vAA`:抛出 vAA 寄存器中指定类型的异常
|
||
|
||
#### 跳转指令
|
||
有三种跳转指令:无条件跳转(goto)、分支跳转(switch)和条件跳转(if)。
|
||
- `goto +AA`
|
||
- `goto/16 +AAAA`
|
||
- `goto/32 +AAAAAAAA`:无条件跳转到指定偏移处,不能为 0
|
||
- `packed-switch vAA, +BBBBBBBB`:分支跳转指令。vAA 寄存器为 switch 分支中需要判断的值,BBBBBBBB 指向一个 packed-switch-payload 格式的偏移表,表中的值是有规律递增的
|
||
- `sparse-switch vAA, +BBBBBBBB`:分支跳转指令。vAA 寄存器为 switch 分支中需要判断的值,BBBBBBBB 指向一个 `sparse-switch-payload` 格式的偏移表,表中的值是无规律的偏移量
|
||
- `if-test vA, vB, +CCCC`:条件跳转指令。比较 vA 寄存器与 vB 寄存器的值,如果比较结果满足就跳转到 CCCC 指定的偏移处,CCCC 不能为 0。`if-test` 类型的指令有:
|
||
- `if-eq`:if(vA==vB)
|
||
- `if-ne`:if(vA!=vB)
|
||
- `if-lt`:if(vA<vB)
|
||
- `if-ge`:if(vA>=vB)
|
||
- `if-gt`:if(vA>vB)
|
||
- `if-le`:if(vA<=vB)
|
||
- `if-testz vAA, +BBBB`:条件跳转指令。拿 vAA 寄存器与 0 比较,如果比较结果满足或值为 0 就跳转到 BBBB 指定的偏移处,BBBB 不能为 0。`if-testz` 类型的指令有:
|
||
- `if-eqz`:if(!vAA)
|
||
- `if-nez`:if(vAA)
|
||
- `if-ltz`:if(vAA<0)
|
||
- `if-gez`:if(vAA>=0)
|
||
- `if-gtz`:if(vAA>0)
|
||
- `if-lez`:if(vAA<=0)
|
||
|
||
#### 比较指令
|
||
对两个寄存器的值进行比较,格式为 cmpkind vAA, vBB, vCC,其中 vBB 和 vCC 寄存器是需要比较的两个寄存器或两个寄存器对,比较的结果放到 vAA 寄存器。指令集中共有5条比较指令:
|
||
- `cmpl-float`
|
||
- `cmpl-double`:如果 vBB 寄存器大于 vCC 寄存器,结果为 -1,相等结果为 0,小于结果为 1
|
||
- `cmpg-float`
|
||
- `cmpg-double`:如果 vBB 寄存器大于 vCC 寄存器,结果为 1,相等结果为 0,小于结果为 -1
|
||
- `cmp-long`:如果 vBB 寄存器大于 vCC 寄存器,结果为 1,相等结果为 0,小于结果为 -1
|
||
|
||
#### 字段操作指令
|
||
用于对对象实例的字段进行读写操作。对普通字段与静态字段操作有两种指令集,分别是 `iinstanceop vA, vB, field@CCCC` 与 `sstaticop vAA, field@BBBB`。扩展为 `iinstanceop/jumbo vAAAA, vBBBB, field@CCCCCCC` 与 `sstaticop/jumbo vAAAA, field@BBBBBBBB`。
|
||
|
||
普通字段指令的指令前缀为 `i`,静态字段的指令前缀为 `s`。字段操作指令后紧跟字段类型的后缀。
|
||
|
||
#### 方法调用指令
|
||
用于调用类实例的方法,基础指令为 `invoke`,有 `invoke-kind {vC, vD, vE, vF, vG}, meth@BBBB` 和 `invoke-kind/range {vCCCC .. vNNNN}, meth@BBBB` 两类。扩展为 `invoke-kind/jumbo {vCCCC .. vNNNN}, meth@BBBBBBBB` 这类指令。
|
||
|
||
根据方法类型的不同,共有如下五条方法调用指令:
|
||
- `invoke-virtual` 或 `invoke-virtual/range`:调用实例的虚方法
|
||
- `invoke-super` 或 `invoke-super/range`:调用实例的父类方法
|
||
- `invoke-direct` 或 `invoke-direct/range`:调用实例的直接方法
|
||
- `invoke-static` 或 `invoke-static/range`:调用实例的静态方法
|
||
- `invoke-interface` 或 `invoke-interface/range`:调用实例的接口方法
|
||
|
||
方法调用的返回值必须使用 `move-result*` 指令来获取,如:
|
||
```
|
||
invoke-static {}, Landroid/os/Parcel;->obtain()Landroid/os/Parcel;
|
||
move-result-object v0
|
||
```
|
||
|
||
#### 数据转换指令
|
||
格式为 `unop vA, vB`,vB 寄存器或vB寄存器对存放需要转换的数据,转换后结果保存在 vA 寄存器或 vA寄存器对中。
|
||
- 求补
|
||
- `neg-int`
|
||
- `neg-long`
|
||
- `neg-float`
|
||
- `neg-double`
|
||
- 求反
|
||
- `not-int`
|
||
- `not-long`
|
||
- 整型数转换
|
||
- `int-to-long`
|
||
- `int-to-float`
|
||
- `int-to-double`
|
||
- 长整型数转换
|
||
- `long-to-int`
|
||
- `long-to-float`
|
||
- `long-to-double`
|
||
- 单精度浮点数转换
|
||
- `float-to-int`
|
||
- `float-to-long`
|
||
- `float-to-double`
|
||
- 双精度浮点数转换
|
||
- `double-to-int`
|
||
- `double-to-long`
|
||
- `double-to-float`
|
||
- 整型转换
|
||
- `int-to-byte`
|
||
- `int-to-char`
|
||
- `int-to-short`
|
||
|
||
#### 数据运算指令
|
||
包括算术运算符与逻辑运算指令。
|
||
|
||
数据运算指令有如下四类:
|
||
- `binop vAA, vBB, vCC`:将 vBB 寄存器与 vCC 寄存器进行运算,结果保存到 vAA 寄存器。以下类似
|
||
- `binop/2addr vA, vB`
|
||
- `binop/lit16 vA, vB, #+CCCC`
|
||
- `binop/lit8 vAA, vBB, #+CC`
|
||
|
||
第一类指令可归类为:
|
||
- `add-type`:vBB + vCC
|
||
- `sub-type`:vBB - vCC
|
||
- `mul-type`:vBB * vCC
|
||
- `div-type`:vBB / vCC
|
||
- `rem-type`:vBB % vCC
|
||
- `and-type`:vBB AND vCC
|
||
- `or-type`:vBB OR vCC
|
||
- `xor-type`:vBB XOR vCC
|
||
- `shl-type`:vBB << vCC
|
||
- `shr-type`:vBB >> vCC
|
||
- `ushr-type`:(无符号数)vBB >> vCC
|
||
|
||
|
||
## smali 语法
|
||
类声明:
|
||
```
|
||
.class <访问权限> [修饰关键字] <类名>
|
||
.super <父类名>
|
||
.source <源文件名>
|
||
```
|
||
|
||
字段声明:
|
||
```
|
||
# static fields
|
||
.field <访问权限> static [修饰关键字] <字段名>:<字段类型>
|
||
|
||
# instance fields
|
||
.field <访问权限> [修饰关键字] <字段名>:<字段类型>
|
||
```
|
||
|
||
方法声明:
|
||
```
|
||
# direct methods
|
||
.method <访问权限> [修饰关键字] <方法原型>
|
||
[.locals]
|
||
[.param]
|
||
[.prologue]
|
||
[.line]
|
||
<代码体>
|
||
.end method
|
||
|
||
# virtual methods
|
||
.method <访问权限> [修饰关键字] <方法原型>
|
||
[.locals]
|
||
[.param]
|
||
[.prologue]
|
||
[.line]
|
||
<代码体>
|
||
.end method
|
||
```
|
||
需要注意的是,在一些老教程中,会看到 `.parameter`,表示使用的寄存器个数,但在最新的语法中已经不存在了,取而代之的是 `.param`,表示方法参数。
|
||
|
||
接口声明:
|
||
```
|
||
# interfaces
|
||
.implements <接口名>
|
||
```
|
||
|
||
注释声明:
|
||
```
|
||
# annotations
|
||
.annotation [注释属性] <注释类名>
|
||
[注释字段 = 值]
|
||
.end annotation
|
||
```
|
||
|
||
#### 循环语句
|
||
```
|
||
# for
|
||
Iterator<对象> <对象名> = <方法返回一个对象列表>;
|
||
for(<对象> <对象名>:<对象列表>){
|
||
[处理单个对象的代码体]
|
||
}
|
||
|
||
# while
|
||
Iterator<对象> <迭代器> = <方法返回一个迭代器>;
|
||
while(<迭代器>.hasNext()){
|
||
<对象> <对象名> = <迭代器>.next();
|
||
[处理单个对象的代码体]
|
||
}
|
||
```
|
||
比如下面的 Java 代码:
|
||
```Java
|
||
public void encrypt(String str) {
|
||
String ans = "";
|
||
for (int i = 0 ; i < str.length(); i++){
|
||
ans += str.charAt(i);
|
||
}
|
||
Log.e("ans:", ans);
|
||
}
|
||
```
|
||
对应下面的 smali:
|
||
```
|
||
# public void encrypt(String str) {
|
||
.method public encrypt(Ljava/lang/String;)V
|
||
.locals 4
|
||
.parameter p1, "str" # Ljava/lang/String;
|
||
.prologue
|
||
|
||
# String ans = "";
|
||
const-string v0, ""
|
||
.local v0, "ans":Ljava/lang/String;
|
||
|
||
# for (int i 0 ; i < str.length(); i++){
|
||
# int i=0 =>v1
|
||
const/4 v1, 0x0
|
||
.local v1, "i":I
|
||
:goto_0 # for_start_place
|
||
|
||
# str.length()=>v2
|
||
invoke-virtual {p1}, Ljava/lang/String;->length()I
|
||
move-result v2
|
||
|
||
# i<str.length()
|
||
if-ge v1, v2, :cond_0
|
||
|
||
# ans += str.charAt(i);
|
||
# str.charAt(i) => v2
|
||
new-instance v2, Ljava/lang/StringBuilder;
|
||
invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
|
||
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||
move-result-object v2
|
||
|
||
#str.charAt(i) => v3
|
||
invoke-virtual {p1, v1}, Ljava/lang/String;->charAt(I)C
|
||
move-result v3
|
||
|
||
# ans += v3 =>v0
|
||
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;
|
||
move-result-object v2
|
||
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||
move-result-object v0
|
||
|
||
# i++
|
||
add-int/lit8 v1, v1, 0x1
|
||
goto :goto_0
|
||
|
||
# Log.e("ans:", ans);
|
||
:cond_0
|
||
const-string v2, "ans:"
|
||
invoke-static {v2, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
|
||
return-void
|
||
.end method
|
||
```
|
||
|
||
#### switch 语句
|
||
```Java
|
||
public void encrypt(int flag) {
|
||
String ans = null;
|
||
switch (flag){
|
||
case 0:
|
||
ans = "ans is 0";
|
||
break;
|
||
default:
|
||
ans = "noans";
|
||
break;
|
||
}
|
||
Log.v("ans:", ans);
|
||
}
|
||
```
|
||
对应下面的 smali:
|
||
```
|
||
# public void encrypt(int flag) {
|
||
.method public encrypt(I)V
|
||
.locals 2
|
||
.param p1, "flag" # I
|
||
.prologue
|
||
|
||
# String ans = null;
|
||
const/4 v0, 0x0
|
||
.local v0, "ans":Ljava/lang/String;
|
||
|
||
# switch (flag){
|
||
packed-switch p1, :pswitch_data_0 # pswitch_data_0指定case区域的开头及结尾
|
||
|
||
# default: ans="noans"
|
||
const-string v0, "noans"
|
||
|
||
# Log.v("ans:", ans)
|
||
:goto_0
|
||
const-string v1, "ans:"
|
||
invoke-static {v1, v0}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
|
||
return-void
|
||
|
||
# case 0: ans="ans is 0"
|
||
:pswitch_0 # pswitch_<case的值>
|
||
const-string v0, "ans is 0"
|
||
goto :goto_0 # break
|
||
nop
|
||
:pswitch_data_0 #case区域的结束
|
||
.packed-switch 0x0 # 定义case的情况
|
||
:pswitch_0 #case 0
|
||
.end packed-switch
|
||
.end method
|
||
```
|
||
根据 switch 语句的不同,case 也有两种方式:
|
||
```
|
||
# packed-switch
|
||
packed-switch p1, :pswitch_data_0
|
||
...
|
||
:pswitch_data_0
|
||
.packed-switch 0x0
|
||
:pswitch_0
|
||
:pswitch_1
|
||
|
||
# spase-switch
|
||
sparse-switch p1,:sswitch_data_0
|
||
...
|
||
sswitch_data_0
|
||
.sparse-switch
|
||
0xa -> : sswitch_0
|
||
0xb -> : sswitch_1 # 字符会转化成数组
|
||
```
|
||
|
||
#### try-catch 语句
|
||
```Java
|
||
public void encrypt(int flag) {
|
||
String ans = null;
|
||
try {
|
||
ans = "ok!";
|
||
} catch (Exception e){
|
||
ans = e.toString();
|
||
}
|
||
Log.d("error", ans);
|
||
}
|
||
```
|
||
对应的下面的 smali:
|
||
```
|
||
# public void encrypt(int flag) {
|
||
.method public encrypt(I)V
|
||
.locals 3
|
||
.param p1, "flag" # I
|
||
.prologue
|
||
|
||
# String ans = null;
|
||
const/4 v0, 0x0
|
||
.line 20
|
||
.local v0, "ans":Ljava/lang/String;
|
||
|
||
# try { ans="ok!"; }
|
||
:try_start_0 # 第一个try开始,
|
||
const-string v0, "ok!"
|
||
:try_end_0 # 第一个try结束(主要是可能有多个try)
|
||
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
|
||
|
||
# Log.d("error", ans);
|
||
:goto_0
|
||
const-string v2, "error"
|
||
invoke-static {v2, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
|
||
return-void
|
||
|
||
# catch (Exception e){ans = e.toString();}
|
||
:catch_0 #第一个catch
|
||
move-exception v1
|
||
.local v1, "e":Ljava/lang/Exception;
|
||
invoke-virtual {v1}, Ljava/lang/Exception;->toString()Ljava/lang/String;
|
||
move-result-object v0
|
||
goto :goto_0
|
||
.end method
|
||
```
|
||
|
||
|
||
## 更多资料
|
||
- 《Android软件安全与逆向分析》
|
||
- [Dalvik opcodes](http://www.blogjava.net/midea0978/archive/2012/01/04/367847.html)
|
||
- [android逆向分析之smali语法](http://lib.csdn.net/article/android/7043)
|