【问题标题】:Overriding execve() with LD_PRELOAD only works sometimes使用 LD_PRELOAD 覆盖 execve() 有时仅适用
【发布时间】: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 +++

这怎么可能?它是完全相同的系统调用,具有完全相同的调用进程环境,但它做了一些不同的事情。如果有任何关于此的指示,我会很高兴。

【问题讨论】:

  • 运行./testexecve 调用由现有的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-&gt;execv("/usr/bin/ls", 0x1fdad10 &lt;no return ...&gt; 但如果我用 LD_PRELOAD 覆盖 execv,也不会调用它。 ://

标签: c linux


【解决方案1】:

好的,解决方案其实很简单:Python 调用 execv,而不是 execve;并且打印的标准输出被调用进程的代码占用,而不是打印到终端。这就是为什么它似乎不起作用(实际上它确实起作用)。

【讨论】:

    猜你喜欢
    • 2011-08-30
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 2011-12-10
    相关资源
    最近更新 更多