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

如何使用Frida Hook带可变参数列表的C方法 提取参数并传递给其他C函数

Frida Hook可变参数C函数实现方案(以execl为例)

针对execl(const char *filename, const char *arg0, ...)这类POSIX标准的可变参数函数,核心逻辑是利用其参数列表必须以NULL指针结尾的约定,结合Frida已经封装好的参数读取接口遍历拿值,不需要手动计算栈偏移。

可变参数读取实现

Interceptor.attach的回调里传入的args类数组对象,已经由Frida统一处理了不同架构下的寄存器传参、栈传参偏移,不管32位/64位程序,直接按索引读取即可:

  • 固定参数按声明顺序对应args[0]、args[1]
  • 可变参数从固定参数的下一个索引开始,逐位读取直到碰到NULL指针终止
const execlAddr = Module.getExportByName(null, "execl");
Interceptor.attach(execlAddr, {
  onEnter(args) {
    // 读取固定参数
    this.filePath = args[0].readCString();
    this.argList = [args[1].readCString()];
    // 遍历读取可变参数
    let argIdx = 2;
    while (true) {
      const curArg = args[argIdx];
      if (curArg.isNull()) break;
      this.argList.push(curArg.readCString());
      argIdx++;
    }
    console.log(`捕获execl调用,路径:${this.filePath},参数:${JSON.stringify(this.argList)}`);
  },
  onLeave(retVal) {
    console.log(`execl调用返回值:${retVal.toInt32()}`);
  }
});

注意:如果是printf这类靠格式串判断参数个数的可变参函数,不能靠NULL终止遍历,需要先解析固定参数中的格式字符串,统计占位符数量和对应类型,再按索引读取对应位置的参数。

可变参数传递给其他C函数的方法

分两种常见场景处理:

  • 目标函数和原函数签名一致(同样接收可变参数列表)
    不要手动拼接栈结构,直接把所有参数(包括末尾必须的NULL指针)按顺序组成数组,用NativeFunction.apply传入即可。示例:
// 保存原函数引用,签名声明时固定参数后加...表示支持可变参
const origExecl = new NativeFunction(execlAddr, "int", ["pointer", "pointer", "..."]);
// 组装新的参数列表,末尾必须带NULL指针
const callArgs = [
  Memory.allocUtf8String("/bin/ls"),
  Memory.allocUtf8String("ls"),
  Memory.allocUtf8String("-al"),
  Memory.allocUtf8String("/root"),
  ptr(0)
];
// 直接调用即可
const ret = origExecl.apply(null, callArgs);
  • 目标函数接收va_list类型参数
    不要手动构造va_list结构(不同架构的va_list内存布局差异极大,手动拼接极易崩溃),用NativeCallback做一层中转即可,Frida会自动把可变参数封装成可直接传递的va_list指针:
// 示例目标函数:接收va_list的execv类函数
const targetVFunc = new NativeFunction(Module.findExportByName(null, "execlv"), "int", ["pointer", "pointer", "pointer"]);
// 中转函数:接收可变参,自动拿到va_list传给目标函数
const trampoline = new NativeCallback(function(path, arg0) {
  const vaList = arguments[2];
  return targetVFunc(path, arg0, vaList);
}, "int", ["pointer", "pointer", "..."]);

避坑提示

  • 读取参数时要匹配类型:如果参数是int、long等基础数值类型,不要用readCString(),对应调用toInt32()/toInt64()等方法转换。
  • 不要手动计算栈偏移读取参数:Frida的args对象已经兼容了x86、x86_64、arm、arm64的传参规则,手动算偏移在寄存器传参的场景下必然读错。
  • 传递可变参时不要漏了末尾的终止符:NULL结尾的可变参函数如果没传最后的NULL指针,会读越界导致程序崩溃。

内容的提问来源于stack exchange,提问作者typeryougishiki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:48:21