Frida Java层Hook学习笔记

Java层Hook

简单的代码框架:

1
2
3
4
5
6
7
8
9
10
11
function main(){
//使用Java平台,Java层hook在Java.perform中执行
Java.perform(
//代码逻辑
function(){
...
}
)
}
//调用main方法
setImmediate(main)

获取类对象

  • Java.use(classname):获取classname对应的类。

普通类:

1
2
//普通类的获取
var clazz = Java.use("com.example.testfrida.demo");

内部类:

1
2
3
//内部类的获取
var clazz = Java.use("com.example.testfrida.demo$innerClass");
var clazz = Java.use('外部类$1') // 获取第一个内部类

匿名类:

匿名类是根据内存生成,没有具体的内部类名,通过 smali 代码来判断,具体参考:Frida Hook匿名内部类 | Xhy’s Blog (xhyeax.com)

1
const clazz = Java.use('包名.MainActivity$1')

枚举类:

1
2
3
4
5
6
7
8
Java.choose("枚举类" {
onMatch: function (obj) {
console.log(obj.ordinal()); // 输出枚举的键
}, onComplete: function () {

}
})
console.log(Java.use("枚举类").values()); // 输出值

获取类对象之后,可以使用类似Java反射的方法,具体如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
var cl = java.use(类名)
//cl.newInstance()
var methods = cl.class.getDeclaredMethods() 、getMethods() // 方法
var constructors = cl.class.getDeclaredConstructors() // 构造函数
var fields = cl.class.getDeclaredFields() // 字段
var classes = cl.class.getDeclaredClasses() // 内部类
var superClass = cl.class.getSuperclass() // 父类(抽象类)
var interfaces = cl.class.getInterfaces() // 所有接口

// 遍历输出
for (const method of methods) {
//method.getName()、invoke()
}

普通方法Hook

测试用例:

1
2
3
4
5
6
7
public class demo {
private int add(int a,int b){
return a + b;
}
}
//应用中主动调用
Log.d("demo", "onCreate: " + new demo().add(3,4));

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
function main(){
Java.perform(
function(){
var clazz = Java.use("com.example.testfrida.demo");
clazz.add.implementation = function(a, b){
//修改传入的参数值
a = 1;
b = 2;
//调用原有的add方法,实现原有逻辑
var result = this.add(a,b);
console.log("value = " + result);
//原add方法需要返回,在这里也需要返回相应的类型值
return result;
}
}
)
}
setImmediate(main)
/*结果
js输出:
value = 3

app log输出:
onCreate: 3
*/

注意:这里只是hook,并没有主动调用add方法,只有应用程序的代码逻辑触发了才会有结果。

重载方法Hook

  • class.methodname.overload(paramterTypes...):通过overload指定特定的重载方法。
  • class.methodname.overloads:获取class类中的methodname方法的所有重载方法。

测试用例:

1
2
3
4
5
6
7
8
9
10
public class demo {
private int add(int a,int b){
return a + b;
}
public int add(int a, int b, int c){
return add(a, b) + c;
}
}
//应用中主动调用
Log.d("demo", "onCreate: " + new demo().add(1,2,3));

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
28
29
30
31
32
33
34
35
36
37
38
39
function main(){
Java.perform(
function(){
var clazz = Java.use("com.example.testfrida.demo");
//通过overload()指定所要hook的重载方法
clazz.add.overload('int', 'int').implementation = function(a, b){
//重写两个参数的add方法的加法逻辑为乘法逻辑
var result = a*b;
console.log("value = " + result);
return result;
}
//通过overload()指定所要hook的重载方法
clazz.add.overload('int', 'int', 'int').implementation = function(a, b, c){
//重写三个参数的add方法的加法逻辑为减法逻辑
var result = this.add(a,b) - c;
console.log("value = " + result);
return result;
}
/*
//function中的参数个数也可以改变!
clazz.add.overload('int', 'int', 'int').implementation = function(a, b, c, d){
d = 4
var result = this.add(a,b) - c - d;
console.log("value = " + result);
return result;
}
*/
}
)
}
setImmediate(main)
/*结果
js输出: 注释掉的:
value = 30 value = 30
value = 23 value = 19

app log输出:
onCreate: 23 onCreate: 19
*/
1
2
3
4
5
var clazz = Java.use("com.example.testfrida.demo")
console.log(clazz.add.overloads);
/*输出
function add(int, int): int,function add(int, int, int): int
*/

构造方法Hook

  • class.$init:获取class类的构造方法,如有多个重载的构造方法,使用.overload(paramterTypes...)指定。

测试用例:

1
2
3
4
5
6
7
8
9
10
11
public class demo {
...
public demo(){}
public demo(String b) {
this.b = b;
}
public demo(String b, boolean flag) {
this.b = b;
this.flag = flag;
}
}

hook代码:

1
2
3
4
5
6
7
8
9
10
11
function main(){
Java.perform(
function(){
var clazz = Java.use("com.example.testfrida.demo")
//多个构造方法使用overload方法指定
clazz.$init.overload().implementation = function(){
//hook后代码逻辑实现
}
}
)
}

实例化对象

  • class.$new(paramters...):实例化class类对象
1
2
3
4
5
6
7
8
Java.perform(
function(){
var clazz = Java.use("com.example.testfrida.demo");
var demoObj = clazz.$new();
//调用add方法
console.log(demoObj.add(1,2));
}
)

主动调用方法

  • 静态方法:可通过类直接调用,也可通过实例调用。

    1
    2
    var clazz = Java.use(类名) 
    clazz.方法()
  • 非静态方法:只能通过实例调用。

    1
    2
    3
    var clazz = Java.use(类名) 
    var Obj = clazz.$new(paramters...)
    Obj.方法(paramters...)

获取已有对象

内存中遍历,找到所有符合条件的对象。

1
2
3
4
5
6
7
8
9
10
Java.choose('类路径', {
//每遍历一个对象都会调用onMatch
onMatch: function (obj) {
//todo...(主要在这里实现代码逻辑)
},
//遍历完成后调用onComplete
onComplete: function () {
//todo...
},
})

字段Hook

注: 如果字段名与方法名一样,则需要给字段名前加下划线_,否则获取到的是方法

静态字段

1
2
3
类.字段//获取类的属性定义,例如Java.Field{holder: <class: com.example.testfrida.demo>, fieldType: 1, fieldReturnType: I, value: 3}
类.字段.value // 获取类的属性值
类.字段.value = newValue // 修改类的值

非静态字段

  • 通过$new实例化对象,然后再获取。

  • 使用Java.choose()遍历内存获取已存在的类对象,然后再获取。

    1
    2
    3
    4
    5
    6
    7
    Java.choose('类路径', {
    onMatch: function (obj) {
    obj.字段.value = ?;
    console.log(obj.字段.value);
    },
    onComplete: function () {},
    })

获取已加载的所有类名

  • Java.enumerateLoadedClassesSync() : 同步获取已加载所有类,返回一个数组,里面的类型是字符串并不是类对象
  • Java.enumerateLoadedClasses() :异步获取已加载所有类,需内部实现onMatch、onComplete方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//1.
var classes = Java.enumerateLoadedClassesSync();
for(var i=0;i<classes.length;i++){
if(classes[i] == "com.example.testfrida.demo"){
console.log(typeof(classes[i]));
console.log(classes[i]);
}
}
//2.
Java.enumerateLoadedClasses({
//对每个类进行操作
onMatch: function(clazz){
console.log(clazz);
},
onComplete: function(){

}
})

获取ClassLoader

  • Java.enumerateClassLoadersSync() : 同步获取已存在的所有类加载器,返回一个数组,里面的是类加载器对象
  • Java.enumerateClassLoaders() :异步获取已存在的所有加载器,需内部实现onMatch、onComplete方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//1.
var loaderlist = Java.enumerateClassLoadersSync()
for(const loader of loaderlist){
console.log(loader);
}
//2.
Java.enumerateClassLoaders({
onMatch: function(loader){
console.log(loader);
},
onComplete: function(){

}
});

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
28
Java.perform(
function(){
Java.enumerateClassLoaders({
onMatch: function(loader){
//需要使用try-catch模块,因为findClass可能因找不到类而报错
try{
if(loader.finClass("com.example.testfrida.nodo")){
//更改js代码当前所使用的classloader为此loader
java.ClassFactory.loader = loader
//进行hook实现
var clazz = Java.use("com.example.testfrida.nodo")
clazz.add.implementation = function(a, b){
return a - b;
}
console.log(clazz.add(4,5));
}
}
catch(error){

}

},
onComplete: function(){

}
});
}
)

构建Java数组

  • Java.array(type,elements):构造elements数组为type类型的Java数组,其中type是Java类型的签名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//字符串数组
var arr = Java.array('Ljava.lang.String;',['1','2','3'])
//int数组
var values = Java.array('int', [ 1003, 1005, 1007 ]);
//byte数组
const values = Java.array('byte', [ 0x48, 0x65, 0x69 ])
//对象数组
var integerclass = Java.use('java.lang.Integer')//获取Java中Integer对象
var booleanclass = Java.use('java.lang.Boolean')//获取Java中Boolean对象
//构建对象数组,基本类型不是对象,需要包装成对象才行
var objarr = Java.array('Ljava.lang.Object;', ['hello', integerclass.$new(1), booleanclass.$new(true)])

//动态数组ArrayList
var arraylist = Java.use('java.util.ArrayList').$new()
var num = Java.use('java.lang.Integer').$new(1)
arraylist.add(num);
arraylist.add('hello')
console.log(arraylist);
arraylist.remove(num);
console.log(arraylist)

取消Hook

1
2
3
4
5
6
7
var cl = java.use(类名)
//对方法进行hook
cl.方法名.implementation = function(){

}
//取消hook
cl.方法名.implementation = null

类型转换

  • Java.cast(handle, klass):将句柄handle转成klass类型的变量/类型。
1
2
const Activity = Java.use('android.app.Activity');
const activity = Java.cast(ptr('0x1234'), Activity);

文件操作

  • Java.openClassFile(filePath):打开dex文件,返回一个对象,该对象拥有load()(加载包含的类)、getClassNames()方法

函数堆栈打印

1
2
3
4
5
function showStack() {
Java.perform(function () {
console.log(Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Throwable').$new()))
})
}

参考:

Android之Frida框架完全使用指南-CSDN博客

https://kuizuo.cn/docs/frida-note

JavaScript API | Frida • A world-class dynamic instrumentation toolkit

Frida用法详解【附用例】_frida -u -f-CSDN博客


Frida Java层Hook学习笔记
http://example.com/2023/12/17/Android安全/Frida Java层Hook学习笔记/
作者
gla2xy
发布于
2023年12月17日
许可协议