Frida开发手册

前言

以下是在使用python开发frida脚本过程中收集到的一些用法, 这里将学习的知识点做个归纳总结

劫持函数

Java普通函数

1
2
3
4
5
6
const Context = Java.use('android.content.Context');
Context.checkCallingOrSelfPermission.implementation = function (permission: string) {
// 以下两种写法都可以
// return Context.checkCallingOrSelfPermission.call(this, permission)
return this.checkCallingOrSelfPermission(permission);
}

如果没有同名的重载函数则直接调用函数的implementation来重写即可

Java重载函数

1
2
3
4
const Context = Java.use('android.content.Context');
Context.checkPermission.overload('java.lang.String', 'int', 'int').implementation = function (permission: string, pid: number, uid: number) {
return this.checkPermission(permission, pid, uid);
}

如果需要 hook特定的重载函数则必须要在函数名后通过overload函数来指定参数列表

函数调用

Java构造器重载函数

1
2
let Person = Java.use("com.example.Person");
let person = Person.$new.overload("java.lang.String", "int").call(Person, "Alice", 25);

Java类重载函数

1
2
3
var EncryptUtil = Java.use("xxx.utils.EncryptUtil");
var encryptHMAC = EncryptUtil.encryptHMAC.overload('java.lang.String', 'java.lang.String');
var new_signature = encryptHMAC.call(EncryptUtil,msg_body, tran_id);

数组集合操作

创建Java数组

1
2
3
4
5
6
7
const buffer = Java.array('byte', [1, 2, 3]);
console.log(buffer.length);
for(let i = 0; i < buffer.length; ++i){
let b = buffer[i];
//todo something
}
console.log(result);

修改Java数组

1
2
3
4
const String = Java.use("java.lang.String");
let stringInstance = String.$new("Hello World");
const Arrays = Java.use("java.util.Arrays");
Arrays.fill(array, 0, 1, stringInstance); // equivalent to var_0[0] = stringInstance

遍历Java集合

1
2
3
4
5
6
7
const ArrayList = Java.use('java.util.ArrayList');
let list = ArrayList.$new();
let iter = list.iterator();
while(iter.hasNext()) {
let item = iter.next();
//todo something
}

异常处理

捕获Java异常

1
2
3
4
5
6
7
8
9
10
11
const Test = Java.use("com.exception.example.Test");
try {
Test.throwException();
} catch (ex) {
//ex is a handle, you have to cast it.
const Exception = Java.use("java.lang.Trowable");
let item = Java.cast(ex,Exception);
let SpecificException = Java.use(item.$className);
item = Java.cast(ex,SpecificException);
console.log(item.attribute.value);
}

抛出Java异常

1
2
3
4
5
const Activity = Java.use('android.app.Activity');
const Exception = Java.use('java.lang.Exception');
Activity.onResume.implementation = function () {
throw Exception.$new('Oops!');
};

对象字段

Java访问修改对象字段

1
2
3
Class Test {
double f = 200.0d;
}

注意如果要访问或者修改对象的f字段则不能直接通过对象.f访问,需要调用.fvalue属性

1
2
3
4
5
const Test = Java.use('com.example.Test');
let test = Test.$new();
console.log(`before=${test.f.value}`);
example.f.value = 100;
console.log(`after=${test.f.value}`);

Java访问修改具有同名函数的对象字段

1
2
3
4
Class Test {
double f = 200.0d;
public void f() {...}
}

如果出现同名的函数会导致frida无法区分是要访问字段还是函数遇到这种情况需要使用_加以区分

1
2
3
4
5
const Test = Java.use('com.example.Test');
let test = Test.$new();
console.log(`before=${test._f.value}`);
example.f.value = 100;
console.log(`after=${test._f.value}`);

实现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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
const SomeBaseClass = Java.use('com.example.SomeBaseClass');
const X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
const MyTrustManager = Java.registerClass({
name: 'com.example.MyTrustManager',
implements: [X509TrustManager],
methods: {
checkClientTrusted(chain, authType) {
},
checkServerTrusted(chain, authType) {
},
getAcceptedIssuers() {
return [];
},
}
});

const MyWeirdTrustManager = Java.registerClass({
name: 'com.example.MyWeirdTrustManager',
superClass: SomeBaseClass,
implements: [X509TrustManager],
fields: {
description: 'java.lang.String',
limit: 'int',
},
methods: {
$init() {
console.log('Constructor called');
},
checkClientTrusted(chain, authType) {
console.log('checkClientTrusted');
},
checkServerTrusted: [{
returnType: 'void',
argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String'],
implementation(chain, authType) {
console.log('checkServerTrusted A');
}
}, {
returnType: 'java.util.List',
argumentTypes: ['[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String'],
implementation(chain, authType, host) {
console.log('checkServerTrusted B');
return null;
}
}],
getAcceptedIssuers() {
console.log('getAcceptedIssuers');
return [];
},
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const myOutputClass = Java.registerClass({
name: 'com.example.MyOutputStream',
superClass: "java.io.ByteArrayOutputStream",
implements: [],
methods: {
write: [{
returnType: 'void',
argumentTypes: ['[B', 'int', 'int'],
implementation: function (b, off, len) {
console.log("MyOutputStream.write(" + JSON.stringify(b) + "," + off + "," + len + ")");
this.$super.write(b, off, len);
}
}, {
returnType: 'void',
argumentTypes: ['int'],
implementation: function (b) {
console.log('MyOutputStream.write(' + b + ")");
this.$super.write(b);
}
}]
}
});
var myInstance = myOutputClass.$new();

获取Android Context

Application

1
2
const ActivityThread = Java.use("android.app.ActivityThread");
let application = ActivityThread.currentApplication();

Activity

1
2
3
4
5
6
const ActivityThread = Java.use("android.app.ActivityThread");
let activityThread = ActivityThread.currentActivityThread();
let mActivities = activityThread.mActivities.value;
const ActivityThread$ActivityClientRecord = Java.use("android.app.ActivityThread$ActivityClientRecord");
let activityClientRecord = Java.cast(mActivities.mArray.value[1], ActivityThread$ActivityClientRecord).activity.value;
let activity = activityClientRecord.activity.value;

逆向工程进阶

Objection 手术刀级别的神器

具体用法参考:https://github.com/sensepost/objection

打印方法调用堆栈

1
2
3
4
5
6
const Log = Java.use('android.util.Log');
const Exception = Java.use('java.lang.Exception');
console.log(Log.getStackTraceString(Exception.$new()));

// 一行解决
console.log(Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Exception').$new()));

查找类加载器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const searchClass = "com.example.Main";
const classLoaders = Java.enumerateClassLoadersSync();
for (let i = 0; i < classLoaders.length; i++) {
const loader = classLoaders[i]
try {
loader.findClass(searchClass);
Java.classFactory.loader = loader;
console.log("found class loader:\n" + loader)
break;
} catch (error) {
if (error.message.includes("ClassNotFoundException")) {
console.log("\n You are trying to load encrypted class, trying next loader");
}
}
}

解决方案

Hook不生效

尝试禁止优化,相关信息:https://github.com/frida/frida/issues/1395

1
Java.deoptimizeEverything();

无法调用Java对象函数

排查问题步骤

  1. 先确认JS的映射对象是否存在这个函数,一下几种方法都可以

    1
    Object.keys(obj).forEach(key => console.log(key));
    1
    2
    3
    for (let key in obj) {
    console.log(key);
    }
    1
    Object.getOwnPropertyNames(obj.__proto__).join('\n\t')
  2. 如果存在映射函数名则可以尝试换种写法

    1
    2
    const Enumeration = Java.use('java.util.Enumeration');
    Enumeration.nextElement.call(networkInterfaces);
  3. 如果不存在则可能自动转型了使用Java.cast函数转成目标对象类型

    1
    2
    3
    let networkInterfaces = ...
    const NetworkInterface = Java.use('java.net.NetworkInterface');
    networkInterfaces = Java.cast(networkInterfaces, NetworkInterface);
Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2015-2024 Kaisar
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信