如何覆盖 A C 系统调用?

How to Override A C System Call?

所以问题如下。项目需要拦截所有文件IO 操作,例如 open()close()。我试图在调用相应的 open()close() 之前添加 printf()。例如,我不应该通过将 open()close() 更改为 myOpen()myClose() 来重写源代码。我一直在尝试使用 LD_PRELOAD 环境变量。但是无限循环的问题出现了。我的问题是这样的 one.

int open(char * path,int flags,int mode)
{
    // print file name
    printf("open :%s\n",path);
    return __open(path,flags,mode);
}

是的,你想要LD_PRELOAD

您需要创建一个共享库 (.so),其中包含您要拦截的所有函数的代码。并且,您想设置 LD_PRELOAD 以使用该共享库

这里是 open 函数的一些示例代码。您需要为每个要拦截的函数做类似的事情:

#define _GNU_SOURCE
#include <dlfcn.h>

int
open(const char *file,int flags,int mode)
{
    static int (*real_open)(const char *file,int flags,int mode) = NULL;
    int fd;

    if (real_open == NULL)
        real_open = dlsym(RTLD_NEXT,"open");

    // do whatever special stuff ...

    fd = real_open(file,flags,mode);

    // do whatever special stuff ...

    return fd;
}

我认为 RTLD_NEXT 是最简单的并且可能就足够了。否则,您可以添加一个在 libc

上执行 dlopen once 的构造函数

更新:

I am not familiar with C and I got the following problems with gcc. "error: 'NULL' undeclared (first use in this function)",

这是由几个 #include 文件定义的,所以尝试 #include <stdio.h>。如果你想打电话给 printf.

,你将需要它

"error: 'RTLD_NEXT' undeclared (first use in this function)",

这是通过 #include <dlfcn.h> [如我的示例所示]

定义的

and "symbol lookup error: ./hack_Whosebug.so: undefined symbol: dlsym".

来自 man dlsym,它说:Link 和 -ldl 所以,将 -ldl 添加到构建您的行.so.

另外,如果 "special stuff" 做了一些在你的拦截函数上循环的事情,你必须小心防止无限递归。

值得注意的是,您想打电话给 printf。如果拦截 write 系统调用,可能会发生不好的事情。

因此,您需要跟踪您何时已经处于拦截功能之一,如果已经处于其中,不要做任何特殊的事情。请参阅 in_self 变量。

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

ssize_t
write(int fd,const void *buf,size_t len)
{
    static ssize_t (*real_write)(int fd,const void *buf,size_t len) = NULL;
    static int in_self = 0;
    ssize_t err;

    if (real_write == NULL)
        real_write = dlsym(RTLD_NEXT,"write");

    ++in_self;

    if (in_self == 1)
        printf("mywrite: fd=%d buf=%p len=%ld\n",fd,buf,len);

    err = real_write(fd,buf,len);

    if (in_self == 1)
        printf("mywrite: fd=%d buf=%p err=%ld\n",fd,buf,err);

    --in_self;

    return err;
}

以上对于单线程 programs/environments 没问题,但是如果你拦截任意线程, 可能是 多线程.

因此,我们必须在 构造函数 中初始化所有 real_* 指针。这是一个具有特殊属性的函数,告诉动态加载器尽快自动调用该函数。

而且,我们必须将 in_self 放入 线程本地存储 。我们通过添加 __thread 属性来做到这一点。

对于多线程版本,您可能需要 link 和 -lpthread 以及 -ldl

编辑: 我们还必须保留正确的 errno

综合起来:

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <errno.h>

static int (*real_open)(const char *file,int flags,int mode) = NULL;
static ssize_t (*real_write)(int fd,const void *buf,size_t len) = NULL;

__attribute__((constructor))
void
my_lib_init(void)
{

    real_open = dlsym(RTLD_NEXT,"open");
    real_write = dlsym(RTLD_NEXT,"write");
}

int
open(const char *file,int flags,int mode)
{
    int fd;

    // do whatever special stuff ...

    fd = real_open(file,flags,mode);

    // do whatever special stuff ...

    return fd;
}

ssize_t
write(int fd,const void *buf,size_t len)
{
    static int __thread in_self = 0;
    int sverr;
    ssize_t ret;

    ++in_self;

    if (in_self == 1)
        printf("mywrite: fd=%d buf=%p len=%ld\n",fd,buf,len);

    ret = real_write(fd,buf,len);

    // preserve errno value for actual syscall -- otherwise, errno may
    // be set by the following printf and _caller_ will get the _wrong_
    // errno value
    sverr = errno;

    if (in_self == 1)
        printf("mywrite: fd=%d buf=%p ret=%ld\n",fd,buf,ret);

    --in_self;

    // restore correct errno value for write syscall
    errno = sverr;

    return ret;
}