You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Frida拦截Android原生方法后如何读取任意参数及判断类型?

关于Frida拦截JNI方法时的参数枚举与类型读取问题

假设存在地址为expfunction.address的原生方法,我用Frida的Interceptor.attach()拦截该方法调用并尝试获取参数,核心代码如下:

Interceptor.attach(expfunction.address, {
    onEnter(args) {
        console.log("\n-----\n[*] Function is called!");
        for(let i in args) {    
            ... 读取args[i]的值 ...
        }
        
    },
    onLeave(retval) {
        console.log("\t\n-----\n[*] Function returns");
        console.log("\t[+] Returns:", retval);
    }
})

我现在遇到两个问题:

  1. 该方法的参数可能是jboolean、jstring、jobject等任意JNI类型,如何准确判断args[i]的数据类型并正确读取其值?
  2. for(let i in args) { ... }是不是枚举args数组的正确方式?因为尝试获取args.length时会触发RangeError: invalid array index错误。

我试过两种方式,但都返回空值:

  • 尝试方案1(失败)
onEnter(args) {
  for(let i in args) {    
        var tmp = new NativePointer(args[i]);
        console.log(tmp.readPointer());
    }     
}
  • 尝试方案2(失败)
function getType(value) {
  return Object.prototype.toString.call(value).toLowerCase();
}
...
onEnter(args) {
  for(let i in args) {    
        var tmp = new NativePointer(args[i]);
        console.log("Type:", getType(tmp));
    }     
}

解答

一、正确枚举args的方式

Frida中的args不是标准JavaScript数组,而是一个类数组对象,不能直接用for...in遍历(会遍历到对象的内置属性,导致非参数内容被处理),也不能直接读取length属性。

正确的做法是使用args.count()获取参数总数,再配合普通for循环遍历:

onEnter(args) {
    console.log("\n-----\n[*] Function is called!");
    // 通过args.count()拿到参数数量
    for (let i = 0; i < args.count(); i++) {
        const arg = args[i];
        // 处理当前参数
    }
}

二、判断JNI参数类型并读取值

JNI参数的类型无法被Frida自动识别,需要结合函数签名或针对常见类型做针对性处理:

1. 优先使用函数签名(最准确)

如果能拿到目标函数的JNI签名(比如(ZLjava/lang/String;Ljava/lang/Object;)V),可以直接按签名顺序对应参数类型处理,这是最可靠的方式。

2. 常见JNI类型的读取方法

如果不知道签名,可以针对常见JNI类型做判断和读取:

  • jboolean:本质是1字节的无符号值,用arg.readU8() !== 0获取布尔结果
  • jstring:通过Java.String(arg)转换为JavaScript字符串
  • jobject:用Java.cast(arg, Java.use("java.lang.Object"))转换为Java对象,之后可以调用对象的toString()或其他方法查看内容;如果是数组类型(如jintArray),则用Java.cast(arg, Java.use("int[]"))转换后读取数组内容
  • 基本数值类型:
    • jint/jshort/jbyte:arg.readInt()/arg.readShort()/arg.readU8()
    • jlong:arg.readLong()
    • jfloat:arg.readFloat()
    • jdouble:arg.readDouble()

示例代码:

onEnter(args) {
    console.log("\n-----\n[*] Function is called!");
    for (let i = 0; i < args.count(); i++) {
        const arg = args[i];
        console.log(`[+] Arg ${i}: ${arg}`);
        
        // 尝试识别常见类型
        try {
            // 先尝试转换为jstring
            const strVal = Java.String(arg);
            console.log(`    -> 类型:jstring,值:${strVal}`);
        } catch (e) {
            // 不是jstring,尝试基本类型
            try {
                const boolVal = arg.readU8() !== 0;
                console.log(`    -> 类型:jboolean,值:${boolVal}`);
            } catch (e) {}
            
            try {
                const intVal = arg.readInt();
                console.log(`    -> 类型:jint,值:${intVal}`);
            } catch (e) {}
            
            try {
                const longVal = arg.readLong();
                console.log(`    -> 类型:jlong,值:${longVal}`);
            } catch (e) {}
            
            // 尝试转换为jobject
            try {
                const objVal = Java.cast(arg, Java.use("java.lang.Object"));
                console.log(`    -> 类型:jobject,值:${objVal.toString()}`);
            } catch (e) {}
        }
    }
}

为什么你的尝试方案失败?

  • 方案1:args[i]本身就是NativePointer类型,不需要再用new NativePointer()包装。直接调用readPointer()会把参数值当成内存地址去读取,而非参数本身的值,所以返回空。
  • 方案2:Object.prototype.toString()只能判断出它是[object nativepointer],无法区分具体的JNI类型——所有JNI参数在Frida中都表现为NativePointer,必须通过JNI相关API转换才能识别具体类型。

内容的提问来源于stack exchange,提问作者Lam Nguyen Chi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 14:42:28