【发布时间】:2016-06-19 20:50:05
【问题描述】:
我想使用 LD_PRELOAD 覆盖 execve() 系统调用,但不知道为什么它有时有效,有时无效。
考虑一下这个覆盖 execve() 的非常简单的代码(我会保持完整,以便您可以尝试一下):
#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
typedef ssize_t (*execve_func_t)(const char* filename, char* const argv[], char* const envp[]);
static execve_func_t old_execve = NULL;
int execve(const char* filename, char* const argv[], char* const envp[]) {
printf("Running hook\n");
old_execve = dlsym(RTLD_NEXT, "execve");
return old_execve(filename, argv, envp);
}
(编译为:gcc -std=c99 -o exec.so -shared exec.c -Wall -Wfatal-errors -fPIC -g -ldl)
还有这个非常简单的测试程序:
#define _GNU_SOURCE
#include <unistd.h>
#include <dlfcn.h>
int main() {
char* args[] = {"ls", "/usr", NULL};
char* envp[] = {"LD_PRELOAD=/path/to/exec.so", NULL};
execve("/usr/bin/ls", args, envp);
return 0;
}
现在,当我在 shell 中执行 export LD_PRELOAD=/path/to/exec.so 时,我希望我运行的任何二进制文件都会首先执行钩子。这不是真的,这让我感到困惑:edit:好的,这部分现在很清楚了。以下问题仍未解决。
» strace -f -e trace=execve ./test
execve("./test", ["./test"], [/* 58 vars */]) = 0
Running hook
execve("/usr/bin/ls", ["ls", "/usr"], [/* 1 var */]) = 0
arm-none-eabi avr bin games include lib lib32 lib64 libexec local python sbin share src usr x86_64-pc-linux-gnu
+++ exited with 0 +++
如你所见,钩子只为第二个 execve 运行,而不是第一个。
仍不清楚:
然而,更让我困惑的是,在某些情况下,代码从未预加载,甚至对于子进程也不预加载;例如,当使用 Python 的 subprocess 模块运行 ls /usr 时,会发生这种情况:
» strace -f -e trace=execve /usr/bin/python -c "import subprocess; subprocess.Popen(['ls', '/usr'])"
execve("/usr/bin/python", ["/usr/bin/python", "-c", "import subprocess; subprocess.Po"...], [/* 58 vars */]) = 0
strace: Process 8350 attached
[pid 8350] execve("/usr/local/sbin/ls", ["ls", "/usr"], [/* 58 vars */]) = -1 ENOENT (No such file or directory)
[pid 8350] execve("/usr/local/bin/ls", ["ls", "/usr"], [/* 58 vars */]) = -1 ENOENT (No such file or directory)
[pid 8350] execve("/usr/bin/ls", ["ls", "/usr"], [/* 58 vars */]) = 0
arm-none-eabi avr bin games include lib lib32 lib64 libexec local python sbin share src usr x86_64-pc-linux-gnu
[pid 8350] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=8350, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++
这怎么可能?它是完全相同的系统调用,具有完全相同的调用进程环境,但它做了一些不同的事情。如果有任何关于此的指示,我会很高兴。
【问题讨论】:
-
运行
./test的execve调用由现有的shell 完成。外壳此时已经加载,并且 LD_PRELOAD 对其没有影响。换个角度看,它不可能在./test运行之前运行你的线路char* envp[] = {"LD_PRELOAD=/path/to/exec.so", NULL};对吗? -
是的,没错,我太愚蠢了。感谢您指出了这一点。第二个问题对我来说仍然是个谜......
-
Python 可能会调用一些其他库函数(例如
execvp),然后它会直接执行execve系统调用,而不是通过您的库包装器。 -
它似乎调用了 execv,至少 ltrace 这么说:
_posixsubprocess.cpython-35m-x86_64-linux-gnu.so->execv("/usr/bin/ls", 0x1fdad10 <no return ...>但如果我用 LD_PRELOAD 覆盖 execv,也不会调用它。 ://