Frida Hook Java

本篇针对几种 Java 层常见的Frida Hook 方式,记录 js 的示例代码并做相关的注释说明,代码仅供参考。Frida 的环境配置及 Python 脚本的框架代码已在上一篇中有所介绍,本篇不再提及。还没安装 Frida 的小伙伴可点击传送门,查看 Frida 的环境配置及简单使用。

Hook普通方法(包括私有、公有、静态)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Java.perform(function () {
// Java.use(类的路径),声明一个要hook的类
var hook_cls = Java.use('com.threetails.demo.Utils');

// 修改该类下normalMethod的实现,参数个数需要与原方法保持一致
hook_cls.normalMethod.implementation = function (a, b) {
console.log("Hook Start...");

send('arg0: '+ a);
send('arg1: '+ b);

send('result: '+ this.normalMethond(a,b));

/* 为了让程序正常运行,需要调用原方法将结果返回。若想修改参数值,可以在调用时直接将参数替换,
如:return this.normalMethond(1,2),但需注意参数类型的一致性。*/
return this.normalMethond(a,b);
}
});

Hook构造方法

1
2
3
4
5
6
7
8
9
10
11
12
// Hook构造方法时与普通方法类似,只不过将方法名改成了 $init
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');
hook_cls.$init.implementation = function (a, b) {
console.log("Hook Start...");

send('arg0: '+ a);
send('arg1: '+ b);

return this.$init(a, b);
}
});

Hook重载方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');

/* ovldMethod是一个重载方法,必须加上overload(参数类型)才能Hook。
若参数类型为字符串,应该传java.lang.String,因为字符串在java中与int不同,它不是基本类型,而是类。
如果不知道参数类型,可以去掉overload,执行一下程序会抛出错误,在错误提示中可以看到对应的参数类型。*/
hook_cls.ovldMethod.overload("int").implementation = function (a) {
console.log("Hook Start...");

send('arg0: '+ a);

send('result: '+ this.ovldMethod.overload("int")(a));

return this.ovldMethod.overload("int")(a);
}
});

构造对象参数

如果方法参数的类型并非 java 的内置类型,而是一个自定义类,你又想在Hook时传入自定义对象,怎么办?假设现在有个自定义类叫 Dog,它有两个属性:name 和 age。看看下面这个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');

// 首先声明一个dog类
var dog_cls = Java.use('com.threetails.demo.Dog');

hook_cls.feed.implementation = function (dog_obj) {
console.log("Hook Start...");
send('old dog:'+dog_obj);

// 调用dog_cls.$new来实例化一个自定义的dog对象
var new_dog = dog_cls.$new("大黄", 5);
send('new dog:'+new_dog);

// 调用原方法并将对象参数替换为自定义的dog
return this.feed(new_dog);
}
});

修改对象属性

还是以 Dog 类为例, 现在我们需要将传入的 Dog 对象的 age 修改为 1。

正常修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');

hook_cls.feed.implementation = function (dog_obj) {
console.log("Hook Start...");
// 需要使用.value来获取属性值
send(dog_obj.age.value);

// 需要使用.value来修改属性值
dog_obj.age.value = 1;
send(dog_obj.name.value);

return this.feed(dog_obj);
}
});

使用反射

修改对象属性还有另外一种方式,也就是使用 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
Java.perform(function () {
var hook_cls = Java.use('com.threetails.demo.Utils');

// 首先需要声明一个类构造器
var clazz = Java.use('java.lang.Class');

hook_cls.feed.implementation = function (dog_obj) {
console.log("Hook Start...");

// 调用Java.cast方法,传入类和类构造器得到类的id,再调用getDeclaredField得到属性id
var ageid = Java.cast(dog_obj.getClass(),clazz).getDeclaredField('age');

// 设置该属性为可访问
ageid.setAccessible(true);

// 调用get方法获取属性值
var old_value = ageid.get(dog_obj);

// 此处必须使用console.log才能打印我们需要的值
console.log(old_value);

// 调用setInt修改属性值
ageid.setInt(dog_obj, 1);

return this.feed(dog_obj);
}
});