diff --git a/doc/1.7.2_dalvik.md b/doc/1.7.2_dalvik.md index 21f331d..e392081 100644 --- a/doc/1.7.2_dalvik.md +++ b/doc/1.7.2_dalvik.md @@ -19,10 +19,15 @@ - [方法调用指令](#方法调用指令) - [数据转换指令](#数据转换指令) - [数据运算指令](#数据运算指令) +- [smali 语法](#smali-语法) + - [循环语句](#循环语句) + - [switch 语句](#switch-语句) + - [try-catch 语句](#trycatch-语句) +- [更多资料](#更多资料) ## Dalvik 虚拟机 -Android 程序运行在 Dalvik 虚拟机中,它与传统的 Java 虚拟机不同,完全基于寄存器架构,数据通过直接通过寄存器传递,大大提高了效率。Dalvik 虚拟机属于 Android 运行时环境,它与一些核心库共同承担 Android 应用程序的运行工作。Dalvik 虚拟机有自己的指令集,即 smali 代码,下面我们详细介绍它们。 +Android 程序运行在 Dalvik 虚拟机中,它与传统的 Java 虚拟机不同,完全基于寄存器架构,数据通过直接通过寄存器传递,大大提高了效率。Dalvik 虚拟机属于 Android 运行时环境,它与一些核心库共同承担 Android 应用程序的运行工作。Dalvik 虚拟机有自己的指令集,即 smali 代码,下面会详细介绍它们。 ## Dalvik 指令集 @@ -48,6 +53,7 @@ Dalvik 寄存器都是 32 位的,如果是 64 位的数据,则使用相邻 #### 类型、方法和字段 Dalvik 字节码只有基本类型和引用类型两种。除了对象类型和数组类型是引用类型外,其余的都是基本类型: + |语法 | 含义 | | --- | --- | | V | void | @@ -252,3 +258,258 @@ move-result-object v0 - `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 v2 +new-instance v2, Ljava/lang/StringBuilder; +invoke-direct {v2}, Ljava/lang/StringBuilder;->()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_ + 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)