summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2024-05-04 07:32:18 -0400
committerMalfurious <m@lfurio.us>2024-05-08 05:57:59 -0400
commit47cf13e8429e813aa2fd2b1f41f87722bc616d19 (patch)
tree9c994ceef54c8141fa40d6924be88160c7c8e19d
parentd187dfc3c81d987ef851b3806fc0ba372c8a3348 (diff)
downloadmisplays-47cf13e8429e813aa2fd2b1f41f87722bc616d19.tar.gz
misplays-47cf13e8429e813aa2fd2b1f41f87722bc616d19.zip
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 <m@lfurio.us>
-rw-r--r--CMakeLists.txt1
-rw-r--r--architecture.c30
-rw-r--r--architecture.h60
-rw-r--r--debugger.c41
-rw-r--r--debugger.h5
-rw-r--r--misplays.c73
6 files changed, 178 insertions, 32 deletions
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 <sys/uio.h>
+#include <capstone/capstone.h>
+
+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, &regs);
+
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 = { &regs, 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, &regs);
+ 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, &regs);
+
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 <sys/user.h>
#include <unistd.h>
+#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, &regs);
+
+ 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, &regs);
+
+ 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, &regs);
+
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));