0%

Android破解检验

简单程序破解

对于简单的字符串类型的校验,客户端的校验破解起来时比较简单的。
java层代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MainActivity extends Activity {

private Button btn_login;
private EditText username;
private EditText password;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_login = (Button) findViewById(R.id.btn_Login);
username = (EditText) findViewById(R.id.username);
password = (EditText) findViewById(R.id.password);
btn_login.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
if(username.getText().toString().equals("wker") && password.getText().toString().equals("wfc"))
{
Toast.makeText(MainActivity.this, "success", Toast.LENGTH_SHORT).show();
}else
{
Toast.makeText(MainActivity.this, "false", Toast.LENGTH_SHORT).show();
}
}
});
}
}

没什么特别的只是单纯的进行了一个字符串的校验,校验完毕就弹出一个toast。

逆向分析

我们先用逆向的工具加载APK,Android Killer就够了,我们得到这个样子的一个文件视图:
文件视图
可以看到,在我们的MainActivity中分出了两个smali文件,一个是我们的内部类,其实我们也就知道了,也就是我们在点击响应事件中的那个内部类,所以我们就直接分析就好了。

1
2
3
.class Lcom/pj/crack_one/MainActivity$1;
.super Ljava/lang/Object;
.source "MainActivity.java"

这个就是简单的声明了一下类的一些信息,名称啊,父类啊之类的。

1
2
# interfaces
.implements Landroid/view/View$OnClickListener;

实现的接口。

1
2
3
4
5
6
7
8
9
# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
value = Lcom/pj/crack_one/MainActivity;->onCreate(Landroid/os/Bundle;)V
.end annotation

.annotation system Ldalvik/annotation/InnerClass;
accessFlags = 0x0
name = null
.end annotation

这个貌似是一个注解,具体是什么意思暂时不了解,不影响。

1
2
# instance fields
.field final synthetic this$0:Lcom/pj/crack_one/MainActivity;

这个就是一个实例对象,也就是外部类自身。

1
2
3
4
5
6
7
8
9
10
11
12
13
# direct methods
.method constructor <init>(Lcom/pj/crack_one/MainActivity;)V
.locals 0

.prologue
.line 1
iput-object p1, p0, Lcom/pj/crack_one/MainActivity$1;->this$0:Lcom/pj/crack_one/MainActivity;

.line 25
invoke-direct {p0}, Ljava/lang/Object;-><init>()V

return-void
.end method

这个就是构造方法,可以看到除了初始的那个无参构造方法,他在前面添加了一个赋值操作,将p1寄存器的值赋值为实例对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# virtual methods
.method public onClick(Landroid/view/View;)V
.locals 3
.param p1, "v" # Landroid/view/View;

.prologue
const/4 v2, 0x0

.line 29
iget-object v0, p0, Lcom/pj/crack_one/MainActivity$1;->this$0:Lcom/pj/crack_one/MainActivity;

# getter for: Lcom/pj/crack_one/MainActivity;->username:Landroid/widget/EditText;
invoke-static {v0}, Lcom/pj/crack_one/MainActivity;->access$0(Lcom/pj/crack_one/MainActivity;)Landroid/widget/EditText;

move-result-object v0

invoke-virtual {v0}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

move-result-object v0

invoke-interface {v0}, Landroid/text/Editable;->toString()Ljava/lang/String;

move-result-object v0

const-string v1, "wker"

invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

move-result v0

if-eqz v0, :cond_0

iget-object v0, p0, Lcom/pj/crack_one/MainActivity$1;->this$0:Lcom/pj/crack_one/MainActivity;

# getter for: Lcom/pj/crack_one/MainActivity;->password:Landroid/widget/EditText;
invoke-static {v0}, Lcom/pj/crack_one/MainActivity;->access$1(Lcom/pj/crack_one/MainActivity;)Landroid/widget/EditText;

move-result-object v0

invoke-virtual {v0}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

move-result-object v0

invoke-interface {v0}, Landroid/text/Editable;->toString()Ljava/lang/String;

move-result-object v0

const-string v1, "wfc"

invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

move-result v0

if-eqz v0, :cond_0

.line 31
iget-object v0, p0, Lcom/pj/crack_one/MainActivity$1;->this$0:Lcom/pj/crack_one/MainActivity;

const-string v1, "success"

invoke-static {v0, v1, 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

.line 36
:goto_0
return-void

.line 34
:cond_0
iget-object v0, p0, Lcom/pj/crack_one/MainActivity$1;->this$0:Lcom/pj/crack_one/MainActivity;

const-string v1, "false"

invoke-static {v0, v1, 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

goto :goto_0
.end method

这个就是我们关注的重点。
首先我们用到了3个寄存器

1
.param p1, "v"    # Landroid/view/View;

参数的一个名字,传进来一个View对象

1
2
.prologue
const/4 v2, 0x0

代码开始,并且将v2寄存器赋值为0x0

1
iget-object v0, p0, Lcom/pj/crack_one/MainActivity$1;->this$0:Lcom/pj/crack_one/MainActivity;

这个就是将v0赋值为我们p0指向的this,其实也就是外部类的对象。

1
2
3
invoke-static {v0}, Lcom/pj/crack_one/MainActivity;->access$0(Lcom/pj/crack_one/MainActivity;)Landroid/widget/EditText;

move-result-object v0

这个用到了access这个方法,这个方法其实就是在内部类调用外部类私有成员的时候自动生成的。

1
2
3
4
5
6
7
8
9
.method static synthetic access$0(Lcom/pj/crack_one/MainActivity;)Landroid/widget/EditText;
.locals 1

.prologue
.line 14
iget-object v0, p0, Lcom/pj/crack_one/MainActivity;->username:Landroid/widget/EditText;

return-object v0
.end method

其实可以看出,也就是返回了username这个编辑框的对象。

1
2
3
4
5
6
7
invoke-virtual {v0}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

move-result-object v0

invoke-interface {v0}, Landroid/text/Editable;->toString()Ljava/lang/String;

move-result-object v0

这个就是一样的东西,就是获取Text,然后给v0,然后获得toString,再给v0。

1
2
3
4
5
const-string v1, "wker"

invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

move-result v0

这个就是给v1赋值之后,将其与v0比较,然后将得到的结果给v0,也就是比较的返回值。

1
if-eqz v0, :cond_0

这个就是判断如果是0的话呢就跳转,也就是打印错误的。
下面判断密码的代码是基本一样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

.line 34
:cond_0
iget-object v0, p0, Lcom/pj/crack_one/MainActivity$1;->this$0:Lcom/pj/crack_one/MainActivity;

const-string v1, "false"

invoke-static {v0, v1, 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

goto :goto_0

可以看到如果跳到cond_0的话呢就是打印错误,所以最简单的方法就是,将两个if语句干掉,就不会跳转,而是直接一直往下执行,执行到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.line 31
iget-object v0, p0, Lcom/pj/crack_one/MainActivity$1;->this$0:Lcom/pj/crack_one/MainActivity;

const-string v1, "success"

invoke-static {v0, v1, 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

.line 36
:goto_0
return-void

也就执行打印成功,最后返回了。
最后编译,结果和我们预想的是一样的。