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