一、smali语言简介
1、宏观的介绍:http://source.android.com/devices/tech/dalvik/instruction-formats.html
2、具体的指令:http://source.android.com/devices/tech/dalvik/dalvik-bytecode.html
3、《Android安全与逆向分析》一书
4、具体指令中的meth@BBBB、filed@BBBB、type@BBBB,方法、字段、类型:https://code.google.com/p/smali/wiki/TypesMethodsAndFields
5、具体指令中的vXXX,寄存器,https://code.google.com/p/smali/wiki/Registers
注:非static函数中,p0代指“this”,p1表示函数的第一个参数,p2代表函数中的第二个参数…而在static函数中p0才对应第一个参数(因为Java的static方法中没有this方法)
6、整体的格式(混淆后的,如果没有混淆结构会更详细)
(1)、类:
.class public Lcom/jltxgcy/crack/MainActivity; .super Landroid/app/Activity;(2)、方法:
# direct methods or vitual methods .method public constructor <init>()V .locals 0 invoke-direct {p0}, Landroid/app/Activity;-><init>()V return-void .end method直接方法指构造函数,私有方法,静态方法。虚方法指的是其他的。
(3)、字段
# instance fields or static filelds .field private a:Landroid/widget/Button;(4)、接口
# interfaces .implements Landroid/view/View$OnClickListener7、还可以参考http://blog.csdn.net/lpohvbe/article/details/7981386
注:invoke-static:调用静态方法,invoke-direct:调用私有或者构造函数,invoke-virtual:调用public或者protected方法。
二、工程源码目录
在Android混淆机制这篇文章中,http://blog.csdn.net/jltxgcy/article/details/22670651,对于上面的CrackApk工程,根据不同的proguard-project.txt的不同规则,导出两个混淆后的apk,一个是CrackApk01.apk,一个是CrackApk02.apk。
CrackApk01.apk没有保留内部类isRegistered方法,CrackApk02.apk保留了内部类isRegistered方法。
使用apktool进行反编译两个apk,参考http://blog.csdn.net/jltxgcy/article/details/22690753。最后生成smali语言。
下面我们来对比分析反编译生成的smali和CrackApk工程中对应的java文件。
首先分析CrackApk01.apk。
MainActivity.java代码如下:
package com.jltxgcy.crack; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends Activity { private Button btnAnno; private Button btnCheckSN; private EditText edtSN; @Override 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(); } }); } private void getAnnotations() { try { Toast.makeText(this, "getAnnotations", Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } public class SNChecker { private String sn; public SNChecker(String sn) { this.sn = sn; } public boolean isRegistered() { boolean result = false; char ch = ‘\0‘; int sum = 0; if (sn == null || (sn.length() < 8)) return result; int len = sn.length(); if (len == 8) { ch = sn.charAt(0); switch (ch) { case ‘a‘: case ‘f‘: result = true; break; default: result = false; break; } if (result) { ch = sn.charAt(3); switch (ch) { case ‘1‘: case ‘2‘: case ‘3‘: case ‘4‘: case ‘5‘: result = true; break; default: result = false; break; } } } else if (len == 16) { for (int i = 0; i < len; i++) { char chPlus = sn.charAt(i); sum += (int) chPlus; } result = ((sum % 6) == 0) ? true : false; } return result; } } }
MainActivity.java对应的反编译过来的smali是以下四个文件,a.smali,b.smali,c.smali,MainActivity.smali
a.smali代码如下:
.class final Lcom/jltxgcy/crack/a; .super Ljava/lang/Object; # interfaces .implements Landroid/view/View$OnClickListener; # instance fields .field final synthetic a:Lcom/jltxgcy/crack/MainActivity; # direct methods .method constructor <init>(Lcom/jltxgcy/crack/MainActivity;)V .locals 0 iput-object p1, p0, Lcom/jltxgcy/crack/a;->a:Lcom/jltxgcy/crack/MainActivity; invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void .end method # virtual methods .method public final onClick(Landroid/view/View;)V .locals 1 iget-object v0, p0, Lcom/jltxgcy/crack/a;->a:Lcom/jltxgcy/crack/MainActivity; invoke-static {v0}, Lcom/jltxgcy/crack/MainActivity;->a(Lcom/jltxgcy/crack/MainActivity;)V return-void .end method
b.smali代码如下:
.class final Lcom/jltxgcy/crack/b; .super Ljava/lang/Object; # interfaces .implements Landroid/view/View$OnClickListener; # instance fields .field final synthetic a:Lcom/jltxgcy/crack/MainActivity; # direct methods .method constructor <init>(Lcom/jltxgcy/crack/MainActivity;)V .locals 0 iput-object p1, p0, Lcom/jltxgcy/crack/b;->a:Lcom/jltxgcy/crack/MainActivity; invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void .end method # virtual methods .method public final onClick(Landroid/view/View;)V .locals 7 const/16 v6, 0x8 const/4 v0, 0x1 const/4 v1, 0x0 new-instance v4, Lcom/jltxgcy/crack/c; iget-object v2, p0, Lcom/jltxgcy/crack/b;->a:Lcom/jltxgcy/crack/MainActivity; iget-object v3, p0, Lcom/jltxgcy/crack/b;->a:Lcom/jltxgcy/crack/MainActivity; invoke-static {v3}, Lcom/jltxgcy/crack/MainActivity;->b(Lcom/jltxgcy/crack/MainActivity;)Landroid/widget/EditText; move-result-object v3 invoke-virtual {v3}, Landroid/widget/EditText;->getText()Landroid/text/Editable; move-result-object v3 invoke-interface {v3}, Landroid/text/Editable;->toString()Ljava/lang/String; move-result-object v3 invoke-direct {v4, v2, v3}, Lcom/jltxgcy/crack/c;-><init>(Lcom/jltxgcy/crack/MainActivity;Ljava/lang/String;)V iget-object v2, v4, Lcom/jltxgcy/crack/c;->a:Ljava/lang/String; if-eqz v2, :cond_0 iget-object v2, v4, Lcom/jltxgcy/crack/c;->a:Ljava/lang/String; invoke-virtual {v2}, Ljava/lang/String;->length()I move-result v2 if-ge v2, v6, :cond_2 :cond_0 move v0, v1 :cond_1 :goto_0 :pswitch_0 if-eqz v0, :cond_5 const-string v0, "\u6ce8\u518c\u6210\u529f" :goto_1 iget-object v2, p0, Lcom/jltxgcy/crack/b;->a:Lcom/jltxgcy/crack/MainActivity; invoke-static {v2, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; move-result-object v0 invoke-virtual {v0}, Landroid/widget/Toast;->show()V return-void :cond_2 iget-object v2, v4, Lcom/jltxgcy/crack/c;->a:Ljava/lang/String; invoke-virtual {v2}, Ljava/lang/String;->length()I move-result v5 if-ne v5, v6, :cond_3 iget-object v2, v4, Lcom/jltxgcy/crack/c;->a:Ljava/lang/String; invoke-virtual {v2, v1}, Ljava/lang/String;->charAt(I)C move-result v2 sparse-switch v2, :sswitch_data_0 move v2, v1 :goto_2 if-eqz v2, :cond_7 iget-object v2, v4, Lcom/jltxgcy/crack/c;->a:Ljava/lang/String; const/4 v3, 0x3 invoke-virtual {v2, v3}, Ljava/lang/String;->charAt(I)C move-result v2 packed-switch v2, :pswitch_data_0 move v0, v1 goto :goto_0 :sswitch_0 move v2, v0 goto :goto_2 :cond_3 const/16 v2, 0x10 if-ne v5, v2, :cond_6 move v2, v1 move v3, v1 :goto_3 if-lt v3, v5, :cond_4 rem-int/lit8 v2, v2, 0x6 if-eqz v2, :cond_1 move v0, v1 goto :goto_0 :cond_4 iget-object v6, v4, Lcom/jltxgcy/crack/c;->a:Ljava/lang/String; invoke-virtual {v6, v3}, Ljava/lang/String;->charAt(I)C move-result v6 add-int/2addr v2, v6 add-int/lit8 v3, v3, 0x1 goto :goto_3 :cond_5 const-string v0, "\u6ce8\u518c\u5931\u8d25" goto :goto_1 :cond_6 move v0, v1 goto :goto_0 :cond_7 move v0, v2 goto :goto_0 nop :sswitch_data_0 .sparse-switch 0x61 -> :sswitch_0 0x66 -> :sswitch_0 .end sparse-switch :pswitch_data_0 .packed-switch 0x31 :pswitch_0 :pswitch_0 :pswitch_0 :pswitch_0 :pswitch_0 .end packed-switch .end method
c.smali代码如下:
.class public final Lcom/jltxgcy/crack/c; .super Ljava/lang/Object; # instance fields .field a:Ljava/lang/String; .field final synthetic b:Lcom/jltxgcy/crack/MainActivity; # direct methods .method public constructor <init>(Lcom/jltxgcy/crack/MainActivity;Ljava/lang/String;)V .locals 0 iput-object p1, p0, Lcom/jltxgcy/crack/c;->b:Lcom/jltxgcy/crack/MainActivity; invoke-direct {p0}, Ljava/lang/Object;-><init>()V iput-object p2, p0, Lcom/jltxgcy/crack/c;->a:Ljava/lang/String; return-void .end method
MainActivity.smali代码如下:
.class public Lcom/jltxgcy/crack/MainActivity; .super Landroid/app/Activity; # instance fields .field private a:Landroid/widget/Button; .field private b:Landroid/widget/Button; .field private c:Landroid/widget/EditText; # direct methods .method public constructor <init>()V .locals 0 invoke-direct {p0}, Landroid/app/Activity;-><init>()V return-void .end method .method static synthetic a(Lcom/jltxgcy/crack/MainActivity;)V .locals 2 :try_start_0 const-string v0, "getAnnotations" const/4 v1, 0x0 invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; move-result-object v0 invoke-virtual {v0}, Landroid/widget/Toast;->show()V :try_end_0 .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0 :goto_0 return-void :catch_0 move-exception v0 invoke-virtual {v0}, Ljava/lang/Exception;->printStackTrace()V goto :goto_0 .end method .method static synthetic b(Lcom/jltxgcy/crack/MainActivity;)Landroid/widget/EditText; .locals 1 iget-object v0, p0, Lcom/jltxgcy/crack/MainActivity;->c:Landroid/widget/EditText; return-object v0 .end method # virtual methods .method public onCreate(Landroid/os/Bundle;)V .locals 2 invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V const/high16 v0, 0x7f03 invoke-virtual {p0, v0}, Lcom/jltxgcy/crack/MainActivity;->setContentView(I)V const/high16 v0, 0x7f08 invoke-virtual {p0, v0}, Lcom/jltxgcy/crack/MainActivity;->findViewById(I)Landroid/view/View; move-result-object v0 check-cast v0, Landroid/widget/Button; iput-object v0, p0, Lcom/jltxgcy/crack/MainActivity;->a:Landroid/widget/Button; const v0, 0x7f080002 invoke-virtual {p0, v0}, Lcom/jltxgcy/crack/MainActivity;->findViewById(I)Landroid/view/View; move-result-object v0 check-cast v0, Landroid/widget/Button; iput-object v0, p0, Lcom/jltxgcy/crack/MainActivity;->b:Landroid/widget/Button; const v0, 0x7f080001 invoke-virtual {p0, v0}, Lcom/jltxgcy/crack/MainActivity;->findViewById(I)Landroid/view/View; move-result-object v0 check-cast v0, Landroid/widget/EditText; iput-object v0, p0, Lcom/jltxgcy/crack/MainActivity;->c:Landroid/widget/EditText; iget-object v0, p0, Lcom/jltxgcy/crack/MainActivity;->a:Landroid/widget/Button; new-instance v1, Lcom/jltxgcy/crack/a; invoke-direct {v1, p0}, Lcom/jltxgcy/crack/a;-><init>(Lcom/jltxgcy/crack/MainActivity;)V invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V iget-object v0, p0, Lcom/jltxgcy/crack/MainActivity;->b:Landroid/widget/Button; new-instance v1, Lcom/jltxgcy/crack/b; invoke-direct {v1, p0}, Lcom/jltxgcy/crack/b;-><init>(Lcom/jltxgcy/crack/MainActivity;)V invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V return-void .end method .method public onCreateOptionsMenu(Landroid/view/Menu;)Z .locals 2 invoke-virtual {p0}, Lcom/jltxgcy/crack/MainActivity;->getMenuInflater()Landroid/view/MenuInflater; move-result-object v0 const/high16 v1, 0x7f07 invoke-virtual {v0, v1, p1}, Landroid/view/MenuInflater;->inflate(ILandroid/view/Menu;)V const/4 v0, 0x1 return v0 .end method
MyApp.java代码如下:
package com.jltxgcy.crack.app; import android.app.Application; import android.widget.Toast; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import com.jltxgcy.crack.MainActivity; public class MyApp extends Application{ @Override public void onCreate() { Toast.makeText(this, "app creat", Toast.LENGTH_SHORT).show(); super.onCreate(); } }
MyApp.java对应的反编译出来的smali文件为MyApp.smali
MyApp.smali代码如下:
.class public Lcom/jltxgcy/crack/app/MyApp; .super Landroid/app/Application; # direct methods .method public constructor <init>()V .locals 0 invoke-direct {p0}, Landroid/app/Application;-><init>()V return-void .end method # virtual methods .method public onCreate()V .locals 2 const-string v0, "app creat" const/4 v1, 0x0 invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; move-result-object v0 invoke-virtual {v0}, Landroid/widget/Toast;->show()V invoke-super {p0}, Landroid/app/Application;->onCreate()V return-void .end method
然后我们来分析CrackApk02.apk,由于混淆机制保存了内部类isRegister,所以b.smail和c.smail有所不同。
b.smali代码如下:
.class final Lcom/jltxgcy/crack/b; .super Ljava/lang/Object; # interfaces .implements Landroid/view/View$OnClickListener; # instance fields .field final synthetic a:Lcom/jltxgcy/crack/MainActivity; # direct methods .method constructor <init>(Lcom/jltxgcy/crack/MainActivity;)V .locals 0 iput-object p1, p0, Lcom/jltxgcy/crack/b;->a:Lcom/jltxgcy/crack/MainActivity; invoke-direct {p0}, Ljava/lang/Object;-><init>()V return-void .end method # virtual methods .method public final onClick(Landroid/view/View;)V .locals 3 new-instance v0, Lcom/jltxgcy/crack/c; iget-object v1, p0, Lcom/jltxgcy/crack/b;->a:Lcom/jltxgcy/crack/MainActivity; iget-object v2, p0, Lcom/jltxgcy/crack/b;->a:Lcom/jltxgcy/crack/MainActivity; invoke-static {v2}, Lcom/jltxgcy/crack/MainActivity;->b(Lcom/jltxgcy/crack/MainActivity;)Landroid/widget/EditText; move-result-object v2 invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable; move-result-object v2 invoke-interface {v2}, Landroid/text/Editable;->toString()Ljava/lang/String; move-result-object v2 invoke-direct {v0, v1, v2}, Lcom/jltxgcy/crack/c;-><init>(Lcom/jltxgcy/crack/MainActivity;Ljava/lang/String;)V invoke-virtual {v0}, Lcom/jltxgcy/crack/c;->isRegistered()Z move-result v0 if-eqz v0, :cond_0 const-string v0, "\u6ce8\u518c\u6210\u529f" :goto_0 iget-object v1, p0, Lcom/jltxgcy/crack/b;->a:Lcom/jltxgcy/crack/MainActivity; const/4 v2, 0x0 invoke-static {v1, v0, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; move-result-object v0 invoke-virtual {v0}, Landroid/widget/Toast;->show()V return-void :cond_0 const-string v0, "\u6ce8\u518c\u5931\u8d25" goto :goto_0 .end method
c.smali代码如下:
.class public final Lcom/jltxgcy/crack/c; .super Ljava/lang/Object; # instance fields .field final synthetic a:Lcom/jltxgcy/crack/MainActivity; .field private b:Ljava/lang/String; # direct methods .method public constructor <init>(Lcom/jltxgcy/crack/MainActivity;Ljava/lang/String;)V .locals 0 iput-object p1, p0, Lcom/jltxgcy/crack/c;->a:Lcom/jltxgcy/crack/MainActivity; invoke-direct {p0}, Ljava/lang/Object;-><init>()V iput-object p2, p0, Lcom/jltxgcy/crack/c;->b:Ljava/lang/String; return-void .end method # virtual methods .method public final isRegistered()Z .locals 6 const/16 v3, 0x8 const/4 v0, 0x1 const/4 v1, 0x0 iget-object v2, p0, Lcom/jltxgcy/crack/c;->b:Ljava/lang/String; if-eqz v2, :cond_0 iget-object v2, p0, Lcom/jltxgcy/crack/c;->b:Ljava/lang/String; invoke-virtual {v2}, Ljava/lang/String;->length()I move-result v2 if-ge v2, v3, :cond_1 :cond_0 :goto_0 return v1 :cond_1 iget-object v2, p0, Lcom/jltxgcy/crack/c;->b:Ljava/lang/String; invoke-virtual {v2}, Ljava/lang/String;->length()I move-result v4 if-ne v4, v3, :cond_3 iget-object v2, p0, Lcom/jltxgcy/crack/c;->b:Ljava/lang/String; invoke-virtual {v2, v1}, Ljava/lang/String;->charAt(I)C move-result v2 sparse-switch v2, :sswitch_data_0 move v2, v1 :goto_1 if-eqz v2, :cond_5 iget-object v2, p0, Lcom/jltxgcy/crack/c;->b:Ljava/lang/String; const/4 v3, 0x3 invoke-virtual {v2, v3}, Ljava/lang/String;->charAt(I)C move-result v2 packed-switch v2, :pswitch_data_0 move v0, v1 :cond_2 :goto_2 :pswitch_0 move v1, v0 goto :goto_0 :sswitch_0 move v2, v0 goto :goto_1 :cond_3 const/16 v2, 0x10 if-ne v4, v2, :cond_0 move v2, v1 move v3, v1 :goto_3 if-lt v3, v4, :cond_4 rem-int/lit8 v2, v2, 0x6 if-eqz v2, :cond_2 move v0, v1 goto :goto_2 :cond_4 iget-object v5, p0, Lcom/jltxgcy/crack/c;->b:Ljava/lang/String; invoke-virtual {v5, v3}, Ljava/lang/String;->charAt(I)C move-result v5 add-int/2addr v2, v5 add-int/lit8 v3, v3, 0x1 goto :goto_3 :cond_5 move v1, v2 goto :goto_0 :sswitch_data_0 .sparse-switch 0x61 -> :sswitch_0 0x66 -> :sswitch_0 .end sparse-switch :pswitch_data_0 .packed-switch 0x31 :pswitch_0 :pswitch_0 :pswitch_0 :pswitch_0 :pswitch_0 .end packed-switch .end method