mstrace.c (3641B)
- // SPDX-FileCopyrightText: 2024 Haelwenn (lanodan) Monnier <contact+mstrace@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _DEFAULT_SOURCE
- #include <errno.h>
- #include <inttypes.h> // PRIu64
- #include <linux/ptrace.h> // ptrace_syscall_info, __u64
- #include <stdbool.h>
- #include <stdio.h> // fprintf
- #include <string.h> // strerror
- #include <sys/ptrace.h> // ptrace()
- #include <sys/syscall.h> // SYS_*
- #include <sys/wait.h>
- #include <unistd.h> // getpid, fork, execvp
- extern void print_syscall(__u64 nr, __u64 args[6]);
- pid_t child = -1;
- static int
- wait_for_syscall(int *status)
- {
- while(1)
- {
- ptrace(PTRACE_SYSCALL, child, 0, 0);
- waitpid(child, status, 0);
- if(WIFSTOPPED(*status) && WSTOPSIG(*status) & 0x80) return 0;
- if(WIFEXITED(*status)) return 1;
- }
- }
- int
- main(int argc, char *argv[])
- {
- argc -= 1;
- argv += 1;
- if(argc < 1)
- {
- fprintf(stderr, "Usage: mstrace <command> [args...]\n");
- return 1;
- }
- child = fork();
- if(child < 0)
- {
- fprintf(stderr, "mstrace: error: Failed creating child process: %s\n", strerror(errno));
- return 1;
- }
- else if(child == 0)
- {
- errno = 0;
- int ret = ptrace(PTRACE_TRACEME);
- if(ret < 0 && errno != 0)
- {
- fprintf(stderr, "mstrace: error: Failed ptrace(PTRACE_TRACEME): %s\n", strerror(errno));
- return 1;
- }
- if(kill(getpid(), SIGSTOP) < 0)
- {
- fprintf(stderr, "mstrace: error: Failed stopping parent: %s\n", strerror(errno));
- return 1;
- }
- execvp(argv[0], argv);
- return 1;
- }
- bool neednl = false;
- struct
- {
- __u64 nr;
- __u64 args[6];
- } entry;
- entry.nr = 0;
- int status;
- waitpid(child, &status, 0);
- ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD);
- while(1)
- {
- if(wait_for_syscall(&status) != 0) break;
- struct ptrace_syscall_info syscall_info;
- long ret =
- ptrace(PTRACE_GET_SYSCALL_INFO, child, sizeof(struct ptrace_syscall_info), &syscall_info);
- if(ret < 0)
- {
- fprintf(
- stderr,
- "mstrace: error: Failed getting syscall information via PTRACE_GET_SYSCALL_INFO: %s\n",
- strerror(errno));
- continue;
- }
- switch(syscall_info.op)
- {
- case PTRACE_SYSCALL_INFO_ENTRY:
- entry.nr = syscall_info.entry.nr;
- for(int i = 0; i < 6; i++)
- entry.args[i] = syscall_info.entry.args[i];
- // print execve(2) at entry time because parameters passed gets cleaned up
- // meanwhile getcwd(2) pass a buffer and so needs to be printed at return time
- if(entry.nr == SYS_execve)
- {
- print_syscall(entry.nr, entry.args);
- neednl = true;
- }
- break;
- case PTRACE_SYSCALL_INFO_EXIT:
- if(entry.nr != SYS_execve) print_syscall(entry.nr, entry.args);
- if(syscall_info.exit.is_error)
- {
- fprintf(stderr,
- " = (errno: %" PRIi64 ") %s\n",
- (int64_t)(-syscall_info.exit.rval),
- strerror(-syscall_info.exit.rval));
- }
- else
- {
- fprintf(stderr, " = %" PRIi64 "\n", (int64_t)syscall_info.exit.rval);
- }
- neednl = false;
- entry.nr = 0;
- break;
- case PTRACE_SYSCALL_INFO_SECCOMP:
- print_syscall(syscall_info.seccomp.nr, syscall_info.seccomp.args);
- neednl = true;
- // TODO: Check how strace manages this
- fprintf(stderr, " [seccomp_ret = %" PRIu32 "]", syscall_info.seccomp.ret_data);
- break;
- case PTRACE_SYSCALL_INFO_NONE:
- if(neednl) fprintf(stderr, "\n");
- fprintf(stderr, "[syscall-info] none\n");
- neednl = false;
- break;
- }
- }
- if(entry.nr != 0)
- {
- print_syscall(entry.nr, entry.args);
- neednl = true;
- }
- if(neednl) fprintf(stderr, "\n");
- if(WIFEXITED(status))
- {
- fprintf(stderr, "+++ exited with %d +++\n", WEXITSTATUS(status));
- return WEXITSTATUS(status);
- }
- return 0;
- }