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

如何正确拦截并重定向libc的fcntl函数调用?

解决fcntl可变参数拦截器的参数传递问题

问题背景

fcntl是个可变参数函数:int fcntl(int fd, int action, ...),其第三个参数类型随action变化——有时是int,有时是void*。用dlsym(RTLD_NEXT, "fcntl")做日志拦截时,直接用va_arg传递参数会因类型大小不匹配出问题,需要针对性处理。

解决方案

核心逻辑是根据action的值明确第三个参数的类型(fcntl的每个action对应的参数类型是固定的,可查man手册),分支处理后调用原始函数,避免泛型可变参数的传递问题。

完整代码示例

#define _GNU_SOURCE
#include <dlfcn.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>

// 定义原始fcntl的函数指针类型
typedef int (*orig_fcntl_t)(int fd, int action, ...);
static orig_fcntl_t orig_fcntl = NULL;

int fcntl(int fd, int action, ...) {
    // 初始化原始函数指针(仅执行一次)
    if (!orig_fcntl) {
        orig_fcntl = (orig_fcntl_t)dlsym(RTLD_NEXT, "fcntl");
        if (!orig_fcntl) {
            fprintf(stderr, "[fcntl-log] dlsym error: %s\n", dlerror());
            return -1;
        }
    }

    // 记录基础调用日志
    fprintf(stderr, "[fcntl-log] fd=%d, action=%d\n", fd, action);

    va_list args;
    va_start(args, action);

    int result;
    switch (action) {
        // 分支1:参数为int类型的action
        case F_DUPFD:
        case F_GETFD:
        case F_SETFD:
        case F_GETFL:
        case F_SETFL: {
            int arg = va_arg(args, int);
            fprintf(stderr, "[fcntl-log] arg(int)=%d\n", arg);
            result = orig_fcntl(fd, action, arg);
            break;
        }
        // 分支2:参数为void*类型的action
        case F_GETLK:
        case F_SETLK:
        case F_SETLKW:
        case F_SETOWN:
        case F_GETOWN: {
            void* arg = va_arg(args, void*);
            fprintf(stderr, "[fcntl-log] arg(void*)=%p\n", arg);
            result = orig_fcntl(fd, action, arg);
            break;
        }
        // 分支3:处理未知/未覆盖的action
        default: {
            // 用long兼容int和void*的大小(32位4字节,64位8字节)
            long arg = va_arg(args, long);
            fprintf(stderr, "[fcntl-log] arg(unknown)=%ld\n", arg);
            result = orig_fcntl(fd, action, arg);
            break;
        }
    }

    va_end(args);
    // 可选:记录返回值
    fprintf(stderr, "[fcntl-log] return=%d\n", result);
    return result;
}

编译与使用

将代码保存为fcntl_log.c,编译成共享库:

gcc -shared -fPIC -o libfcntl_log.so fcntl_log.c -ldl

通过LD_PRELOAD加载拦截器运行目标程序:

LD_PRELOAD=./libfcntl_log.so ./your_target_program

关键说明

  1. 按action分支处理:fcntl的每个action对应的参数类型是标准定义的,无需泛型处理,直接分支匹配最可靠。
  2. 类型兼容性:未知action用long作为参数载体,因为long的大小在32位和64位系统中分别与int/指针一致,避免参数传递时的栈对齐错误。
  3. 初始化逻辑:原始函数指针只初始化一次,避免重复调用dlsym影响性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 05:35:19