好的,这是一份 Frida JavaScript API 的详细总结,按照功能模块进行分类,并附有常用示例。
Frida JavaScript API 总结
Frida 的 API 主要分为两大领域:操作 Java 运行时 (Android) 和 操作 Native 代码 (C/C++)。你的脚本通常在目标进程的上下文中执行。
核心模块概览
模块名 主要用途 目标环境
Java 与 Java 运行时交互 (Android) Java (ART/Dalvik)
Interceptor 拦截和修改函数调用 Native
Module 操作已加载的库和查找符号 Native
Memory 内存读写和分配 Native
Process 查询进程信息 (模块、线程等) Native
Console 日志输出 通用
send(), recv() 与外部控制端通信 通用
- Java API (Android)
用于在 Android 应用的 Java 虚拟机 (JVM) 中进行操作。
Java.perform(fn)
确保你的代码在 JVM 线程中执行。这是几乎所有 Java 层 Hook 的入口点。
javascript
Java.perform(function () {
// 你的 Hook 代码写在这里
console.log("Inside Java runtime!");
});
Java.use(className)
获取一个 JavaScript wrapper 来操作指定的 Java 类。
javascript
var StringClass = Java.use("java.lang.String");
var MyAppClass = Java.use("com.example.myapp.MainActivity");
Class.new() 与 init
创建新的 Java 对象实例。$init 是构造方法的实现。
javascript
var Exception = Java.use("java.lang.Exception");
var e = Exception.$new("My custom exception"); // 创建新实例
// Hook 构造方法
StringClass.$init.overload('java.lang.String').implementation = function (s) {
console.log("Creating string: " + s);
return this.$init(s); // 调用原始构造方法
};
Method.implementation
替换一个方法的实现。
javascript
var MyClass = Java.use("com.example.MyClass");
MyClass.targetMethod.implementation = function (a, b) {
console.log("targetMethod called: " + a + ", " + b);
// 调用原方法
var result = this.targetMethod(a, b);
// 修改返回值
return result + 100;
};
overload(signature)
指定要 Hook 的重载方法。参数类型必须完全匹配。
javascript
// 假设方法有多个重载:encrypt(String) 和 encrypt(String, String)
MyClass.encrypt.overload('java.lang.String').implementation = function (data) { ... };
MyClass.encrypt.overload('java.lang.String', 'java.lang.String').implementation = function (data, key) { ... };
Java.choose(className, callbacks)
在堆上枚举已存在的类实例,对每个匹配的实例执行操作。
javascript
Java.choose("com.example.MyClass", {
onMatch: function (instance) {
// 对每个找到的实例执行
console.log("Found instance: " + instance);
console.log("Value of field: " + instance.field.value);
},
onComplete: function () {
// 枚举完成时执行
console.log("Search complete.");
}
});
Java.enumerateLoadedClasses()
枚举所有已加载的 Java 类。
javascript
Java.enumerateLoadedClasses({
onMatch: function (className) {
if (className.includes("crypto")) { // 过滤类名
console.log(className);
}
},
onComplete: function () {}
});
字段操作
通过 .value 读取或修改字段值。
javascript
var MyClass = Java.use("com.example.MyClass");
// 修改静态字段
MyClass.staticField.value = "new_value";
// 在实例上修改字段 (需要在 onMatch 回调中)
instance.instanceField.value = 123;
- Interceptor API (Native)
用于拦截和替换本机函数(C/C++)的调用。
Interceptor.attach(target, callbacks)
在函数调用时附加回调。
javascript
// 通过函数名附加
Interceptor.attach(Module.getExportByName("libc.so", "strcmp"), {
onEnter: function (args) {
// args[0] 和 args[1] 是 strcmp 的两个参数
this.arg0 = args[0]; // 保存供 onLeave 使用
this.arg1 = args[1];
console.log("strcmp called with: " + args[0].readCString() + ", " + args[1].readCString());
},
onLeave: function (retval) {
// retval 是返回值 (int)
console.log("strcmp returned: " + retval);
// retval.replace(0); // 强制返回 0 (相等)
}
});
// 通过地址附加
var funcPtr = ptr(0x1234abcd);
Interceptor.attach(funcPtr, { ... });
Interceptor.replace(target, replacement)
用新的实现完全替换一个函数。
javascript
// 创建一个新的本地回调函数
var newStrlen = new NativeCallback(function (ptr) {
console.log("Fake strlen called for: " + ptr.readCString());
return 5; // 总是返回 5
}, 'int', ['pointer']); // 返回类型, [参数类型列表]
// 替换原来的函数
Interceptor.replace(Module.getExportByName("libc.so", "strlen"), newStrlen);
- Module API (Native)
用于操作已加载的共享库(SO 文件)。
Module.findBaseAddress(name)
查找模块的加载基地址。
javascript
var baseAddr = Module.findBaseAddress("libtarget.so");
console.log("libtarget.so base address: " + baseAddr);
Module.getExportByName(moduleName|null, exportName)
获取导出函数的地址。如果 moduleName 为 null,则在所有模块中查找。
javascript
// 从特定模块查找
var openAddr = Module.getExportByName("libc.so", "open");
// 从任何模块查找 (更慢)
var fopenAddr = Module.getExportByName(null, "fopen");
Module.enumerateImports() / Module.enumerateExports()
枚举模块的导入/导出函数。
javascript
// 枚举 libssl.so 的所有导出
Module.enumerateExports("libssl.so", {
onMatch: function (exp) {
console.log(exp.name + " @ " + exp.address);
},
onComplete: function () {}
});
// 枚举所有模块
Process.enumerateModules().forEach(function (module) {
console.log(module.name + " @ " + module.base);
});
- Memory API (Native)
用于读写和分配进程内存。
Memory.readByteArray(address, length)
从指定地址读取一段字节。
javascript
var bytes = Memory.readByteArray(ptr(0x1234), 100);
console.log(hexdump(bytes, { offset: 0, length: 100 }));
Memory.readCString(address)
从指定地址读取一个 C 风格字符串(以 NULL 结尾)。
javascript
var str = Memory.readCString(ptr(0x5678));
console.log("Read string: " + str);
Memory.writeUtf8String(address, str)
向指定地址写入一个 UTF-8 字符串。
javascript
Memory.writeUtf8String(ptr(0x5678), "New String Content");
Memory.alloc(size)
在目标进程中分配内存。
javascript
var buffer = Memory.alloc(100); // 分配 100 字节
Memory.writeUtf8String(buffer, "Data to pass to native function");
Memory.protect(address, size, protection)
修改内存页的保护属性(如 rwx)。
javascript
Memory.protect(ptr(0x4000), 4096, 'rwx'); // 使页面可读、写、执行
Memory.scan(address, size, pattern)
扫描内存以查找模式(支持通配符 ??)。
javascript
var results = Memory.scan(Module.findBaseAddress("libtarget.so"), 0x10000, "12 34 ?? 78 9a");
results.on('match', function (match) {
console.log('Pattern found at:', match.address);
});
- Process API
用于查询当前进程的信息。
获取当前进程的 PID。
javascript
console.log("Current PID: " + Process.id);
Process.enumerateModules()
枚举所有已加载的模块。
javascript
Process.enumerateModules().forEach(function (module) {
console.log(module.name + " - Base: " + module.base + " - Size: " + module.size);
});
Process.enumerateThreads()
枚举所有线程。
javascript
Process.enumerateThreads().forEach(function (thread) {
console.log("Thread ID: " + thread.id + ", State: " + thread.state);
});
- 工具函数与类型
ptr(address)
将数字或字符串转换为 NativePointer 对象。
javascript
var address = ptr("0x1234");
var address2 = ptr(0x5678);
NULL
空指针常量。
javascript
if (somePointer.equals(NULL)) {
console.log("This is a NULL pointer");
}
new NativeCallback(func, returnType, argTypes)
创建一个新的函数,可供本机代码调用。
javascript
var callback = new NativeCallback(function (a, b) {
return a + b;
}, 'int', ['int', 'int']);
- 通信 API
send(message[, data])
发送消息到外部控制端(如 Python 脚本)。
javascript
send({ type: 'info', payload: 'Function hooked successfully!' });
// 发送二进制数据
send({ type: 'data' }, new Uint8Array([0x01, 0x02, 0x03]).buffer);
recv([type], callback)
接收来自外部控制端的消息。
javascript
recv('execute', function (message) {
console.log("Received command: " + message.payload);
// 执行操作...
});
- 控制台输出
console.log(message), console.warn(message), console.error(message)
输出日志信息,显示在 Frida 控制台中。
javascript
console.log("This is a log message.");
console.warn("This is a warning!");
console.error("This is an error!");
hexdump(source[, options])
以十六进制格式转储内存或字节数组。
javascript
var bytes = Memory.readByteArray(somePointer, 64);
console.log(hexdump(bytes));
常用模式总结
- Java 层 Hook: Java.perform -> Java.use -> method.implementation
- Native 层 Hook: Interceptor.attach(Module.getExportByName(...), {onEnter, onLeave})
- 内存扫描: Memory.scan(base, size, "pattern")
- 进程信息: Process.enumerateModules(), Process.enumerateThreads()
- 通信: send() / recv() 与外部 Python 脚本交互
这份总结涵盖了 90% 的常用场景。对于更高级的用法,请始终参考官方文档:https://frida.re/docs/javascript-api/