最近在学习Android逆向,刚接触smali层的代码逆向,在其他论坛找了几个入门级别的Android CrackMe,这个是其中的一个,由于刚接触,所以分析的比较慢,断断续续搞了2天,后面还会对其他的CrackMe进行分析,今天先发一下这个的分析。
运行截图:
首先使用apktool将apk进行反编译,看了下程序清单文件,程序启动后显示运行LisenceCheck类
到反编译好的smali文件中,打开LisenceCheck.smali
为了学习和熟悉smali语法,我把LisenceCheck.smali 和 它的一个匿名类LisenceCheck$1.smali从头到尾都做了注释,注释如下:
LisenceCheck.smali的注释
//当前的类
.class public Lcom/mstar/test/LisenceCheck;
//当前类的父类
.super Landroid/app/Activity;
//当前类所在的源码文件
.source "LisenceCheck.java"
//该类的普通数据成员
# instance fields
//有一个默认访问权限的数据成员,名为a,类型为int
.field a:I
//有一个私有数据成员,名为appButtonOnClickListener,类型为View$OnClickListener对象引用
//不知道是否是编译器自动生成的
.field private appButtonOnClickListener:Landroid/view/View$OnClickListener;
//有一个默认访问权限的数据成员,名为mbutton,类型为Button对象引用
.field mbutton:Landroid/widget/Button;
//有一个默认访问权限的数据成员,名为mbuttonclear,类型为Button对象引用
.field mbuttonclear:Landroid/widget/Button;
//有一个默认访问权限的数据成员,名为meditsn,类型为EditText对象引用
.field meditsn:Landroid/widget/EditText;
//有一个默认访问权限的数据成员,名为meditun,类型为EditText对象引用
.field meditun:Landroid/widget/EditText;
//有一个默认访问权限的数据成员,名为rd1,类型为Random对象引用
.field rd1:Ljava/util/Random;
//该类的直接方法
# direct methods
//无参构造
.method public constructor <init>()V
//该方法寄存器变量个数1
.locals 1
//该方法的代码开始
.prologue
.line 17
//构造父类
invoke-direct {p0}, Landroid/app/Activity;-><init>()V
.line 51
//一个匿名类
new-instance v0, Lcom/mstar/test/LisenceCheck$1;
//匿名类的构造
invoke-direct {v0, p0}, Lcom/mstar/test/LisenceCheck$1;-><init>(Lcom/mstar/test/LisenceCheck;)V
//保存到了外部类的数据成员中,由此可以看出上面的
//.field private appButtonOnClickListener:Landroid/view/View$OnClickListener;
//应该是编译器自动生成的
iput-object v0, p0, Lcom/mstar/test/LisenceCheck;->appButtonOnClickListener:Landroid/view/View$OnClickListener;
//该构造应该是编译器自动生成的,当时应该没有写构造函数
.line 17
return-void
.end method
//该类的普通方法
# virtual methods
//一个公有的onCreate方法,参数为Bundle对象引用,无返回值
.method public onCreate(Landroid/os/Bundle;)V
//该方法寄存器变量个数2
.locals 2
//参数p1名为savedInstanceState,类型为Bundle对象引用
.param p1, "savedInstanceState" # Landroid/os/Bundle;
//该方法的代码开始
.prologue
.line 28
//调用父类Activity的onCreate方法,参数为Bundle对象引用,隐含传递this指针(p0)
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
.line 29
//寄存器v0 = 0x7f030000
const/high16 v0, 0x7f030000
//调用该类的普通方法setContentView,参数为int,无返回值
invoke-virtual {p0, v0}, Lcom/mstar/test/LisenceCheck;->setContentView(I)V
.line 31
//寄存器v0 = 0x7f050002
const v0, 0x7f050002
//调用该类的普通方法findViewById,参数为int,返回值为View对象引用
invoke-virtual {p0, v0}, Lcom/mstar/test/LisenceCheck;->findViewById(I)Landroid/view/View;
//获取返回值保存到v0
move-result-object v0
// View v = xxxxxx;
//对象类型转换成Button类型 (父类转子类) Button b = (Button)v;
check-cast v0, Landroid/widget/Button;
//设置普通数据成员mbutton,该应该成员是在onCreate方法外定义的,不是在方法内定义的
//mbutton = v0
iput-object v0, p0, Lcom/mstar/test/LisenceCheck;->mbutton:Landroid/widget/Button;
.line 32
//寄存器v0 = 0x7f050003
const v0, 0x7f050003
//调用该类的普通方法findViewById,参数为int,返回值为View对象引用
invoke-virtual {p0, v0}, Lcom/mstar/test/LisenceCheck;->findViewById(I)Landroid/view/View;
//获取返回值保存到v0
move-result-object v0
// View v = xxxxxx;
//对象类型转换成Button类型 (父类转子类) Button b = (Button)v;
check-cast v0, Landroid/widget/Button;
//设置普通数据成员mbuttonclear,该应该成员是在onCreate方法外定义的,不是在方法内定义的
//mbuttonclear = v0
iput-object v0, p0, Lcom/mstar/test/LisenceCheck;->mbuttonclear:Landroid/widget/Button;
.line 34
//寄存器v0 = 0x7f050000
const/high16 v0, 0x7f050000
//调用该类的普通方法findViewById,参数为int,返回值为View对象引用
invoke-virtual {p0, v0}, Lcom/mstar/test/LisenceCheck;->findViewById(I)Landroid/view/View;
//获取返回值保存到v0
move-result-object v0
// View v = xxxxxx;
//对象类型转换成EditText类型 (父类转子类) EditText e = (EditText)v;
check-cast v0, Landroid/widget/EditText;
//设置普通数据成员meditun,该应该成员是在onCreate方法外定义的,不是在方法内定义的
//meditun = v0
iput-object v0, p0, Lcom/mstar/test/LisenceCheck;->meditun:Landroid/widget/EditText;
.line 35
//寄存器v0 = 0x7f050001
const v0, 0x7f050001
//调用该类的普通方法findViewById,参数为int,返回值为View对象引用
invoke-virtual {p0, v0}, Lcom/mstar/test/LisenceCheck;->findViewById(I)Landroid/view/View;
//获取返回值保存到v0
move-result-object v0
// View v = xxxxxx;
//对象类型转换成EditText类型 (父类转子类) EditText e = (EditText)v;
check-cast v0, Landroid/widget/EditText;
//设置普通数据成员meditsn,该应该成员是在onCreate方法外定义的,不是在方法内定义的
//meditsn = v0
iput-object v0, p0, Lcom/mstar/test/LisenceCheck;->meditsn:Landroid/widget/EditText;
.line 37
//new Random对象
new-instance v0, Ljava/util/Random;
//构造new好的对象
invoke-direct {v0}, Ljava/util/Random;-><init>()V
//设置普通数据成员rd1,该应该成员是在onCreate方法外定义的,不是在方法内定义的
//rd1 = v0
iput-object v0, p0, Lcom/mstar/test/LisenceCheck;->rd1:Ljava/util/Random;
.line 39
//获取普通数据成员rd1,该应该成员是在onCreate方法外定义的,不是在方法内定义的
//v0 = rd1
iget-object v0, p0, Lcom/mstar/test/LisenceCheck;->rd1:Ljava/util/Random;
//寄存器v1 = 0x2710
const/16 v1, 0x2710
//调用Random类的普通方法nextInt,参数为int,返回值为int
invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I
//获取返回值保存到v0
move-result v0
//设置普通数据成员a,该应该成员是在onCreate方法外定义的,不是在方法内定义的
//a = v0
iput v0, p0, Lcom/mstar/test/LisenceCheck;->a:I
.line 41
//获取普通数据成员mbutton,该应该成员是在onCreate方法外定义的,不是在方法内定义的
//v0 = mbutton
iget-object v0, p0, Lcom/mstar/test/LisenceCheck;->mbutton:Landroid/widget/Button;
//获取普通数据成员appButtonOnClickListener,该成员应该是编译器自动生成的
//v1 = appButtonOnClickListener
iget-object v1, p0, Lcom/mstar/test/LisenceCheck;->appButtonOnClickListener:Landroid/view/View$OnClickListener;
//调用(mbutton)Button类的setOnClickListener方法,参数为View$OnClickListener对象引用,无返回值
invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
.line 42
//获取普通数据成员mbuttonclear,该应该成员是在onCreate方法外定义的,不是在方法内定义的
//v0 = mbuttonclear
iget-object v0, p0, Lcom/mstar/test/LisenceCheck;->mbuttonclear:Landroid/widget/Button;
//获取普通数据成员appButtonOnClickListener,该成员应该是编译器自动生成的
//v1 = appButtonOnClickListener
iget-object v1, p0, Lcom/mstar/test/LisenceCheck;->appButtonOnClickListener:Landroid/view/View$OnClickListener;
//调用(mbuttonclear)Button类的setOnClickListener方法,参数为View$OnClickListener对象引用,无返回值
invoke-virtual {v0, v1}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V
.line 43
//函数退出,无返回值
return-void
.end method
//一个保护的方法,名为showToast,参数为int,无返回值
.method protected showToast(I)V
//该方法寄存器数量为3
.locals 3
//参数p1的变量名为type,类型为int
.param p1, "type" # I
//该方法的代码开始
.prologue
.line 47
//调用普通方法getApplicationContext,无参数,返回值为Context对象引用
invoke-virtual {p0}, Lcom/mstar/test/LisenceCheck;->getApplicationContext()Landroid/content/Context;
//获取返回值保存到v0
move-result-object v0
//寄存器赋值 v1 = "\u5e8f\u5217\u53f7\u6b63\u786e\uff01"
//v1 = "序列号正确!"
const-string v1, "\u5e8f\u5217\u53f7\u6b63\u786e\uff01"
.line 48
//寄存器赋值 v2 = 0
const/4 v2, 0x0
.line 47
//调用Toast类的makeText静态方法,三个参数
//第一个参数为Context对象引用
//第二个参数为CharSequence对象引用
//第三个参数为int
//返回值为Toast对象引用
invoke-static {v0, v1, v2}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
//获取返回值保存到v0
move-result-object v0
.line 48
//调用Toast类的show普通方法,无参数,无返回值
//vo为this指针
invoke-virtual {v0}, Landroid/widget/Toast;->show()V
.line 49
//函数退出,无返回值
return-void
.end method
LisenceCheck$1.smali的注释
//当前类
.class Lcom/mstar/test/LisenceCheck$1;
//当前类的父类
.super Ljava/lang/Object;
//当前类所在的源码文件
.source "LisenceCheck.java"
//接口注释
# interfaces
.implements Landroid/view/View$OnClickListener;
//注释
# annotations
.annotation system Ldalvik/annotation/EnclosingClass;
value = Lcom/mstar/test/LisenceCheck;
.end annotation
//匿名类 name = null
.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x0
name = null
.end annotation
//编译器自动添加了一个数据成员为外部类的this指针
//外部类 ==> LisenceCheck
# instance fields
.field final synthetic this$0:Lcom/mstar/test/LisenceCheck;
//该类的直接方法
# direct methods
//编译器自动生成的构造
.method constructor <init>(Lcom/mstar/test/LisenceCheck;)V
.locals 0
.prologue
.line 1
iput-object p1, p0, Lcom/mstar/test/LisenceCheck$1;->this$0:Lcom/mstar/test/LisenceCheck;
.line 51
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
//该类的普通方法
# virtual methods
//有一个公有的方法,名为onClick,参数为View对象引用,无返回值
.method public onClick(Landroid/view/View;)V
//该方法寄存器个数10
.locals 10
//该方法参数p1的名为v,类型为View对象引用
.param p1, "v" # Landroid/view/View;
//该方法的代码开始
.prologue
//寄存器赋值v9 = 0
const/4 v9, 0x0
//寄存器赋值 v8 = ""
const-string v8, ""
.line 53
//对象类型转换,将参数p1转换成Button类型
check-cast p1, Landroid/widget/Button;
.end local p1 # "v":Landroid/view/View;
//获取外部类的this指针给v6
//v6 = LisenceCheck$1.this$0
iget-object v6, p0, Lcom/mstar/test/LisenceCheck$1;->this$0:Lcom/mstar/test/LisenceCheck;
//获取外部类的数据成员mbutton给v6
//v6 = LisenceCheck.mbutton
iget-object v6, v6, Lcom/mstar/test/LisenceCheck;->mbutton:Landroid/widget/Button;
//如果p1不等于n6跳转到 :cond_5标号处
if-ne p1, v6, :cond_5
.line 55
//new了String给v4
new-instance v4, Ljava/lang/String;
//寄存器赋值v6 = ""
const-string v6, ""
//构造刚才new的String对象,参数为v8
invoke-direct {v4, v8}, Ljava/lang/String;-><init>(Ljava/lang/String;)V
.line 56
//刚才new的String对象变量名为s1
.local v4, "s1":Ljava/lang/String;
//获取外部类this指针
iget-object v6, p0, Lcom/mstar/test/LisenceCheck$1;->this$0:Lcom/mstar/test/LisenceCheck;
//获取外部类的数据成员meditun给v6
//v6 = LisenceCheck.meditun
iget-object v6, v6, Lcom/mstar/test/LisenceCheck;->meditun:Landroid/widget/EditText;
//调用EditText类的getText方法,返回值为Editable对象引用
invoke-virtual {v6}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
//获取返回值保存到v6
move-result-object v6
//调用接口类的toString方法,返回值为String对象引用
invoke-interface {v6}, Landroid/text/Editable;->toString()Ljava/lang/String;
//获取返回值保存到v4
move-result-object v4
.line 57
//new了一String保存到v5
new-instance v5, Ljava/lang/String;
//寄存器赋值 v6 = ""
const-string v6, ""
//构造刚才new的String,参数为String对象引用
invoke-direct {v5, v8}, Ljava/lang/String;-><init>(Ljava/lang/String;)V
.line 58
//刚才new的String变量名为s2
.local v5, "s2":Ljava/lang/String;
//获取外部类this指针
iget-object v6, p0, Lcom/mstar/test/LisenceCheck$1;->this$0:Lcom/mstar/test/LisenceCheck;
//获取外部类的数据成员meditsn给v6
//v6 = LisenceCheck.meditsn
iget-object v6, v6, Lcom/mstar/test/LisenceCheck;->meditsn:Landroid/widget/EditText;
//调用EditText类的getText方法,返回值为Editable对象引用
invoke-virtual {v6}, Landroid/widget/EditText;->getText()Landroid/text/Editable;
//获取返回值保存到v6
move-result-object v6
//调用接口类的toString方法,返回值为String对象引用
invoke-interface {v6}, Landroid/text/Editable;->toString()Ljava/lang/String;
//获取返回值保存到v5
move-result-object v5
.line 60
//寄存器赋值 v1 = 0
const/4 v1, 0x0
//寄存器v1的变量名为i,类型为int
.local v1, "i":I
//寄存器赋值 v2 = 0
const/4 v2, 0x0
.line 62
//寄存器v2的变量名为k1,类型为int
.local v2, "k1":I
//寄存器赋值 v1 = 0
const/4 v1, 0x0
//标号,用于goto 标号
:goto_0
//调用String的length方法,返回值为int
//.local v4, "s1":Ljava/lang/String;
invoke-virtual {v4}, Ljava/lang/String;->length()I
//获取返回值保存到v6
move-result v6
//如果v1小于v6则跳转到:cond_1
//v1 = 0
if-lt v1, v6, :cond_1
.line 69
//标号
:cond_0
//异或 v2 = v2 ^ 0x5678
xor-int/lit16 v2, v2, 0x5678
.line 72
//v3 = 0
const/4 v3, 0x0
.line 73
//寄存器v3的变量名为k2,类型为int
.local v3, "k2":I
//v1 = 0
const/4 v1, 0x0
//标号
:goto_1
//调用String的length方法,返回值为int
//.local v5, "s2":Ljava/lang/String;
invoke-virtual {v5}, Ljava/lang/String;->length()I
//获取返回值到v6
move-result v6
//如果v1小于v6则跳转到:cond_3
//v1 = 0
if-lt v1, v6, :cond_3
.line 78
//异或 v3 = v3 ^ 0x1234
xor-int/lit16 v3, v3, 0x1234
.line 80
//如果v2不等于v3则跳转到:cond_4
if-ne v2, v3, :cond_4
.line 81
//获取外部类this指针
iget-object v6, p0, Lcom/mstar/test/LisenceCheck$1;->this$0:Lcom/mstar/test/LisenceCheck;
//调用外部类的getApplicationContext方法,返回值为Context对象引用
invoke-virtual {v6}, Lcom/mstar/test/LisenceCheck;->getApplicationContext()Landroid/content/Context;
//获取返回值到v6
move-result-object v6
//v7 = "Lisence Correct\uff01"
const-string v7, "Lisence Correct\uff01"
//调用Toast类的makeText静态方法
//第一个参数Context对象引用
//第二个参数CharSequence对象引用
//第三个参数int
//返回值Toast对象引用
invoke-static {v6, v7, v9}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
//获取返回值到v6
move-result-object v6
//调用Toast类的show方法,无返回值
invoke-virtual {v6}, Landroid/widget/Toast;->show()V
.line 92
.end local v1 # "i":I
.end local v2 # "k1":I
.end local v3 # "k2":I
.end local v4 # "s1":Ljava/lang/String;
.end local v5 # "s2":Ljava/lang/String;
//标号
:goto_2
//函数退出,无返回值
return-void
.line 64
.restart local v1 # "i":I
.restart local v2 # "k1":I
.restart local v4 # "s1":Ljava/lang/String;
.restart local v5 # "s2":Ljava/lang/String;
//标号
:cond_1
//调用String类的charAt方法,参数为int,返回值为char
invoke-virtual {v4, v1}, Ljava/lang/String;->charAt(I)C
//获取返回值到v0
move-result v0
.line 65
//v0的变量名为ch, 类型为char
.local v0, "ch":C
//v6 = 0x41
const/16 v6, 0x41
//如果v0小于v6则跳转到:cond_0
if-lt v0, v6, :cond_0
.line 66
//v6 = 0x5a
const/16 v6, 0x5a
//如果v0小于等于v6则跳转到:cond_2
if-le v0, v6, :cond_2
//v6 = 0x20
const/16 v6, 0x20
//v6 = v0 - v6
sub-int v6, v0, v6
//将(v6)int类型转成char保存到v0
int-to-char v0, v6
.line 67
//标号
:cond_2
//v2 = v2 + v0
add-int/2addr v2, v0
.line 62
//v1 = v1 + 1
add-int/lit8 v1, v1, 0x1
//无条件跳转到:goto_0
goto :goto_0
.line 74
.end local v0 # "ch":C
.restart local v3 # "k2":I
//标号
:cond_3
//调用String类的charAt方法,参数为int,返回值为char
invoke-virtual {v5, v1}, Ljava/lang/String;->charAt(I)C
//获取返回值到v0
move-result v0
.line 75
//v0的变量名还是ch
.restart local v0 # "ch":C
//v6 = 0x30
const/16 v6, 0x30
//v6 = v0 - v6
sub-int v6, v0, v6
//将(v6)int类型转成char保存到v0
int-to-char v0, v6
.line 76
//v6 = v3 * 0xa
mul-int/lit8 v6, v3, 0xa
//v3 = v6 + v0
add-int v3, v6, v0
.line 73
//v1 = v1 + 1
add-int/lit8 v1, v1, 0x1
//无条件跳转到:goto_1
goto :goto_1
.line 83
.end local v0 # "ch":C
//标号
:cond_4
//获取外部类this指针
iget-object v6, p0, Lcom/mstar/test/LisenceCheck$1;->this$0:Lcom/mstar/test/LisenceCheck;
//调用外部类getApplicationContext方法,返回值为Context对象引用
invoke-virtual {v6}, Lcom/mstar/test/LisenceCheck;->getApplicationContext()Landroid/content/Context;
//获取返回值到v6
move-result-object v6
//v7 = "Lisence Uncorrect\uff01" \uff01 == !
const-string v7, "Lisence Uncorrect\uff01"
//调用Toast类的makeText静态方法
//第一个参数Context对象引用
//第二个参数CharSequence对象引用
//第三个参数int
//返回值Toast对象引用
invoke-static {v6, v7, v9}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;
//获取返回值到v6
move-result-object v6
//调用Toast类的show方法,无返回值
invoke-virtual {v6}, Landroid/widget/Toast;->show()V
//无条件跳转到:goto_2
goto :goto_2
.line 88
.end local v1 # "i":I
.end local v2 # "k1":I
.end local v3 # "k2":I
.end local v4 # "s1":Ljava/lang/String;
.end local v5 # "s2":Ljava/lang/String;
//标号
//此标号往下是将用于输入帐号和密码的EditText内容清空,然后程序退出
:cond_5
//获取外部类this指针
iget-object v6, p0, Lcom/mstar/test/LisenceCheck$1;->this$0:Lcom/mstar/test/LisenceCheck;
//获取外部类数据成员meditun到v6
iget-object v6, v6, Lcom/mstar/test/LisenceCheck;->meditun:Landroid/widget/EditText;
//v7 = ""
const-string v7, ""
//调用EditText类的setText方法,参数为CharSequence对象引用,无返回值
invoke-virtual {v6, v8}, Landroid/widget/EditText;->setText(Ljava/lang/CharSequence;)V
.line 89
//获取外部类this指针
iget-object v6, p0, Lcom/mstar/test/LisenceCheck$1;->this$0:Lcom/mstar/test/LisenceCheck;
//获取外部类数据成员meditsn到v6
iget-object v6, v6, Lcom/mstar/test/LisenceCheck;->meditsn:Landroid/widget/EditText;
//v7 = ""
const-string v7, ""
//调用EditText类的setText方法,参数为CharSequence对象引用,无返回值
invoke-virtual {v6, v8}, Landroid/widget/EditText;->setText(Ljava/lang/CharSequence;)V
//无条件跳转到:goto_2
goto :goto_2
.end method
通过对smali代码的阅读,模拟还原了下这个CrackMe的算法C代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char szName[64] = {0};
char szKey[64] = {0};
char ch = '\0';
int k1 = 0;
int k2 = 0;
printf("请输入用户名: ");
scanf("%63s", szName);
fflush(stdin);
printf("请输入注册码: ");
scanf("%63s", szKey);
for (unsigned int i = 0; i < strlen(szName); i++)
{
ch = szName[i];
if (ch < 0x41)
{
break;
}
if (ch <= 0x5a)
{
k1 += ch;
continue;
}
k1 = k1 + ch - 0x20;
}
for (i = 0; i < strlen(szKey); i++)
{
ch = szKey[i];
k2 = (k2 * 0xa) + (ch - 0x30);
}
if ((k1 ^ 0x5678) == (k2 ^ 0x1234))
{
puts("注册成功");
}
else
{
puts("注册失败");
}
system("pause");
return 0;
}
已知算法后也就可以写出注册机:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char szName[64] = {0};
char ch = '\0';
int k1 = 0;
printf("请输入用户名: ");
scanf("%63s", szName);
for (unsigned int i = 0; i < strlen(szName); i++)
{
ch = szName[i];
if (ch < 0x41)
{
break;
}
if (ch <= 0x5a)
{
k1 += ch;
continue;
}
k1 = k1 + ch - 0x20;
}
printf("注册码: %d\r\n", (k1 ^ 0x5678) ^ 0x1234);
system("pause");
return 0;
}
附一张注册成功截图:
由于刚开始学习,如果有写的不对的地方,希望大家可以告诉我一下,谢谢。附件中打包了apk和注释的.smali文件。
CrackMe.apk.rar
smali.rar
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课