jshook 中文文档 下载 App

Interceptor 拦截器用法——attach / replace / 调用时机

attach:在函数入口/出口插钩子

Interceptor.attach 不替换原函数,只是在函数执行前(onEnter)和执行后(onLeave)插入自定义逻辑。原函数照常执行。

Interceptor.attach(functionPtr, {
    onEnter: function (args) {
        // 函数执行前调用
        // args 是参数数组(NativePointer)
        console.log('[+] 函数被调用');
    },
    onLeave: function (retval) {
        // 函数执行后调用
        // retval 是返回值(NativePointer)
        console.log('[+] 返回值: ' + retval);
    }
});

什么时候触发 onEnter / onLeave

两个回调里都可以修改参数或返回值,但要注意时机:

修改返回值

Interceptor.attach(funcPtr, {
    onLeave: function (retval) {
        retval.replace(ptr(1));  // 强制把返回值改成 1
    }
});

修改参数

Interceptor.attach(funcPtr, {
    onEnter: function (args) {
        args[1] = ptr(999);  // 把第二个参数改成 999
    }
});

replace:直接替换整个函数

Interceptor.replace 把函数实现完全替换成你指定的地址。原函数不再被调用。

// 先写一个替换函数
var replacement = new NativeCallback(function (arg1, arg2) {
    console.log('[+] 替换函数被调用');
    return 42;
}, 'int', ['int', 'int']);

// 替换
Interceptor.replace(originalPtr, replacement);

和 attach 的区别:

方面attachreplace
原函数执行✅ 照常执行❌ 完全不执行
能保留原逻辑✅(只是加钩子)❌(必须自己重写)
性能开销略高(两次回调)很低(直接跳转)
适合场景加日志、改参数/返回值完全改写行为

Java 层的 Interceptor

Java.use().method.implementation 本质上就是 Java 层的 replace。Interceptor 主要用于 native 层。

如果要在 Java 层用类似 attach 的效果(只加钩子不替换实现),用 implementation + 在末尾调原方法:

TargetClass.method.implementation = function () {
    console.log('[+] 加钩子');
    return this.method.apply(this, arguments);  // 原方法照常执行
};

调用时机

spawn vs attach

Frida 有两种注入时机:

JsHook 的 Hook 服务开关就涉及这个差异:

注入在运行途中(解决闪退问题)

如果目标应用一启动就闪退,可能是注入时机太早干扰了初始化流程。JsHook 支持运行途中注入:

  1. 先关 Hook 服务
  2. 启动目标应用,等它完全跑起来(几秒)
  3. 开 Hook 服务
  4. Frida 会 attach 到已运行的进程

这时候注入发生在应用运行途中,避开了初始化阶段。更多闪退排查见 目标应用一开就闪退。

常见坑

args 是 NativePointer,不是原始类型

// 错误:直接把 args[1] 当数字用
console.log('参数: ' + args[1]);  // 输出 NativePointer(0x1)

// 正确:先转成对应类型
var val = args[1].toInt32();
console.log('参数: ' + val);  // 输出 1

返回值也一样

// 错误
console.log('返回: ' + retval);

// 正确
var returnVal = retval.toInt32();
console.log('返回: ' + returnVal);