I want to override the execve() syscall by using LD_PRELOAD and can't figure out why it sometimes works and sometimes doesn't.
Consider this very simple code overriding execve() (I'll keep it complete so you can try it if you like):
#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);
}
(compile with: gcc -std=c99 -o exec.so -shared exec.c -Wall -Wfatal-errors -fPIC -g -ldl
)
and this very simple test program:
#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;
}
Now, when I do export LD_PRELOAD=/path/to/exec.so
in my shell, I would expect any binary I run to first execute the hook. That is not true already, which confuses me: edit: Ok, this part is clear now. The issue below is still unsolved.
» 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 +++
As you see, the hook is only run for the second execve, not for the first.
Still unclear:
What confuses me even more however is that in some cases, the code is not preloaded ever, not even for the child processes; for example, when running ls /usr
with Python's subprocess module, this happens:
» 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 +++
How is that possible? It's the exact same syscall with the exact same environment for the calling process, but it does something different. I'd be happy about any pointers about this.
Ok, the solution is actually quite simple: Python calls execv, not execve; and the standard output printed is taken up by the code calling the process and not printed to the terminal. That's why it appears to not work (while it actually does).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With