From b4abda51217101ceffd19c3d403e40781e15dcec Mon Sep 17 00:00:00 2001 From: Malfurious Date: Tue, 19 Sep 2023 11:02:03 -0400 Subject: Multithread version 2 Signed-off-by: Malfurious --- debugger.c | 468 +++++++++++++++++++++++++------------------------------------ debugger.h | 75 +++------- helpers.h | 2 + misplays.c | 372 ++++++++++++++++++++++-------------------------- 4 files changed, 379 insertions(+), 538 deletions(-) diff --git a/debugger.c b/debugger.c index 5fc3978..956b065 100644 --- a/debugger.c +++ b/debugger.c @@ -1,39 +1,41 @@ #include -#include -#include +#include #include -#include #include #include #include #include -/* #include "config.h" */ #include "debugger.h" #include "helpers.h" -static const int PTRACE_OPTIONS = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE; -static const int INTERRUPT_ON_SEIZE = 1; +#define BREAKPOINT_INSN 0xcc + +//struct list global_processes = {0}; +//struct thread *global_thread = NULL; + +static const int PTRACE_OPTIONS = + PTRACE_O_TRACECLONE | + PTRACE_O_TRACEEXIT | + PTRACE_O_TRACESYSGOOD; static int detect_breakpoint(struct thread *th) { - struct user_regs_struct regs; int check = 0; int restart = 0; + struct user_regs_struct regs; ptrace(PTRACE_GETREGS, th->id, NULL, ®s); struct list *breaks = &th->proc->breakpoints; for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { - /* should be if (b->active) ? - * but there is a potential other bug that can happen - * with the order of events in dbg_wait() */ - if (b->enabled) { + if (b->installed) { if (!check) { if (regs.rip - 1 == b->address) { regs.rip--; ptrace(PTRACE_SETREGS, th->id, NULL, ®s); check = 1; + b->hits++; if (b->stack != 0 && b->stack != regs.rsp) { restart = 1; @@ -42,9 +44,15 @@ static int detect_breakpoint(struct thread *th) { if (b->tid != 0 && b->tid != th->id) { restart = 1; } + + if (!b->enabled) { + restart = 1; + } } } + /* this needs moved, we have to finish the for-loop + * to actually know if restart is true or false... */ if (b->enabled < 0 && !restart) { struct breakpoint *del = b; b = b->prev; @@ -57,17 +65,17 @@ static int detect_breakpoint(struct thread *th) { return restart; } -static void install_breakpoints(struct thread *th) { - struct list *breaks = &th->proc->breakpoints; +static void install_breakpoints(struct process *proc) { + struct list *breaks = &proc->breakpoints; for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { - if (b->enabled && !b->active) { - unsigned long data; - data = ptrace(PTRACE_PEEKTEXT, th->id, b->address, NULL); - b->orig = data; - - data = (data & ~0xff) | BREAKPOINT_INSN; - ptrace(PTRACE_POKETEXT, th->id, b->address, data); - b->active = 1; + if (!b->installed) { + unsigned long word; + word = ptrace(PTRACE_PEEKTEXT, proc->id, b->address, NULL); + b->text = word; + + word = (word & ~0xff) | BREAKPOINT_INSN; + ptrace(PTRACE_POKETEXT, proc->id, b->address, word); + b->installed = 1; } } } @@ -75,14 +83,14 @@ static void install_breakpoints(struct thread *th) { static void uninstall_breakpoints(struct process *proc) { struct list *breaks = &proc->breakpoints; for (struct breakpoint *b = breaks->tail; b != breaks->end; b = b->prev) { - if (b->active) { - ptrace(PTRACE_POKETEXT, proc->id, b->address, b->orig); - b->active = 0; + if (b->installed) { + ptrace(PTRACE_POKETEXT, proc->id, b->address, b->text); + b->installed = 0; } } } -static void clear_breakpoints(struct process *proc) { +static void free_breakpoints(struct process *proc) { while (proc->breakpoints.head != proc->breakpoints.end) { struct breakpoint *b = proc->breakpoints.head; list_remove(b); @@ -90,7 +98,7 @@ static void clear_breakpoints(struct process *proc) { } } -static void clear_states(struct thread *th) { +static void free_states(struct thread *th) { while (th->states.head != th->states.end) { struct state *s = th->states.head; @@ -109,9 +117,9 @@ static void clear_states(struct thread *th) { th->clearstates = 0; } -static void capture_state(struct thread *th) { +static void capture_state_thread(struct thread *th) { if (th->clearstates) { - clear_states(th); + free_states(th); } struct state *s = xmalloc(sizeof(*s)); @@ -148,13 +156,33 @@ static void capture_state(struct thread *th) { } } -static struct thread *new_thread(struct process *proc, pid_t id) { +static void capture_state(struct thread *th, int all) { + if (all) { + struct list *threads = &th->proc->threads; + for (struct thread *t = threads->head; t != threads->end; t = t->next) { + capture_state_thread(t); + } + } else { + capture_state_thread(th); + } +} + +static struct process *new_process(pid_t pid, int child) { + struct process *proc = xmalloc(sizeof(*proc)); + proc->id = pid; + proc->child = child; + list_init(&proc->threads); + list_init(&proc->breakpoints); + return proc; +} + +static struct thread *new_thread(struct process *proc, pid_t tid) { struct thread *th = xmalloc(sizeof(*th)); th->proc = proc; list_init(&th->states); th->state = NULL; th->clearstates = 0; - th->id = id; + th->id = tid; th->stopped = 0; th->signal = 0; th->cont = 0; @@ -162,24 +190,28 @@ static struct thread *new_thread(struct process *proc, pid_t id) { return th; } -static void interrupt_all_threads(struct process *proc) { +static int interrupt_all_threads(struct process *proc) { + int stopped = 0; struct list *threads = &proc->threads; for (struct thread *th = threads->head; th != threads->end; th = th->next) { if (!th->stopped) { ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL); - while (!dbg_wait(th, 0)) {} + while (!dbg_wait(th, 1)) {} + stopped = 1; } } + return stopped; } void add_breakpoint(struct process *proc, unsigned long address, unsigned long stack, pid_t tid, int enabled) { struct breakpoint *b = xmalloc(sizeof(*b)); - b->orig = 0; b->address = address; + b->text = 0; + b->installed = 0; + b->hits = 0; b->stack = stack; b->tid = tid; b->enabled = enabled; - b->active = 0; list_insert(proc->breakpoints.end, b); } @@ -193,18 +225,27 @@ int is_breakpoint(struct process *proc, unsigned long address) { return 0; } -int dbg_process(struct process *proc, pid_t pid, int child) { - proc->id = pid; - proc->child = child; - list_init(&proc->breakpoints); - list_init(&proc->threads); +//int dbg_attach(pid_t pid, int child) { +struct process *dbg_attach(pid_t pid, int child) { + //if (global_processes.end == NULL) { + // list_init(&global_processes); + //} + + struct process *proc = new_process(pid, child); + //list_insert(global_processes.end, proc); + + int options = PTRACE_OPTIONS; + if (child) { + options |= PTRACE_O_EXITKILL; + } char taskpath[32]; snprintf(taskpath, sizeof(taskpath), "/proc/%li/task", (long)pid); DIR *taskdir = opendir(taskpath); if (!taskdir) { - return -1; + dbg_detach(proc); + return NULL; } struct dirent *task; @@ -212,9 +253,10 @@ int dbg_process(struct process *proc, pid_t pid, int child) { pid_t id = strtoul(task->d_name, NULL, 0); if (id != 0) { - if (ptrace(PTRACE_SEIZE, id, NULL, PTRACE_OPTIONS) < 0) { + if (ptrace(PTRACE_SEIZE, id, NULL, options) < 0) { closedir(taskdir); - return -1; + dbg_detach(proc); + return NULL; } struct thread *th = new_thread(proc, id); @@ -222,37 +264,44 @@ int dbg_process(struct process *proc, pid_t pid, int child) { } } - if (INTERRUPT_ON_SEIZE) { - interrupt_all_threads(proc); - } - closedir(taskdir); - return 0; + + //global_thread = proc->threads.head; + interrupt_all_threads(proc); + return proc; } int dbg_detach(struct process *proc) { interrupt_all_threads(proc); uninstall_breakpoints(proc); - clear_breakpoints(proc); + free_breakpoints(proc); + + if (proc->child) { + kill(proc->id, SIGKILL); + } while (proc->threads.head != proc->threads.end) { struct thread *th = proc->threads.head; if (th->id >= 0) { ptrace(PTRACE_DETACH, th->id, NULL, th->signal); } - clear_states(th); + free_states(th); list_remove(th); free(th); } + list_remove(proc); + free(proc); return 0; } -int dbg_wait(struct thread *th, int dostops) { +int dbg_wait(struct thread *th, int recursion) { if (th->id < 0) { return -1; } + /* todo: check what happens if we call on an already known + * stopped thread? */ int status; if (waitpid(th->id, &status, __WALL | WNOHANG) <= 0) { return 0; @@ -276,42 +325,45 @@ int dbg_wait(struct thread *th, int dostops) { return 1; } - unsigned long eventmsg; + int stopped; struct thread *newth; + + unsigned long eventmsg; ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg); if (WIFSTOPPED(status)) { switch (status >> 8) { + /* todo: other ptrace event stops */ case SIGTRAP | (PTRACE_EVENT_CLONE << 8): newth = new_thread(th->proc, eventmsg); list_insert(th->proc->threads.end, newth); - while (!dbg_wait(newth, 0)) {} + while (!dbg_wait(newth, 1)) {} th->stopped = 1; th->signal = 0; th->cont = 0; th->status = "CLONE EVENT"; - if (dostops) { - interrupt_all_threads(th->proc); + if (!recursion) { + stopped = interrupt_all_threads(th->proc); uninstall_breakpoints(th->proc); + capture_state(th, stopped); } - capture_state(th); return 1; case SIGTRAP | (PTRACE_EVENT_EXIT << 8): th->stopped = 1; - th->signal = 0; //eventmsg; + th->signal = 0; /* eventmsg has exit code, but would inject sig */ th->cont = 0; th->status = "EXIT EVENT"; - if (dostops) { - interrupt_all_threads(th->proc); + if (!recursion) { + stopped = interrupt_all_threads(th->proc); uninstall_breakpoints(th->proc); + capture_state(th, stopped); } - capture_state(th); return 1; case SIGTRAP | (PTRACE_EVENT_STOP << 8): @@ -320,12 +372,12 @@ int dbg_wait(struct thread *th, int dostops) { th->cont = 0; th->status = "STOP EVENT"; - if (dostops) { - interrupt_all_threads(th->proc); + if (!recursion) { + stopped = interrupt_all_threads(th->proc); uninstall_breakpoints(th->proc); + capture_state(th, stopped); } - capture_state(th); return 1; case SIGTRAP | 0x80: @@ -334,20 +386,23 @@ int dbg_wait(struct thread *th, int dostops) { th->cont = 0; th->status = "SYSCALL EVENT"; - if (dostops) { - interrupt_all_threads(th->proc); + if (!recursion) { + stopped = interrupt_all_threads(th->proc); uninstall_breakpoints(th->proc); + capture_state(th, stopped); } - capture_state(th); return 1; case SIGTRAP: th->stopped = 1; th->signal = 0; + th->status = "STEP/BREAKPOINT"; if (th->cont != 0) { - install_breakpoints(th); + /* gdb this portion. are there race conditions + * that matter?? */ + install_breakpoints(th->proc); ptrace(th->cont, th->id, NULL, NULL); th->cont = 0; th->stopped = 0; @@ -355,18 +410,23 @@ int dbg_wait(struct thread *th, int dostops) { return 0; } - if (dostops) { - interrupt_all_threads(th->proc); + /* todo: Test two threads hitting a breakpoint at + * the same time. */ + int restart = detect_breakpoint(th); + + if (!recursion) { + stopped = interrupt_all_threads(th->proc); uninstall_breakpoints(th->proc); + if (!restart) { + capture_state(th, stopped); + } } - if (detect_breakpoint(th)) { + if (restart) { dbg_cont(th, PTRACE_CONT); return 0; } - th->status = "STEP/BREAKPOINT"; - capture_state(th); return 1; default: @@ -375,12 +435,12 @@ int dbg_wait(struct thread *th, int dostops) { th->cont = 0; th->status = "SIGNAL DELIVERY"; - if (dostops) { - interrupt_all_threads(th->proc); + if (!recursion) { + stopped = interrupt_all_threads(th->proc); uninstall_breakpoints(th->proc); + capture_state(th, stopped); } - capture_state(th); return 1; } } @@ -388,6 +448,15 @@ int dbg_wait(struct thread *th, int dostops) { return -1; } +int dbg_intr(struct thread *th) { + if (th->id < 0 || th->stopped) { + return -1; + } + + ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL); + return 0; +} + int dbg_cont(struct thread *th, int cont) { if (th->id < 0 || !th->stopped) { return -1; @@ -404,51 +473,81 @@ int dbg_cont(struct thread *th, int cont) { t->clearstates = 1; } - /* are there timing concerns here? Critically, we - * are doing this before the event loop has a chance to - * wait on any thread and actually start it in the cont */ - /* cant do it here, since no thread is stopped (ptrace - * returns ESRCH). */ - //install_breakpoints(th->proc); return 0; } -int dbg_stepin(struct thread *th) { - if (th->id < 0 || !th->stopped) { +int dbg_step(struct thread *th, int stepover) { + // todo: support step-out + //if (th->id < 0 || !th->stopped) { + // return -1; + //} + + if (!th->stopped) { return -1; } - if (ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal) < 0) { + if (th->state != th->states.tail) { + th->state = th->state->next; + return 0; + } + + if (th->id < 0) { return -1; } + csh cshandle; + cs_open(CS_ARCH_X86, CS_MODE_64, &cshandle); + cs_insn *insn = cs_malloc(cshandle); + + uint64_t address = th->state->regs.rip; + size_t size = 128; + const uint8_t *code = deref(th, address, size); + + cs_disasm_iter(cshandle, &code, &size, &address, insn); + + if (insn->id == X86_INS_CALL && stepover) { + add_breakpoint(th->proc, address, th->state->regs.rsp, th->id, -1); + th->cont = PTRACE_CONT; + } else { + th->cont = 0; + } + + ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); + th->stopped = 0; th->signal = 0; - th->cont = 0; th->status = "RUNNING"; - th->clearstates = 0; + + cs_free(insn, 1); + cs_close(&cshandle); return 0; } -int dbg_intr(struct thread *th) { - if (th->id < 0 || th->stopped) { +int dbg_pets(struct thread *th) { + if (!th->stopped) { return -1; } - ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL); - return 0; + if (th->state != th->states.head) { + th->state = th->state->prev; + return 0; + } + + return -1; } -void *deref(struct thread *th, unsigned long addr, size_t size) { +void *deref(struct thread *th, unsigned long address, size_t size) { + /* todo: size, and mapping breaks */ (void)size; + if (!th->state) { return NULL; } struct list *maps = &th->state->maps; for (struct map *m = maps->head; m != maps->end; m = m->next) { - if (m->start <= addr && addr < m->end) { - return (unsigned char *)m->data + (addr - m->start); + if (m->start <= address && address < m->end) { + return (unsigned char *)m->data + (address - m->start); } } @@ -459,164 +558,11 @@ void *deref(struct thread *th, unsigned long addr, size_t size) { - - - - - - - - - - -///* -//static const char *strstatus(int status) { -// static char buff[128]; -// -// if (WIFEXITED(status)) { -// snprintf(buff, sizeof(buff), "exited: %i", WEXITSTATUS(status)); -// } else if (WIFSIGNALED(status)) { -// snprintf(buff, sizeof(buff), "terminated: %s", strsignal(WTERMSIG(status))); -// } else if (WIFSTOPPED(status)) { -// snprintf(buff, sizeof(buff), "stopped: %s", strsignal(WSTOPSIG(status))); -// } else { -// snprintf(buff, sizeof(buff), "UNKNOWN STATUS VALUE"); -// } -// -// return buff; -//} -//*/ -// -//static int all_cont(struct process *proc) { -// /* install breakpoints */ -// struct list *breaks = &proc->breakpoints; -// for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) { -// if (b->enabled) { -// unsigned long data; -// data = ptrace(PTRACE_PEEKTEXT, proc->id, b->address, NULL); -// b->orig = data; -// -// data = (data & ~0xff) | BREAKPOINT_INSN; -// ptrace(PTRACE_POKETEXT, proc->id, b->address, data); -// b->active = 1; -// } -// } -// -// /* continue all threads */ -// struct list *threads = &proc->threads; -// for (struct thread *th = threads->head; th != threads->end; th = th->next) { -// ptrace(PTRACE_); -// } -// return -1; -//} -// ///* current problem to solve: // * if a SINGLESTEP is interrupted (it was over a blocking syscall for example), // * a CONT out of that interruption will stop AS IF the SINGLESTEP had completed. // */ -///* -// * new: -// * next step is to trigger all threads starting on continue. -// */ - - - - - - - - -/* PTRACE_GETSIGINFO ! */ -/* handle ptrace() -> ESRCH as unexpectedly dead tracee */ - - - - - - - - - -// -//int dbg_new_process(struct tracee *dbg, char **argv, struct console *cons) { -// pid_t pid = fork(); -// if (pid < 0) { -// return -1; -// } -// -// if (pid == 0) { -// console_configslave(cons); -// setpgid(0, 0); -// ptrace(PTRACE_TRACEME, 0, NULL, NULL); -// close_range(STDERR_FILENO+1, ~0U, CLOSE_RANGE_UNSHARE); -// execvp(argv[0], argv); -// exit(-1); -// } -// -// list_init(&dbg->breaks); -// list_init(&dbg->states); -// dbg->state = NULL; -// dbg->id = pid; -// dbg->gid = pid; -// dbg->child = 1; -// dbg->stopped = 0; -// dbg->status = 0; -// dbg->signal = 0; -// dbg->cont = 0; -// dbg->buff = NULL; -// dbg->buffsize = 0; -// -// while (!dbg_wait(dbg, NULL)) {} -// ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_EXITKILL | PTRACE_OPTIONS); -// return 0; -//} -// -///* note: move head < state < tail checks from main into these function -// * add a dbg_stepback function */ -// -//int dbg_stepin(struct tracee *dbg) { -// /* consider making this a no-op if we are not stopped on a call */ -// ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL); -// dbg->stopped = 0; -// dbg->cont = 0; -// return 0; -//} -// -//int dbg_stepover(struct tracee *dbg) { -// csh handle; -// cs_open(CS_ARCH_X86, CS_MODE_64, &handle); -// -// uint64_t address = dbg->state->regs.rip; -// size_t size = 128; -// const uint8_t *code = deref(dbg, address, size); -// cs_insn *insn = cs_malloc(handle); -// -// cs_disasm_iter(handle, &code, &size, &address, insn); -// -// if (insn->id == X86_INS_CALL) { -// struct breakpoint *b = xmalloc(sizeof(*b)); -// b->address = address; -// b->stack = dbg->state->regs.rsp; -// b->enabled = -1; -// b->active = 0; -// list_insert(dbg->breaks.end, b); -// dbg->cont = PTRACE_CONT; -// //dbg_cont(dbg, PTRACE_CONT); -// } else { -// dbg->cont = 0; -// //dbg_stepin(dbg); -// } -// -// ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL); -// dbg->stopped = 0; -// -// cs_free(insn, 1); -// cs_close(&handle); -// return 0; -//} -// -///* //int dbg_stepout(struct tracee *dbg) { // unsigned long bp = dbg->state->regs.rbp; // unsigned long ra = *(unsigned long *)deref(dbg, bp+8, sizeof(unsigned long)); @@ -634,35 +580,3 @@ void *deref(struct thread *th, unsigned long addr, size_t size) { // dbg->cont = PTRACE_CONT; // return 0; //} -//*/ -// -//int dbg_cont(struct tracee *dbg, int cont) { -// ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL); -// clear_states(dbg); -// dbg->stopped = 0; -// dbg->cont = cont; -// return 0; -//} -// -//void *deref(struct tracee *dbg, unsigned long addr, size_t size) { -// (void)size; // todo -// -// struct list *maps = &dbg->state->maps; -// for (struct map *m = maps->head; m != maps->end; m = m->next) { -// if (m->start <= addr && addr < m->end) { -// return (unsigned char *)m->data + (addr - m->start); -// } -// } -// -// return NULL; -//} -// -//void dbg_free(struct tracee *dbg) { -// while (dbg->breaks.head != dbg->breaks.end) { -// struct breakpoint *b = dbg->breaks.head; -// list_remove(b); -// free(b); -// } -// clear_states(dbg); -// free(dbg->buff); -//} diff --git a/debugger.h b/debugger.h index cf2c442..845bf69 100644 --- a/debugger.h +++ b/debugger.h @@ -3,24 +3,19 @@ #include #include -#include "console.h" #include "list.h" -#define BREAKPOINT_INSN 0xcc - -extern PANEL *consolepan; - struct breakpoint { LINKEDLIST; - unsigned long orig; + unsigned long address; + unsigned long text; + int installed; + int hits; + unsigned long stack; pid_t tid; int enabled; - int active; - /* add count field - * and stop boolean - * to implement checkpoints? */ }; struct map { @@ -38,10 +33,11 @@ struct state { }; struct process { + LINKEDLIST; pid_t id; int child; - struct list breakpoints; struct list threads; + struct list breakpoints; }; struct thread { @@ -56,58 +52,23 @@ struct thread { int stopped; int signal; int cont; + const char *status; }; +//extern struct list global_processes; +//extern struct thread *global_thread; + extern void add_breakpoint(struct process *proc, unsigned long address, unsigned long stack, pid_t tid, int enabled); extern int is_breakpoint(struct process *proc, unsigned long address); -extern int dbg_process(struct process *proc, pid_t pid, int child); +extern struct process *dbg_attach(pid_t pid, int child); extern int dbg_detach(struct process *proc); -extern int dbg_wait(struct thread *th, int dostops); -extern int dbg_cont(struct thread *th, int cont); -extern int dbg_stepin(struct thread *th); -extern int dbg_intr(struct thread *th); -extern void *deref(struct thread *th, unsigned long addr, size_t size); - - - - - - - - - -/* how to do an async 'all-cont': - * - * mark all threads for PTRACE_CONT - * singlestep all threads - * this is needed to step past any thread that may be starting at a breakpoint - * wait all single steps (note that some may hang...) - * once all single steps completed, install breakpoints and actually cont threads - */ - -//extern void add_breakpoint(struct process *proc, unsigned long address, unsigned long stack, pid_t tid, int enabled); -//extern int is_breakpoint(struct process *proc, unsigned long address); -// -//extern int dbg_process(struct process *proc, pid_t pid); -//extern int dbg_wait(struct thread *th, int dostops); -//extern int dbg_stepin(struct thread *th); -//extern int dbg_stepover(struct thread *th); -//extern int dbg_cont(struct thread *th, int cont); -//extern int dbg_intr(struct thread *th); -//extern int dbg_detach(struct process *proc); -//extern void *deref(struct thread *th, unsigned long addr, size_t size); - - +extern int dbg_wait(struct thread *th, int recursion); +extern int dbg_intr(struct thread *th); +extern int dbg_cont(struct thread *th, int cont); +extern int dbg_step(struct thread *th, int stepover); +extern int dbg_pets(struct thread *th); -//extern int dbg_process(struct tracee *dbg, pid_t pid); -//extern int dbg_new_process(struct tracee *dbg, char **argv, struct console *cons); -//extern int dbg_wait(struct tracee *dbg, PANEL *pan); -//extern int dbg_stepin(struct tracee *dbg); -//extern int dbg_stepover(struct tracee *dbg); -//extern int dbg_stepout(struct tracee *dbg); -//extern int dbg_cont(struct tracee *dbg, int mode); -//extern void *deref(struct tracee *dbg, unsigned long addr, size_t size); -//extern void dbg_free(struct tracee *dbg); +extern void *deref(struct thread *th, unsigned long address, size_t size); diff --git a/helpers.h b/helpers.h index 941c9b0..02607bb 100644 --- a/helpers.h +++ b/helpers.h @@ -4,6 +4,8 @@ #include #include +#define KEY_ESCAPE 0x1b + extern void *xmalloc(size_t size); extern void cursinit(void); diff --git a/misplays.c b/misplays.c index a1f11a4..4fec040 100644 --- a/misplays.c +++ b/misplays.c @@ -1,10 +1,7 @@ -#include #include +#include #include -#include #include -#include -#include #include #include @@ -12,85 +9,33 @@ #include "console.h" #include "debugger.h" #include "helpers.h" +#include "list.h" static PANEL *left, *right; -static struct console cons; -static int mode = 0; -PANEL *consolepan; - -/* -static void describe_status(struct tracee *_dbg, PANEL *pan) { - struct tracee dbg = *_dbg; - int status = dbg.status; - struct ptrace_syscall_info info; - - if (WIFEXITED(status)) { - pprintw(pan, "exited with code: %u\n", WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - pprintw(pan, "terminated by signal: %s\n", strsignal(WTERMSIG(status))); - } else { - unsigned long msg; - if (ptrace(PTRACE_GETEVENTMSG, dbg.id, NULL, &msg) < 0) { - perror("PTRACE_GETEVENTMSG"); - } - - if (WIFSTOPPED(status)) { - switch (status >> 8) { - case ((PTRACE_EVENT_VFORK << 8) | SIGTRAP): - pprintw(pan, "child vfork()'d to: %lu\n", msg); - break; - case ((PTRACE_EVENT_FORK << 8) | SIGTRAP): - pprintw(pan, "child fork()'d to: %lu\n", msg); - break; - case ((PTRACE_EVENT_CLONE << 8) | SIGTRAP): - pprintw(pan, "child clone()'d to: %lu\n", msg); - break; - case ((PTRACE_EVENT_VFORK_DONE << 8) | SIGTRAP): - pprintw(pan, "finished vfork\n"); - break; - case ((PTRACE_EVENT_EXEC << 8) | SIGTRAP): - pprintw(pan, "child exec()'d from: %lu\n", msg); - break; - case ((PTRACE_EVENT_EXIT << 8) | SIGTRAP): - pprintw(pan, "exiting with code: %lu\n", msg); - break; - case ((PTRACE_EVENT_STOP << 8) | SIGTRAP): - pprintw(pan, "child stopped\n"); - break; - case ((PTRACE_EVENT_SECCOMP << 8) | SIGTRAP): - pprintw(pan, "child seccomp event\n"); - break; - - case (SIGTRAP | 0x80): - ptrace(PTRACE_GET_SYSCALL_INFO, dbg.id, sizeof(info), &info); - pprintw(pan, "child entering syscall: %llu (%llx, %llx, %llx, %llx, %llx, %llx)\n", - info.entry.nr, - info.entry.args[0], - info.entry.args[1], - info.entry.args[2], - info.entry.args[3], - info.entry.args[4], - info.entry.args[5]); - break; +static pid_t parse_pid(const char *nptr) { + char *endptr; + pid_t pid = strtoul(nptr, &endptr, 0); + return (*endptr ? 0 : pid); +} - case SIGSTOP: - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - pprintw(pan, "child group-stopped\n"); - break; +static pid_t dofork(char **argv, struct console *cons) { + pid_t pid = fork(); + if (pid < 0) { + return -1; + } - default: - pprintw(pan, "received signal: %s\n", strsignal(WSTOPSIG(status))); - break; - } - } else { - pprintw(pan, "child stop event unrecognized\n"); - } + if (pid == 0) { + console_configslave(cons); + close_range(STDERR_FILENO+1, ~0U, CLOSE_RANGE_UNSHARE); + setpgid(0, 0); + raise(SIGSTOP); // ptrace(PTRACE_TRACEME, 0, NULL, NULL); + execvp(argv[0], argv); + exit(EXIT_FAILURE); } + + return pid; } -*/ static void list_breakpoints(struct thread *dbg, PANEL *pan) { struct list *breaks = &dbg->proc->breakpoints; @@ -148,7 +93,7 @@ static void disasm(struct thread *dbg, PANEL *pan) { cs_insn *insn; if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK) { - perror("capstone open"); + //perror("capstone open"); } else { uint64_t address = dbg->state->regs.rip; size_t codez = 128; @@ -185,83 +130,97 @@ static void disasm(struct thread *dbg, PANEL *pan) { } } -static void info_update(struct thread *dbg, PANEL *pan) { +static void info_update(struct thread *th, PANEL *pan) { pclear(pan); - pprintw(pan, "TID: %li\n", (long)dbg->id); - //if (!dbg->stopped) { - // pprintw(pan, "Thread is running...\n"); - //} else { - //describe_status(dbg, pan); - pprintw(pan, "%s (%i)\n", dbg->status, dbg->signal); - list_breakpoints(dbg, pan); - describe_states(dbg, pan); - dump_registers(dbg, pan); - pprintw(pan, "---\n"); - dump_stack(dbg, pan); - pprintw(pan, "---\n"); - disasm(dbg, pan); - //} + pprintw(pan, "TID: %li\n", (long)th->id); + pprintw(pan, "%s (%i)\n", th->status, th->signal); + list_breakpoints(th, pan); + describe_states(th, pan); + dump_registers(th, pan); + pprintw(pan, "---\n"); + dump_stack(th, pan); + pprintw(pan, "---\n"); + disasm(th, pan); +} + +static void wait_all_threads(struct list *processes) { + for (struct process *proc = processes->head; proc != processes->end; proc = proc->next) { + struct list *threads = &proc->threads; + for (struct thread *th = threads->head; th != threads->end; th = th->next) { + dbg_wait(th, 0); + } + } } -static void layout(struct process *proc, struct thread *th) { +static void layout(struct list *processes, struct thread *th) { int w = COLS/2; reset_panel(left, LINES-2, w, 1, 0); reset_panel(right, LINES-2, COLS-w, 1, w); clear(); attron(COLOR_PAIR(1)); - struct list *threads = &proc->threads; - for (struct thread *t = threads->head; t != threads->end; t = t->next) { - if (t == th) { - printw("**"); - } - printw("%li (%s)", (long)t->id, t->status); - if (t == th) { - printw("**"); + + for (struct process *proc = processes->head; proc != processes->end; proc = proc->next) { + struct list *threads = &proc->threads; + for (struct thread *t = threads->head; t != threads->end; t = t->next) { + if (t == th) { + printw("**"); + } + printw("%li (%s)", (long)t->id, t->status); + if (t == th) { + printw("**"); + } + printw(" "); } - printw(" "); } - attroff(COLOR_PAIR(1)); - //refresh(); -} -static void wait_all_threads(struct process *proc) { - struct list *threads = &proc->threads; - for (struct thread *th = threads->head; th != threads->end; th = th->next) { - dbg_wait(th, 1); - } + attroff(COLOR_PAIR(1)); } int main(int argc, char **argv) { if (argc < 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); - return 1; + fprintf(stderr, "Usage: %s \n", argv[0]); + return EXIT_FAILURE; } - //getchar(); + getchar(); - cursinit(); - left = newpan(0, 0, 0, 0); - right = newpan(0, 0, 0, 0); + struct console cons = {0}; console_init(&cons); - consolepan = right; - struct process proc; - struct thread *th; + struct list processes = {0}; + struct thread *th = NULL; + list_init(&processes); - pid_t pid = strtoul(argv[1], NULL, 0); - if (dbg_process(&proc, pid, 0)) { - pprintw(right, "failed to attach process\n"); + int child = 0; + pid_t pid = parse_pid(argv[1]); + argv[argc] = NULL; + + if (pid == 0) { + pid = dofork(argv+1, &cons); + child = 1; + } + + struct process *proc = dbg_attach(pid, child); + if (!proc) { + fprintf(stderr, "Failed to attach to process %li\n", (long)pid); + return EXIT_FAILURE; } + list_insert(processes.end, proc); + th = proc->threads.head; - th = proc.threads.head; + cursinit(); + left = newpan(0, 0, 0, 0); + right = newpan(0, 0, 0, 0); + layout(&processes, th); int quit = 0; + int mode = 0; while (!quit) { - wait_all_threads(&proc); + wait_all_threads(&processes); + layout(&processes, th); console_update(&cons, right); info_update(th, left); - layout(&proc, th); cursupdate(); int ch = getch(); @@ -269,7 +228,7 @@ int main(int argc, char **argv) { if (mode == 0) { switch (ch) { case KEY_RESIZE: - layout(&proc, th); + layout(&processes, th); break; case 'q': quit = 1; @@ -279,53 +238,30 @@ int main(int argc, char **argv) { console_enter(&cons, right); break; case 'j': - dbg_stepin(th); - /* - if (dbg->stopped) { - if (dbg->state != dbg->states.tail) { - dbg->state = dbg->state->next; - } else { - dbg_stepover(dbg); - } + dbg_step(th, 1); + break; + case 'k': + dbg_pets(th); + break; + case 'l': + dbg_step(th, 0); + break; + case 'h': + /* todo: step out */ + break; + case 'g': + if (th->stopped) { + th->state = th->states.head; + } + break; + case 'G': + if (th->stopped) { + th->state = th->states.tail; } - */ break; - //case 'k': - // if (dbg->stopped) { - // if (dbg->state != dbg->states.head) { - // dbg->state = dbg->state->prev; - // } - // } - // break; - //case 'l': - // if (dbg->stopped) { - // if (dbg->state != dbg->states.tail) { - // //dbg.state = dbg.state->next; - // } else { - // dbg_stepin(dbg); - // } - // } - // break; - //case 'h': - // if (dbg.stopped) { - // dbg_stepout(&dbg); - // } - // break; - //case 'g': - // if (dbg->stopped) { - // dbg->state = dbg->states.head; - // } - // break; - //case 'G': - // if (dbg->stopped) { - // dbg->state = dbg->states.tail; - // } - // break; - //case 's': - // if (dbg->stopped) { - // dbg_cont(dbg, PTRACE_SYSCALL); - // } - // break; + case 's': + dbg_cont(th, PTRACE_SYSCALL); + break; case 'c': dbg_cont(th, PTRACE_CONT); break; @@ -333,38 +269,19 @@ int main(int argc, char **argv) { dbg_intr(th); break; case 't': + proc = th->proc; do { th = th->next; - } while (th == proc.threads.end); + } while (th == proc->threads.end); + layout(&processes, th); break; case 'T': + proc = th->proc; do { th = th->prev; - } while (th == proc.threads.end); - break; - //case 'd': - // if (dbg->stopped) { - // while (dbg->breaks.head != dbg->breaks.end) { - // struct breakpoint *b = dbg->breaks.head; - // list_remove(b); - // free(b); - // } - // } - // break; - //case 'D': - // if (dbg->stopped) { - // if (ptrace(PTRACE_DETACH, dbg->id, NULL, NULL) < 0) { - // pprintw(right, "PTRACE_DETACH: %s\n", strerror(errno)); - // } - // struct tracee *rm = dbg; - // do { - // dbg = dbg->next; - // } while (dbg == tracees.end); - // list_remove(rm); - // dbg_free(rm); - // free(rm); - // } - // break; + } while (th == proc->threads.end); + layout(&processes, th); + break; /* todo: next/prev process bindings */ case ':': mvprintw(LINES-1, 0, ":"); curs_set(TRUE); @@ -376,24 +293,27 @@ int main(int argc, char **argv) { noecho(); timeout(25); clear(); - //refresh(); + layout(&processes, th); char *t = cmd; int en = 1; if (t[0] == '!') { en = -1; t++; + } else if (t[0] == '#') { + en = 0; + t++; } - unsigned long addr = strtoul(t, NULL, 0); - add_breakpoint(&proc, addr, 0, 0, en); + unsigned long address = strtoul(t, NULL, 0); + add_breakpoint(th->proc, address, 0, 0, en); break; } } else { switch (ch) { case KEY_RESIZE: - layout(&proc, th); + layout(&processes, th); break; - case 0x1b: + case KEY_ESCAPE: mode = 0; console_leave(&cons, right); break; @@ -406,7 +326,51 @@ int main(int argc, char **argv) { } } - dbg_detach(&proc); + dbg_detach(th->proc); /* todo: detach all procs */ endwin(); - return 0; + return EXIT_SUCCESS; } + + + + + + + + + + + + + + +//#include +//#include +//#include +//#include +//#include +//#include + +// //case 'd': +// // if (dbg->stopped) { +// // while (dbg->breaks.head != dbg->breaks.end) { +// // struct breakpoint *b = dbg->breaks.head; +// // list_remove(b); +// // free(b); +// // } +// // } +// // break; +// //case 'D': +// // if (dbg->stopped) { +// // if (ptrace(PTRACE_DETACH, dbg->id, NULL, NULL) < 0) { +// // pprintw(right, "PTRACE_DETACH: %s\n", strerror(errno)); +// // } +// // struct tracee *rm = dbg; +// // do { +// // dbg = dbg->next; +// // } while (dbg == tracees.end); +// // list_remove(rm); +// // dbg_free(rm); +// // free(rm); +// // } +// // break; -- cgit v1.2.3