#include #include #include #include #include #include #include #include "console.h" #include "debugger.h" #include "helpers.h" static PANEL *left, *right; static struct console cons; static int mode = 0; 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; case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: pprintw(pan, "child group-stopped\n"); break; default: pprintw(pan, "received signal: %s\n", strsignal(WSTOPSIG(status))); break; } } else { pprintw(pan, "child stop event unrecognized\n"); } } } static void list_breakpoints(struct tracee *dbg, PANEL *pan) { struct list *breaks = &dbg->breaks; if (breaks->head != breaks->end) { pprintw(pan, "---\n"); for (struct breakpoint *bp=breaks->head; bp!=breaks->end; bp=bp->next) { pprintw(pan, "0x%lx ", bp->address); } pprintw(pan, "\n"); } } static void describe_states(struct tracee *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 ? '#' : '-')); } pprintw(pan, "\n"); } static void dump_registers(struct tracee *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); pprintw(pan, "rcx = 0x%016llx\n", regs->rcx); pprintw(pan, "rdx = 0x%016llx\n", regs->rdx); pprintw(pan, "rdi = 0x%016llx\n", regs->rdi); pprintw(pan, "rsi = 0x%016llx\n", regs->rsi); pprintw(pan, "rsp = 0x%016llx\n", regs->rsp); pprintw(pan, "rbp = 0x%016llx\n", regs->rbp); pprintw(pan, "rip = 0x%016llx\n", regs->rip); } static void dump_stack(struct tracee *dbg, PANEL *pan) { unsigned long sp = dbg->state->regs.rsp; for (size_t i = 0; i < 16; i++, sp += 8) { unsigned long word = *(unsigned long *)deref(dbg, sp,sizeof(unsigned long)); pprintw(pan, "0x%lx:\t0x%lx\n", sp, word); } } static void disasm(struct tracee *dbg, PANEL *pan) { csh handle; cs_insn *insn; if (cs_open(CS_ARCH_X86, CS_MODE_64, &handle) != CS_ERR_OK) { perror("capstone open"); } else { uint64_t address = dbg->state->regs.rip; size_t codez = 128; const uint8_t *code = deref(dbg, address, codez); insn = cs_malloc(handle); for (size_t i = 0; i < 16; i++) { if (!cs_disasm_iter(handle, &code, &codez, &address, insn)) { break; } pprintw(pan, "0x%"PRIx64":\t%s %s\n", insn->address, insn->mnemonic, insn->op_str); } cs_free(insn, 1); cs_close(&handle); } } static void info_update(struct tracee *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); 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); } } static void layout(void) { int w = COLS/2; reset_panel(left, LINES-1, w, 0, 0); reset_panel(right, LINES-1, COLS-w, 0, w); } int main(int argc, char **argv) { if (argc < 3) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } cursinit(); left = newpan(0, 0, 0, 0); right = newpan(0, 0, 0, 0); layout(); console_init(&cons); argv[argc] = NULL; struct tracee dbg; if (dbg_new_process(&dbg, argv+2, &cons)) { pprintw(right, "Failed to start child\n"); } 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); } int quit = 0; while (!quit) { //if (dbg.stopped == NULL) { dbg_wait(&dbg); //} console_update(&cons, right); info_update(&dbg, left); cursupdate(); int ch = getch(); if (mode == 0) { switch (ch) { case KEY_RESIZE: layout(); break; case 'q': quit = 1; break; case 'i': mode = 1; console_enter(&cons, right); break; case 'j': 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); } } 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 'c': if (dbg.stopped) { dbg_cont(&dbg, PTRACE_CONT); } break; case 'p': if (!dbg.stopped) { tgkill(dbg.id, dbg.id, SIGSTOP); } break; case ':': mvprintw(LINES-1, 0, ":"); curs_set(TRUE); echo(); timeout(-1); char cmd[128] = {0}; getstr(cmd); curs_set(FALSE); noecho(); timeout(25); clear(); refresh(); char *t = cmd; struct breakpoint *b = xmalloc(sizeof(*b)); b->enabled = 1; b->active = 0; if (t[0] == '!') { b->enabled = -1; t++; } b->address = strtoul(t, NULL, 0); b->stack = 0; list_insert(dbg.breaks.end, b); break; } } else { switch (ch) { case KEY_RESIZE: layout(); break; case 0x1b: mode = 0; console_leave(&cons, right); break; case ERR: break; default: console_input(&cons, ch); break; } } } endwin(); return 0; }