如何覆盖C系统调用?

时间:2019-01-11 06:14:15

标签: c override system-calls

所以问题出在下面。项目需要拦截所有文件IO 操作,例如open()close()。我试图在调用相应的printf()open()之前添加close()。我不应该通过将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);
}

1 个答案:

答案 0 :(得分:2)

是的,您想要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最简单,可能就足够了。否则,您可以添加一个对dlopen

执行一次libc 的构造函数

更新:

  

我不熟悉C,并且gcc遇到以下问题。 “错误:未声明'NULL'(此功能首次使用)”,

这是由几个#include文件定义的,因此请尝试#include <stdio.h>。如果您要致电printf,则需要使用此电话。

  

“错误:未声明'RTLD_NEXT'(此功能首次使用)”,

这是通过执行#include <dlfcn.h>来定义的(如我的示例所示)

  

和“符号查找错误:./hack_stackoverflow.so:未定义的符号:dlsym”。

man dlsym中说:-ldl 链接因此,将-ldl添加到构建.so的行中。

此外,如果“特殊内容”所做的事情会在您的拦截函数上循环返回,则必须小心以防止无限递归。

值得注意的是,您想致电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;
}

上面的方法对于单线程程序/环境来说还可以,但是如果您拦截任意一个程序,则可能是多线程

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

而且,我们必须将in_self放入线程本地存储。为此,我们添加了__thread属性。

对于多线程版本,您可能需要链接-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;
}