如何使用 LD_PRELOAD 通过 exec* 包装器调用拦截 execve()?

问题描述

我试图通过 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 包装器相同。