summaryrefslogblamecommitdiffstats
path: root/misplays.c
blob: 8f6e0eeab9aaa193f295d4b76058fafafbd94ba6 (plain) (tree)
1
2
3
4
5
6
7
8
9
                   
                   


                         

                   

                              
                    
                     
                    
 
                           
                           

                    
























































































































































                                                                                                       

                          

                                              

 



                                                                 
     
 
               



                               

















                                                   


                   




                                     
                     
 



                         


                                



                             
                             
                                                
                          







                                                           
                          





                                                           
                          







                                                           
                          








                                                    
                          











































                                                               








                                
                                                



                          
                                             







                          
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <linux/ptrace.h>
#include <unistd.h>

#include <capstone/capstone.h>

#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 <stop> <command>\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;
}