summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2023-09-19 11:02:03 -0400
committerMalfurious <m@lfurio.us>2024-04-24 13:31:08 -0400
commitb4abda51217101ceffd19c3d403e40781e15dcec (patch)
tree1418becd7e2d8437fee1f5f3eaa27f7c6e1bb690
parentd070fde6478431c71fb4a55e783a577439c7cb99 (diff)
downloadmisplays-b4abda51217101ceffd19c3d403e40781e15dcec.tar.gz
misplays-b4abda51217101ceffd19c3d403e40781e15dcec.zip
Multithread version 2
Signed-off-by: Malfurious <m@lfurio.us>
-rw-r--r--debugger.c468
-rw-r--r--debugger.h75
-rw-r--r--helpers.h2
-rw-r--r--misplays.c372
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 <dirent.h>
-#include <errno.h>
-#include <stdio.h>
+#include <signal.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <capstone/capstone.h>
-/* #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, &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 (b->installed) {
if (!check) {
if (regs.rip - 1 == b->address) {
regs.rip--;
ptrace(PTRACE_SETREGS, th->id, NULL, &regs);
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 <sys/user.h>
#include <unistd.h>
-#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 <panel.h>
#include <stddef.h>
+#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 <errno.h>
#include <signal.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/ptrace.h>
-#include <sys/wait.h>
-#include <linux/ptrace.h>
#include <unistd.h>
#include <capstone/capstone.h>
@@ -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 <pid>\n", argv[0]);
- return 1;
+ fprintf(stderr, "Usage: %s <pid | cmdline>\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 <errno.h>
+//#include <signal.h>
+//#include <string.h>
+//#include <sys/ptrace.h>
+//#include <sys/wait.h>
+//#include <linux/ptrace.h>
+
+// //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;