From 47cf13e8429e813aa2fd2b1f41f87722bc616d19 Mon Sep 17 00:00:00 2001 From: Malfurious Date: Sat, 4 May 2024 07:32:18 -0400 Subject: Parameterize architecture-specific details Abstract architecture details into architecture.h and add x86 constants. This is slightly complicated by the fact that 64-bit hosts can run 32-bit code, so we do still need to resolve some values dynamically. The architecture_info() function is intented to address this, and performs parameter lookups based on the current state of the guest process. Resolving values on a per-process-state basis is important due to the process model under Linux. If we fork to debug a 32-bit program, the forked process will be native 64-bit until the execve system call. And of course, the process is then free to exec anything it likes later on as well. Signed-off-by: Malfurious --- CMakeLists.txt | 1 + architecture.c | 30 ++++++++++++++++++++++++ architecture.h | 60 +++++++++++++++++++++++++++++++++++++++++++++++ debugger.c | 41 +++++++++++++++++++++++++-------- debugger.h | 5 ++-- misplays.c | 73 ++++++++++++++++++++++++++++++++++++++++++---------------- 6 files changed, 178 insertions(+), 32 deletions(-) create mode 100644 architecture.c create mode 100644 architecture.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a7ff378..f35b859 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ add_compile_definitions( ) add_executable(${PROJECT_NAME} + architecture.c console.c debugger.c helpers.c diff --git a/architecture.c b/architecture.c new file mode 100644 index 0000000..67838a0 --- /dev/null +++ b/architecture.c @@ -0,0 +1,30 @@ +#include "architecture.h" + +void architecture_info(struct archinfo *ai, const struct iovec *regs) { + user_regs_t *data = regs->iov_base; + + /* Not every platform supports 64-bits, but those that do are generally + * backward compatible with 32-bits, so this is the one we explicitly + * compare with. */ + if (regs->iov_len == sizeof(struct user_regs_32)) { + ai->progmctr = data->PROGMCTR_32; + ai->stackptr = data->STACKPTR_32; + ai->bp_insn = BREAKPOINT_INSN_32; + ai->bp_mask = BREAKPOINT_MASK_32; + ai->bp_adjust = BREAKPOINT_ADJS_32; + ai->cs_arch = CAPSTONE_ARCH_32; + ai->cs_mode = CAPSTONE_MODE_32; + ai->cs_call = CAPSTONE_CALL_32; + ai->wordsize = WORDSIZE_32; + } else { + ai->progmctr = data->PROGMCTR_64; + ai->stackptr = data->STACKPTR_64; + ai->bp_insn = BREAKPOINT_INSN_64; + ai->bp_mask = BREAKPOINT_MASK_64; + ai->bp_adjust = BREAKPOINT_ADJS_64; + ai->cs_arch = CAPSTONE_ARCH_64; + ai->cs_mode = CAPSTONE_MODE_64; + ai->cs_call = CAPSTONE_CALL_64; + ai->wordsize = WORDSIZE_64; + } +} diff --git a/architecture.h b/architecture.h new file mode 100644 index 0000000..27cec2d --- /dev/null +++ b/architecture.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +struct archinfo { + unsigned long progmctr; + unsigned long stackptr; + unsigned long bp_insn; + unsigned long bp_mask; + unsigned long bp_adjust; + int cs_arch; + int cs_mode; + unsigned cs_call; + unsigned wordsize; +}; + +extern void architecture_info(struct archinfo *ai, const struct iovec *regs); + +/* Architecture Definitions */ +#if defined(__x86_64__) || defined(i386) || defined(__i386__) + +typedef union { + struct user_regs_64 { + unsigned long long int r15, r14, r13, r12, rbp, rbx, r11, r10, r9, r8, + rax, rcx, rdx, rsi, rdi, orig_rax, rip, cs, eflags, rsp, + ss, fs_base, gs_base, ds, es, fs, gs; + } x86_64; + + struct user_regs_32 { + unsigned int ebx, ecx, edx, esi, edi, ebp, eax, xds, xes, xfs, xgs, + orig_eax, eip, xcs, eflags, esp, xss; + } x86_32; +} user_regs_t; + +#define ARCH_X86 + +#define PROGMCTR_64 x86_64.rip +#define STACKPTR_64 x86_64.rsp +#define BREAKPOINT_INSN_64 0xccul +#define BREAKPOINT_MASK_64 0xfful +#define BREAKPOINT_ADJS_64 0x1 +#define CAPSTONE_ARCH_64 CS_ARCH_X86 +#define CAPSTONE_MODE_64 CS_MODE_64 +#define CAPSTONE_CALL_64 X86_INS_CALL +#define WORDSIZE_64 8 + +#define PROGMCTR_32 x86_32.eip +#define STACKPTR_32 x86_32.esp +#define BREAKPOINT_INSN_32 0xccul +#define BREAKPOINT_MASK_32 0xfful +#define BREAKPOINT_ADJS_32 0x1 +#define CAPSTONE_ARCH_32 CS_ARCH_X86 +#define CAPSTONE_MODE_32 CS_MODE_32 +#define CAPSTONE_CALL_32 X86_INS_CALL +#define WORDSIZE_32 4 + +#else +#error Detected architecture is not supported! +#endif diff --git a/debugger.c b/debugger.c index fdff032..401f6dd 100644 --- a/debugger.c +++ b/debugger.c @@ -23,7 +23,6 @@ static const int PTRACE_OPTIONS = static const int PTRACE_CHILD_OPTIONS = PTRACE_OPTIONS | PTRACE_O_EXITKILL; //static const int STOP_ALL_ON_EVENT = 1; static const useconds_t SCHEDULER_DELAY = 10000; -static const unsigned int BREAKPOINT_INSN = 0xcc; static struct process *new_process(pid_t id, int child) { struct process *proc = xmalloc(sizeof(*proc)); @@ -69,6 +68,10 @@ static void free_breakpoints(struct process *proc) { } static void install_breakpoints(struct thread *th) { + struct archinfo archinfo; + struct iovec regs = { &th->state->regs, th->state->regsize }; + architecture_info(&archinfo, ®s); + struct list *breaks = &th->proc->breakpoints; for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { if (!b->installed) { @@ -76,7 +79,7 @@ static void install_breakpoints(struct thread *th) { word = ptrace(PTRACE_PEEKTEXT, th->id, b->address, NULL); b->text = word; - word = (word & ~0xff) | BREAKPOINT_INSN; + word = (word & ~archinfo.bp_mask) | archinfo.bp_insn; ptrace(PTRACE_POKETEXT, th->id, b->address, word); b->installed = 1; b->previously_installed = 1; @@ -108,18 +111,31 @@ static int detect_breakpoint(struct thread *th, int *restart) { int ret = 0; *restart = 0; - struct user_regs_struct regs; + /* Hack: Need to manually fetch registers here, since capture_state() has + * not yet run for this stop. It is not guaranteed that we even want to + * preserve the current state. */ + user_regs_t regs; struct iovec ivregs = { ®s, sizeof(regs) }; ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, &ivregs); - struct breakpoint *b = get_breakpoint(th->proc, regs.rip - 1); + struct archinfo archinfo; + architecture_info(&archinfo, &ivregs); + + unsigned long breakpt_address = archinfo.progmctr - archinfo.bp_adjust; + struct breakpoint *b = get_breakpoint(th->proc, breakpt_address); if (b && b->installed && th->doing != PTRACE_SINGLESTEP) { - regs.rip--; + /* restore actual program counter to breakpoint address */ + if (ivregs.iov_len == sizeof(struct user_regs_32)) { + regs.PROGMCTR_32 = breakpt_address; + } else { + regs.PROGMCTR_64 = breakpt_address; + } ptrace(PTRACE_SETREGSET, th->id, NT_PRSTATUS, &ivregs); + b->hits++; /* todo: consider whether this is firing too much */ ret = b->user; - if (b->stack != 0 && b->stack != regs.rsp) { + if (b->stack != 0 && b->stack != archinfo.stackptr) { *restart = 1; } else if (b->tid != 0 && b->tid != th->id) { *restart = 1; @@ -161,6 +177,7 @@ static void capture_state(struct process *proc) { struct state *s = xmalloc(sizeof(*s)); struct iovec regs = { &s->regs, sizeof(s->regs) }; ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, ®s); + s->regsize = regs.iov_len; list_init(&s->maps); list_insert(th->states.end, s); @@ -620,20 +637,24 @@ int dbg_stepover(struct thread *th) { return -1; } + struct archinfo archinfo; + struct iovec regs = { &th->state->regs, th->state->regsize }; + architecture_info(&archinfo, ®s); + csh handle; - cs_open(CS_ARCH_X86, CS_MODE_64, &handle); + cs_open(archinfo.cs_arch, archinfo.cs_mode, &handle); cs_insn *insn = cs_malloc(handle); - uint64_t address = th->state->regs.rip; + uint64_t address = archinfo.progmctr; size_t size = 128; const uint8_t *code = deref(th, address, size); cs_disasm_iter(handle, &code, &size, &address, insn); - if (insn->id == X86_INS_CALL) { + if (insn->id == archinfo.cs_call) { struct breakpoint *b = add_breakpoint(th->proc, address); b->user = 0; - b->stack = th->state->regs.rsp; + b->stack = archinfo.stackptr; b->tid = th->id; b->enabled = -1; diff --git a/debugger.h b/debugger.h index f14459f..b26adf8 100644 --- a/debugger.h +++ b/debugger.h @@ -1,8 +1,8 @@ #pragma once -#include #include +#include "architecture.h" #include "list.h" struct breakpoint { @@ -29,7 +29,8 @@ struct map { struct state { LINKEDLIST; - struct user_regs_struct regs; + size_t regsize; + user_regs_t regs; struct list maps; }; diff --git a/misplays.c b/misplays.c index 70fe45b..9ae6122 100644 --- a/misplays.c +++ b/misplays.c @@ -62,31 +62,60 @@ static void describe_states(struct thread *dbg, PANEL *pan) { } static void dump_registers(struct thread *dbg, PANEL *pan) { - struct user_regs_struct *regs = &dbg->state->regs; - pprintw(pan, "orig_rax = 0x%016llx\n", regs->orig_rax); - pprintw(pan, "rax = 0x%016llx\n", regs->rax); - pprintw(pan, "rbx = 0x%016llx\n", regs->rbx); - pprintw(pan, "rcx = 0x%016llx\n", regs->rcx); - pprintw(pan, "rdx = 0x%016llx\n", regs->rdx); - pprintw(pan, "rdi = 0x%016llx\n", regs->rdi); - pprintw(pan, "rsi = 0x%016llx\n", regs->rsi); - pprintw(pan, "rsp = 0x%016llx\n", regs->rsp); - pprintw(pan, "rbp = 0x%016llx\n", regs->rbp); - pprintw(pan, "rip = 0x%016llx\n", regs->rip); +#ifdef ARCH_X86 + if (dbg->state->regsize == sizeof(struct user_regs_32)) { + struct user_regs_32 *regs = &dbg->state->regs.x86_32; + pprintw(pan, "orig_eax = 0x%08x\n", regs->orig_eax); + pprintw(pan, "eax = 0x%08x\n", regs->eax); + pprintw(pan, "ebx = 0x%08x\n", regs->ebx); + pprintw(pan, "ecx = 0x%08x\n", regs->ecx); + pprintw(pan, "edx = 0x%08x\n", regs->edx); + pprintw(pan, "edi = 0x%08x\n", regs->edi); + pprintw(pan, "esi = 0x%08x\n", regs->esi); + pprintw(pan, "esp = 0x%08x\n", regs->esp); + pprintw(pan, "ebp = 0x%08x\n", regs->ebp); + pprintw(pan, "eip = 0x%08x\n", regs->eip); + } else { + struct user_regs_64 *regs = &dbg->state->regs.x86_64; + pprintw(pan, "orig_rax = 0x%016llx\n", regs->orig_rax); + pprintw(pan, "rax = 0x%016llx\n", regs->rax); + pprintw(pan, "rbx = 0x%016llx\n", regs->rbx); + pprintw(pan, "rcx = 0x%016llx\n", regs->rcx); + pprintw(pan, "rdx = 0x%016llx\n", regs->rdx); + pprintw(pan, "rdi = 0x%016llx\n", regs->rdi); + pprintw(pan, "rsi = 0x%016llx\n", regs->rsi); + pprintw(pan, "rsp = 0x%016llx\n", regs->rsp); + pprintw(pan, "rbp = 0x%016llx\n", regs->rbp); + pprintw(pan, "rip = 0x%016llx\n", regs->rip); + } +#endif } static void dump_stack(struct thread *dbg, PANEL *pan) { - unsigned long sp = dbg->state->regs.rsp; - for (size_t i = 0; i < 16; i++, sp += 8) { - unsigned long word = *(unsigned long *)deref(dbg, sp,sizeof(unsigned long)); - pprintw(pan, "0x%lx:\t0x%lx\n", sp, word); + struct archinfo archinfo; + struct iovec regs = { &dbg->state->regs, dbg->state->regsize }; + architecture_info(&archinfo, ®s); + + unsigned long sp = archinfo.stackptr; + for (size_t i = 0; i < 16; i++, sp += archinfo.wordsize) { + if (dbg->state->regsize == sizeof(struct user_regs_32)) { + unsigned int *word = deref(dbg, sp, sizeof(unsigned int)); + pprintw(pan, "0x%lx:\t0x%08x\n", sp, *word); + } else { + unsigned long *word = deref(dbg, sp, sizeof(unsigned long)); + pprintw(pan, "0x%lx:\t0x%016lx\n", sp, *word); + } } } static int rip_visited(struct thread *th, unsigned long rip) { struct list *states = &th->states; for (struct state *s = states->head; s != states->end; s = s->next) { - if (rip == s->regs.rip) { + struct archinfo archinfo; + struct iovec regs = { &s->regs, s->regsize }; + architecture_info(&archinfo, ®s); + + if (rip == archinfo.progmctr) { return 1; } } @@ -94,13 +123,17 @@ static int rip_visited(struct thread *th, unsigned long rip) { } static void disasm(struct thread *dbg, PANEL *pan) { + struct archinfo archinfo; + struct iovec regs = { &dbg->state->regs, dbg->state->regsize }; + architecture_info(&archinfo, ®s); + csh handle; cs_insn *insn; - if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK) { + if (cs_open(archinfo.cs_arch, archinfo.cs_mode, &handle) != CS_ERR_OK) { //perror("capstone open"); } else { - uint64_t address = dbg->state->regs.rip; + uint64_t address = archinfo.progmctr; size_t codez = 128; const uint8_t *code = deref(dbg, address, codez); insn = cs_malloc(handle); @@ -110,7 +143,7 @@ static void disasm(struct thread *dbg, PANEL *pan) { break; } if (dbg->stopped) { - if (insn->address == dbg->state->regs.rip) { + if (insn->address == archinfo.progmctr) { pattron(pan, COLOR_PAIR(1)); } else if (get_breakpoint(dbg->proc, insn->address)) { pattron(pan, COLOR_PAIR(3)); @@ -120,7 +153,7 @@ static void disasm(struct thread *dbg, PANEL *pan) { } pprintw(pan, "0x%"PRIx64":\t%s %s\n", insn->address, insn->mnemonic, insn->op_str); if (dbg->stopped) { - if (insn->address == dbg->state->regs.rip) { + if (insn->address == archinfo.progmctr) { pattroff(pan, COLOR_PAIR(1)); } else if (get_breakpoint(dbg->proc, insn->address)) { pattroff(pan, COLOR_PAIR(3)); -- cgit v1.2.3