/* ************************************************************************** */ /* */ /* ::: :::::::: */ /* utils.c :+: :+: :+: */ /* +:+ +:+ +:+ */ /* By: thrieg +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2025/12/11 04:31:15 by thrieg #+# #+# */ /* Updated: 2025/12/11 23:27:01 by thrieg ### ########.fr */ /* */ /* ************************************************************************** */ #include "../includes/ft_strace.h" #include "../includes/syscalls_x86.h" #include "../includes/syscalls_x64.h" #include // for EI_NIDENT, EI_CLASS, ELFCLASS32, ELFCLASS64 #include // for open #include #include // for user_regs_struct //returns 64 for x86_64, or 32 for 32 bits, -1 for open/read error, -2 for unrecognised file type ssize_t binary_type(char *path_to_binary) { int fd; unsigned char ident[EI_NIDENT]; ssize_t ret; fd = open(path_to_binary, O_RDONLY); if (fd == -1) return (-1); ret = read(fd, ident, EI_NIDENT); close(fd); if (ret == -1) return (-1); if (ret != EI_NIDENT) return (-2); /* Check this is an ELF file (binary) */ if (ident[0] != 0x7f || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F') return (-2); if (ident[EI_CLASS] == ELFCLASS64) return (64); if (ident[EI_CLASS] == ELFCLASS32) return (32); return (-2); //don't know wtf this file is at this point } static void read_regs(pid_t pid, struct user_regs_struct *regs) { struct iovec io; io.iov_base = regs; io.iov_len = sizeof(*regs); if (ptrace(PTRACE_GETREGSET, pid, (void*)NT_PRSTATUS, &io) == -1) { fprintf(stderr, "PTRACE_GETREGSET failed: %s\n", strerror(errno)); return; } } static void fill_args(long long args[6], struct user_regs_struct *regs, size_t binary_type) { if (binary_type == 64) { args[0] = (long long)regs->rdi; args[1] = (long long)regs->rsi; args[2] = (long long)regs->rdx; args[3] = (long long)regs->r10; args[4] = (long long)regs->r8; args[5] = (long long)regs->r9; } else if (binary_type == 32) { args[0] = (long long)(long)regs->ebx; args[1] = (long long)(long)regs->ecx; args[2] = (long long)(long)regs->edx; args[3] = (long long)(long)regs->esi; args[4] = (long long)(long)regs->edi; args[5] = (long long)(long)regs->ebp; } } //also sets argc, returns NULL if syscall not recognised static const char *get_syscall_name(size_t binary_type, struct user_regs_struct *regs, unsigned char *argc) { if (binary_type == 64 && regs->orig_rax < g_syscalls_64_len) { *argc = g_syscalls_64[regs->orig_rax].argc; return (g_syscalls_64[regs->orig_rax].name); } else if (binary_type == 32 && regs->orig_eax < g_syscalls_32_len) { *argc = g_syscalls_32[regs->orig_eax].argc; return (g_syscalls_32[regs->orig_eax].name); } *argc = 6; return (NULL); } void read_regs_and_print_entry(pid_t pid, size_t binary_type) { struct user_regs_struct regs; read_regs(pid, ®s); long long args[6]; fill_args(args, ®s, binary_type); unsigned char argc = 6; const char *syscall_name = get_syscall_name(binary_type, ®s, &argc); if (syscall_name) { char buffer[400]; //shouldn't be able to overflow because we just print the 64bits registers' values in hex without trying to interpret the string or anything, so max len = syscall_name + 20*argc (64 bit in hex is 16 chars) ssize_t buffer_len = snprintf(buffer, sizeof(buffer), "%s(", syscall_name); if (buffer_len < 0) { printf("error: unexpected syscall name"); return; } for (unsigned char i = 0; i < argc; i++) { ssize_t wrote; if (i > 0) { wrote = snprintf(buffer + buffer_len, sizeof(buffer) - buffer_len, ", %lld", args[i]); } else { wrote = snprintf(buffer + buffer_len, sizeof(buffer) - buffer_len, "%lld", args[i]); } if (wrote < 0) { //shouldn't happen break; } buffer_len += wrote; } if ((size_t)buffer_len < sizeof(buffer) - 2) { buffer[buffer_len++] = ')'; buffer[buffer_len] = '\0'; } else { buffer[sizeof(buffer) - 1] = '\0'; } printf("%-100s", buffer); } else { char placeholder_syscall_name[50]; //max 20 chars in a 64 bit register rax at worst, + leeway snprintf(placeholder_syscall_name, sizeof(placeholder_syscall_name), "unknown_syscall_id_%zu", binary_type == 32 ? (size_t)regs.orig_eax : (size_t)regs.orig_rax); char buffer[400]; //shouldn't be able to overflow because we just print the 64bits registers' values in hex without trying to interpret the string or anything, so max len = syscall_name + 20*argc (64 bit in hex is 16 chars) ssize_t buffer_len = snprintf(buffer, sizeof(buffer), "%s(", placeholder_syscall_name); if (buffer_len < 0) { printf("error: unexpected syscall name"); return; } for (unsigned char i = 0; i < argc; i++) { ssize_t wrote; if (i > 0) { wrote = snprintf(buffer + buffer_len, sizeof(buffer) - buffer_len, ", %lld", args[i]); } else { wrote = snprintf(buffer + buffer_len, sizeof(buffer) - buffer_len, "%lld", args[i]); } if (wrote < 0) { //shouldn't happen break; } buffer_len += wrote; } if ((size_t)buffer_len < sizeof(buffer) - 2) { buffer[buffer_len++] = ')'; buffer[buffer_len] = '\0'; } else { buffer[sizeof(buffer) - 1] = '\0'; } printf("%-100s", buffer); } } void read_regs_and_print_exit(pid_t pid, size_t binary_type) { struct user_regs_struct regs; long ret; read_regs(pid, ®s); if (binary_type == 64) ret = (long)regs.rax; else ret = (long)regs.eax; printf(" = %ld\n", ret); fflush(stdout); } void print_signal(pid_t pid, int sig) { siginfo_t si; int valid = 0; // Try to get the siginfo from the kernel if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == -1) { // If it fails, we’ll just print the basic signal name valid = 0; } else { valid = 1; } //Basic signal name const char *name = strsignal(sig); if (!name) name = "UNKNOWN"; if (valid) { /* Print something like: --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=1234, si_uid=1000} --- */ printf("--- %s {", name); printf("si_signo=%d, ", si.si_signo); printf("si_code=%d", si.si_code); /* Some signals have extra fields — decode some common ones */ if (si.si_code == SI_USER || si.si_code == SI_QUEUE) { /* Sender PID and UID */ printf(", si_pid=%d, si_uid=%d", si.si_pid, si.si_uid); } /* For faults like SIGSEGV, SIGBUS, SIGILL, print fault address */ if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL) { /* si_addr is a void* pointing to the faulting address */ printf(", si_addr=%p", si.si_addr); } printf("} ---\n"); } else { // Fallback if GETSIGINFO failed: just print the signal printf("--- %s ---\n", name); } fflush(stdout); }