打开app首先映入眼前是一个常规的登录界面,输入用户名和密码,提示Login filed.显然用户名和密码随便输入肯定是不行了。
首先我们用objection定位到他的页面位置activity,再结合反编译工具对源码进行分析。
1
2
3
4
|
adb shell dumpsys window | grep CurrentFocus
#返回
mCurrentFocus
=
Window{
5b2914b
u0 com.example.androiddemo
/
com.example.androiddemo.Activity.LoginActivity}
|
可以发现当前类的类已经找到了,objection hook这个类,再触发按钮,观察情况,打印出调用的方法,再hook之。
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
|
android hooking
list
activities
#返回
com.example.androiddemo.Activity.BaseFridaActivity
com.example.androiddemo.Activity.FridaActivity1
com.example.androiddemo.Activity.FridaActivity2
com.example.androiddemo.Activity.FridaActivity3
com.example.androiddemo.Activity.FridaActivity4
com.example.androiddemo.Activity.FridaActivity5
com.example.androiddemo.Activity.FridaActivity6
com.example.androiddemo.Activity.FridaActivity7
com.example.androiddemo.Activity.LoginActivity
com.example.androiddemo.MainActivity
android hooking watch
class
com.example.androiddemo.Activity.LoginActivity
-
-
dump
-
args
-
-
dump
-
backtrace
-
-
dump
-
return
#返回
(agent) [vv23m49gobd] Called com.example.androiddemo.Activity.LoginActivity.access$
100
(java.lang.Strinjava.lang.String)
(agent) [vv23m49gobd] Called com.example.androiddemo.Activity.LoginActivity.a(java.lang.String, java.lang.String)
(agent) [vv23m49gobd] Called com.example.androiddemo.Activity.LoginActivity.a([B)
(agent) [vv23m49gobd] Called com.example.androiddemo.Activity.LoginActivity.access$
000
(com.example.androiddemo.Activity.LoginActivity)
android hooking watch class_method com.example.androiddemo.Activity.LoginActivity.a
-
-
dump
-
args
-
-
dump
-
return
#返回:
(agent) [
810lsp00gmj
] Arguments com.example.androiddemo.Activity.LoginActivity.a([
object
Object
])
(agent) [
810lsp00gmj
] Return Value:
82d476df642d6c882dcc438e028c6e0908af286439b7cd18975dc971387eb33a
(agent) [
810lsp00gmj
] Return Value:
82d476df642d6c882dcc438e028c6e0908af286439b7cd18975dc971387eb33a
|
通过返回值可以大胆猜测,密码经过了加密,将返回值输入的确通过了第一关,不过还需要结合源码分析下。
发现是 HmacSHA256 加密,然后我们可以输入加密对比下结果没问题,输入密码进入下一关。
界面提示点击进入下一关,肯定没那么简单,不出意外提示报错了。
这里只能结合源码分析了。
将 R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL= 反向还原得到输入的密码,再用frida hook住b方法,则可进入到下一题。
a()b()方法的源码:
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
|
public static String a(byte[] bArr) throws Exception {
StringBuilder sb
=
new StringBuilder();
for
(
int
i
=
0
; i <
=
bArr.length
-
1
; i
+
=
3
) {
byte[] bArr2
=
new byte[
4
];
byte b
=
0
;
for
(
int
i2
=
0
; i2 <
=
2
; i2
+
+
) {
int
i3
=
i
+
i2;
if
(i3 <
=
bArr.length
-
1
) {
bArr2[i2]
=
(byte) (b | ((bArr[i3] &
255
) >>> ((i2
*
2
)
+
2
)));
b
=
(byte) ((((bArr[i3] &
255
) << (((
2
-
i2)
*
2
)
+
2
)) &
255
) >>>
2
);
}
else
{
bArr2[i2]
=
b;
b
=
64
;
}
}
bArr2[
3
]
=
b;
for
(
int
i4
=
0
; i4 <
=
3
; i4
+
+
) {
if
(bArr2[i4] <
=
63
) {
sb.append(table[bArr2[i4]]);
}
else
{
sb.append(
'='
);
}
}
}
return
sb.toString();
}
public static byte[] b(String
str
) {
try
{
ByteArrayOutputStream byteArrayOutputStream
=
new ByteArrayOutputStream();
GZIPOutputStream gZIPOutputStream
=
new GZIPOutputStream(byteArrayOutputStream);
gZIPOutputStream.write(
str
.getBytes());
gZIPOutputStream.finish();
gZIPOutputStream.close();
byte[] byteArray
=
byteArrayOutputStream.toByteArray();
try
{
byteArrayOutputStream.close();
return
byteArray;
} catch (Exception e) {
e.printStackTrace();
return
byteArray;
}
} catch (Exception unused) {
return
null;
}
}
|
正规的算法还原,这里直接Hook通过了,后续在还原这个算法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var FridaActivity1
=
Java.use(
"com.example.androiddemo.Activity.FridaActivity1"
);
FridaActivity1[
"b"
].implementation
=
function (
str
) {
console.log(
"FridaActivity1.b is called: str=${str}"
,
str
);
var result
=
this[
"b"
](
str
);
console.log(
"FridaActivity1.b result=${result}"
, JSON.stringify(result));
return
result;
};
FridaActivity1[
"a"
].implementation
=
function (bArr) {
console.log(
"FridaActivity1.a is called: bArr=${bArr}"
, JSON.stringify(bArr));
var result
=
this[
"a"
](bArr);
console.log(
"FridaActivity1.a result=${result}"
, result);
result
=
'R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL='
return
result;
};
|
修改 静态成员变量和非静态成员变量的值 即可通过,但是给出来了2个函数,出题人的想法应该是让我们触发按钮的时候,主动调用这2个函数,来改变成员变量的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
var FridaActivity2
=
Java.use(
"com.example.androiddemo.Activity.FridaActivity2"
);
FridaActivity2[
"onCheck"
].implementation
=
function () {
Java.choose(
"com.example.androiddemo.Activity.FridaActivity2"
, {
onMatch: function (instance) {
instance.setStatic_bool_var();
instance.setBool_var();
},
onComplete: function () { }
})
this[
"onCheck"
]();
};
|
观察代码:
只要将三个成员变量的值改为true 即可通向下一关。
1
2
3
4
5
6
7
8
9
10
|
var FridaActivity3
=
Java.use(
"com.example.androiddemo.Activity.FridaActivity3"
);
/
/
设置静态成员变量的值
FridaActivity3.static_bool_var.value
=
true;
Java.choose(
"com.example.androiddemo.Activity.FridaActivity3"
,{
onMatch:function(instance){
instance.bool_var.value
=
true;
instance._same_name_bool_var.value
=
true;
},
onComplete:function(){}
})
|
看源码:
将check的值全部改为 true,即可通过下一关。
1
2
3
4
5
6
7
8
9
10
11
12
|
var InnerClasses
=
Java.use(
"com.example.androiddemo.Activity.FridaActivity4$InnerClasses"
);
InnerClasses[
"check1"
].implementation
=
function () {
var result
=
this[
"check1"
]();
result
=
true;
return
result;
};
InnerClasses[
"check2"
].implementation
=
function () {
var result
=
this[
"check2"
]();
result
=
true;
return
result;
};
...
|
代码:
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
|
function hook_dyn_dex() {
Java.perform(function () {
var FridaActivity5
=
Java.use(
"com.example.androiddemo.Activity.FridaActivity5"
);
Java.choose(
"com.example.androiddemo.Activity.FridaActivity5"
, {
onMatch: function (instance) {
console.log(instance.getDynamicDexCheck().$className);
}, onComplete: function () {
}
});
/
/
hook 动态加载的dex
Java.enumerateClassLoaders({
onMatch: function (loader) {
try
{
if
(loader.findClass(
"com.example.androiddemo.Dynamic.DynamicCheck"
)) {
console.log(loader);
Java.classFactory.loader
=
loader;
/
/
切换classloader
}
} catch (error) {
}
}, onComplete: function () {
}
});
var DynamicCheck
=
Java.use(
"com.example.androiddemo.Dynamic.DynamicCheck"
);
console.log(DynamicCheck);
DynamicCheck.check.implementation
=
function () {
console.log(
"DynamicCheck.check"
);
return
true;
}
});
}
}
|
代码:
将调用的三个class的方法改成true,即可通关。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
var Frida6Class0
=
Java.use(
"com.example.androiddemo.Activity.Frida6.Frida6Class0"
);
Frida6Class0[
"check"
].implementation
=
function () {
var result
=
this[
"check"
]();
result
=
true;
return
result;
};
var Frida6Class1
=
Java.use(
"com.example.androiddemo.Activity.Frida6.Frida6Class1"
);
Frida6Class1[
"check"
].implementation
=
function () {
var result
=
this[
"check"
]();
result
=
true;
return
result;
};
var Frida6Class2
=
Java.use(
"com.example.androiddemo.Activity.Frida6.Frida6Class2"
);
Frida6Class2[
"check"
].implementation
=
function () {
var result
=
this[
"check"
]();
result
=
true;
return
result;
};
|
更多【frida基础 闯关训练 七层关卡】相关视频教程:www.yxfzedu.com