我想运行一个程序,当该程序尝试读取特定文件时,我希望它读取我选择的不同文件。
具体来说,该程序尝试读取配置文件,但设计很差,不允许用户指定配置文件的位置。我也无权在程序尝试读取的位置编辑文件。
我知道有可能探测程序使用 进行的系统调用strace
,并且我可以open()
通过在 下运行该程序来查看该程序进行的唯一系统调用strace
。有什么办法可以截距该系统调用并更改其行为以打开我选择的不同文件?
答案1
LD_PRELOAD
可以在Linux上做到这一点;首先我们的应用程序要修改,app.c
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
char c;
int fd;
fd = open(*++argv, O_RDONLY);
read(fd, &c, 1);
printf("%c\n", c);
return 0;
}
它用于从文件中读取字符:
$ make app
cc app.c -o app
$ echo a > a
$ echo b > b
$ ./app a ; ./app b
a
b
改变这个需要一个伪造的库open
,fakeopen.c
:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
typedef int (*orig_open) (const char *path, int oflag, ...);
int open(const char *path, int oflag, ...)
{
orig_open fn;
mode_t cmode = 0;
va_list ap;
if ((oflag & O_CREAT) == O_CREAT) {
va_start(ap, oflag);
cmode = (mode_t) va_arg(ap, int);
va_end(ap);
}
if (strncmp(path, "a", 2) == 0)
path = getenv("FAKE");
fn = (orig_open) dlsym(RTLD_NEXT, "open");
return fn(path, oflag, cmode);
}
当通过编译和使用时LD_PRELOAD
,当文件名是我们正在寻找的并假设FAKE
有路径时:
$ cat Makefile
fakeopen.so: fakeopen.c
$(CC) $(CFLAGS) -shared -fPIC -ldl fakeopen.c -o fakeopen.so
$ rm fakeopen.so
$ cat Makefile
fakeopen.so: fakeopen.c
$(CC) $(CFLAGS) -shared -fPIC -ldl fakeopen.c -o fakeopen.so
$ make fakeopen.so
cc -shared -fPIC -ldl fakeopen.c -o fakeopen.so
$ FAKE=b LD_PRELOAD=`pwd`/fakeopen.so ./app a
b
我们可以改为./app a
读取文件b
。当然,需要更多的错误检查和其他可能会被踩到的耙子,但这应该是修改open(2)
调用的要点。