LD_PRELOAD и клон ()

Я использую скрипт для запуска программы с LD_PRELOAD с библиотекой, созданной мной для перехвата некоторых вызовов, она работает хорошо, но в какой-то момент процесс вызывает clone(), и я теряю способность перехватывать то, что дальше (программа запускается снова без моей библиотеки), есть ли способ преодолеть это? вызов

clone(child_stack, 
  CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | 
  CLONE_SYSVSEM | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID, 
  parent_tidptr, tls, child_tidptr)

Просматривая параметры клона, я обнаружил, что есть возможность отслеживать и дочерний процесс, но ничего не относится к предварительной загрузке.

Я должен также упомянуть, что я пытаюсь перехватить все вызовы определенного дескриптора файла и процесса клонирует файловые дескрипторы, так что я даже не уверен, что можно было бы сделать то, что я хочу, без какого-либо флага для клонирования (проблема в том, что я не понимаю их всех).

ОБНОВЛЕНИЕ: я использую это, пытаясь записать все действия, выполненные qemu-dm (который выполняется xen)

#define _LARGEFILE64_SOURCE
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdarg.h>

#define dprintf(...) if(__debug__) { char tmp[256]; int cnt = sprintf(tmp, __VA_ARGS__); _write_f_(2, tmp, cnt); _write_f_(__outfile__, tmp, cnt); }

typedef int (*_open_f_t_)(const char *path, int flags, ...);
typedef int (*_open64_f_t_)(const char *path, int flags, ...);
typedef FILE *(*_fopen_f_t_)(const char *path, const char *mode);
typedef int (*_close_f_t_)(int fd);
typedef ssize_t (*_read_f_t_)(int fd, void *buf, size_t count);
typedef ssize_t (*_write_f_t_)(int fd, const void *buf, size_t count);
typedef off_t (*_lseek_f_t_)(int fd, off_t offset, int whence);

static _open_f_t_ _open_f_ = NULL;
static _open64_f_t_ _open64_f_ = NULL;
static _fopen_f_t_ _fopen_f_ = NULL;
static _close_f_t_ _close_f_ = NULL;
static _read_f_t_ _read_f_ = NULL;
static _write_f_t_ _write_f_ = NULL;
static _lseek_f_t_ _lseek_f_ = NULL;
static int __outfile__ = NULL;
static int __debug__ = 0;

void __init__ ()
{
    _open_f_ = (_open_f_t_)dlsym(RTLD_NEXT, "open");
    _open64_f_ = (_open64_f_t_)dlsym(RTLD_NEXT, "open64");
    _fopen_f_ = (_fopen_f_t_)dlsym(RTLD_NEXT, "fopen");
    _close_f_ = (_close_f_t_)dlsym(RTLD_NEXT, "close");
    _read_f_ = (_read_f_t_)dlsym(RTLD_NEXT, "read");
    _write_f_ = (_write_f_t_)dlsym(RTLD_NEXT, "write");
    _lseek_f_ = (_lseek_f_t_)dlsym(RTLD_NEXT, "lseek");
    unlink("/tmp/qemu-dm-preload.log");
    __outfile__ = _open_f_("/tmp/out-0", O_WRONLY | O_CREAT | O_APPEND);
    __debug__ = 1;
}

void __fini__ ()
{
    __debug__ = 0;
    fsync(__outfile__);
    _close_f_(__outfile__);
}

int open(const char *path, int flags, ...)
{
    //replace this
    int result;
    if (flags & O_CREAT)
    {
        va_list arg;
        int mode = 0;
        va_start (arg, flags);
        mode = va_arg (arg, int);
        va_end (arg);
        result = _open_f_(path, flags, mode);
        dprintf("open(%s, %d, %d) => %d\n", path, flags, mode, result);
    } else {
        result = _open_f_(path, flags);
        dprintf("open(%s, %d) => %d\n", path, flags, result);
    }
    return result;
}

int open64(const char *path, int flags, ...)
{
    //replace this
    int result;
    if (flags & O_CREAT)
    {
        va_list arg;
        int mode = 0;
        va_start (arg, flags);
        mode = va_arg (arg, int);
        va_end (arg);
        result = _open64_f_(path, flags, mode);
        dprintf("open(%s, %d, %d) => %d\n", path, flags, mode, result);
    } else {
        result = _open64_f_(path, flags);
        dprintf("open(%s, %d) => %d\n", path, flags, result);
    }

    return result;
}

FILE * fopen(const char *path, const char *mode)
{
    FILE *result = _fopen_f_(path, mode);
    dprintf("fopen(%s, %s) => %p\n", path, mode, result);
    return result;
}

int close(int fd)
{
    //replace this
    int result = _close_f_(fd);
    dprintf("close(%d) => %d\n", fd, result);
    return result;
}

ssize_t read(int fd, void *buf, size_t count)
{
    // replace this
    ssize_t result = _read_f_(fd, buf, count);
    dprintf("read(%d, %p, %lu) => %ld\n", fd, buf, count, result);
    return result;
}

ssize_t write(int fd, const void *buf, size_t count)
{
    // replace this
    ssize_t result = _write_f_(fd, buf, count);
    dprintf("write(%d, %p, %lu) => %ld\n", fd, buf, count, result);
    return result;
}

off_t lseek(int fd, off_t offset, int whence)
{
    // replace this
    off_t result = _lseek_f_(fd, offset, whence);
    dprintf("lseek(%d, %ld, %d) => %ld\n", fd, offset, whence, result);
    return result;
}

составлено с gcc -ggdb -shared -fPIC -Wl,-init,__init__ -Wl,-fini,__fini__ -o fileaccesshooks.so -ldl fileaccesshooks.c

Содержимое сценария оболочки:

#!/bin/bash
export LD_PRELOAD=/home/xception/work/fileaccesshooks.so
exec /usr/lib/xen/bin/qemu-dm-orig "$@"

Как отмечено в комментариях ниже, среда фактически одинакова для задачи и процесса (LD_PRELOAD одинакова для обоих /proc/8408/task/8526/ Environment и / proc / 8408 / Environment), однако после вызова клонирования больше нет данные записываютсяgrep -e "testfile" -e "(11" /tmp/out-0

open(/root/testfile.raw, 2) => 11
read(11, 0x7fffb7259d00, 512) => 512
read(11, 0x7fba6e341200, 512) => 512
read(11, 0x7fba6e341200, 512) => 512
read(11, 0x7fba6e341200, 512) => 512
read(11, 0x7fba6e341200, 512) => 512
read(11, 0x7fba6e341200, 512) => 512
read(11, 0x7fba6e341200, 512) => 512

это то, что я получаю, однако сравнительно вывод strace -f запустить на том же исполняемом файле содержит значительно больше операций чтения и поиска

2 ответа

Решение

После долгого расследования вот мои выводы:

  • #include <unistd.h> была самой большой ошибкой, поскольку она перенаправляла вызовы доступа к файлам на их 64-битные эквиваленты, таким образом действительно ограничивая то, что я могу фактически поймать (я мог поймать только более низкие чтения, поскольку более высокие использовали вместо этого read64 или pread64)
  • необходимо реализовать все функции как с 32-битной, так и с 64-битной версиями
  • хотя strace сообщает о большом количестве вызовов lseek и read, xemu-dm на самом деле вместо этого использует pread и pread64 (что тот же самый strace сообщает правильно при использовании qemu по некоторым причинам)
  • определение _GNU_SOURCE (которое требуется для RTLD_NEXT) определяет off_t так же, как off64_t, поэтому убедитесь, что вы используете правильные типы для смещений в качестве приложения, которое вы пытаетесь перехватить

После удаления unistd.h включить и внедрить open, open64. fopen, fopen64, чтение, read64, запись, запись64, pread, pread64, preadv, preadv64, pwrite, pwrite64, pwritev, pwritev64, close Теперь я наконец-то получаю значительно больше вывода, чем раньше, и реализация на самом деле работает (все еще есть какой-то пропавший файл функции доступа, которые должны быть определены для полного решения, но причина, по которой я открыл этот вопрос, решена).

От clone параметры CLONE_VM и похоже, похоже, этот призыв к clone просто создает новый поток, а не новый процесс. Я не ожидал бы, что результирующий поток перезагрузит какие-либо библиотеки, и, следовательно, я не ожидал бы, что вашей предварительно загруженной библиотеке придется снова действовать в новом потоке - ваши существующие реализации функций должны "просто работать"; все инструкции перехода в вашу библиотеку должны оставаться одинаково действительными в новом потоке, как и в старом.

Поэтому я подозреваю, что это не ваша проблема и что clone красная сельдь

Мои единственные теории:

  • Там есть exec где-то
  • __init__ код в вашей библиотеке вызывается для каждого нового потока, хотя на самом деле это кажется маловероятным.

Последнее, что касается QEMU конкретно - современный qemu использует сопрограммы для многих вещей ввода-вывода. Он использует различные бэкэнды в зависимости от того, что доступно в хост-системе - если вам не повезло, он создает поток для каждого, что может привести к очень, очень большому количеству потоков. Читайте здесь - http://lists.gnu.org/archive/html/qemu-devel/2011-07/msg02894.html - есть какой-то способ получить qemu configure материал для отчета о том, какой внутренний интерфейс он использует. Тем не менее, я подозреваю, что Xen qemu-dm может быть слишком стар, чтобы иметь эти сопрограммы? Я не знаю.

Другие вопросы по тегам