这是2020年Google CTF资格赛中的一道安卓reverse题,纯java,没有so。虽然很简单,但因为是我第一次做题,所以并不顺利。在此给大家分享一下我的思路,也算是一次总结吧。
首先使用特征函数定位——OnClick
使用jadx反编译apk(无壳无混淆),搜索OnClick
,定位到了验证的位置
验证部分所在文件的完整代码如下:
"验证成功"代表验证成功,两处地方有"验证成功",我们先分析上面一个"验证成功"附近的代码。
代码如下:
代码解读:
该部分代码根据作用分为两部分,第一部分生成Key
,第二部分验证Key
。
第一部分
首先构造一个StringBuilder
来存放Key
,接着new
了一个Object对象数组并初始化,循环取数组中的元素强转为Character
类型,并调用charValue()
方法得到字符添加到Key
中。
第一部分会出现java.lang.ClassCastException
异常,java.lang.Integer
类不能转换为java.lang.Character
类,因为他们并不存在子父类关系,所以此处是一个障眼法。
通过代码发现,出现的异常的代码被try-catch
结构包裹了起来,所以出现的异常会被捕获,所以我们继续看catch
部分的代码
代码如下:
代码解读:
第一部分:flag初步处理
首先得到的信息是flag的长度必须为48,紧接着是for
循环,每次对flag中的4个字符进行如下处理后,放到this.f2[i]
中
补充一个信息:
this.f2
在ActivityC0000
的构造函数中被new了12个元素;this.f0class
被初始化,一共12个元素;this.f1
被初始化为0
所以对flag
初步处理后,我们得到了12个long
型数据
第二部分:flag进一步处理
代码中用到的递归函数R.m0(a,b)
的代码如下:
首先是把上面flag初步处理后得到的数据this.f2
中的一个元素(this.f1
作为索引)和4294967296L一起放入递归函数R.m0(a,b)
中进行处理,返回一个long
型数组,并取其中的第一个元素,接着把这第一个元素做一系列的算术运算,将最终得到的运算结果和this.f0class
中的元素(this.f1
作为索引)进行对比,如果不相等则验证失败。如果验证成功,则继续第三部分
第三部分:判断flag是否验证完毕
第二部分中的验证,每次只验证1个元素(即验证flag的4个字符),所以需要循环12次才能验证完毕
this.f1
在第二部分作为数组索引,同时在这里也作为循环判断条件,this.f2.length
是12,如果循环次数<
12,则主动抛出异常,但没有捕获异常,到这里程序就崩了。站在程序设计者的角度来看,抛出异常之后,应该捕获异常,然后继续验证flag。所以我觉得异常应该是被捕获并处理的了,只是可能反编译出来的Java
代码有问题,所以直接看smali
代码
这是抛出异常throw new RuntimeException();
附近的代码,我们发现,异常是被catch_11
捕获了的
在catch_11
处,我们发现又直接跳转到goto_205
我们发现goto_205
处的代码正是第一部分:flag初步处理处的代码,所以循环成立,flag的循环验证通过异常来进行循环的
通过上面的分析,可以得出flag的验证流程图如下:
通过分析可知,我们的flag是由12组,每组4个字符组成的。每次循环取其中的4个字符通过一系列的运算,最终得到一个结果并与数组{40999019, 2789358025L, 656272715, 18374979, 3237618335L, 1762529471, 685548119, 382114257, 1436905469, 2126016673, 3318315423L, 797150821}
中的元素做比较,一共12次循环。
我采用暴力枚举的方式算出flag,算法如下:
之所以采用ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?\_{}
作为迭代数,是因为这些是flag中常见的字符
最终得到的flag为CTF{y0u_c4n_k3ep_y0u?_m4gic_1_h4Ue_laser_b3ams!}
,说一个小技巧,adb shell input text "CTF{y0u_c4n_k3ep_y0u?_m4gic_1_h4Ue_laser_b3ams!}"
可以快速输入字符到编辑框
Google CTF 2020: Android
apk见附件
package com.google.ctf.sandbox;
import
android.app.Activity;
import
android.os.Bundle;
import
android.view.View;
import
android.widget.Button;
import
android.widget.EditText;
import
android.widget.TextView;
/
*
renamed
from
: com.google.ctf.sandbox.ő reason: contains
not
printable characters
*
/
public
class
ActivityC0000 extends Activity {
/
*
renamed
from
:
class
reason:
not
valid java name
*
/
long
[] f0class;
/
*
renamed
from
: ő reason: contains
not
printable characters
*
/
int
f1;
/
*
renamed
from
: ő reason: contains
not
printable characters
and
collision with other field name
*
/
long
[] f2;
public ActivityC0000() {
try
{
this.f0class
=
new
long
[]{
40999019
,
2789358025L
,
656272715
,
18374979
,
3237618335L
,
1762529471
,
685548119
,
382114257
,
1436905469
,
2126016673
,
3318315423L
,
797150821
};
this.f2
=
new
long
[
12
];
this.f1
=
0
;
} catch (I unused) {
}
}
/
*
access modifiers changed
from
: protected
*
/
public void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText editText
=
(EditText) findViewById(R.
id
.editText);
final TextView textView
=
(TextView) findViewById(R.
id
.textView);
((Button) findViewById(R.
id
.button)).setOnClickListener(new View.OnClickListener() {
/
*
class
com.google.ctf.sandbox.ActivityC0000.AnonymousClass1
*
/
public void onClick(View v) {
ActivityC0000.this.f1
=
0
;
try
{
StringBuilder keyString
=
new StringBuilder();
for
(
Object
chr
: new
Object
[]{
65
,
112
,
112
,
97
,
114
,
101
,
110
,
116
,
108
,
121
,
32
,
116
,
104
,
105
,
115
,
32
,
105
,
115
,
32
,
110
,
111
,
116
,
32
,
116
,
104
,
101
,
32
,
102
,
108
,
97
,
103
,
46
,
32
,
87
,
104
,
97
,
116
,
39
,
115
,
32
,
103
,
111
,
105
,
110
,
103
,
32
,
111
,
110
,
63
}) {
keyString.append(((Character)
chr
).charValue());
}
if
(editText.getText().toString().equals(keyString.toString())) {
textView.setText(
"验证成功"
);
}
else
{
textView.setText(
"验证失败"
);
}
} catch (J | Error | Exception unused) {
String flagString
=
editText.getText().toString();
if
(flagString.length() !
=
48
) {
textView.setText(
"验证失败"
);
return
;
}
for
(
int
i
=
0
; i < flagString.length()
/
4
; i
+
+
) {
ActivityC0000.this.f2[i]
=
(
long
) (flagString.charAt((i
*
4
)
+
3
) <<
24
);
long
[] jArr
=
ActivityC0000.this.f2;
jArr[i]
=
jArr[i] | ((
long
) (flagString.charAt((i
*
4
)
+
2
) <<
16
));
long
[] jArr2
=
ActivityC0000.this.f2;
jArr2[i]
=
jArr2[i] | ((
long
) (flagString.charAt((i
*
4
)
+
1
) <<
'\b'
));
long
[] jArr3
=
ActivityC0000.this.f2;
jArr3[i]
=
jArr3[i] | ((
long
) flagString.charAt(i
*
4
));
}
ActivityC0000 r6
=
ActivityC0000.this;
if
(((R.m0(ActivityC0000.this.f2[ActivityC0000.this.f1],
4294967296L
)[
0
]
%
4294967296L
)
+
4294967296L
)
%
4294967296L
!
=
ActivityC0000.this.f0class[ActivityC0000.this.f1]) {
textView.setText(
"验证失败"
);
return
;
}
ActivityC0000.this.f1
+
+
;
if
(ActivityC0000.this.f1 >
=
ActivityC0000.this.f2.length) {
textView.setText(
"验证成功"
);
return
;
}
throw new RuntimeException();
}
}
});
}
}
package com.google.ctf.sandbox;
import
android.app.Activity;
import
android.os.Bundle;
import
android.view.View;
import
android.widget.Button;
import
android.widget.EditText;
import
android.widget.TextView;
/
*
renamed
from
: com.google.ctf.sandbox.ő reason: contains
not
printable characters
*
/
public
class
ActivityC0000 extends Activity {
/
*
renamed
from
:
class
reason:
not
valid java name
*
/
long
[] f0class;
/
*
renamed
from
: ő reason: contains
not
printable characters
*
/
int
f1;
/
*
renamed
from
: ő reason: contains
not
printable characters
and
collision with other field name
*
/
long
[] f2;
public ActivityC0000() {
try
{
this.f0class
=
new
long
[]{
40999019
,
2789358025L
,
656272715
,
18374979
,
3237618335L
,
1762529471
,
685548119
,
382114257
,
1436905469
,
2126016673
,
3318315423L
,
797150821
};
this.f2
=
new
long
[
12
];
this.f1
=
0
;
} catch (I unused) {
}
}
/
*
access modifiers changed
from
: protected
*
/
public void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final EditText editText
=
(EditText) findViewById(R.
id
.editText);
final TextView textView
=
(TextView) findViewById(R.
id
.textView);
((Button) findViewById(R.
id
.button)).setOnClickListener(new View.OnClickListener() {
/
*
class
com.google.ctf.sandbox.ActivityC0000.AnonymousClass1
*
/
public void onClick(View v) {
ActivityC0000.this.f1
=
0
;
try
{
StringBuilder keyString
=
new StringBuilder();
for
(
Object
chr
: new
Object
[]{
65
,
112
,
112
,
97
,
114
,
101
,
110
,
116
,
108
,
121
,
32
,
116
,
104
,
105
,
115
,
32
,
105
,
115
,
32
,
110
,
111
,
116
,
32
,
116
,
104
,
101
,
32
,
102
,
108
,
97
,
103
,
46
,
32
,
87
,
104
,
97
,
116
,
39
,
115
,
32
,
103
,
111
,
105
,
110
,
103
,
32
,
111
,
110
,
63
}) {
keyString.append(((Character)
chr
).charValue());
}
if
(editText.getText().toString().equals(keyString.toString())) {
textView.setText(
"验证成功"
);
}
else
{
textView.setText(
"验证失败"
);
}
} catch (J | Error | Exception unused) {
String flagString
=
editText.getText().toString();
if
(flagString.length() !
=
48
) {
textView.setText(
"验证失败"
);
return
;
}
for
(
int
i
=
0
; i < flagString.length()
/
4
; i
+
+
) {
ActivityC0000.this.f2[i]
=
(
long
) (flagString.charAt((i
*
4
)
+
3
) <<
24
);
long
[] jArr
=
ActivityC0000.this.f2;
jArr[i]
=
jArr[i] | ((
long
) (flagString.charAt((i
*
4
)
+
2
) <<
16
));
long
[] jArr2
=
ActivityC0000.this.f2;
jArr2[i]
=
jArr2[i] | ((
long
) (flagString.charAt((i
*
4
)
+
1
) <<
'\b'
));
long
[] jArr3
=
ActivityC0000.this.f2;
jArr3[i]
=
jArr3[i] | ((
long
) flagString.charAt(i
*
4
));
}
ActivityC0000 r6
=
ActivityC0000.this;
if
(((R.m0(ActivityC0000.this.f2[ActivityC0000.this.f1],
4294967296L
)[
0
]
%
4294967296L
)
+
4294967296L
)
%
4294967296L
!
=
ActivityC0000.this.f0class[ActivityC0000.this.f1]) {
textView.setText(
"验证失败"
);
return
;
}
ActivityC0000.this.f1
+
+
;
if
(ActivityC0000.this.f1 >
=
ActivityC0000.this.f2.length) {
textView.setText(
"验证成功"
);
return
;
}
throw new RuntimeException();
}
}
});
}
}
try
{
StringBuilder keyString
=
new StringBuilder();
for
(
Object
chr
: new
Object
[]{
65
,
112
,
112
,
97
,
114
,
101
,
110
,
116
,
108
,
121
,
32
,
116
,
104
,
105
,
115
,
32
,
105
,
115
,
32
,
110
,
111
,
116
,
32
,
116
,
104
,
101
,
32
,
102
,
108
,
97
,
103
,
46
,
32
,
87
,
104
,
97
,
116
,
39
,
115
,
32
,
103
,
111
,
105
,
110
,
103
,
32
,
111
,
110
,
63
}) {
keyString.append(((Character)
chr
).charValue());
}
if
(editText.getText().toString().equals(keyString.toString())) {
textView.setText(
"验证成功"
);
}
else
{
textView.setText(
"验证失败"
);
}
} catch (J | Error | Exception unused) {
......
}
try
{
StringBuilder keyString
=
new StringBuilder();
for
(
Object
chr
: new
Object
[]{
65
,
112
,
112
,
97
,
114
,
101
,
110
,
116
,
108
,
121
,
32
,
116
,
104
,
105
,
115
,
32
,
105
,
115
,
32
,
110
,
111
,
116
,
32
,
116
,
104
,
101
,
32
,
102
,
108
,
97
,
103
,
46
,
32
,
87
,
104
,
97
,
116
,
39
,
115
,
32
,
103
,
111
,
105
,
110
,
103
,
32
,
111
,
110
,
63
}) {
keyString.append(((Character)
chr
).charValue());
}
if
(editText.getText().toString().equals(keyString.toString())) {
textView.setText(
"验证成功"
);
}
else
{
textView.setText(
"验证失败"
);
}
} catch (J | Error | Exception unused) {
......
}
String flagString
=
editText.getText().toString();
if
(flagString.length() !
=
48
) {
textView.setText(
"验证失败"
);
return
;
}
for
(
int
i
=
0
; i < flagString.length()
/
4
; i
+
+
) {
ActivityC0000.this.f2[i]
=
(
long
) (flagString.charAt((i
*
4
)
+
3
) <<
24
);
long
[] jArr
=
ActivityC0000.this.f2;
jArr[i]
=
jArr[i] | ((
long
) (flagString.charAt((i
*
4
)
+
2
) <<
16
));
long
[] jArr2
=
ActivityC0000.this.f2;
jArr2[i]
=
jArr2[i] | ((
long
) (flagString.charAt((i
*
4
)
+
1
) <<
'\b'
));
long
[] jArr3
=
ActivityC0000.this.f2;
jArr3[i]
=
jArr3[i] | ((
long
) flagString.charAt(i
*
4
));
}
ActivityC0000 r6
=
ActivityC0000.this;
if
(((R.m0(ActivityC0000.this.f2[ActivityC0000.this.f1],
4294967296L
)[
0
]
%
4294967296L
)
+
4294967296L
)
%
4294967296L
!
=
ActivityC0000.this.f0class[ActivityC0000.this.f1]) {
textView.setText(
"验证失败"
);
return
;
}
ActivityC0000.this.f1
+
+
;
if
(ActivityC0000.this.f1 >
=
ActivityC0000.this.f2.length) {
textView.setText(
"验证成功"
);
return
;
}
throw new RuntimeException();
String flagString
=
editText.getText().toString();
if
(flagString.length() !
=
48
) {
textView.setText(
"验证失败"
);
return
;
}
for
(
int
i
=
0
; i < flagString.length()
/
4
; i
+
+
) {
ActivityC0000.this.f2[i]
=
(
long
) (flagString.charAt((i
*
4
)
+
3
) <<
24
);
long
[] jArr
=
ActivityC0000.this.f2;
jArr[i]
=
jArr[i] | ((
long
) (flagString.charAt((i
*
4
)
+
2
) <<
16
));
long
[] jArr2
=
ActivityC0000.this.f2;
jArr2[i]
=
jArr2[i] | ((
long
) (flagString.charAt((i
*
4
)
+
1
) <<
'\b'
));
long
[] jArr3
=
ActivityC0000.this.f2;
jArr3[i]
=
jArr3[i] | ((
long
) flagString.charAt(i
*
4
));
}
ActivityC0000 r6
=
ActivityC0000.this;
if
(((R.m0(ActivityC0000.this.f2[ActivityC0000.this.f1],
4294967296L
)[
0
]
%
4294967296L
)
+
4294967296L
)
%
4294967296L
!
=
ActivityC0000.this.f0class[ActivityC0000.this.f1]) {
textView.setText(
"验证失败"
);
return
;
}
ActivityC0000.this.f1
+
+
;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-2-5 17:31
被genliese编辑
,原因: