我知道我可以通过中断 gdb 中的进程、使用文件描述符关闭然后使用我想要的文件名重新打开来更改程序写入的文件。有没有办法在运行时做同样的事情?
例如,我知道我想要更改的文件使用文件描述符 5,所以我尝试了
./myexe 5>/dev/null
但所做的只是改变一些事情,因此感兴趣的文件位于 fd=6 上。
答案1
当程序打开一个文件时,该文件最终会出现在当时空闲的文件描述符上。通过在程序启动之前打开一个文件,您只会使另一个文件描述符变得繁忙,因此您感兴趣的文件可能最终会出现在不同的描述符上。如果您希望程序打开不同的文件,则需要在打开操作发生时对其进行修改,或者事后进行干预。
修改操作的一种方法是在程序和系统库之间插入一些代码,方法是预加载一小段代码。这假设该程序是动态链接的二进制文件,或者是由动态链接的二进制文件执行的脚本(即它不是静态链接的)。将以下代码写入文件override_fopen.c
:
#include <dlfcn.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef FROM
#error "Define FROM to the path to override in double quotes, e.g. -DFROM='\"/bad\"'"
#endif
#ifndef TO
#error "Define TO to the path to use instead in double quotes, e.g. -DFROM='\"/good\"'"
#endif
FILE *fopen(const char *path, const char *mode) {
void *(*original_fopen)(const char *, const char *) = dlsym(RTLD_NEXT, "fopen");
if (!strcmp(path, FROM)) {
path = TO;
}
return original_fopen(path, mode);
}
int open(const char *path, int oflag, ...) {
int (*original_open)(const char *, int, ...) = dlsym(RTLD_NEXT, "open");
int ret;
va_list args;
if (!strcmp(path, FROM)) {
path = TO;
}
va_start(args, oflag);
if (oflag & O_CREAT) {
ret = original_open(path, oflag, (mode_t)va_arg(args, mode_t));
} else {
ret = original_open(path, oflag);
}
va_end(args);
return ret;
}
使用以下命令进行编译(适用于 Linux,其他 Unix 变体可能需要不同的选项)。请注意要覆盖的路径周围的引号。
gcc -DFROM='"/some/path"' -DTO='"/dev/null"' -D_GNU_SOURCE -O -Wall -fPIC -shared -o override_fopen.so override_fopen.c -ldl
按如下方式运行程序(在 OSX 上,使用DYLD_PRELOAD
代替LD_PRELOAD
):
LD_PRELOAD=./override_fopen.so ./myexe
仅当程序调用fopen
或open
库函数时,此方法才有效。如果它调用其他函数,您需要覆盖该函数。您可以使用ltrace
查看程序调用了哪些库。