ft_strace/srcs/utils.c

258 lines
8.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* utils.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: thrieg <thrieg@student.42mulhouse.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/12/11 04:31:15 by thrieg #+# #+# */
/* Updated: 2025/12/12 00:19:01 by thrieg ### ########.fr */
/* */
/* ************************************************************************** */
#include "../includes/ft_strace.h"
#include "../includes/syscalls_x86.h"
#include "../includes/syscalls_x64.h"
#include <elf.h> // for EI_NIDENT, EI_CLASS, ELFCLASS32, ELFCLASS64
#include <fcntl.h> // for open
#include <sys/uio.h>
#include <sys/user.h> // 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)(regs->rbx & 0xFFFFFFFF);
args[1] = (long long)(regs->rcx & 0xFFFFFFFF);
args[2] = (long long)(regs->rdx & 0xFFFFFFFF);
args[3] = (long long)(regs->rsi & 0xFFFFFFFF);
args[4] = (long long)(regs->rdi & 0xFFFFFFFF);
args[5] = (long long)(regs->rbp & 0xFFFFFFFF);
}
}
//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_rax & 0xFFFFFFFF) < g_syscalls_32_len)
{
*argc = g_syscalls_32[regs->orig_rax & 0xFFFFFFFF].argc;
return (g_syscalls_32[regs->orig_rax & 0xFFFFFFFF].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, &regs);
long long args[6];
fill_args(args, &regs, binary_type);
unsigned char argc = 6;
const char *syscall_name = get_syscall_name(binary_type, &regs, &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_rax & 0xFFFFFFFF) : (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, &regs);
if (binary_type == 64)
ret = (long)regs.rax;
else
ret = (long)regs.rax & 0xFFFFFFFF;
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, well 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);
}