android 逆向工程-语言篇 Smali(三)

2023-10-23 22:58

本文主要是介绍android 逆向工程-语言篇 Smali(三),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

数据类型

  • B---byte
  • C---char
  • D---double
  • F---float
  • I---int
  • J---long
  • S---short
  • V---void
  • Z---boolean
  • [XXX---array
  • Lxxx/yyy---object
基本语法
.field private isFlag:z定义变量
.method方法
.parameter方法参数
.prologue方法开始
.line 12此方法位于第12行
invoke-super调用父函数
const/high16  v0, 0x7fo3把0x7fo3赋值给v0
invoke-direct调用函数
return-void函数返回void
.end method函数结束
new-instance创建实例
iput-object对象赋值
iget-object调用对象
invoke-static调用静态函数
条件跳转分支:
if-eq vA, vB, :cond_**如果vA等于vB则跳转到:cond_**
if-ne vA, vB, :cond_**如果vA不等于vB则跳转到:cond_**
if-lt vA, vB, :cond_**如果vA小于vB则跳转到:cond_**
if-ge vA, vB, :cond_**如果vA大于等于vB则跳转到:cond_**
if-gt vA, vB, :cond_**如果vA大于vB则跳转到:cond_**
if-le vA, vB, :cond_**如果vA小于等于vB则跳转到:cond_**
if-eqz vA, :cond_**如果vA等于0则跳转到:cond_**
if-nez vA, :cond_**如果vA不等于0则跳转到:cond_**
if-ltz vA, :cond_**如果vA小于0则跳转到:cond_**
if-gez vA, :cond_**如果vA大于等于0则跳转到:cond_**
if-gtz vA, :cond_**如果vA大于0则跳转到:cond_**
if-lez vA, :cond_**如果vA小于等于0则跳转到:cond_**
smali文件格式
.class < 访问权限> [ 修饰关键字] < 类名>  
.super < 父类名>  
.source <源文件名>  
MainActivity.smali展示
.class public Lcom/droider/crackme0502/MainActivity;     //指令指定了当前类的类名。  
.super Landroid/app/Activity;                            //指令指定了当前类的父类。  
.source "MainActivity.java"                              //指令指定了当前类的源文件名。  
smali文件中字段的声明使用“.field”指令。字段有静态字段与实例字段两种。静态字段的声明格式如下:
.field < 访问权限> static [ 修饰关键字] < 字段名>:< 字段类型>  
实例字段的声明与静态字段类似,只是少了static关键字,它的格式如下:
.field < 访问权限> [ 修饰关键字] < 字段名>:< 字段类型>  

比如以下的实例字段声明。  

.field private btnAnno:Landroid/widget/Button;  //私有字段  
smali 文件中方法的声明使用“.method ”指令,方法有直接方法与虚方法两种。
.method <访问权限> [ 修饰关键字] < 方法原型>  <.locals>                 //指定了使用的局部变量的个数  [.parameter]                   //指定了方法的参数  [.prologue]                    //指定了代码的开始处,混淆过的代码可能去掉了该指令  [.line]                    //指定了该处指令在源代码中的行号  
<代码体>  
.end method  
虚方法的声明与直接方法相同,只是起始处的注释为“virtual methods”,如果一个类实现了接口,会在smali 文件中使用“.implements ”指令指出,相应的格式声明如下:
.implements < 接口名>        //接口关键字  
如果一个类使用了注解,会在 smali 文件中使用“.annotation ”指令指出,注解的格式声明如下:  
.annotation [ 注解属性] < 注解类名>  [ 注解字段 =  值]  
.end annotation  
注解的作用范围可以是类、方法或字段。如果注解的作用范围是类,“.annotation ”指令会直接定义在smali 文件中,如果是方法或字段,“.annotation ”指令则会包含在方法或字段定义中。例如:
.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/MainActivity$SNChecker;  
.super Ljava/lang/Object;  
.source "MainActivity.java"  # annotations  
.annotation system Ldalvik/annotation/EnclosingClass;  value = Lcom/droider/crackme0502/MainActivity;  
.end annotation  
.annotation system Ldalvik/annotation/InnerClass;  accessFlags = 0x1  name = "SNChecker"  
.end annotation  # instance fields  
.field private sn:Ljava/lang/String;  
.field final synthetic this$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 与this$0 、一个直接方法 init()、一个虚方法isRegistered() 。this$0 是内部类自动保留的一个指向所在外部类的引用。左边的 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$1; #新建一个  MainActivity$1实例  invoke-direct {v1, p0}, Lcom/droider/crackme0502/MainActivity$1;  
-><init>(Lcom/droider/crackme0502/MainActivity;)V # 初始化MainActivity$1  
实例  invoke-virtual {v0, v1}, Landroid/widget/Button;  
->setOnClickListener(Landroid/view/View$OnClickListener;)V # 设置按钮点击事件  
监听器  .line 40  iget-object v0, p0, Lcom/droider/crackme0502/MainActivity;  
->btnCheckSN:Landroid/widget/Button;  new-instance v1, Lcom/droider/crackme0502/MainActivity$2; #新建一个  
MainActivity$2实例  invoke-direct {v1, p0}, Lcom/droider/crackme0502/MainActivity$2  
-><init>(Lcom/droider/crackme0502/MainActivity;)V; # 初始化MainActivity$2实例  invoke-virtual {v0, v1}, Landroid/widget/Button;  
->setOnClickListener(Landroid/view/View$OnClickListener;)V#设置按钮点击事件  
监听器  .line 50  return-void  
.end method  
在MainActivity$1.smali 文件的开头使用了“.implements ”指令指定该类实现了按钮点击事件的监听器接口,因此,这个类实现了它的OnClick()方法,这也是我们在分析程序时关心的地方。另外,程序中的注解与监听器的构造函数都是编译器为我们自己生成的,实际分析过程中不必关心。
3、注解类
注解是Java 的语言特性,在 Android的开发过程中也得到了广泛的使用。Android系统中涉及到注解的包共有两个:一个是dalvik.annotation;另一个是 android.annotation。

例如:

.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、R$attr.smali 、R$dimen.smali、R$drawable.smali、R$id.smali、R$layout.smali、R$menu.smali 、R$string.smali 、R$style.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/ActivityManager$RunningAppProcessInfo;>;"  new-instance v3, Ljava/lang/StringBuilder;  # 新建一个StringBuilder 对象  invoke-direct {v3}, Ljava/lang/StringBuilder;-><init>()V  # 调用  StringBuilder 构造函数  .line 37  .local v3, sb:Ljava/lang/StringBuilder;  invoke-interface {v2}, Ljava/util/List;->iterator()Ljava/util/Iterator;  #获取进程列表的迭代器  move-result-object v4  :goto_0 #迭代循环开始  invoke-interface {v4}, Ljava/util/Iterator;->hasNext()Z #开始迭代  move-result v5  if-nez v5, :cond_0  # 如果迭代器不为空就跳走  .line 40  invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/  String;  move-result-object v4  # StringBuilder转为字符串  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 v4  invoke-virtual {v4}, Landroid/widget/Toast;->show()V # 弹出StringBuilder  的内容  .line 41  return-void  # 方法返回  .line 37  :cond_0  invoke-interface {v4}, Ljava/util/Iterator;->next()Ljava/lang/Object;   # 循环获取每一项  move-result-object v1  check-cast v1, Landroid/app/ActivityManager$RunningAppProcessInfo;  .line 38  .local v1, info:Landroid/app/ActivityManager$RunningAppProcessInfo;  new-instance v5, Ljava/lang/StringBuilder;  # 新建一个临时的StringBuilder  iget-object v6, v1, Landroid/app/ActivityManager$RunningAppProcessInfo;  ->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(三)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/270992

相关文章

C语言中联合体union的使用

本文编辑整理自: http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=179471 一、前言 “联合体”(union)与“结构体”(struct)有一些相似之处。但两者有本质上的不同。在结构体中,各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量

C++工程编译链接错误汇总VisualStudio

目录 一些小的知识点 make工具 可以使用windows下的事件查看器崩溃的地方 dumpbin工具查看dll是32位还是64位的 _MSC_VER .cc 和.cpp 【VC++目录中的包含目录】 vs 【C/C++常规中的附加包含目录】——头文件所在目录如何怎么添加,添加了以后搜索头文件就会到这些个路径下搜索了 include<> 和 include"" WinMain 和

大语言模型(LLMs)能够进行推理和规划吗?

大语言模型(LLMs),基本上是经过强化训练的 n-gram 模型,它们在网络规模的语言语料库(实际上,可以说是我们文明的知识库)上进行了训练,展现出了一种超乎预期的语言行为,引发了我们的广泛关注。从训练和操作的角度来看,LLMs 可以被认为是一种巨大的、非真实的记忆库,相当于为我们所有人提供了一个外部的系统 1(见图 1)。然而,它们表面上的多功能性让许多研究者好奇,这些模型是否也能在通常需要系

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

android 带与不带logo的二维码生成

该代码基于ZXing项目,这个网上能下载得到。 定义的控件以及属性: public static final int SCAN_CODE = 1;private ImageView iv;private EditText et;private Button qr_btn,add_logo;private Bitmap logo,bitmap,bmp; //logo图标private st

Android多线程下载见解

通过for循环开启N个线程,这是多线程,但每次循环都new一个线程肯定很耗内存的。那可以改用线程池来。 就以我个人对多线程下载的理解是开启一个线程后: 1.通过HttpUrlConnection对象获取要下载文件的总长度 2.通过RandomAccessFile流对象在本地创建一个跟远程文件长度一样大小的空文件。 3.通过文件总长度/线程个数=得到每个线程大概要下载的量(线程块大小)。

人工和AI大语言模型成本对比 ai语音模型

这里既有AI,又有生活大道理,无数渺小的思考填满了一生。 上一专题搭建了一套GMM-HMM系统,来识别连续0123456789的英文语音。 但若不是仅针对数字,而是所有普通词汇,可能达到十几万个词,解码过程将非常复杂,识别结果组合太多,识别结果不会理想。因此只有声学模型是完全不够的,需要引入语言模型来约束识别结果。让“今天天气很好”的概率高于“今天天汽很好”的概率,得到声学模型概率高,又符合表达