Как перехватить execve() через вызовы оболочки exec* с помощью LD_PRELOAD?

Я пытаюсь перехватить 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 [], 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;
}

Насколько я знаю, все остальные вызовы 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 тоже следует за дочерними процессами? Перехватываются ли звонки детей и потомков?


person quasarguru    schedule 16.02.2021    source источник
comment
AFAIK, all other exec* calls are wrapper over execve() system call. I also validated the program above by running with strace. - не обязательно. Библиотечные функции могут вызывать системный вызов, используя syscall напрямую, без вызова его функции-оболочки из библиотеки. И strace показывает вам фактические системные вызовы, а не вызванные библиотечные функции.   -  person qrdl    schedule 16.02.2021
comment
Вы имеете в виду execl() -> syscall(<number for execve>, ...) вместо execl() -> execve() -> syscall(<number for execve>, ...)?   -  person quasarguru    schedule 16.02.2021
comment
Точно, но чтобы быть уверенным, вам нужно проверить исходный код вашей стандартной библиотеки.   -  person qrdl    schedule 16.02.2021
comment
Спасибо. Кроме того, есть ли способ перехватывать вызовы разветвленных процессов (дочерних)?   -  person quasarguru    schedule 16.02.2021
comment
Конечно, просто добавьте LD_PRELOAD=yourlib.so с помощью putenv() перед вызовом exec()   -  person qrdl    schedule 16.02.2021


Ответы (1)


Краткий ответ: проблема в том, что с вышеописанным реализованным «libexec» будут перехватываться только вызовы execve между основной программой и execve. Вызовы внутри 'libc' между execl и execve не будут перехватываться оболочкой libexec.

Подробный ответ: при LD_LIBRARY_PATH=libexec.so устанавливается следующее дерево вызовов: main -> libexec.so -> libc.so. Вызовы из main будут перехватываться libexec, но вызовы внутри libc.so (например, execl -> execve) не перехватываются (по умолчанию).

Решение состоит в том, чтобы добавить оболочку для execl в libexec.so, следуя той же структуре, что и уже реализованная оболочка execve.

person dash-o    schedule 16.02.2021
comment
Спасибо. Это относится к libc.so? Потому что я также пробовал это с вторичными вызовами malloc() (создал разделяемую библиотеку-оболочку для malloc - libmalloc.so и использовал ее с LD_PRELOAD ), где моя программа вызывает некоторую функцию реальной разделяемой библиотеки (с которой она динамически связана) и эта функция вызывает malloc() (я проверил исходный код этой функции), и это сработало. - person quasarguru; 16.02.2021
comment
Это неправильно. Например, если вы перехватите malloc и вызовете printf из собственной реализации malloc, ваша программа рухнет с переполнением стека, потому что printf вызывает malloc внутри, и она вызовет вашу пользовательскую версию malloc, а не библиотечную. - person qrdl; 16.02.2021
comment
Да, я понимаю. Но дело не в этом. Моя программа вызывала только функцию из общей библиотеки, а в моей оболочке malloc не было printf. Она сразу же возвращала NULL, что приводило к сбою библиотечной функции в моей программе. - person quasarguru; 16.02.2021
comment
Это был комментарий к @dash-o, что его ответ неверен - person qrdl; 16.02.2021
comment
@qrdl, а, хорошо, если бы вы могли добавить свой комментарий в качестве ответа, я приму его. - person quasarguru; 16.02.2021
comment
Это была просто идея, я не уверен, как стандартная библиотека работает таким образом на вашей платформе (я думаю, это стандартная библиотека C GNU для Linux), поэтому я не думаю, что это подходит для ответа. - person qrdl; 16.02.2021