Как переопределить системный вызов C? - PullRequest
0 голосов
/ 11 января 2019

Итак, проблема в следующем. Проект должен перехватить все файлы ввода / вывода операции, такие как 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 голосов
/ 11 января 2019

Да, вы хотите 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


UPDATE:

Я не знаком с C, и у меня возникли следующие проблемы с gcc. "error: 'NULL' undeclared (первое использование в этой функции)",

Это определяется несколькими #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;
}
...