3

我正在使用脚本运行带有 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/environ 和 /proc/8408/environ 都是相同的)但是在调用克隆之后不再记录数据 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在同一可执行文件上运行的输出包含更多的读取和搜索

4

2 回答 2

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东西报告它正在使用的协程后端。但是,我怀疑 Xenqemu-dm可能太旧而无法拥有这些协程的东西?我不知道。

于 2013-07-17T15:24:40.730 回答
3

经过长时间的调查,这是我的发现:

  • #include <unistd.h>最大的错误,因为它将文件访问调用重定向到它们的 64 位等价物,因此实际上限制了我可以实际捕获的内容(我只能捕获较低的读取,因为较高的读取使用 read64 或 pread64 代替)
  • 需要实现 32 位和 64 位版本的所有功能
  • 尽管 strace 报告了很多 lseek 和 read 调用,但 xen 的 qemu-dm 实际上使用了 pread 和 pread64 (由于某种原因,当使用 qemu 时,相同的 strace 会正确报告)
  • 定义 _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 我现在终于得到比以前更多的输出,并且实现确实有效(仍然有一些丢失的文件访问需要为完整解决方案定义的功能,但我打开此问题的原因已解决)。

于 2013-07-18T10:28:47.043 回答