summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2023-07-20 14:55:13 -0400
committerMalfurious <m@lfurio.us>2024-04-24 13:31:08 -0400
commitd070fde6478431c71fb4a55e783a577439c7cb99 (patch)
tree1ff5f980eca499518943e4c36e5103d8be1847a8
parent1b5f8d2e5a118a80a4373a7be1ca4e4eceebf7be (diff)
downloadmisplays-d070fde6478431c71fb4a55e783a577439c7cb99.tar.gz
misplays-d070fde6478431c71fb4a55e783a577439c7cb99.zip
Multithread version 1
Signed-off-by: Malfurious <m@lfurio.us>
-rw-r--r--debugger.c744
-rw-r--r--debugger.h86
-rw-r--r--helpers.c15
-rw-r--r--helpers.h2
-rw-r--r--misplays.c261
5 files changed, 825 insertions, 283 deletions
diff --git a/debugger.c b/debugger.c
index 19d9edc..5fc3978 100644
--- a/debugger.c
+++ b/debugger.c
@@ -1,5 +1,8 @@
+#include <dirent.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
@@ -10,18 +13,117 @@
#include "debugger.h"
#include "helpers.h"
-static const int PTRACE_OPTIONS = PTRACE_O_TRACESYSGOOD;
+static const int PTRACE_OPTIONS = PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACECLONE;
+static const int INTERRUPT_ON_SEIZE = 1;
+
+static int detect_breakpoint(struct thread *th) {
+ struct user_regs_struct regs;
+ int check = 0;
+ int restart = 0;
+
+ ptrace(PTRACE_GETREGS, th->id, NULL, &regs);
+
+ 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 (!check) {
+ if (regs.rip - 1 == b->address) {
+ regs.rip--;
+ ptrace(PTRACE_SETREGS, th->id, NULL, &regs);
+ check = 1;
+
+ if (b->stack != 0 && b->stack != regs.rsp) {
+ restart = 1;
+ }
+
+ if (b->tid != 0 && b->tid != th->id) {
+ restart = 1;
+ }
+ }
+ }
+
+ if (b->enabled < 0 && !restart) {
+ struct breakpoint *del = b;
+ b = b->prev;
+ list_remove(del);
+ free(del);
+ }
+ }
+ }
+
+ return restart;
+}
+
+static void install_breakpoints(struct thread *th) {
+ struct list *breaks = &th->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;
+ }
+ }
+}
+
+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;
+ }
+ }
+}
+
+static void clear_breakpoints(struct process *proc) {
+ while (proc->breakpoints.head != proc->breakpoints.end) {
+ struct breakpoint *b = proc->breakpoints.head;
+ list_remove(b);
+ free(b);
+ }
+}
+
+static void clear_states(struct thread *th) {
+ while (th->states.head != th->states.end) {
+ struct state *s = th->states.head;
+
+ while (s->maps.head != s->maps.end) {
+ struct map *m = s->maps.head;
+ list_remove(m);
+ free(m->data);
+ free(m);
+ }
+
+ list_remove(s);
+ free(s);
+ }
+
+ th->state = NULL;
+ th->clearstates = 0;
+}
+
+static void capture_state(struct thread *th) {
+ if (th->clearstates) {
+ clear_states(th);
+ }
-static void capture_state(struct tracee *dbg) {
struct state *s = xmalloc(sizeof(*s));
- ptrace(PTRACE_GETREGS, dbg->id, NULL, &s->regs);
- ptrace(PTRACE_GETFPREGS, dbg->id, NULL, &s->fpregs);
+ ptrace(PTRACE_GETREGS, th->id, NULL, &s->regs);
+ ptrace(PTRACE_GETFPREGS, th->id, NULL, &s->fpregs);
list_init(&s->maps);
- list_insert(dbg->states.end, s);
- dbg->state = s;
- char mapspath[128], entry[512];
- snprintf(mapspath, sizeof(mapspath), "/proc/%li/maps", (long)dbg->id);
+ list_insert(th->states.end, s);
+ th->state = s;
+
+ char mapspath[32], entry[512];
+ snprintf(mapspath, sizeof(mapspath), "/proc/%li/maps", (long)th->id);
FILE *maps = fopen(mapspath, "r");
if (maps) {
@@ -33,7 +135,7 @@ static void capture_state(struct tracee *dbg) {
struct iovec loc = { m->data, size };
struct iovec rem = { (void *)m->start, size };
- if (process_vm_readv(dbg->id, &loc, 1, &rem, 1, 0) < 0) {
+ if (process_vm_readv(th->id, &loc, 1, &rem, 1, 0) < 0) {
free(m->data);
free(m);
continue;
@@ -46,232 +148,304 @@ static void capture_state(struct tracee *dbg) {
}
}
-static void clear_states(struct tracee *dbg) {
- while (dbg->states.head != dbg->states.end) {
- struct state *s = dbg->states.head;
- while (s->maps.head != s->maps.end) {
- struct map *m = s->maps.head;
- list_remove(m);
- free(m->data);
- free(m);
+static struct thread *new_thread(struct process *proc, pid_t id) {
+ struct thread *th = xmalloc(sizeof(*th));
+ th->proc = proc;
+ list_init(&th->states);
+ th->state = NULL;
+ th->clearstates = 0;
+ th->id = id;
+ th->stopped = 0;
+ th->signal = 0;
+ th->cont = 0;
+ th->status = "RUNNING";
+ return th;
+}
+
+static void interrupt_all_threads(struct process *proc) {
+ 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)) {}
}
- list_remove(s);
- free(s);
}
- dbg->state = NULL;
}
-static void install_breakpoints(struct tracee *dbg) {
- struct list *breaks = &dbg->breaks;
- for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) {
- if (b->enabled) {
- unsigned long data;
- data = ptrace(PTRACE_PEEKTEXT, dbg->id, b->address, NULL);
- b->orig = data;
+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->stack = stack;
+ b->tid = tid;
+ b->enabled = enabled;
+ b->active = 0;
+ list_insert(proc->breakpoints.end, b);
+}
- data = (data & ~0xff) | BREAKPOINT_INSN;
- ptrace(PTRACE_POKETEXT, dbg->id, b->address, data);
- b->active = 1;
+int is_breakpoint(struct process *proc, unsigned long address) {
+ struct list *breaks = &proc->breakpoints;
+ for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) {
+ if (b->address == address) {
+ return 1;
}
}
+ return 0;
}
-static int uninstall_breakpoints(struct tracee *dbg, int stop) {
- struct user_regs_struct regs;
- int ch = 0;
- int reenter = 0;
- if (stop) {
- ptrace(PTRACE_GETREGS, dbg->id, NULL, &regs);
- }
+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);
- struct list *breaks = &dbg->breaks;
- for (struct breakpoint *b = breaks->head; b != breaks->end; b = b->next) {
- if (b->active) {
- ptrace(PTRACE_POKETEXT, dbg->id, b->address, b->orig);
- b->active = 0;
+ char taskpath[32];
+ snprintf(taskpath, sizeof(taskpath), "/proc/%li/task", (long)pid);
- /* detect stop at breakpoint */
- if (stop && !ch) {
- if (regs.rip - 1 == b->address) {
- regs.rip--;
- ptrace(PTRACE_SETREGS, dbg->id, NULL, &regs);
- ch = 1;
+ DIR *taskdir = opendir(taskpath);
+ if (!taskdir) {
+ return -1;
+ }
- if (b->stack != 0 && b->stack != regs.rsp) {
- reenter = 1;
- }
- }
- }
+ struct dirent *task;
+ while ((task = readdir(taskdir))) {
+ pid_t id = strtoul(task->d_name, NULL, 0);
- if (b->enabled < 0 && !reenter) {
- struct breakpoint *del = b;
- b = b->prev;
- list_remove(del);
- free(del);
+ if (id != 0) {
+ if (ptrace(PTRACE_SEIZE, id, NULL, PTRACE_OPTIONS) < 0) {
+ closedir(taskdir);
+ return -1;
}
+
+ struct thread *th = new_thread(proc, id);
+ list_insert(proc->threads.end, th);
}
}
- return reenter;
-}
-
-int dbg_process(struct tracee *dbg, pid_t pid) {
- if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
- return -1;
+ if (INTERRUPT_ON_SEIZE) {
+ interrupt_all_threads(proc);
}
- list_init(&dbg->breaks);
- list_init(&dbg->states);
- dbg->state = NULL;
- dbg->id = pid;
- dbg->child = 0;
- dbg->stopped = 0;
- dbg->status = 0;
- dbg->signal = 0;
- dbg->cont = 0;
- dbg->buff = NULL;
- dbg->buffsize = 0;
-
- while (!dbg_wait(dbg)) {}
- ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_OPTIONS);
+ closedir(taskdir);
return 0;
}
-int dbg_new_process(struct tracee *dbg, char **argv, struct console *cons) {
- pid_t pid = fork();
- if (pid < 0) {
- return -1;
- }
+int dbg_detach(struct process *proc) {
+ interrupt_all_threads(proc);
+ uninstall_breakpoints(proc);
+ clear_breakpoints(proc);
- 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);
+ 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);
+ list_remove(th);
+ free(th);
}
- list_init(&dbg->breaks);
- list_init(&dbg->states);
- dbg->state = NULL;
- dbg->id = 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)) {}
- ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_EXITKILL | PTRACE_OPTIONS);
return 0;
}
-int dbg_wait(struct tracee *dbg) {
- if (dbg->stopped) {
- return 1;
+int dbg_wait(struct thread *th, int dostops) {
+ if (th->id < 0) {
+ return -1;
}
- if (waitpid(dbg->id, &dbg->status, WNOHANG | __WALL) <= 0) {
+ int status;
+ if (waitpid(th->id, &status, __WALL | WNOHANG) <= 0) {
return 0;
}
- if (dbg->cont != 0) {
- install_breakpoints(dbg);
- ptrace(dbg->cont, dbg->id, NULL, NULL);
- dbg->cont = 0;
- return 0;
+ if (WIFEXITED(status)) {
+ th->id = -1;
+ th->stopped = 1;
+ th->signal = WEXITSTATUS(status);
+ th->cont = 0;
+ th->status = "EXITED";
+ return 1;
}
- if (uninstall_breakpoints(dbg, 1)) {
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- dbg->cont = PTRACE_CONT;
- return 0;
+ if (WIFSIGNALED(status)) {
+ th->id = -2;
+ th->stopped = 1;
+ th->signal = WTERMSIG(status);
+ th->cont = 0;
+ th->status = "TERMINATED";
+ return 1;
}
- capture_state(dbg);
- dbg->stopped = 1;
- return 1;
-}
+ unsigned long eventmsg;
+ struct thread *newth;
+ ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg);
+
+ if (WIFSTOPPED(status)) {
+ switch (status >> 8) {
+ case SIGTRAP | (PTRACE_EVENT_CLONE << 8):
+ newth = new_thread(th->proc, eventmsg);
+ list_insert(th->proc->threads.end, newth);
+ while (!dbg_wait(newth, 0)) {}
+
+ th->stopped = 1;
+ th->signal = 0;
+ th->cont = 0;
+ th->status = "CLONE EVENT";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
-/* note: move head < state < tail checks from main into these function
- * add a dbg_stepback function */
+ capture_state(th);
+ return 1;
-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;
+ case SIGTRAP | (PTRACE_EVENT_EXIT << 8):
+ th->stopped = 1;
+ th->signal = 0; //eventmsg;
+ th->cont = 0;
+ th->status = "EXIT EVENT";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ capture_state(th);
+ return 1;
+
+ case SIGTRAP | (PTRACE_EVENT_STOP << 8):
+ th->stopped = 1;
+ th->signal = 0;
+ th->cont = 0;
+ th->status = "STOP EVENT";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ capture_state(th);
+ return 1;
+
+ case SIGTRAP | 0x80:
+ th->stopped = 1;
+ th->signal = 0;
+ th->cont = 0;
+ th->status = "SYSCALL EVENT";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ capture_state(th);
+ return 1;
+
+ case SIGTRAP:
+ th->stopped = 1;
+ th->signal = 0;
+
+ if (th->cont != 0) {
+ install_breakpoints(th);
+ ptrace(th->cont, th->id, NULL, NULL);
+ th->cont = 0;
+ th->stopped = 0;
+ th->status = "RUNNING";
+ return 0;
+ }
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ if (detect_breakpoint(th)) {
+ dbg_cont(th, PTRACE_CONT);
+ return 0;
+ }
+
+ th->status = "STEP/BREAKPOINT";
+ capture_state(th);
+ return 1;
+
+ default:
+ th->stopped = 1;
+ th->signal = WSTOPSIG(status);
+ th->cont = 0;
+ th->status = "SIGNAL DELIVERY";
+
+ if (dostops) {
+ interrupt_all_threads(th->proc);
+ uninstall_breakpoints(th->proc);
+ }
+
+ capture_state(th);
+ return 1;
+ }
+ }
+
+ return -1;
}
-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);
+int dbg_cont(struct thread *th, int cont) {
+ if (th->id < 0 || !th->stopped) {
+ return -1;
}
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- dbg->stopped = 0;
+ struct list *threads = &th->proc->threads;
+ for (struct thread *t = threads->head; t != threads->end; t = t->next) {
+ ptrace(PTRACE_SINGLESTEP, t->id, NULL, t->signal);
+
+ t->stopped = 0;
+ t->signal = 0;
+ t->cont = cont;
+ t->status = "RUNNING";
+ t->clearstates = 1;
+ }
- cs_free(insn, 1);
- cs_close(&handle);
+ /* 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_stepout(struct tracee *dbg) {
- unsigned long bp = dbg->state->regs.rbp;
- unsigned long ra = *(unsigned long *)deref(dbg, bp+8, sizeof(unsigned long));
+int dbg_stepin(struct thread *th) {
+ if (th->id < 0 || !th->stopped) {
+ return -1;
+ }
- struct breakpoint *b = xmalloc(sizeof(*b));
- b->address = ra;
- b->stack = 0;
- b->enabled = -1;
- b->active = 0;
- list_insert(dbg->breaks.end, b);
+ if (ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal) < 0) {
+ return -1;
+ }
- ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
- //clear_states(dbg); // keep this?
- dbg->stopped = 0;
- dbg->cont = PTRACE_CONT;
+ th->stopped = 0;
+ th->signal = 0;
+ th->cont = 0;
+ th->status = "RUNNING";
+ th->clearstates = 0;
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;
+int dbg_intr(struct thread *th) {
+ if (th->id < 0 || th->stopped) {
+ return -1;
+ }
+
+ ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL);
return 0;
}
-void *deref(struct tracee *dbg, unsigned long addr, size_t size) {
- (void)size; // todo
+void *deref(struct thread *th, unsigned long addr, size_t size) {
+ (void)size;
+ if (!th->state) {
+ return NULL;
+ }
- struct list *maps = &dbg->state->maps;
+ 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);
@@ -280,3 +454,215 @@ void *deref(struct tracee *dbg, unsigned long addr, size_t size) {
return NULL;
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+///*
+//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));
+//
+// struct breakpoint *b = xmalloc(sizeof(*b));
+// b->address = ra;
+// b->stack = 0;
+// b->enabled = -1;
+// b->active = 0;
+// list_insert(dbg->breaks.end, b);
+//
+// ptrace(PTRACE_SINGLESTEP, dbg->id, NULL, NULL);
+// //clear_states(dbg); // keep this?
+// dbg->stopped = 0;
+// 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 6305e96..cf2c442 100644
--- a/debugger.h
+++ b/debugger.h
@@ -8,13 +8,19 @@
#define BREAKPOINT_INSN 0xcc
+extern PANEL *consolepan;
+
struct breakpoint {
LINKEDLIST;
+ unsigned long orig;
unsigned long address;
unsigned long stack;
- unsigned long orig;
+ pid_t tid;
int enabled;
int active;
+ /* add count field
+ * and stop boolean
+ * to implement checkpoints? */
};
struct map {
@@ -31,25 +37,77 @@ struct state {
struct list maps;
};
-struct tracee {
- struct list breaks;
+struct process {
+ pid_t id;
+ int child;
+ struct list breakpoints;
+ struct list threads;
+};
+
+struct thread {
+ LINKEDLIST;
+ struct process *proc;
+
struct list states;
struct state *state;
+ int clearstates;
+
pid_t id;
- int child;
int stopped;
- int status;
int signal;
int cont;
- void *buff;
- size_t buffsize;
+ const char *status;
};
-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);
-extern int dbg_stepin(struct tracee *dbg);
-extern int dbg_stepover(struct tracee *dbg);
+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 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_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 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);
diff --git a/helpers.c b/helpers.c
index f7cc9c2..8e3be13 100644
--- a/helpers.c
+++ b/helpers.c
@@ -20,6 +20,13 @@ void cursinit(void) {
curs_set(FALSE);
timeout(25);
refresh();
+
+ start_color();
+ use_default_colors();
+
+ init_pair(1, COLOR_GREEN, -1);
+ init_pair(2, COLOR_CYAN, -1);
+ init_pair(3, COLOR_RED, -1);
}
void cursupdate(void) {
@@ -57,3 +64,11 @@ int pprintw(PANEL *pan, const char *fmt, ...) {
int pclear(PANEL *pan) {
return wclear(panel_window(pan));
}
+
+int pattron(PANEL *pan, int attrs) {
+ return wattron(panel_window(pan), attrs);
+}
+
+int pattroff(PANEL *pan, int attrs) {
+ return wattroff(panel_window(pan), attrs);
+}
diff --git a/helpers.h b/helpers.h
index f01e024..941c9b0 100644
--- a/helpers.h
+++ b/helpers.h
@@ -13,3 +13,5 @@ extern void delpan(PANEL *pan);
extern void reset_panel(PANEL *pan, int h, int w, int y, int x);
extern int pprintw(PANEL *pan, const char *fmt, ...);
extern int pclear(PANEL *pan);
+extern int pattron(PANEL *pan, int attrs);
+extern int pattroff(PANEL *pan, int attrs);
diff --git a/misplays.c b/misplays.c
index 8f6e0ee..a1f11a4 100644
--- a/misplays.c
+++ b/misplays.c
@@ -1,7 +1,9 @@
+#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
+#include <sys/wait.h>
#include <linux/ptrace.h>
#include <unistd.h>
@@ -15,6 +17,9 @@ 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;
@@ -85,9 +90,10 @@ static void describe_status(struct tracee *_dbg, PANEL *pan) {
}
}
}
+*/
-static void list_breakpoints(struct tracee *dbg, PANEL *pan) {
- struct list *breaks = &dbg->breaks;
+static void list_breakpoints(struct thread *dbg, PANEL *pan) {
+ struct list *breaks = &dbg->proc->breakpoints;
if (breaks->head != breaks->end) {
pprintw(pan, "---\n");
@@ -98,7 +104,7 @@ static void list_breakpoints(struct tracee *dbg, PANEL *pan) {
}
}
-static void describe_states(struct tracee *dbg, PANEL *pan) {
+static void describe_states(struct thread *dbg, PANEL *pan) {
struct list *states = &dbg->states;
for (struct state *s = states->head; s != states->end; s = s->next) {
pprintw(pan, "%c", (s == dbg->state ? '#' : '-'));
@@ -106,7 +112,7 @@ static void describe_states(struct tracee *dbg, PANEL *pan) {
pprintw(pan, "\n");
}
-static void dump_registers(struct tracee *dbg, PANEL *pan) {
+static void dump_registers(struct thread *dbg, PANEL *pan) {
struct user_regs_struct *regs = &dbg->state->regs;
pprintw(pan, "rax = 0x%016llx\n", regs->rax);
pprintw(pan, "rbx = 0x%016llx\n", regs->rbx);
@@ -119,7 +125,7 @@ static void dump_registers(struct tracee *dbg, PANEL *pan) {
pprintw(pan, "rip = 0x%016llx\n", regs->rip);
}
-static void dump_stack(struct tracee *dbg, PANEL *pan) {
+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));
@@ -127,7 +133,17 @@ static void dump_stack(struct tracee *dbg, PANEL *pan) {
}
}
-static void disasm(struct tracee *dbg, PANEL *pan) {
+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) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void disasm(struct thread *dbg, PANEL *pan) {
csh handle;
cs_insn *insn;
@@ -139,11 +155,29 @@ static void disasm(struct tracee *dbg, PANEL *pan) {
const uint8_t *code = deref(dbg, address, codez);
insn = cs_malloc(handle);
- for (size_t i = 0; i < 16; i++) {
+ for (size_t i = 0; i < 32; i++) {
if (!cs_disasm_iter(handle, &code, &codez, &address, insn)) {
break;
}
+ if (dbg->stopped) {
+ if (insn->address == dbg->state->regs.rip) {
+ pattron(pan, COLOR_PAIR(1));
+ } else if (is_breakpoint(dbg->proc, insn->address)) {
+ pattron(pan, COLOR_PAIR(3));
+ } else if (rip_visited(dbg, insn->address)) {
+ pattron(pan, COLOR_PAIR(2));
+ }
+ }
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) {
+ pattroff(pan, COLOR_PAIR(1));
+ } else if (is_breakpoint(dbg->proc, insn->address)) {
+ pattroff(pan, COLOR_PAIR(3));
+ } else if (rip_visited(dbg, insn->address)) {
+ pattroff(pan, COLOR_PAIR(2));
+ }
+ }
}
cs_free(insn, 1);
@@ -151,13 +185,14 @@ static void disasm(struct tracee *dbg, PANEL *pan) {
}
}
-static void info_update(struct tracee *dbg, PANEL *pan) {
+static void info_update(struct thread *dbg, PANEL *pan) {
pclear(pan);
- pprintw(pan, "PID: %li\n", (long)dbg->id);
- if (!dbg->stopped) {
- pprintw(pan, "Process is running...\n");
- } else {
- describe_status(dbg, 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);
@@ -165,52 +200,68 @@ static void info_update(struct tracee *dbg, PANEL *pan) {
dump_stack(dbg, pan);
pprintw(pan, "---\n");
disasm(dbg, pan);
- }
+ //}
}
-static void layout(void) {
+static void layout(struct process *proc, struct thread *th) {
int w = COLS/2;
- reset_panel(left, LINES-1, w, 0, 0);
- reset_panel(right, LINES-1, COLS-w, 0, w);
+ 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("**");
+ }
+ 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);
+ }
}
int main(int argc, char **argv) {
- if (argc < 3) {
- fprintf(stderr, "Usage: %s <stop> <command>\n", argv[0]);
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <pid>\n", argv[0]);
return 1;
}
+ //getchar();
+
cursinit();
left = newpan(0, 0, 0, 0);
right = newpan(0, 0, 0, 0);
- layout();
-
console_init(&cons);
+ consolepan = right;
- argv[argc] = NULL;
- struct tracee dbg;
- if (dbg_new_process(&dbg, argv+2, &cons)) {
- pprintw(right, "Failed to start child\n");
- }
+ struct process proc;
+ struct thread *th;
- unsigned long stop = strtoul(argv[1], NULL, 0);
- if (stop != 0) {
- struct breakpoint *b = xmalloc(sizeof(*b));
- b->address = stop;
- b->stack = 0;
- b->enabled = -1;
- b->active = 0;
- list_insert(dbg.breaks.end, b);
- dbg_cont(&dbg, PTRACE_CONT);
+ pid_t pid = strtoul(argv[1], NULL, 0);
+ if (dbg_process(&proc, pid, 0)) {
+ pprintw(right, "failed to attach process\n");
}
+ th = proc.threads.head;
+
int quit = 0;
while (!quit) {
- //if (dbg.stopped == NULL) {
- dbg_wait(&dbg);
- //}
+ wait_all_threads(&proc);
console_update(&cons, right);
- info_update(&dbg, left);
+ info_update(th, left);
+ layout(&proc, th);
cursupdate();
int ch = getch();
@@ -218,7 +269,7 @@ int main(int argc, char **argv) {
if (mode == 0) {
switch (ch) {
case KEY_RESIZE:
- layout();
+ layout(&proc, th);
break;
case 'q':
quit = 1;
@@ -228,60 +279,92 @@ int main(int argc, char **argv) {
console_enter(&cons, right);
break;
case 'j':
- if (dbg.stopped) {
- if (dbg.state != dbg.states.tail) {
- dbg.state = dbg.state->next;
+ dbg_stepin(th);
+ /*
+ if (dbg->stopped) {
+ if (dbg->state != dbg->states.tail) {
+ dbg->state = dbg->state->next;
} else {
- dbg_stepover(&dbg);
- }
- }
- 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);
+ dbg_stepover(dbg);
}
}
+ */
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 '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 'c':
- if (dbg.stopped) {
- dbg_cont(&dbg, PTRACE_CONT);
- }
+ dbg_cont(th, PTRACE_CONT);
break;
case 'p':
- if (!dbg.stopped) {
- tgkill(dbg.id, dbg.id, SIGSTOP);
- }
+ dbg_intr(th);
break;
+ case 't':
+ do {
+ th = th->next;
+ } while (th == proc.threads.end);
+ break;
+ case 'T':
+ 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;
case ':':
mvprintw(LINES-1, 0, ":");
curs_set(TRUE);
@@ -293,25 +376,22 @@ int main(int argc, char **argv) {
noecho();
timeout(25);
clear();
- refresh();
+ //refresh();
char *t = cmd;
- struct breakpoint *b = xmalloc(sizeof(*b));
- b->enabled = 1;
- b->active = 0;
+ int en = 1;
if (t[0] == '!') {
- b->enabled = -1;
+ en = -1;
t++;
}
- b->address = strtoul(t, NULL, 0);
- b->stack = 0;
- list_insert(dbg.breaks.end, b);
+ unsigned long addr = strtoul(t, NULL, 0);
+ add_breakpoint(&proc, addr, 0, 0, en);
break;
}
} else {
switch (ch) {
case KEY_RESIZE:
- layout();
+ layout(&proc, th);
break;
case 0x1b:
mode = 0;
@@ -326,6 +406,7 @@ int main(int argc, char **argv) {
}
}
+ dbg_detach(&proc);
endwin();
return 0;
}