2013-07-17 5 views
3

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

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

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

UPDATE: Я использую это пытается войти всю деятельность, проделанную в Qemu-дм (который находится в ведении 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 "[email protected]" 

Как отмечалось в комментариях ниже, среда фактически одинакова для задачи и процесса (LD_PRELOAD одинаково для обоих/proc/8408/ta ск/8526/окружать и/Proc/8408/окружать), однако после вызова не клонировать не больше данных регистрируется 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 работать на том же исполняемый содержит значительно больше операций чтения, а также стремится

+1

Что происходит после вызова 'clone'? Процесс клонирования должен иметь тот же набор переменных среды LD_PRELOAD; среда не изменяется. – Joni

+1

вы должны иметь возможность посмотреть среду через/proc/{PID}/environ, есть ли LD_PRELOAD? –

+0

не создается новый процесс, а только новая задача, и он действительно сохраняет среду/proc/8408/task/8526/environ, хм, думаю, мне нужно сделать некоторые дополнительные проверки в библиотеке, чтобы получить задание id и проверить, совпадает ли это с идентификатором процесса – xception

ответ

2

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

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

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

5

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

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

Мои только теории:

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

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

+0

Мои первоначальные мысли заключались в том, что он должен вести себя как поток, но strace говорит мне, что в файловом дескрипторе происходит гораздо больше, чем то, что я могу перехватить ... running qemu напрямую использует pread/preadv/pwrite/pwritev вместо этого, также вывод 'strace -f -e trace = process, fork, clone ...' показывает только начальный 'execve'' 'arch_prctl', несколько' clone' вызывает a несколько '_exit' вызывают' exit_group' и некоторые SIGALRM и SIGUSR2. Боюсь, что это может быть использование syscalls прямо, и именно поэтому я не могу его перехватить?!? – xception

+0

Также очень странно и то, что сделало меня подозрительным к клону, действующему как еще один процесс, состоит в том, что для того, чтобы 'strace' перехватывал больше чтений и поисковые вызовы, которых я не делал, я должен был запустить его с' -f параметр – xception