logo

mstrace

Small Linux strace(1) implementation
commit: c3e7239fd9a12f02c1fbccf760e863f1546cf9f3
parent daacd26949b5f587c45a9784580fd44f076e402d
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Sun, 24 Nov 2024 00:39:01 +0100

handle bog standard syscalls properly

Diffstat:

MMakefile6+++---
Mministrace.c46+++++++++++-----------------------------------
Aprint_syscall.c220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 234 insertions(+), 38 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,8 +1,8 @@ # SPDX-FileCopyrightText: 2024 Haelwenn (lanodan) Monnier <contact+ministrace@hacktivis.me> # SPDX-License-Identifier: MPL-2.0 -ministrace: ministrace.c strsyscall.c - ${CC} -std=c99 ${CFLAGS} -o ministrace ministrace.c strsyscall.c ${LDFLAGS} ${LDSTATIC} +ministrace: ministrace.c strsyscall.c print_syscall.c + ${CC} -std=c99 ${CFLAGS} -o ministrace ministrace.c strsyscall.c print_syscall.c ${LDFLAGS} ${LDSTATIC} tmp_strsyscall.h: ${CC} -undef -E -P -fdirectives-only -o tmp_strsyscall.h - <<<'#include <sys/syscall.h>' @@ -16,4 +16,4 @@ clean: .PHONY: format format: - clang-format -style=file -assume-filename=.clang-format -i ministrace.c + clang-format -style=file -assume-filename=.clang-format -i ministrace.c print_syscall.c diff --git a/ministrace.c b/ministrace.c @@ -3,22 +3,22 @@ #define _DEFAULT_SOURCE -#include "strsyscall.h" - -#include <assert.h> #include <errno.h> #include <inttypes.h> // PRIu64 -#include <linux/ptrace.h> // ptrace_syscall_info +#include <linux/ptrace.h> // ptrace_syscall_info, __u64 #include <stdbool.h> -#include <stdio.h> // fprintf -#include <string.h> // strerror -#include <sys/ptrace.h> -#include <sys/syscall.h> // SYS_* +#include <stdio.h> // fprintf +#include <string.h> // strerror +#include <sys/ptrace.h> // ptrace() #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(pid_t child, int *status) +wait_for_syscall(int *status) { while(1) { @@ -31,30 +31,6 @@ wait_for_syscall(pid_t child, int *status) } } -static void -print_syscall(__u64 nr, __u64 args[6]) -{ - assert(nr < SYSCALLS_MAX); - if(str_syscalls[nr] != NULL) - { - fprintf(stderr, "%s(", str_syscalls[nr]); - } - else - { - fprintf(stderr, "syscall(%" PRIu64 ", ", (uint64_t)nr); - } - - fprintf(stderr, - "0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 - ")", - (uint64_t)args[0], - (uint64_t)args[1], - (uint64_t)args[2], - (uint64_t)args[3], - (uint64_t)args[4], - (uint64_t)args[5]); -} - int main(int argc, char *argv[]) { @@ -67,7 +43,7 @@ main(int argc, char *argv[]) return 1; } - pid_t child = fork(); + child = fork(); if(child < 0) { fprintf(stderr, "ministrace: error: Failed creating child process: %s\n", strerror(errno)); @@ -101,7 +77,7 @@ main(int argc, char *argv[]) ptrace(PTRACE_SETOPTIONS, child, 0, PTRACE_O_TRACESYSGOOD); while(1) { - if(wait_for_syscall(child, &status) != 0) break; + if(wait_for_syscall(&status) != 0) break; struct ptrace_syscall_info syscall_info; long ret = diff --git a/print_syscall.c b/print_syscall.c @@ -0,0 +1,220 @@ +// SPDX-FileCopyrightText: 2024 Haelwenn (lanodan) Monnier <contact+ministrace@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _DEFAULT_SOURCE + +#include "strsyscall.h" + +#include <assert.h> +#include <inttypes.h> // PRIu64 +#include <limits.h> // PATH_MAX +#include <linux/ptrace.h> // __u64 +#include <stdio.h> // fprintf +#include <string.h> // memcpy +#include <sys/ptrace.h> // ptrace() +#include <sys/syscall.h> // SYS_* +#include <unistd.h> // pid_t + +extern pid_t child; + +static char * +read_str(intptr_t addr, size_t len) +{ + static char str_buf[sizeof(unsigned long) * 128] = ""; + if(len == 0 || len > sizeof(unsigned long) * 128) len = sizeof(unsigned long) * 128; + + size_t i = 0; + while(i < len) + { + unsigned long tmp = ptrace(PTRACE_PEEKDATA, child, addr + i); + + memcpy(str_buf + i, &tmp, sizeof(tmp)); + i += sizeof(tmp); + + if(memchr(&tmp, 0, sizeof(tmp)) != NULL) break; + + if(i > len) + { + str_buf[len - 1] = '\0'; + break; + } + } + + return str_buf; +} + +void +print_syscall(__u64 nr, __u64 args[6]) +{ + // Handle bog-standard syscalls, limited to ones in POSIX but exhaustivity isn't warranted + // Ordered by number-of-arguments/complexity + switch(nr) + { + // path, buf, size + case SYS_readlinkat: + fprintf(stderr, + "%s(%" PRIi64 ",\"%s\", 0x%" PRIx64 ", %zd)", + str_syscalls[nr], + (int64_t)args[0], + read_str(args[0], PATH_MAX), + (int64_t)args[1], + (size_t)args[2]); + return; + // int, path, hex, hex + case SYS_openat: + case SYS_faccessat: + fprintf(stderr, + "openat(%" PRIi64 ", \"%s\", %" PRIx64 ", %" PRIx64 ")", + (int64_t)args[0], + read_str(args[1], PATH_MAX), + (int64_t)args[2], + (int64_t)args[3]); + return; + // path, hex, hex + case SYS_execve: + fprintf(stderr, + "execve(\"%s\", %" PRIx64 ", %" PRIx64 ")", + read_str(args[0], PATH_MAX), + (uint64_t)args[1], + (uint64_t)args[2]); + return; + // int, buf, size + case SYS_write: + case SYS_read: + // Need handling of non-printable characters to print buf + fprintf(stderr, + "%s(%" PRIi64 ", 0x%" PRIx64 ", %zd)", + str_syscalls[nr], + (int64_t)args[0], + (int64_t)args[1], + (size_t)args[2]); + return; + // int, path, path + case SYS_linkat: + case SYS_symlinkat: + fprintf(stderr, + "%s(%" PRIi64 ", \"%s\", \"%s\")", + str_syscalls[nr], + (int64_t)args[0], + read_str(args[1], PATH_MAX), + read_str(args[2], PATH_MAX)); + return; + // path, buf, size + case SYS_readlink: + fprintf(stderr, + "%s(\"%s\", 0x%" PRIx64 ", %zd)", + str_syscalls[nr], + read_str(args[0], PATH_MAX), + (int64_t)args[1], + (size_t)args[2]); + return; + // path, int, int + case SYS_open: + case SYS_chown: + case SYS_lchown: + fprintf(stderr, + "%s(\"%s\", %" PRIi64 ", %" PRIi64 ")", + str_syscalls[nr], + read_str(args[0], PATH_MAX), + (int64_t)args[1], + (int64_t)args[2]); + return; + // path, path + case SYS_link: + case SYS_symlink: + fprintf(stderr, + "%s(\"%s\", \"%s\")", + str_syscalls[nr], + read_str(args[0], PATH_MAX), + read_str(args[1], PATH_MAX)); + return; + // path, off_t + case SYS_truncate: + fprintf(stderr, + "%s(\"%s\", %" PRIu64 ")", + str_syscalls[nr], + read_str(args[0], PATH_MAX), + (off_t)args[1]); + return; + // path, size + case SYS_getcwd: + fprintf( + stderr, "%s(\"%s\", %zd)", str_syscalls[nr], read_str(args[0], PATH_MAX), (size_t)args[1]); + return; + // path, int + case SYS_access: + case SYS_stat: + case SYS_lstat: + fprintf(stderr, + "%s(\"%s\", 0x%" PRIx64 ")", + str_syscalls[nr], + read_str(args[0], PATH_MAX), + (int64_t)args[1]); + return; + // int, path + case SYS_unlinkat: + fprintf(stderr, "unlinkat(%" PRIu64 "\"%s\")", (int64_t)args[0], read_str(args[1], PATH_MAX)); + return; + // int, off_t + case SYS_ftruncate: + fprintf( + stderr, "%s(%" PRIu64 ", %" PRIu64 ")", str_syscalls[nr], (int64_t)args[0], (off_t)args[1]); + return; + // int, int + case SYS_kill: + case SYS_dup2: + fprintf( + stderr, "%s(%" PRIi64 ", %" PRIi64 ")", str_syscalls[nr], (int64_t)args[0], (int64_t)args[1]); + return; + // path + case SYS_unlink: + case SYS_rmdir: + case SYS_chdir: + fprintf(stderr, "%s(\"%s\")", str_syscalls[nr], read_str(args[0], PATH_MAX)); + return; + // hex + case SYS_brk: + fprintf(stderr, "%s(%" PRIx64 ")", str_syscalls[nr], (int64_t)args[0]); + return; + // int + case SYS_dup: + case SYS_close: + fprintf(stderr, "%s(%" PRIi64 ")", str_syscalls[nr], (int64_t)args[0]); + return; + // uint + case SYS_alarm: + fprintf(stderr, "%s(%" PRIu64 ")", str_syscalls[nr], (uint64_t)args[0]); + return; + // syscall0 + case SYS_getpid: + case SYS_getppid: + case SYS_munlockall: + case SYS_setsid: + case SYS_sync: + case SYS_fork: + case SYS_pause: + fprintf(stderr, "%s()", str_syscalls[nr]); + return; + // fallback + default: + assert(nr < SYSCALLS_MAX); + if(str_syscalls[nr] != NULL) + { + fprintf(stderr, "%s(", str_syscalls[nr]); + } + else + { + fprintf(stderr, "syscall(%" PRIu64 ", ", (uint64_t)nr); + } + + fprintf(stderr, + "0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRIx64 + ")", + (uint64_t)args[0], + (uint64_t)args[1], + (uint64_t)args[2], + (uint64_t)args[3], + (uint64_t)args[4], + (uint64_t)args[5]); + } +}