我正在使用脚本运行一个带有自己创建的库的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的输出包含明显更多的读取和查找
发布于 2013-07-18 18:28:47
经过长时间的调查,我的发现如下:
#include <unistd.h>是最大的错误,因为它将文件访问调用重定向到64位等效项,因此,实际上限制了我可以捕获的内容(我只能捕获较低的读取,因为较高的读取使用了read64或pread64 )的应用程序的适当偏移类型
在删除unistd.h include并实现open之后,open64。fopen,fopen64,read,read64,write,write64,pread,pread64,pread,pread,pread64,pread,pwrite,pwrite64,pwritev,pwritev64,close我现在终于得到了比以前多得多的输出,并且实现实际工作(仍然有一些缺失的文件访问函数需要定义,以获得完整的解决方案,但我打开这个问题的原因已经解决了)。
发布于 2013-07-17 23:24:41
从CLONE_VM和类似的clone参数来看,这个对clone的调用看起来只是创建了一个新线程,而不是一个新进程。我不期望得到的线程重新加载任何库,因此我也不期望你的预加载的库需要在新线程中再次执行-你现有的函数实现应该“只是工作”;进入你的库的所有跳转指令在新线程中应该和旧的一样有效。
因此,我怀疑这不是你的问题,clone是转移注意力的问题。
我唯一的理论是:
exec,__init__代码,尽管这看起来确实不太可能。关于qemu的最后一点--现代的qemu使用协程来做很多IO的事情。它使用不同的后端,这取决于主机系统上的可用资源-如果你不走运,它会为每个后端创建一个线程,这可能会导致非常非常大量的线程。阅读这里- http://lists.gnu.org/archive/html/qemu-devel/2011-07/msg02894.html -有一些方法可以让qemu configure的东西报告它正在使用的协程后端。然而,我怀疑Xen qemu-dm可能太老了,不能再有这个协同程序了?我不知道。
https://stackoverflow.com/questions/17700712
复制相似问题