如何覆盖C系统调用? [英] How to Override A C System Call?

查看:94
本文介绍了如何覆盖C系统调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So the problem is the following. The project needs to intercept all file IO operations, like open() and close(). I am trying to add printf() before calling the corresponding open() or close(). I am not supposed to rewrite the source code by changing open() or close() to myOpen() or myClose() for example. I have been trying to use LD_PRELOAD environment variable. But the indefinite loop problem came up. My problem is like this 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以使用该共享库

You need to create a shared library (.so) that has code for all functions that you want to intercept. And, you want to set LD_PRELOAD to use that shared library

这是open函数的一些示例代码.您需要对要拦截的每个函数执行类似的操作:

Here is some sample code for the open function. You'll need to do something similar for each function you want to intercept:

#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

I believe RTLD_NEXT is easiest and may be sufficient. Otherwise, you could add a constructor that does dlopen once on libc

更新:

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

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,则需要此.

This is defined by several #include files, so try #include <stdio.h>. You'll need that if you want to call printf.

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

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

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

That is defined by doing #include <dlfcn.h> [as shown in my example]

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

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

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

From man dlsym, it says: Link with -ldl So, add -ldl to the line that builds your .so.

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

Also, you have to be careful to prevent infinite recursion if the "special stuff" does something that loops back on your intercept function.

值得注意的是,您想致电printf.如果您拦截write系统调用,则可能会发生不良情况.

Notably, you want to call printf. If you intercept the write syscall, bad things may happen.

因此,您需要跟踪何时已进入某个拦截函数中,并且(如果已存在)进行一些特殊操作.请参见in_self变量.

So, you need to keep track of when you're already in one of your intercept functions and not do anything special if already there. See the in_self variable.

#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;
}


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


The above works okay for single threaded programs/environments, but if you're intercepting an arbitrary one, it could be multithreaded.

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

So, we'd have to initialize all the real_* pointers in a constructor. This is a function with a special attribute that tells the dynamic loader to call the function ASAP automatically.

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

And, we have to put in_self into thread local storage. We do this by adding the __thread attribute.

对于多线程版本,您可能需要链接-lpthread-ldl.

You may need to link with -lpthread as well as -ldl for the multithreaded version.

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

We also have to preserve the correct errno value

将它们放在一起:

#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;
}

这篇关于如何覆盖C系统调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆