diff options
| -rw-r--r-- | debugger.c | 592 | ||||
| -rw-r--r-- | debugger.h | 30 | ||||
| -rw-r--r-- | misplays.c | 27 | 
3 files changed, 330 insertions, 319 deletions
@@ -1,6 +1,9 @@  #include <dirent.h> +#include <elf.h>  #include <signal.h> +#include <stdio.h>  #include <stdlib.h> +#include <string.h>  #include <sys/ptrace.h>  #include <sys/uio.h>  #include <sys/wait.h> @@ -10,60 +13,32 @@  #include "debugger.h"  #include "helpers.h" -#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_TRACEEXEC  |      PTRACE_O_TRACESYSGOOD; -static int detect_breakpoint(struct thread *th) { -    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) { -        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; -                    } +static const int PTRACE_CHILD_OPTIONS = PTRACE_OPTIONS | PTRACE_O_EXITKILL; +static const int STOP_ALL_ON_EVENT = 1; +static const useconds_t SCHEDULER_DELAY = 100000; +static const unsigned int BREAKPOINT_INSN = 0xcc; -                    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; -            //    list_remove(del); -            //    free(del); -            //} +static struct thread *thread_by_id(struct process *proc, pid_t id) { +    struct list *threads = &proc->threads; +    for (struct thread *th = threads->head; th != threads->end; th = th->next) { +        if (th->id == id) { +            return th;          }      } +    return NULL; +} -    return restart; +static void free_breakpoints(struct process *proc) { +    while (proc->breakpoints.head != proc->breakpoints.end) { +        struct breakpoint *b = proc->breakpoints.head; +        list_remove(b); +        free(b); +    }  }  static void install_breakpoints(struct thread *th) { @@ -88,22 +63,51 @@ static void uninstall_breakpoints(struct thread *th) {              ptrace(PTRACE_POKETEXT, th->id, b->address, b->text);              b->installed = 0;          } -          if (b->enabled < 0) { -            struct breakpoint *del = b; -            b = b->next; -            list_remove(del); -            free(del); +            struct thread *t; +            if (b->tid == 0 || +                    ((t = thread_by_id(th->proc, b->tid)) && !t->shouldcont)) { +                struct breakpoint *del = b; +                b = b->next; +                list_remove(del); +                free(del); +            }          }      }  } -static void free_breakpoints(struct process *proc) { -    while (proc->breakpoints.head != proc->breakpoints.end) { -        struct breakpoint *b = proc->breakpoints.head; -        list_remove(b); -        free(b); +static int detect_breakpoint(struct thread *th, int *restart) { +    int ret = 0; +    *restart = 0; + +    struct user_regs_struct regs; +    struct iovec ivregs = { ®s, sizeof(regs) }; +    ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, &ivregs); + +    /* implement with get_breakpoint? */ +    struct list *breaks = &th->proc->breakpoints; +    for (struct breakpoint *b = breaks->tail; b != breaks->end; b = b->prev) { +        if (b->installed && (regs.rip - 1 == b->address)) { +            regs.rip--; +            ptrace(PTRACE_SETREGSET, th->id, NT_PRSTATUS, &ivregs); +            b->hits++; +            ret = b->user; + +            if (b->stack != 0 && b->stack != regs.rsp) { +                *restart = 1; +            } +            if (b->tid != 0 && b->tid != th->id) { +                *restart = 1; +            } +            if (!b->enabled) { +                *restart = 1; +            } + +            break; +        }      } + +    return ret;  }  static void free_states(struct thread *th) { @@ -125,155 +129,166 @@ static void free_states(struct thread *th) {      th->clearstates = 0;  } -static void capture_state_thread(struct thread *th) { -    if (th->clearstates) { -        free_states(th); -    } - -    struct state *s = xmalloc(sizeof(*s)); -    ptrace(PTRACE_GETREGS, th->id, NULL, &s->regs); -    ptrace(PTRACE_GETFPREGS, th->id, NULL, &s->fpregs); -    list_init(&s->maps); - -    list_insert(th->states.end, s); -    th->state = s; +static void capture_state(struct process *proc) { +    struct list *threads = &proc->threads; +    for (struct thread *th = threads->head; th != threads->end; th = th->next) { +        if (!th->shouldcont) { +            if (th->clearstates) { +                free_states(th); +            } -    char mapspath[32], entry[512]; -    snprintf(mapspath, sizeof(mapspath), "/proc/%li/maps", (long)th->id); +            struct state *s = xmalloc(sizeof(*s)); +            struct iovec regs = { &s->regs, sizeof(s->regs) }; +            ptrace(PTRACE_GETREGSET, th->id, NT_PRSTATUS, ®s); +            list_init(&s->maps); + +            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) { +                while (fgets(entry, sizeof(entry), maps)) { +                    struct map *m = xmalloc(sizeof(*m)); +                    sscanf(entry, "%lx-%lx ", &m->start, &m->end); +                    size_t size = m->end - m->start; +                    m->data = xmalloc(size); + +                    struct iovec loc = { m->data, size }; +                    struct iovec rem = { (void *)m->start, size }; +                    if (process_vm_readv(th->id, &loc, 1, &rem, 1, 0) < 0) { +                        free(m->data); +                        free(m); +                        continue; +                    } -    FILE *maps = fopen(mapspath, "r"); -    if (maps) { -        while (fgets(entry, sizeof(entry), maps)) { -            struct map *m = xmalloc(sizeof(*m)); -            sscanf(entry, "%lx-%lx ", &m->start, &m->end); -            size_t size = m->end - m->start; -            m->data = xmalloc(size); +                    list_insert(s->maps.end, m); +                } -            struct iovec loc = { m->data, size }; -            struct iovec rem = { (void *)m->start, size }; -            if (process_vm_readv(th->id, &loc, 1, &rem, 1, 0) < 0) { -                free(m->data); -                free(m); -                continue; +                fclose(maps);              } +        } +    } +} -            list_insert(s->maps.end, m); +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)) {} +        } +        if (STOP_ALL_ON_EVENT) { +            th->shouldcont = 0;          } +    } +} -        fclose(maps); +static void continue_all_threads(struct process *proc) { +    struct list *threads = &proc->threads; +    for (struct thread *th = threads->head; th != threads->end; th = th->next) { +        if (th->id > 0) { +            ptrace(PTRACE_CONT, th->id, NULL, th->signal); +            th->stopped = 0; +            th->signal = 0; +            th->cont = 0; +            th->shouldcont = 2; +            strcpy(th->status, "RUNNING"); +            th->clearstates = 1; +        }      }  } -static void capture_state(struct thread *th, int all) { -    (void)all; -    struct list *threads = &th->proc->threads; -    for (struct thread *t = threads->head; t != threads->end; t = t->next) { -        if (!t->state) { -            capture_state_thread(t); +/* 0: stop  1: singlestep  2: cont  3: syscall */ +/* this is simplified, surely incorrect, and needs updated once testable */ +static void resume_threads(struct process *proc) { +    struct list *threads = &proc->threads; +    for (struct thread *th = threads->head; th != threads->end; th = th->next) { +        if (th->shouldcont) { +            install_breakpoints(th); +            ptrace(PTRACE_CONT, th->id, NULL, th->signal); +            th->stopped = 0; +            th->signal = 0; +            th->cont = 0; +            strcpy(th->status, "RUNNING");          }      } -    //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) { +static struct process *new_process(pid_t id, int child) {      struct process *proc = xmalloc(sizeof(*proc)); -    proc->id = pid; +    proc->id = id;      proc->child = child; -    list_init(&proc->threads);      list_init(&proc->breakpoints); +    list_init(&proc->threads); +    memset(proc->status, 0, sizeof(proc->status));      return proc;  } -static struct thread *new_thread(struct process *proc, pid_t tid) { +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 = tid; +    th->id = id;      th->stopped = 0;      th->signal = 0;      th->cont = 0; -    th->status = "RUNNING"; +    th->shouldcont = 0; +    strcpy(th->status, "RUNNING");      return th;  } -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, 1)) {} -            stopped = 1; -        } -    } -    return stopped; -} - -void add_breakpoint(struct process *proc, unsigned long address, unsigned long stack, pid_t tid, int enabled) { +struct breakpoint *add_breakpoint(struct process *proc, unsigned long address) {      struct breakpoint *b = xmalloc(sizeof(*b));      b->address = address;      b->text = 0;      b->installed = 0;      b->hits = 0; -    b->stack = stack; -    b->tid = tid; -    b->enabled = enabled; +    b->user = 1; +    b->stack = 0; +    b->tid = 0; +    b->enabled = 1;      list_insert(proc->breakpoints.end, b); +    return b;  } -int is_breakpoint(struct process *proc, unsigned long address) { +struct breakpoint *get_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 b;          }      } -    return 0; +    return NULL;  } -//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; -    } +    const int OPTIONS = (child ? PTRACE_CHILD_OPTIONS : PTRACE_OPTIONS);      char taskpath[32];      snprintf(taskpath, sizeof(taskpath), "/proc/%li/task", (long)pid);      DIR *taskdir = opendir(taskpath);      if (!taskdir) { -        dbg_detach(proc);          return NULL;      }      struct dirent *task; +    struct process *proc = new_process(pid, child); +      while ((task = readdir(taskdir))) { -        pid_t id = strtoul(task->d_name, NULL, 0); +        pid_t id = strict_strtoul(task->d_name, 0);          if (id != 0) { -            if (ptrace(PTRACE_SEIZE, id, NULL, options) < 0) { +            if (ptrace(PTRACE_SEIZE, id, NULL, OPTIONS) < 0) {                  closedir(taskdir);                  dbg_detach(proc);                  return NULL;              } -              struct thread *th = new_thread(proc, id);              list_insert(proc->threads.end, th);          } @@ -281,137 +296,137 @@ struct process *dbg_attach(pid_t pid, int child) {      closedir(taskdir); -    //global_thread = proc->threads.head;      interrupt_all_threads(proc); -    capture_state(proc->threads.head, 0); +    capture_state(proc);      return proc;  } -int dbg_detach(struct process *proc) { +void dbg_detach(struct process *proc) { +    /* ptrace requires that threads be stopped before calling PTRACE_DETACH. +     * In some cases, a blocked thread can possibly have a pending trap which +     * would crash the process if encountered after ptracing.  We cycle an extra +     * continue/interrupt to consume this trap event ourselves before detaching. +     * Threads must actually get scheduled for this to be effective, which is +     * the reason for the usleep.  A better approach is welcome here. */      interrupt_all_threads(proc); -    dbg_realcont(proc->threads.head); -    usleep(100000); +    continue_all_threads(proc); +    usleep(SCHEDULER_DELAY);      interrupt_all_threads(proc); -    uninstall_breakpoints(proc->threads.head); -    free_breakpoints(proc); +    /* Supplement to PTRACE_O_EXITKILL */      if (proc->child) {          kill(proc->id, SIGKILL);      }      while (proc->threads.head != proc->threads.end) {          struct thread *th = proc->threads.head; -        if (th->id >= 0) { +        if (th->id > 0) { +            uninstall_breakpoints(th);              ptrace(PTRACE_DETACH, th->id, NULL, th->signal); +            th->id = 0;          } -        free_states(th); -        list_remove(th); -        free(th); +        dbg_free(th);      } +    free_breakpoints(proc);      list_remove(proc);      free(proc); -    return 0;  } -int dbg_wait(struct thread *th, int recursion) { -    if (th->id < 0) { +int dbg_free(struct thread *th) { +    if (th->id <= 0) { +        free_states(th); +        list_remove(th); +        free(th); +        return 0; +    } +    return -1; +} + +int dbg_wait(struct thread *th, int primary) { +    if (th->id <= 0) {          return -1; +    } else if (th->stopped) { +        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;      }      if (WIFEXITED(status)) { -        th->id = -1; +        th->id = 0;          th->stopped = 1;          th->signal = WEXITSTATUS(status);          th->cont = 0; -        th->status = "EXITED"; +        th->shouldcont = 0; +        strcpy(th->status, "EXITED");          return 1;      }      if (WIFSIGNALED(status)) { -        th->id = -2; +        th->id = 0;          th->stopped = 1;          th->signal = WTERMSIG(status);          th->cont = 0; -        th->status = "TERMINATED"; +        th->shouldcont = 0; +        strcpy(th->status, "TERMINATED");          return 1;      } -    int stopped; -    struct thread *newth; -    struct list *threads; - +    struct thread *newth; // event thread      unsigned long eventmsg;      ptrace(PTRACE_GETEVENTMSG, th->id, NULL, &eventmsg); +    /* todo: process status messages */      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, 1)) {} +                while (!dbg_wait(newth, 0)) {}                  th->stopped = 1;                  th->signal = 0;                  th->cont = 0; -                th->status = "CLONE EVENT"; +                th->shouldcont = 0; +                strcpy(th->status, "CLONE");                  th->state = NULL; -                if (!recursion) { -                    stopped = interrupt_all_threads(th->proc); +                if (primary) { +                    interrupt_all_threads(th->proc);                      uninstall_breakpoints(th); -                    capture_state(th, stopped); +                    capture_state(th->proc); +                    resume_threads(th->proc);                  }                  return 1;              case SIGTRAP | (PTRACE_EVENT_EXEC << 8): -                /* eventmsg contains the tid that actually did the execve */ -                threads = &th->proc->threads; -                for (struct thread *t = threads->head; t != threads->end; t = t->next) { -                    if (t->id == (pid_t)eventmsg && (pid_t)eventmsg != th->id) { -                        t->id = -1; -                        t->stopped = 1; -                        t->signal = 0; -                        t->cont = 0; -                        t->status = "EXITED"; -                        break; -                    } +                if ((pid_t)eventmsg != th->id) { +                    newth = thread_by_id(th->proc, eventmsg); +                    newth->id = 0; +                    newth->stopped = 1; +                    newth->signal = 0; +                    newth->cont = 0; +                    newth->shouldcont = 0; +                    strcpy(newth->status, "EXITED");                  }                  th->stopped = 1;                  th->signal = 0;                  th->cont = 0; -                th->status = "EXEC EVENT"; -                th->state = NULL; - -                if (!recursion) { -                    stopped = interrupt_all_threads(th->proc); -                    uninstall_breakpoints(th); -                    capture_state(th, stopped); -                } - -                return 1; - -            case SIGTRAP | (PTRACE_EVENT_EXIT << 8): -                th->stopped = 1; -                th->signal = 0; /* eventmsg has exit code, but would inject sig */ -                th->cont = 0; -                th->status = "EXIT EVENT"; +                th->shouldcont = 0; +                strcpy(th->status, "EXECVE");                  th->state = NULL; -                if (!recursion) { -                    stopped = interrupt_all_threads(th->proc); +                if (primary) { +                    interrupt_all_threads(th->proc);                      uninstall_breakpoints(th); -                    capture_state(th, stopped); +                    capture_state(th->proc); +                    resume_threads(th->proc);                  }                  return 1; @@ -420,28 +435,30 @@ int dbg_wait(struct thread *th, int recursion) {                  th->stopped = 1;                  th->signal = 0;                  th->cont = 0; -                th->status = "STOP EVENT"; -                th->state = NULL; +                strcpy(th->status, "STOPPED"); -                if (!recursion) { -                    stopped = interrupt_all_threads(th->proc); +                if (primary) { +                    interrupt_all_threads(th->proc);                      uninstall_breakpoints(th); -                    capture_state(th, stopped); +                    capture_state(th->proc); +                    resume_threads(th->proc);                  } -                return 1; +                return th->stopped;              case SIGTRAP | 0x80:                  th->stopped = 1;                  th->signal = 0;                  th->cont = 0; -                th->status = "SYSCALL EVENT"; +                th->shouldcont = 0; +                strcpy(th->status, "SYSCALL");                  th->state = NULL; -                if (!recursion) { -                    stopped = interrupt_all_threads(th->proc); +                if (primary) { +                    interrupt_all_threads(th->proc);                      uninstall_breakpoints(th); -                    capture_state(th, stopped); +                    capture_state(th->proc); +                    resume_threads(th->proc);                  }                  return 1; @@ -449,52 +466,35 @@ int dbg_wait(struct thread *th, int recursion) {              case SIGTRAP:                  th->stopped = 1;                  th->signal = 0; -                th->status = "STEP/BREAKPOINT"; - -                if (th->cont != 0) { -                    /* gdb this portion.  are there race conditions -                     * that matter?? */ -                    install_breakpoints(th); -                    ptrace(th->cont, th->id, NULL, NULL); -                    th->cont = 0; -                    th->stopped = 0; -                    th->status = "RUNNING"; -                    return 0; -                } - -                /* todo: Test two threads hitting a breakpoint at -                 * the same time. */ -                int restart = detect_breakpoint(th); -                if (!restart) { -                    th->state = NULL; -                } -                if (!recursion) { -                    stopped = interrupt_all_threads(th->proc); -                    if (!restart) { -                        uninstall_breakpoints(th); -                        capture_state(th, stopped); -                    } -                } +                int restart; +                int bp = detect_breakpoint(th, &restart); +                strcpy(th->status, (bp ? "BREAKPOINT" : "STEP")); +                //th->shouldcont = (b && b->enabled == 0); +                th->shouldcont = (restart ? 2 : 0); -                if (restart) { -                    dbg_cont(th, PTRACE_CONT); -                    return 0; +                if (primary) { +                    interrupt_all_threads(th->proc); +                    uninstall_breakpoints(th); +                    capture_state(th->proc); +                    resume_threads(th->proc);                  } -                return 1; +                return th->stopped;              default:                  th->stopped = 1;                  th->signal = WSTOPSIG(status);                  th->cont = 0; -                th->status = "SIGNAL DELIVERY"; +                th->shouldcont = 0; +                strcpy(th->status, "SIGNAL DELIVERY");                  th->state = NULL; -                if (!recursion) { -                    stopped = interrupt_all_threads(th->proc); +                if (primary) { +                    interrupt_all_threads(th->proc);                      uninstall_breakpoints(th); -                    capture_state(th, stopped); +                    capture_state(th->proc); +                    resume_threads(th->proc);                  }                  return 1; @@ -505,58 +505,70 @@ int dbg_wait(struct thread *th, int recursion) {  }  int dbg_intr(struct thread *th) { -    if (th->id < 0 || th->stopped) { +    if (th->id <= 0 || th->stopped) {          return -1;      }      ptrace(PTRACE_INTERRUPT, th->id, NULL, NULL); +    th->shouldcont = 0;      return 0;  } -int dbg_cont(struct thread *th, int cont) { -    if (th->id < 0 || !th->stopped) { +int dbg_cont(struct thread *th) { +    if (th->id <= 0 || !th->stopped) {          return -1;      } -    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); +    ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); +    th->stopped = 0; +    th->signal = 0; +    th->cont = PTRACE_CONT; +    th->shouldcont = 1; +    strcpy(th->status, "RUNNING"); +    th->clearstates = 1; +    return 0; +} -        t->stopped = 0; -        t->signal = 0; -        t->cont = cont; -        t->status = "RUNNING"; -        t->clearstates = 1; +int dbg_syscall(struct thread *th) { +    if (th->id <= 0 || !th->stopped) { +        return -1;      } +    ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); +    th->stopped = 0; +    th->signal = 0; +    th->cont = PTRACE_SYSCALL; +    th->shouldcont = 1; +    strcpy(th->status, "RUNNING"); +    th->clearstates = 1;      return 0;  } -int dbg_realcont(struct thread *th) { -    if (th->id < 0 || !th->stopped) { +int dbg_stepin(struct thread *th) { +    if (!th->stopped) {          return -1;      } -    struct list *threads = &th->proc->threads; -    for (struct thread *t = threads->head; t != threads->end; t = t->next) { -        ptrace(PTRACE_CONT, t->id, NULL, NULL); +    if (th->state != th->states.tail) { +        th->state = th->state->next; +        return 0; +    } -        t->stopped = 0; -        t->signal = 0; -        t->cont = 0; -        t->status = "RUNNING"; -        t->clearstates = 1; +    if (th->id <= 0) { +        return -1;      } +    ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); +    th->stopped = 0; +    th->signal = 0; +    th->cont = PTRACE_SINGLESTEP; +    th->shouldcont = 1; +    strcpy(th->status, "RUNNING"); +    th->clearstates = 0;      return 0;  } -int dbg_step(struct thread *th, int stepover) { -    // todo: support step-out -    //if (th->id < 0 || !th->stopped) { -    //    return -1; -    //} - +int dbg_stepover(struct thread *th) {      if (!th->stopped) {          return -1;      } @@ -566,39 +578,39 @@ int dbg_step(struct thread *th, int stepover) {          return 0;      } -    if (th->id < 0) { +    if (th->id <= 0) {          return -1;      } -    csh cshandle; -    cs_open(CS_ARCH_X86, CS_MODE_64, &cshandle); -    cs_insn *insn = cs_malloc(cshandle); +    csh handle; +    cs_open(CS_ARCH_X86, CS_MODE_64, &handle); +    cs_insn *insn = cs_malloc(handle);      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); +    cs_disasm_iter(handle, &code, &size, &address, insn); + +    int ret = -1; +    if (insn->id == X86_INS_CALL) { +        struct breakpoint *b = add_breakpoint(th->proc, address); +        b->user = 0; +        b->stack = th->state->regs.rsp; +        b->tid = th->id; +        b->enabled = -1; -    if (insn->id == X86_INS_CALL && stepover) { -        add_breakpoint(th->proc, address, th->state->regs.rsp, th->id, -1); -        th->cont = PTRACE_CONT; +        ret = dbg_cont(th);      } else { -        th->cont = 0; +        ret = dbg_stepin(th);      } -    ptrace(PTRACE_SINGLESTEP, th->id, NULL, th->signal); - -    th->stopped = 0; -    th->signal = 0; -    th->status = "RUNNING"; -      cs_free(insn, 1); -    cs_close(&cshandle); -    return 0; +    cs_close(&handle); +    return ret;  } -int dbg_pets(struct thread *th) { +int dbg_stepback(struct thread *th) {      if (!th->stopped) {          return -1;      } @@ -633,10 +645,6 @@ void *deref(struct thread *th, unsigned long address, size_t size) { -///* 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. -// */  //int dbg_stepout(struct tracee *dbg) {  //    unsigned long bp = dbg->state->regs.rbp; @@ -12,6 +12,7 @@ struct breakpoint {      unsigned long text;      int installed;      int hits; +    int user;      unsigned long stack;      pid_t tid; @@ -28,7 +29,6 @@ struct map {  struct state {      LINKEDLIST;      struct user_regs_struct regs; -    struct user_fpregs_struct fpregs;      struct list maps;  }; @@ -36,8 +36,9 @@ struct process {      LINKEDLIST;      pid_t id;      int child; -    struct list threads;      struct list breakpoints; +    struct list threads; +    char status[128];  };  struct thread { @@ -52,24 +53,25 @@ struct thread {      int stopped;      int signal;      int cont; +    int shouldcont; -    const char *status; +    char status[128];  }; -//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 struct breakpoint*add_breakpoint(struct process*proc,unsigned long address); +extern struct breakpoint*get_breakpoint(struct process*proc,unsigned long address);  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 recursion); +extern void dbg_detach(struct process *proc); +extern int dbg_free(struct thread *th); + +extern int dbg_wait(struct thread *th, int primary);  extern int dbg_intr(struct thread *th); -extern int dbg_cont(struct thread *th, int cont); -extern int dbg_realcont(struct thread *th); -extern int dbg_step(struct thread *th, int stepover); -extern int dbg_pets(struct thread *th); +extern int dbg_cont(struct thread *th); +extern int dbg_syscall(struct thread *th); +extern int dbg_stepin(struct thread *th); +extern int dbg_stepover(struct thread *th); +extern int dbg_stepback(struct thread *th);  extern void *deref(struct thread *th, unsigned long address, size_t size); @@ -44,7 +44,10 @@ static void list_breakpoints(struct thread *dbg, PANEL *pan) {          pprintw(pan, "---\n");          for (struct breakpoint *bp=breaks->head; bp!=breaks->end; bp=bp->next) { -            pprintw(pan, "0x%lx (%c)  ", bp->address, (bp->installed ? '*' : ' ')); +            pprintw(pan, "0x%lx (%c) (%i)  ", +                    bp->address, +                    (bp->installed ? '*' : ' '), +                    bp->hits);          }          pprintw(pan, "\n");      } @@ -108,7 +111,7 @@ static void disasm(struct thread *dbg, PANEL *pan) {              if (dbg->stopped) {                  if (insn->address == dbg->state->regs.rip) {                      pattron(pan, COLOR_PAIR(1)); -                } else if (is_breakpoint(dbg->proc, insn->address)) { +                } else if (get_breakpoint(dbg->proc, insn->address)) {                      pattron(pan, COLOR_PAIR(3));                  } else if (rip_visited(dbg, insn->address)) {                      pattron(pan, COLOR_PAIR(2)); @@ -118,7 +121,7 @@ static void disasm(struct thread *dbg, PANEL *pan) {              if (dbg->stopped) {                  if (insn->address == dbg->state->regs.rip) {                      pattroff(pan, COLOR_PAIR(1)); -                } else if (is_breakpoint(dbg->proc, insn->address)) { +                } else if (get_breakpoint(dbg->proc, insn->address)) {                      pattroff(pan, COLOR_PAIR(3));                  } else if (rip_visited(dbg, insn->address)) {                      pattroff(pan, COLOR_PAIR(2)); @@ -211,7 +214,7 @@ int main(int argc, char **argv) {      th = proc->threads.head;      if (child) { -        dbg_cont(th, PTRACE_CONT); +        dbg_cont(th);      }      cursinit(); @@ -243,13 +246,13 @@ int main(int argc, char **argv) {                      console_enter(&cons, right);                      break;                  case 'j': -                    dbg_step(th, 1); +                    dbg_stepover(th);                      break;                  case 'k': -                    dbg_pets(th); +                    dbg_stepback(th);                      break;                  case 'l': -                    dbg_step(th, 0); +                    dbg_stepin(th);                      break;                  case 'h':                      /* todo: step out */ @@ -265,13 +268,10 @@ int main(int argc, char **argv) {                      }                      break;                  case 's': -                    dbg_cont(th, PTRACE_SYSCALL); +                    dbg_syscall(th);                      break;                  case 'c': -                    dbg_cont(th, PTRACE_CONT); -                    break; -                case 'C': -                    dbg_realcont(th); +                    dbg_cont(th);                      break;                  case 'p':                      dbg_intr(th); @@ -313,7 +313,8 @@ int main(int argc, char **argv) {                          t++;                      }                      unsigned long address = strtoul(t, NULL, 0); -                    add_breakpoint(th->proc, address, 0, 0, en); +                    struct breakpoint *b = add_breakpoint(th->proc, address); +                    b->enabled = en;                      break;              }          } else {  | 
