Frida入门练习(一) - 七层关卡

附件:见[原创]frida基础 闯关训练 七层关卡-Android安全-看雪-安全社区|安全招聘|kanxue.com


LoginActivity

onClick()方法中判断了输入不能为空,且要满足LoginActivity.a(obj, obj).equals(obj2)才行,方法如下:

很简单,也就是密码是账号的HmacSha256加密,加密使用的密钥是账号本身。

这里我们使用frida来操作,编写js脚本来hooka()方法,打印我们需要的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function login(){
Java.perform(
function(){
var clazz = Java.use("com.example.androiddemo.Activity.LoginActivity");
clazz.a.overload("java.lang.String", "java.lang.String").implementation = function(str, str2){
var result = this.a(str, str2);
console.log("HmacSHA256: " + result);
return Java.use("java.lang.String").$new(result);
}

}
)
}

setImmediate(login)

输出结果如下:

1
HmacSHA256: 4e4feaea959d426155a480dc07ef92f4754ee93edbe56d993d74f131497e66fb

我们可以通过adb shell input text "xxx"将这段值输入到获得焦点的EditText控件中。

FridaActivity1

整个UI界面就一个按钮可以触发,对应代码如下:

由于我们无法输入,所以得hooka()方法,使其直接返回后面这串字符。

1
2
3
4
5
6
7
8
9
10
11
12
function FridaActivity1(){
Java.perform(
function(){
var clazz = Java.use("com.example.androiddemo.Activity.FridaActivity1");
clazz.a.implementation = function(bArr){
return Java.use("java.lang.String").$new("R4jSLLLLLLLLLLOrLE7/5B+Z6fsl65yj6BgC6YWz66gO6g2t65Pk6a+P65NK44NNROl0wNOLLLL=");
}
}
)
}

setImmediate(FridaActivity1)

FridaActivity2

CheckSuccess()为父类方法,是个空的,可以不用管它。分析可知,我们需要hook FridaActivity2实例,篡改这两个变量的值就行了(直接改或者调用set方法改).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function FridaActivity2(){
Java.perform(
function(){
var clazz = Java.use("com.example.androiddemo.Activity.FridaActivity2");
Java.choose("com.example.androiddemo.Activity.FridaActivity2",{
onMatch: function(obj){
console.log("found instance: FridaActivity2");
obj.static_bool_var.value = true;//注意是boolean基本类型,而不是Boolean类,所以Java.use("java.lang.Boolean").$new()会报错
obj.bool_var.value = true;
console.log("static_bool_var = " + obj.static_bool_var + "; bool_var = " + obj.bool_var);
},
onComplete: function(){
console.log("finish")
}
})
}
)
}

setImmediate(FridaActivity2)

FridaActivity3

整个操作跟FridaActivity2的一样,不过需要注意的是:如果字段名与方法名一样,则需要给字段名前加下划线”_”,否则获取到的是方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function FridaActivity3(){
Java.perform(
function(){
Java.choose("com.example.androiddemo.Activity.FridaActivity3",{
onMatch: function(obj){
console.log("found instance: FridaActivity3");
obj.static_bool_var.value = true;
obj.bool_var.value = true;
//属性名和方法名相同,引用属性名需要前置"_"
obj._same_name_bool_var.value = true;
console.log("static_bool_var = " + obj.static_bool_var.value + "; bool_var = " + obj.bool_var.value + "; obj.same_name_bool_var = " + obj._same_name_bool_var.value);
console.log(obj.same_name_bool_var());
},
onComplete: function(){
console.log("finish");

}
})
}
)
}

setImmediate(FridaActivity3)

FridaActivity4

这个很简单,只要将这些方法全部hook一遍,修改返回值为true就行了。

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
function FridaActivity4(){
Java.perform(
function(){
var clazz = Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses");
clazz.check1.implementation = function(){
return true;
};
clazz.check2.implementation = function(){
return true;
};
clazz.check3.implementation = function(){
return true;
};
clazz.check4.implementation = function(){
return true;
};
clazz.check5.implementation = function(){
return true;
};
clazz.check6.implementation = function(){
return true;
};

}
)
}

setImmediate(FridaActivity4)

FridaActivity5

仔细分析整个代码的话,不难知道,程序通过loaddex()方法创建DexClassLoader来动态加载dex文件,不过它将DexClassLoader强转为CheckInterface接口类,里面的check()方法由其子类DynamicCheck实现,也就是说,当调用getDynamicDexCheck().check()是,执行的是子类DynamicCheck的check()方法。所以,我们需要hook该方法,使其返回true。

但是,由于当前的类加载器是加载AndoridDemo.apk的,而check()方法在加载DynamicPlugin.dex的类加载器中,所以需要遍历类加载器寻找。

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
function FridaActivity5(){
Java.perform(
function(){
/*由于当前js使用的类加载器是加载AndoridDemo.apk的,
所以需要加载DynamicPlugin.dex的类加载器,
之后才能implementate DynamicCheck的check方法*/
Java.enumerateClassLoaders({
onMatch: function(loader){
//console.log(loader);
try {
if(loader.loadClass("com.example.androiddemo.Dynamic.DynamicCheck")){
console.log("success");
Java.classFactory.loader = loader;
console.log(loader);
var clazz = Java.use("com.example.androiddemo.Dynamic.DynamicCheck");
clazz.check.implementation = function () {
return true;
}
}
} catch (error) {

}

},
onComplete: function(){
console.log("classloader change");
}
})
}
)
}

setImmediate(FridaActivity5)

FridaActivity6

跟FridaActivity4一样,hook这些方法修改返回值就可以了(不需要像FridaActivity5一样,因为这些方法又不是动态加载来的)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function FridaActivity6(){
Java.perform(
function(){
var Frida6Class0 = Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class0");
Frida6Class0.check.implementation = function () {
return true;
}

var Frida6Class1 = Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class1");
Frida6Class1.check.implementation = function () {
return true;
}

var Frida6Class2 = Java.use("com.example.androiddemo.Activity.Frida6.Frida6Class2");
Frida6Class2.check.implementation = function () {
return true;
}
}
)
}

setImmediate(FridaActivity6)

然后到了第七关FridaActivity7,看源码可以知道整个练习就结束了。


Frida入门练习(一) - 七层关卡
http://example.com/2023/12/20/Android安全/Frida入门练习(一) - 七层关卡/
作者
gla2xy
发布于
2023年12月20日
许可协议