简体   繁体   中英

How to Override A C System Call?

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

Yes, you want 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

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

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


UPDATE:

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

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

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

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

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

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.

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

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

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

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.

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

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

Edit: We also have to preserve the correct errno value

Putting it all together:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM