问题描述
我试图通过 execve()
拦截 execl()
。这是我的包装器调用(构建为共享库 - libexec.so
)。
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
static int (*real_exec)(const char*,char *const [],char *const []) = 0;
static void __attribute__((constructor))init(void) {
real_exec = (int (*)(const char*,char *const []))dlsym(RTLD_NEXT,"execve");
}
int execve(const char* arg,char *const argv[],char *const envp[]) {
printf ("In wrapped execve\n");
return (*real_exec)(arg,argv,envp);
}
这是我执行的程序。
//run.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int pid;
pid = fork();
if (pid == 0) {
execl("/usr/bin/date","date",NULL);
} else {
wait (NULL);
}
return 0;
}
AFAIK,所有其他 exec*
调用都是 execve()
系统调用的包装器。我还通过运行 strace 验证了上面的程序。
> strace -f -e execve ./run
execve("./run",["./run"],0x7ffcefad6a28 /* 61 vars */) = 0
strace: Process 1491914 attached
[pid 1491914] execve("/usr/bin/date",["date"],0x7fff28393cc8 /* 61 vars */) = 0
Tuesday 16 February 2021 12:52:16 PM UTC
[pid 1491914] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD,si_code=CLD_EXITED,si_pid=1491914,si_uid=1000,si_status=0,si_utime=0,si_stime=0} ---
+++ exited with 0 +++
但是当我按照以下方式运行程序时,
> LD_PRELOAD=/home/user/libexec.so ./run
呼叫没有被拦截。即我没有看到 In wrapped execve\n
被打印出来。
我在这里缺少什么?如果我直接在 execve()
中调用 run.c
,它会起作用。
其次,LD_PRELOAD
是否也跟随子进程?孩子和后代的电话是否也被拦截了?
解决方法
简短回答:问题在于,上面实现的“libexec”只会拦截主程序与 execve 之间的 execve 调用。 execl 和 execve 之间的 'libc' 内部调用不会被拦截到 libexec 包装器。
长答案: 使用 LD_LIBRARY_PATH=libexec.so,建立如下调用树:"main" -> "libexec.so" -> "libc.so"。来自“main”的调用将被libexec拦截,但“libc.so”内部的调用(例如execl -> execve)不会被拦截(默认)。
解决方案是在 libexec.so 的“execl”中添加一个包装器,其结构与已经实现的 execve 包装器相同。