Frida JavaScript API 总结

好的,这是一份 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() 与外部控制端通信 通用


  1. 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;

  1. 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);

  1. 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);
});

  1. 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);
});

  1. Process API

用于查询当前进程的信息。

Process.id

获取当前进程的 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);
});

  1. 工具函数与类型

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']);

  1. 通信 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);
    // 执行操作...
});

  1. 控制台输出

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));

常用模式总结

  1. Java 层 Hook: Java.perform -> Java.use -> method.implementation
  2. Native 层 Hook: Interceptor.attach(Module.getExportByName(...), {onEnter, onLeave})
  3. 内存扫描: Memory.scan(base, size, "pattern")
  4. 进程信息: Process.enumerateModules(), Process.enumerateThreads()
  5. 通信: send() / recv() 与外部 Python 脚本交互

这份总结涵盖了 90% 的常用场景。对于更高级的用法,请始终参考官方文档:https://frida.re/docs/javascript-api/