smali语法详解
smali文件格式
每个smali文件都由若干条语句组成,所有的语句都遵循着一套语法规则。在smali 文件的头3 行描述了当前类的一些信息,格式如下:
- .class < 访问权限> [ 修饰关键字] < 类名>
- .super < 父类名>
- .source <源文件名>
打开MainActivity.smali 文件,头3 行代码如下:
- .class public Lcom/droider/crackme0502/MainActivity; //指令指定了当前类的类名。
- .super Landroid/app/Activity;<span style=“white-space:pre”> </span>//指令指定了当前类的父类。
- .source ”MainActivity.java”<span style=“white-space:pre”> </span>//指令指定了当前类的源文件名。
smali文件中字段的声明使用“.field”指令。字段有静态字段与实例字段两种。静态字段的声明格式如下:
- # static fields
- .field < 访问权限> static [ 修饰关键字] < 字段名>:< 字段类型>
实例字段的声明与静态字段类似,只是少了static关键字,它的格式如下:
- # instance fields
- .field < 访问权限> [ 修饰关键字] < 字段名>:< 字段类型>
- 比如以下的实例字段声明。
- # instance fields //baksmali 生成的注释
- .field private btnAnno:Landroid/widget/Button; //私有字段
smali 文件中方法的声明使用“.method ”指令,方法有直接方法与虚方法两种。
直接方法的声明格式如下:
- # direct methods //添加的注释
- .method <访问权限> [ 修饰关键字] < 方法原型>
- <.locals> //指定了使用的局部变量的个数
- [.parameter] //指定了方法的参数
- [.prologue] //指定了代码的开始处,混淆过的代码可能去掉了该指令
- [.line] //指定了该处指令在源代码中的行号
- <代码体>
- .end method
虚方法的声明与直接方法相同,只是起始处的注释为“virtual methods”,如果一个类实现了接口,会在smali 文件中使用“.implements ”指令指出,相应的格式声明如下:
- # interfaces
- .implements < 接口名> //接口关键字
- 如果一个类使用了注解,会在 smali 文件中使用“.annotation ”指令指出,注解的格式声明如下:
- # annotations
- .annotation [ 注解属性] < 注解类名>
- [ 注解字段 = 值]
- .end annotation
注解的作用范围可以是类、方法或字段。如果注解的作用范围是类,“.annotation ”指令会直接定义在smali 文件中,如果是方法或字段,“.annotation ”指令则会包含在方法或字段定义中。例如:
- # instance fields
- .field public sayWhat:Ljava/lang/String; //String 类型 它使用了 com.droider.anno.MyAnnoField 注解,注解字段info 值为“Hello my friend”
- .annotation runtime Lcom/droider/anno/MyAnnoField;
- info = ”Hello my friend”
- .end annotation
- .end field
Android 程序中的类
1、内部类
Java 语言允许在一个类的内部定义另一个类,这种在类中定义的类被称为内部类(Inner Class)。内部类可分为成员内部类、静态嵌套类、方法内部类、匿名内部类。在反编译dex 文件的时候,会为每个类单独生成了一个 smali 文件,内部类作为一个独立的类,它也拥有自己独立的smali 文件,只是内部类的文件名形式为“[外部类]$[内部类].smali ”,例如:
- class Outer {
- class Inner{}
- }
反编译上述代码后会生成两个文件:Outer.smali 与Outer$Inner.smali。打开文件,代码结构如下:
- .class public Lcom/droider/crackme0502/MainActivitySNChecker;&nbsp;&nbsp;</span></span></li><li><span>.<span class="keyword">super</span><span>&nbsp;Ljava/lang/Object;&nbsp;&nbsp;</span></span></li><li class="alt"><span>.source&nbsp;<span class="string">"MainActivity.java"</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;</span></li><li class="alt"><span>#&nbsp;annotations&nbsp;&nbsp;</span></li><li><span>.annotation&nbsp;system&nbsp;Ldalvik/annotation/EnclosingClass;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;value&nbsp;=&nbsp;Lcom/droider/crackme0502/MainActivity;&nbsp;&nbsp;</span></li><li><span>.end&nbsp;annotation&nbsp;&nbsp;</span></li><li class="alt"><span>.annotation&nbsp;system&nbsp;Ldalvik/annotation/InnerClass;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;accessFlags&nbsp;=&nbsp;<span class="number">0x1</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;=&nbsp;<span class="string">"SNChecker"</span><span>&nbsp;&nbsp;</span></span></li><li><span>.end&nbsp;annotation&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;</span></li><li><span>#&nbsp;instance&nbsp;fields&nbsp;&nbsp;</span></li><li class="alt"><span>.field&nbsp;<span class="keyword">private</span><span>&nbsp;sn:Ljava/lang/String;&nbsp;&nbsp;</span></span></li><li><span>.field&nbsp;<span class="keyword">final</span><span>&nbsp;synthetic&nbsp;</span><span class="keyword">this</span><span>" role="presentation">SNChecker; </span></span></li><li><span>.<span class="keyword">super</span><span> Ljava/lang/Object; </span></span></li><li class="alt"><span>.source <span class="string">"MainActivity.java"</span><span> </span></span></li><li><span> </span></li><li class="alt"><span># annotations </span></li><li><span>.annotation system Ldalvik/annotation/EnclosingClass; </span></li><li class="alt"><span> value = Lcom/droider/crackme0502/MainActivity; </span></li><li><span>.end annotation </span></li><li class="alt"><span>.annotation system Ldalvik/annotation/InnerClass; </span></li><li><span> accessFlags = <span class="number">0x1</span><span> </span></span></li><li class="alt"><span> name = <span class="string">"SNChecker"</span><span> </span></span></li><li><span>.end annotation </span></li><li class="alt"><span> </span></li><li><span># instance fields </span></li><li class="alt"><span>.field <span class="keyword">private</span><span> sn:Ljava/lang/String; </span></span></li><li><span>.field <span class="keyword">final</span><span> synthetic </span><span class="keyword">this</span><span>SNChecker; </span></span></li><li><span>.<span class="keyword">super</span><span> Ljava/lang/Object; </span></span></li><li class="alt"><span>.source <span class="string">"MainActivity.java"</span><span> </span></span></li><li><span> </span></li><li class="alt"><span># annotations </span></li><li><span>.annotation system Ldalvik/annotation/EnclosingClass; </span></li><li class="alt"><span> value = Lcom/droider/crackme0502/MainActivity; </span></li><li><span>.end annotation </span></li><li class="alt"><span>.annotation system Ldalvik/annotation/InnerClass; </span></li><li><span> accessFlags = <span class="number">0x1</span><span> </span></span></li><li class="alt"><span> name = <span class="string">"SNChecker"</span><span> </span></span></li><li><span>.end annotation </span></li><li class="alt"><span> </span></li><li><span># instance fields </span></li><li class="alt"><span>.field <span class="keyword">private</span><span> sn:Ljava/lang/String; </span></span></li><li><span>.field <span class="keyword">final</span><span> synthetic </span><span class="keyword">this</span><span>0:Lcom/droider/crackme0502/MainActivity;
- # direct methods
- .method public constructor
- <init>(Lcom/droider/crackme0502/MainActivity;Ljava/lang/String;)V
- ……
- .end method
- # virtual methods
- .method public isRegistered()Z
- ……
- .end method
发现它有两个注解定义块“Ldalvik/annotation/EnclosingClass;”与“Ldalvik/annotation/ InnerClass; ”、两个实例字段sn 与this0、一个直接方法init()、一个虚方法isRegistered()。this" role="presentation">0、一个直接方法init()、一个虚方法isRegistered()。this0、一个直接方法init()、一个虚方法isRegistered()。this0 是内部类自动保留的一个指向所在外部类的引用。左边的 this 表示为父类的引用,右边的数值0 表示引用的层数。
2、监听器
Android程序开发中大量使用到了监听器,如Button的点击事件响应OnClickListener、Button的长按事件响应OnLongClickListener、ListView列表项的点击事件响应 OnItemSelected-Listener等。
实例源码以及反编译设置按钮点击事件监听器的代码如下:
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- btnAnno = (Button) findViewById(R.id.btn_annotation);
- btnCheckSN = (Button) findViewById(R.id.btn_checksn);
- edtSN = (EditText) findViewById(R.id.edt_sn);
- btnAnno.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- getAnnotations();
- }
- });
- btnCheckSN.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- SNChecker checker = new SNChecker(edtSN.getText().toString());
- String str = checker.isRegistered() ? ”注册码正确” : “注册码错误”;
- Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
- }
- });
- }
- 反编译如下:
- .method public onCreate(Landroid/os/Bundle;)V
- .locals 2
- .parameter ”savedInstanceState”
- ……
- .line 32
- iget-object v0, p0, Lcom/droider/crackme0502/MainActivity;->btnAnno:
- Landroid/widget/Button;
- new-instance v1, Lcom/droider/crackme0502/MainActivity</span><span class="number">1</span><span>;&nbsp;#新建一个&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;MainActivity" role="presentation"></span><span class="number">1</span><span>; #新建一个 </span></span></li><li><span> MainActivity</span><span class="number">1</span><span>; #新建一个 </span></span></li><li><span> MainActivity1实例
- invoke-direct {v1, p0}, Lcom/droider/crackme0502/MainActivity<span class="number">1</span><span>;&nbsp;&nbsp;</span></span></li><li><span>-&gt;&lt;init&gt;(Lcom/droider/crackme0502/MainActivity;)V&nbsp;#&nbsp;初始化MainActivity" role="presentation"><span class="number">1</span><span>; </span></span></li><li><span>-><init>(Lcom/droider/crackme0502/MainActivity;)V # 初始化MainActivity<span class="number">1</span><span>; </span></span></li><li><span>-><init>(Lcom/droider/crackme0502/MainActivity;)V # 初始化MainActivity1
- 实例
- invoke-virtual {v0, v1}, Landroid/widget/Button;
- ->setOnClickListener(Landroid/view/ViewOnClickListener;)V&nbsp;#&nbsp;设置按钮点击事件&nbsp;&nbsp;</span></li><li><span>监听器&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;.line&nbsp;<span class="number">40</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;iget-object&nbsp;v0,&nbsp;p0,&nbsp;Lcom/droider/crackme0502/MainActivity;&nbsp;&nbsp;</span></li><li class="alt"><span>-&gt;btnCheckSN:Landroid/widget/Button;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">new</span><span>-instance&nbsp;v1,&nbsp;Lcom/droider/crackme0502/MainActivity" role="presentation">OnClickListener;)V # 设置按钮点击事件 </span></li><li><span>监听器 </span></li><li class="alt"><span> .line <span class="number">40</span><span> </span></span></li><li><span> iget-object v0, p0, Lcom/droider/crackme0502/MainActivity; </span></li><li class="alt"><span>->btnCheckSN:Landroid/widget/Button; </span></li><li><span> <span class="keyword">new</span><span>-instance v1, Lcom/droider/crackme0502/MainActivityOnClickListener;)V # 设置按钮点击事件 </span></li><li><span>监听器 </span></li><li class="alt"><span> .line <span class="number">40</span><span> </span></span></li><li><span> iget-object v0, p0, Lcom/droider/crackme0502/MainActivity; </span></li><li class="alt"><span>->btnCheckSN:Landroid/widget/Button; </span></li><li><span> <span class="keyword">new</span><span>-instance v1, Lcom/droider/crackme0502/MainActivity2; #新建一个
- MainActivity<span class="number">2</span><span>实例&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;invoke-direct&nbsp;{v1,&nbsp;p0},&nbsp;Lcom/droider/crackme0502/MainActivity" role="presentation"><span class="number">2</span><span>实例 </span></span></li><li><span> invoke-direct {v1, p0}, Lcom/droider/crackme0502/MainActivity<span class="number">2</span><span>实例 </span></span></li><li><span> invoke-direct {v1, p0}, Lcom/droider/crackme0502/MainActivity2
- -><init>(Lcom/droider/crackme0502/MainActivity;)V; # 初始化MainActivity<span class="number">2</span><span>实例&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;invoke-virtual&nbsp;{v0,&nbsp;v1},&nbsp;Landroid/widget/Button;&nbsp;&nbsp;</span></li><li class="alt"><span>-&gt;setOnClickListener(Landroid/view/View" role="presentation"><span class="number">2</span><span>实例 </span></span></li><li><span> invoke-virtual {v0, v1}, Landroid/widget/Button; </span></li><li class="alt"><span>->setOnClickListener(Landroid/view/View<span class="number">2</span><span>实例 </span></span></li><li><span> invoke-virtual {v0, v1}, Landroid/widget/Button; </span></li><li class="alt"><span>->setOnClickListener(Landroid/view/ViewOnClickListener;)V#设置按钮点击事件
- 监听器
- .line 50
- return-void
- .end method
在MainActivity$1.smali 文件的开头使用了“.implements ”指令指定该类实现了按钮点击事件的监听器接口,因此,这个类实现了它的OnClick()方法,这也是我们在分析程序时关心的地方。另外,程序中的注解与监听器的构造函数都是编译器为我们自己生成的,实际分析过程中不必关心。
3、注解类
注解是Java 的语言特性,在 Android的开发过程中也得到了广泛的使用。Android系统中涉及到注解的包共有两个:一个是dalvik.annotation;另一个是 android.annotation。
例如:
- # annotations
- .annotation system Ldalvik/annotation/AnnotationDefault;
- value = .subannotation Lcom/droider/anno/MyAnnoClass;
- value = ”MyAnnoClass”
- .end subannotation
- .end annotation
除了SuppressLint与TargetApi注解,android.annotation 包还提供了SdkConstant与Widget两个注解,这两个注解在注释中被标记为“@hide”,即在 SDK 中是不可见的。SdkConstant注解指定了SDK中可以被导出的常量字段值,Widget 注解指定了哪些类是 UI类,这两个注解在分析Android程序时基本上碰不到,此处就不去探究了。
4、自动生成的类
使用 Android SDK 默认生成的工程会自动添加一些类。例如:
- public final class R {
- public static final class attr { //属性
- }
- public static final class dimen { //尺寸
- public static final int padding_large=0x7f040002;
- public static final int padding_medium=0x7f040001;
- public static final int padding_small=0x7f040000;
- }
- public static final class drawable { //图片
- public static final int ic_action_search=0x7f020000;
- public static final int ic_launcher=0x7f020001;
- }
- public static final class id { //id标识
- public static final int btn_annotation=0x7f080000;
- public static final int btn_checksn=0x7f080002;
- public static final int edt_sn=0x7f080001;
- public static final int menu_settings=0x7f080003;
- }
- public static final class layout { // 布局
- public static final int activity_main=0x7f030000;
- }
- public static final class menu { // 菜单
- public static final int activity_main=0x7f070000;
- }
- public static final class string { // 字符串
- public static final int app_name=0x7f050000;
- public static final int hello_world=0x7f050001;
- public static final int menu_settings=0x7f050002;
- public static final int title_activity_main=0x7f050003;
- }
- public static final class style { // 样式
- public static final int AppTheme=0x7f060000;
- }
- }
由于这些资源类都是R 类的内部类,因此它们都会独立生成一个类文件,在反编译出的代码中,可以发现有R.smali、Rattr.smali、R" role="presentation">attr.smali、Rattr.smali、Rdimen.smali、Rdrawable.smali、R" role="presentation">drawable.smali、Rdrawable.smali、Rid.smali、Rlayout.smali、R" role="presentation">layout.smali、Rlayout.smali、Rmenu.smali 、Rstring.smali、R" role="presentation">string.smali、Rstring.smali、Rstyle.smali 等几个文件。
阅读smali反编译的代码
smali 文件中的语句特点:
1、循环语句
在 Android开发过程中,常见的循环结构有迭代器循环、for 循环、while循环、do while 循环。我们在编写迭代器循环代码时,一般是如下形式的代码:
- Iterator< 对象> <对象名> = <方法返回一个对象列表>;
- for (< 对象> <对象名> : <对象列表>) {
- [处理单个对象的代码体]
- }
- 或者:
- Iterator< 对象> <迭代器> = <方法返回一个迭代器>;
- while (<迭代器>.hasNext()) {
- <对象> <对象名> = <迭代器>.next();
- [处理单个对象的代码体]
- }
- .method private iterator()V
- .locals 7
- .prologue
- .line 34
- const-string v4, “activity”
- invoke-virtual {p0, v4}, Lcom/droider/circulate/MainActivity;->
- getSystemService
- (Ljava/lang/String;)Ljava/lang/Object; # 获取ActivityManager
- move-result-object v0
- check-cast v0, Landroid/app/ActivityManager;
- .line 35
- .local v0, activityManager:Landroid/app/ActivityManager;
- invoke-virtual {v0}, Landroid/app/ActivityManager;->getRunningAppProcesses()
- Ljava/util/List;
- move-result-object v2 #正在运行的进程列表
- .line 36
- .local v2, psInfos:Ljava/util/List;,
- ”Ljava/util/List<Landroid/app/ActivityManagerRunningAppProcessInfo;&gt;;"</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">new</span><span>-instance&nbsp;v3,&nbsp;Ljava/lang/StringBuilder;&nbsp;&nbsp;#&nbsp;新建一个StringBuilder&nbsp;对象&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;invoke-direct&nbsp;{v3},&nbsp;Ljava/lang/StringBuilder;-&gt;&lt;init&gt;()V&nbsp;&nbsp;#&nbsp;调用&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;StringBuilder&nbsp;构造函数&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;.line&nbsp;<span class="number">37</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;.local&nbsp;v3,&nbsp;sb:Ljava/lang/StringBuilder;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;invoke-<span class="keyword">interface</span><span>&nbsp;{v2},&nbsp;Ljava/util/List;-&gt;iterator()Ljava/util/Iterator;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;#获取进程列表的迭代器&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;move-result-object&nbsp;v4&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;:goto_0&nbsp;#迭代循环开始&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;invoke-<span class="keyword">interface</span><span>&nbsp;{v4},&nbsp;Ljava/util/Iterator;-&gt;hasNext()Z&nbsp;#开始迭代&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;move-result&nbsp;v5&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span><span>-nez&nbsp;v5,&nbsp;:cond_0&nbsp;&nbsp;#&nbsp;如果迭代器不为空就跳走&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;.line&nbsp;<span class="number">40</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;invoke-virtual&nbsp;{v3},&nbsp;Ljava/lang/StringBuilder;-&gt;toString()Ljava/lang/&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;String;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;move-result-object&nbsp;v4&nbsp;&nbsp;#&nbsp;StringBuilder转为字符串&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">const</span><span>/</span><span class="number">4</span><span>&nbsp;v5,&nbsp;</span><span class="number">0x0</span><span>&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;invoke-<span class="keyword">static</span><span>&nbsp;{p0,&nbsp;v4,&nbsp;v5},&nbsp;Landroid/widget/Toast;-&gt;makeText&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;widget/Toast;&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;move-result-object&nbsp;v4&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;invoke-virtual&nbsp;{v4},&nbsp;Landroid/widget/Toast;-&gt;show()V&nbsp;#&nbsp;弹出StringBuilder&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;的内容&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;.line&nbsp;<span class="number">41</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">return</span><span>-</span><span class="keyword">void</span><span>&nbsp;&nbsp;#&nbsp;方法返回&nbsp;&nbsp;</span></span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;.line&nbsp;<span class="number">37</span><span>&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;:cond_0&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;invoke-<span class="keyword">interface</span><span>&nbsp;{v4},&nbsp;Ljava/util/Iterator;-&gt;next()Ljava/lang/Object;&nbsp;&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;#&nbsp;循环获取每一项&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;move-result-object&nbsp;v1&nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;check-cast&nbsp;v1,&nbsp;Landroid/app/ActivityManager" role="presentation">RunningAppProcessInfo;>;"</span><span> </span></span></li><li class="alt"><span> <span class="keyword">new</span><span>-instance v3, Ljava/lang/StringBuilder; # 新建一个StringBuilder 对象 </span></span></li><li><span> invoke-direct {v3}, Ljava/lang/StringBuilder;-><init>()V # 调用 </span></li><li class="alt"><span> StringBuilder 构造函数 </span></li><li><span> .line <span class="number">37</span><span> </span></span></li><li class="alt"><span> .local v3, sb:Ljava/lang/StringBuilder; </span></li><li><span> invoke-<span class="keyword">interface</span><span> {v2}, Ljava/util/List;->iterator()Ljava/util/Iterator; </span></span></li><li class="alt"><span> #获取进程列表的迭代器 </span></li><li><span> move-result-object v4 </span></li><li class="alt"><span> :goto_0 #迭代循环开始 </span></li><li><span> invoke-<span class="keyword">interface</span><span> {v4}, Ljava/util/Iterator;->hasNext()Z #开始迭代 </span></span></li><li class="alt"><span> move-result v5 </span></li><li><span> <span class="keyword">if</span><span>-nez v5, :cond_0 # 如果迭代器不为空就跳走 </span></span></li><li class="alt"><span> .line <span class="number">40</span><span> </span></span></li><li><span> invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/ </span></li><li class="alt"><span> String; </span></li><li><span> move-result-object v4 # StringBuilder转为字符串 </span></li><li class="alt"><span> <span class="keyword">const</span><span>/</span><span class="number">4</span><span> v5, </span><span class="number">0x0</span><span> </span></span></li><li><span> invoke-<span class="keyword">static</span><span> {p0, v4, v5}, Landroid/widget/Toast;->makeText </span></span></li><li class="alt"><span> (Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/ </span></li><li><span> widget/Toast; </span></li><li class="alt"><span> move-result-object v4 </span></li><li><span> invoke-virtual {v4}, Landroid/widget/Toast;->show()V # 弹出StringBuilder </span></li><li class="alt"><span> 的内容 </span></li><li><span> .line <span class="number">41</span><span> </span></span></li><li class="alt"><span> <span class="keyword">return</span><span>-</span><span class="keyword">void</span><span> # 方法返回 </span></span></li><li><span> .line <span class="number">37</span><span> </span></span></li><li class="alt"><span> :cond_0 </span></li><li><span> invoke-<span class="keyword">interface</span><span> {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object; </span></span></li><li class="alt"><span> # 循环获取每一项 </span></li><li><span> move-result-object v1 </span></li><li class="alt"><span> check-cast v1, Landroid/app/ActivityManagerRunningAppProcessInfo;>;"</span><span> </span></span></li><li class="alt"><span> <span class="keyword">new</span><span>-instance v3, Ljava/lang/StringBuilder; # 新建一个StringBuilder 对象 </span></span></li><li><span> invoke-direct {v3}, Ljava/lang/StringBuilder;-><init>()V # 调用 </span></li><li class="alt"><span> StringBuilder 构造函数 </span></li><li><span> .line <span class="number">37</span><span> </span></span></li><li class="alt"><span> .local v3, sb:Ljava/lang/StringBuilder; </span></li><li><span> invoke-<span class="keyword">interface</span><span> {v2}, Ljava/util/List;->iterator()Ljava/util/Iterator; </span></span></li><li class="alt"><span> #获取进程列表的迭代器 </span></li><li><span> move-result-object v4 </span></li><li class="alt"><span> :goto_0 #迭代循环开始 </span></li><li><span> invoke-<span class="keyword">interface</span><span> {v4}, Ljava/util/Iterator;->hasNext()Z #开始迭代 </span></span></li><li class="alt"><span> move-result v5 </span></li><li><span> <span class="keyword">if</span><span>-nez v5, :cond_0 # 如果迭代器不为空就跳走 </span></span></li><li class="alt"><span> .line <span class="number">40</span><span> </span></span></li><li><span> invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/ </span></li><li class="alt"><span> String; </span></li><li><span> move-result-object v4 # StringBuilder转为字符串 </span></li><li class="alt"><span> <span class="keyword">const</span><span>/</span><span class="number">4</span><span> v5, </span><span class="number">0x0</span><span> </span></span></li><li><span> invoke-<span class="keyword">static</span><span> {p0, v4, v5}, Landroid/widget/Toast;->makeText </span></span></li><li class="alt"><span> (Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/ </span></li><li><span> widget/Toast; </span></li><li class="alt"><span> move-result-object v4 </span></li><li><span> invoke-virtual {v4}, Landroid/widget/Toast;->show()V # 弹出StringBuilder </span></li><li class="alt"><span> 的内容 </span></li><li><span> .line <span class="number">41</span><span> </span></span></li><li class="alt"><span> <span class="keyword">return</span><span>-</span><span class="keyword">void</span><span> # 方法返回 </span></span></li><li><span> .line <span class="number">37</span><span> </span></span></li><li class="alt"><span> :cond_0 </span></li><li><span> invoke-<span class="keyword">interface</span><span> {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object; </span></span></li><li class="alt"><span> # 循环获取每一项 </span></li><li><span> move-result-object v1 </span></li><li class="alt"><span> check-cast v1, Landroid/app/ActivityManagerRunningAppProcessInfo;
- .line 38
- .local v1, info:Landroid/app/ActivityManagerRunningAppProcessInfo;&nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">new</span><span>-instance&nbsp;v5,&nbsp;Ljava/lang/StringBuilder;&nbsp;&nbsp;#&nbsp;新建一个临时的StringBuilder&nbsp;&nbsp;</span></span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;iget-object&nbsp;v6,&nbsp;v1,&nbsp;Landroid/app/ActivityManager" role="presentation">RunningAppProcessInfo; </span></li><li><span> <span class="keyword">new</span><span>-instance v5, Ljava/lang/StringBuilder; # 新建一个临时的StringBuilder </span></span></li><li class="alt"><span> iget-object v6, v1, Landroid/app/ActivityManagerRunningAppProcessInfo; </span></li><li><span> <span class="keyword">new</span><span>-instance v5, Ljava/lang/StringBuilder; # 新建一个临时的StringBuilder </span></span></li><li class="alt"><span> iget-object v6, v1, Landroid/app/ActivityManagerRunningAppProcessInfo;
- ->processName:Ljava/lang/String; #获取进程的进程名
- invoke-static {v6}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)
- Ljava/lang/String;
- move-result-object v6
- invoke-direct {v5, v6}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/
- String;)V
- const/16 v6, 0xa #换行符
- invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(C)Ljava/
- lang/StringBuilder;
- move-result-object v5 # 组合进程名与换行符
- invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/
- String;
- move-result-object v5
- invoke-virtual {v3, v5}, Ljava/lang/StringBuilder; # 将组合后的字符串添加到
- StringBuilder 末尾
- ->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
- goto :goto_0 #跳转到循环开始处
- .end method
这段代码的功能是获取正在运行的进程列表,然后使用Toast弹出所有的进程名。
forCirculate() 方法如下:
- .method private forCirculate()V
- .locals 8
- .prologue
- .line 47
- invoke-virtual {p0}, Lcom/droider/circulate/MainActivity;-
- >getApplicationContext()Landroid/content/Context;
- move-result-object v6
- invoke-virtual {v6}, Landroid/content/Context; #获取PackageManager
- ->getPackageManager()Landroid/content/pm/PackageManager;
- move-result-object v3
- .line 49
- .local v3, pm:Landroid/content/pm/PackageManager;
- const/16 v6, 0x2000
- .line 48
- invoke-virtual {v3, v6}, Landroid/content/pm/PackageManager;
- ->getInstalledApplications(I)Ljava/util/List; #获取已安装的程序列表
- move-result-object v0
- .line 50
- .local v0, appInfos:Ljava/util/List;,”Ljava/util/List<Landroid/content/pm
- /ApplicationInfo;>;”
- invoke-interface {v0}, Ljava/util/List;->size()I # 获取列表中ApplicationInfo
- 对象的个数
- move-result v5
- .line 51
- .local v5, size:I
- new-instance v4, Ljava/lang/StringBuilder; # 新建一个
- StringBuilder 对象
- invoke-direct {v4}, Ljava/lang/StringBuilder;-><init>()V # 调用
- StringBuilder 的构造函数
- .line 52
- .local v4, sb:Ljava/lang/StringBuilder;
- const/4 v1, 0x0
- .local v1, i:I #初始化v1为0
- :goto_0 #循环开始
- if-lt v1, v5, :cond_0 #如果v1小于v5,则跳转到cond_0 标号处
- .line 56
- invoke-virtual {v4}, Ljava/lang/StringBuilder;->toString()Ljava/
- lang/String;
- move-result-object v6
- const/4 v7, 0x0
- invoke-static {p0, v6, v7}, Landroid/widget/Toast; #构造Toast
- ->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)
- Landroid/widget/Toast;
- move-result-object v6
- invoke-virtual {v6}, Landroid/widget/Toast;->show()V #显示已安装的程序列表
- .line 57
- return-void # 方法返回
- .line 53
- :cond_0
- invoke-interface {v0, v1}, Ljava/util/List;->get(I)Ljava/lang/Object;
- # 单个ApplicationInfo
- move-result-object v2
- check-cast v2, Landroid/content/pm/ApplicationInfo;
- .line 54
- .local v2, info:Landroid/content/pm/ApplicationInfo;
- new-instance v6, Ljava/lang/StringBuilder; # 新建一个临时StringBuilder对象
- iget-object v7, v2, Landroid/content/pm/ApplicationInfo;->packageName:
- Ljava/lang/String;
- invoke-static {v7}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)
- Ljava/lang/String;
- move-result-object v7 # 包名
- invoke-direct {v6, v7}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/
- String;)V
- const/16 v7, 0xa #换行符
- invoke-virtual {v6, v7}, Ljava/lang/StringBuilder;->append(C)Ljava/
- lang/StringBuilder;
- move-result-object v6 # 组合包名与换行符
- invoke-virtual {v6}, Ljava/lang/StringBuilder;->toString()Ljava/lang
- /String; #转换为字符串
- move-result-object v6
- invoke-virtual {v4, v6}, Ljava/lang/StringBuilder;-
- >append(Ljava/lang/String;)Ljava/lang/StringBuilder; # 添加到循环外 的StringBuilder 中
- .line 52
- add-int/lit8 v1, v1, 0x1 #下一个索引
- goto :goto_0 #跳转到循环起始处
- .end method
这段代码的功能是获取所有安装的程序,然后使用Toast弹出所有的软件包名。
2、switch分支语句
packedSwitch()方法的代码如下:
- .method private packedSwitch(I)Ljava/lang/String;
- .locals 1
- .parameter ”i”
- .prologue
- .line 21
- const/4 v0, 0x0
- .line 22
- .local v0, str:Ljava/lang/String; #v0为字符串,0表示null
- packed-switch p1, :pswitch_data_0 #packed-switch分支,pswitch_data_0指
- 定case区域
- .line 36
- const-string v0, “she is a person” #default分支
- .line 39
- :goto_0 #所有case的出口
- return-object v0 #返回字符串v0
- .line 24
- :pswitch_0 #case 0
- const-string v0, “she is a baby”
- .line 25
- goto :goto_0 #跳转到goto_0标号处
- .line 27
- :pswitch_1 #case 1
- const-string v0, “she is a girl”
- .line 28
- goto :goto_0 #跳转到goto_0标号处
- .line 30
- :pswitch_2 #case 2
- const-string v0, “she is a woman”
- .line 31
- goto :goto_0 #跳转到goto_0标号处
- .line 33
- :pswitch_3 #case 3
- const-string v0, “she is an obasan”
- .line 34
- goto :goto_0 #跳转到goto_0标号处
- .line 22
- nop
- :pswitch_data_0
- .packed-switch 0x0 #case 区域,从0开始,依次递增
- :pswitch_0 #case 0
- :pswitch_1 #case 1
- :pswitch_2 #case 2
- :pswitch_3 #case 3
- .end packed-switch
- .end method
代码中的switch 分支使用的是 packed-switch 指令。p1为传递进来的 int 类型的数值,pswitch_data_0 为case 区域,在 case 区域中,第一条指令“.packed-switch”指定了比较的初始值为0 ,pswitch_0~ pswitch_3分别是比较结果为“case 0 ”到“case 3 ”时要跳转到的地址。
3、try/catch 语句
tryCatch()方法代码如下:
- .method private tryCatch(ILjava/lang/String;)V
- .locals 10
- .parameter ”drumsticks”
- .parameter ”peple”
- .prologue
- const/4 v9, 0x0
- .line 19
- :try_start_0 # 第1个try开始
- invoke-static {p2}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;)I
- #将第2个参数转换为int 型
- :try_end_0 # 第1个try结束
- .catch Ljava/lang/NumberFormatException; {:try_start_0 .. :try_end_0} :
- catch_1 # catch_1
- move-result v1 #如果出现异常这里不会执行,会跳转到catch_1标号处
- .line 21
- .local v1, i:I #.local声明的变量作用域在.local声明与.end local 之间
- :try_start_1 #第2个try 开始
- div-int v2, p1, v1 # 第1个参数除以第2个参数
- .line 22
- .local v2, m:I #m为商
- mul-int v5, v2, v1 #m * i
- sub-int v3, p1, v5 #v3 为余数
- .line 23
- .local v3, n:I
- const-string v5, ”\u5171\u6709%d\u53ea\u9e21\u817f\uff0c%d
- \u4e2a\u4eba\u5e73\u5206\uff0c\u6bcf\u4eba\u53ef\u5206\u5f97%d
- \u53ea\uff0c\u8fd8\u5269\u4e0b%d\u53ea” # 格式化字符串
- const/4 v6, 0x4
- new-array v6, v6, [Ljava/lang/Object;
- const/4 v7, 0x0
- .line 24
- invoke-static {p1}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
- move-result-object v8
- aput-object v8, v6, v7
- const/4 v7, 0x1
- invoke-static {v1}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
- move-result-object v8
- aput-object v8, v6, v7
- const/4 v7, 0x2
- invoke-static {v2}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
- move-result-object v8
- aput-object v8, v6, v7
- const/4 v7, 0x3
- invoke-static {v3}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
- move-result-object v8
- aput-object v8, v6, v7
- .line 23
- invoke-static {v5, v6}, Ljava/lang/String;
- ->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
- move-result-object v4
- .line 25
- .local v4, str:Ljava/lang/String;
- const/4 v5, 0x0
- invoke-static {p0, v4, v5}, Landroid/widget/Toast;
- ->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)
- Landroid/widget/Toast;
- move-result-object v5
- invoke-virtual {v5}, Landroid/widget/Toast;->show()V # 使用Toast 显示格
- 式化后的结果
- :try_end_1 #第2个try 结束
- .catch Ljava/lang/ArithmeticException; {:try_start_1 .. :try_end_1} :
- catch_0 # catch_0
- .catch Ljava/lang/NumberFormatException; {:try_start_1 .. :try_end_1} :
- catch_1 # catch_1
- .line 33
- .end local v1 #i:I
- .end local v2 #m:I
- .end local v3 #n:I
- .end local v4 #str:Ljava/lang/String;
- :goto_0
- return-void # 方法返回
- .line 26
- .restart local v1 #i:I
- :catch_0
- move-exception v0
- .line 27
- .local v0, e:Ljava/lang/ArithmeticException;
- :try_start_2 #第3个try 开始
- const-string v5, “\u4eba\u6570\u4e0d\u80fd\u4e3a0” #“人数不能为0”
- const/4 v6, 0x0
- invoke-static {p0, v5, v6}, Landroid/widget/Toast;
- ->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)
- Landroid/widget/Toast;
- move-result-object v5
- invoke-virtual {v5}, Landroid/widget/Toast;->show()V # 使用Toast 显示异 常原因
- :try_end_2 #第3个try 结束
- .catch Ljava/lang/NumberFormatException; {:try_start_2 .. :try_end_2} :
- catch_1
- goto :goto_0 #返回
- .line 29
- .end local v0 #e:Ljava/lang/ArithmeticException;
- .end local v1 #i:I
- :catch_1
- move-exception v0
- .line 30
- .local v0, e:Ljava/lang/NumberFormatException;
- const-string v5, “\u65e0\u6548\u7684\u6570\u503c\u5b57\u7b26\u4e32”
- #“无效的数值字符串”
- invoke-static {p0, v5, v9}, Landroid/widget/Toast;
- ->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)
- Landroid/widget/Toast;
- move-result-object v5
- invoke-virtual {v5}, Landroid/widget/Toast;->show()V # 使用Toast 显示异
- 常原因
- goto :goto_0 #返回
- .end method
整段代码的功能比较简单,输入鸡腿数与人数,然后使用Toast弹出鸡腿的分配方案。传入人数时为了演示Try/Catch效果,使用了String 类型。代码中有两种情况下会发生异常:第一种是将String 类型转换成 int 类型时可能会发生 NumberFormatException异常;第二种是计算分配方法时除数为零的ArithmeticException异常。
在Dalvik 指令集中,并没有与Try/Catch相关的指令,在处理Try/Catch语句时,是通过相关的数据结构来保存异常信息的。
小结
静态分析是软件分析过程中最基础也是最重要的一种手段,我们向Android逆向破解又迈进了一大步。
smali语法详解的更多相关文章
- (3)Smali系列学习之Smali语法详解
数据类型 Dalvik字节码只有两种格式:基本类型和引用类型.对象和数组属于引用类型 语法 含义 V void,只用于返回值类型 Z boolean B byte S short C char I i ...
- (4)Smali系列学习之Smali语法详解内部类
在这一节,我们来介绍一下内部类.对于Java文件中的每一个内部类,都会产生一个单独的smali文件,比如ActivityThread$1.smali.这些文件的命名规范是如果是匿名内部类,则命名规则是 ...
- Velocity魔法堂系列二:VTL语法详解
一.前言 Velocity作为历史悠久的模板引擎不单单可以替代JSP作为Java Web的服务端网页模板引擎,而且可以作为普通文本的模板引擎来增强服务端程序文本处理能力.而且Velocity被移植到不 ...
- Hive笔记--sql语法详解及JavaAPI
Hive SQL 语法详解:http://blog.csdn.net/hguisu/article/details/7256833Hive SQL 学习笔记(常用):http://blog.sina. ...
- Hadoop Hive sql语法详解
Hadoop Hive sql语法详解 Hive 是基于Hadoop 构建的一套数据仓库分析系统,它提供了丰富的SQL查询方式来分析存储在Hadoop 分布式文件系统中的数据,可以将结构 化的数据文件 ...
- Thymeleaf3语法详解和实战
Thymeleaf3语法详解 Thymeleaf是Spring boot推荐使用的模版引擎,除此之外常见的还有Freemarker和Jsp.Jsp应该是我们最早接触的模版引擎.而Freemarker工 ...
- Xpath语法详解
1.简介 XPath是一门在XML和HTML文档中查找信息的语言,可以用来在XML和HTML文档中对元素和属性进行遍历 XPath的安装 Chrome插件XPath Helper 点Chrome浏览器 ...
- mysql用户授权、数据库权限管理、sql语法详解
mysql用户授权.数据库权限管理.sql语法详解 —— NiceCui 某个数据库所有的权限 ALL 后面+ PRIVILEGES SQL 某个数据库 特定的权限SQL mysql 授权语法 SQL ...
- Java8的Stream语法详解(转载)
1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...
随机推荐
- python theading线程开发与加锁、信号量、事件等详解
线程有2种调用方式,如下: 直接调用 import threading import time def sayhi(num): #定义每个线程要运行的函数 print("running on ...
- Web App和Native App的比较
一.Web App vs. Native App 比起手机App,网站有一些明显的优点. 跨平台:所有系统都能运行 免安装:打开浏览器,就能使用 快速部署:升级只需在服务器更新代码 超链接:可以与其他 ...
- PAT 1011 World Cup Betting
1011 World Cup Betting (20 分) With the 2010 FIFA World Cup running, football fans the world over w ...
- Ping 的TTL理解
http://www.webkaka.com/tutorial/zhanzhang/2017/061570/ 根据自己的扩展重新整理了一下,虽然不是运维,想了解一点东西就希望了解清楚. 一.含义 “T ...
- InnoDB存储引擎介绍-(3)InnoDB缓冲池配置详解
原文链接 http://www.ywnds.com/?p=9886 一.InnoDB缓冲池 InnoDB维护一个称为缓冲池的内存存储区域 ,用于缓存内存中的数据和索引.了解InnoDB缓冲池的工作原 ...
- 精通Oracle的关键是……(Ask Tom上最经常被问到的问题)(转)
原文地址:http://www.ituring.com.cn/article/37548 这是我在asktom上最经常收到的问题:我需要怎么做才能变成一个专家呢?关于Oracle,有这样的一个关键事物 ...
- hadoop -- fsck
hadoop -- fsck shell命令: hdfs fsck /1708a1 -files -blocks -locations -racks /1708a1:是hdfs 中的文件 查看hdfs ...
- Hadoop---集群的搭建(仅主机模式)
Hadoop---集群的搭建 我有一个虚拟机是用来克隆的,里面设置了java环境,开启不启动防火墙的配置. 准备: 1.我的3个虚拟机: hu_hadoop1(主+从):nameNode+dataNo ...
- Binary Analysis Tool安装使用教程
Binary Analysis Tool(BAT)是一个用于检测二进制文件使用到的开源组件,协助及早发现程序发布后可能会面临的开源协议解执的开源免费检测工具. 一.安装BAT和bat-extratoo ...
- 关于TCP长连接和发送心跳的一些理解
原因 TCP是一种有连接的协议,但是这个连接并不是指有一条实际的电路,而是一种虚拟的电路.TCP的建立连接和断开连接都是通过发送数据实现的,也就是我们常说的三次握手.四次挥手.TCP两端保存了一种数据 ...