简体   繁体   English

LD_PRELOAD和clone()

[英]LD_PRELOAD and clone()

I'm using a script to run a program with LD_PRELOAD with a library created by me to intercept some calls, it works well but at some point the process calls clone() and I lose the ability to intercept what's next (the program is run again without my library), is there any way to overcome this? 我正在使用一个脚本运行一个带有LD_PRELOAD的程序和一个由我创建的库拦截一些调用,它运行良好但是在某些时候进程调用clone()并且我失去了拦截下一步的能力(程序运行)再没有我的图书馆),有什么方法可以克服这个问题吗? call is 打电话是

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)

Looking over the parameters of clone I saw that there is the ability to trace the child process as well, but nothing pertaining to preloading. 查看克隆的参数我发现有能力跟踪子进程,但没有任何与预加载有关的内容。

I should also mention that I'm trying to intercept all calls on a specific file descriptor and the process clones file descriptors so I'm not even sure if it would be possible to do what I want without some flag to clone (problem is I don't understand all of them). 我还要提一下,我试图拦截特定文件描述符上的所有调用和进程克隆文件描述符,所以我甚至不确定是否有可能做我想要的而没有一些标记来克隆(问题是我不明白所有这些)。

UPDATE: I'm using this trying to log all activity done by qemu-dm (which is run by xen) 更新:我正在使用此尝试记录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;
}

compiled with gcc -ggdb -shared -fPIC -Wl,-init,__init__ -Wl,-fini,__fini__ -o fileaccesshooks.so -ldl fileaccesshooks.c 使用gcc -ggdb -shared -fPIC -Wl,-init,__init__ -Wl,-fini,__fini__ -o fileaccesshooks.so -ldl fileaccesshooks.c编译gcc -ggdb -shared -fPIC -Wl,-init,__init__ -Wl,-fini,__fini__ -o fileaccesshooks.so -ldl fileaccesshooks.c

wrapper script contents: 包装脚本内容:

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

As observed in comments below the environment is actually the same for the task and the process (LD_PRELOAD is the same for both /proc/8408/task/8526/environ and /proc/8408/environ) however after the call to clone no more data is logged grep -e "testfile" -e "(11" /tmp/out-0 正如在下面的评论中所观察到的,环境对于任务和过程实际上是相同的(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

this is what I get, however comparatively the output of strace -f run on the same executable contains significantly more reads as well as seeks 这就是我得到的,但相对而言,相同的可执行文件上运行的strace -f的输出包含更多的读取和搜索

From the clone parameters of CLONE_VM and similar, it looks like this call to clone is simply creating a new thread rather than a new process. CLONE_VMclone参数和类似的,看起来这个clone调用只是创建一个新的线程而不是一个新的进程。 I wouldn't expect the resulting thread to reload any libraries and therefore I would not expect your preloaded library to need to act again in the new thread - your existing function implementations should 'just work'; 我不希望结果线程重新加载任何库,因此我不希望你的预加载库需要在新线程中再次行动 - 你现有的函数实现应该'正常工作'; all the jump instructions into your library should remain equally valid in the new thread as the old. 所有跳转到库中的指令应该在旧线程中保持同样有效。

I am therefore suspicious that this is not your problem and that the clone is a red herring. 因此,我怀疑这不是你的问题, clone是一个红鲱鱼。

My only theories are: 我唯一的理论是:

  • There's an exec somewhere as well 某个地方也有一个exec
  • The __init__ code in your library is getting called for each new thread, though this seems very unlikely indeed. 每个新线程都会调用库中的__init__代码,尽管这似乎不太可能。

One last point regarding qemu specifically - modern qemu uses coroutines for lots of IO things. 关于qemu的最后一点 - 现代qemu使用协同程序来处理许多IO事物。 It uses various backends depending on what's available on the host system - if you're unlucky, it creates a thread for each one which can result in very, very large numbers of threads. 它使用各种后端,具体取决于主机系统上的可用内容 - 如果你运气不好,它会为每个后端创建一个线程,这可能会产生非常非常大量的线程。 Read here - http://lists.gnu.org/archive/html/qemu-devel/2011-07/msg02894.html - there's some way to get the qemu configure stuff to report what coroutine backend it's using. 请阅读此处 - http://lists.gnu.org/archive/html/qemu-devel/2011-07/msg02894.html - 有一些方法可以让qemu configure内容来报告它正在使用的协程后端。 However, I suspect the Xen qemu-dm might be too old to have this coroutine stuff? 但是,我怀疑Xen qemu-dm可能太老而无法获得这些协同程序的东西? I don't know. 我不知道。

After a long investigation here are my findings: 经过长时间的调查,我的发现是:

  • #include <unistd.h> was the biggest mistake as it redirects file access calls to their 64-bit equivalents, thus really limiting what I can actually catch (I could only catch the lower reads as the higher ones used read64 or pread64 instead) #include <unistd.h>最大的错误,因为它将文件访问调用重定向到它们的64位等价物,因此真正限制了我实际可以捕获的内容(我只能捕获较低的读取,因为较高的读取使用read64或pread64)
  • need to implement all functions with both 32-bit and 64-bit versions 需要实现32位和64位版本的所有功能
  • although strace reports a lot of lseek and read calls xen's qemu-dm actually uses pread and pread64 instead (which the same strace reports correctly when using qemu instead for some reason) 虽然strace报告了很多lseek和读取调用xen的qemu-dm实际上使用了pread和pread64(由于某些原因,当使用qemu时,相同的strace报告正确)
  • defining _GNU_SOURCE (which is required for RTLD_NEXT) defines off_t to be the same as off64_t so make sure you use the proper types for offsets as the application you are trying to intercept 定义_GNU_SOURCE(这是RTLD_NEXT所必需的)将off_t定义为与off64_t相同,因此请确保使用正确的偏移类型作为您尝试拦截的应用程序

After removing the unistd.h include and implementing open, open64. 删除unistd.h包含并实现open,open64。 fopen, fopen64, read, read64, write, write64, pread, pread64, preadv, preadv64, pwrite, pwrite64, pwritev, pwritev64, close I now finally get significantly more output than before and the implementation actually works (there are still some missing file access functions that need to be defined for a complete solution but the reason I opened this question is solved). fopen,fopen64,read,read64,write,write64,pread,pread64,preadv,preadv64,pwrite,pwrite64,pwritev,pwritev64,close我现在终于获得了比以前更多的输出并且实现实际工作(仍然有一些丢失的文件访问需要为完整解决方案定义的函数,但解决了我打开这个问题的原因)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM