/* radare - LGPL - Copyright 2009-2014 - pancake */ static void dot_r_graph_traverse(RCore *core, RGraph *t) { RGraphNode *n, *n2; RListIter *iter, *iter2; const char *gfont = r_config_get (core->config, "graph.font"); r_cons_printf ("digraph code {\n" "graph [bgcolor=white];\n" " node [color=lightgray, style=filled" " shape=box fontname=\"%s\" fontsize=\"8\"];\n", gfont); r_list_foreach (t->nodes, iter, n) { r_cons_printf ("\"0x%08"PFMT64x"\" [URL=\"0x%08"PFMT64x "\" color=\"lightgray\" label=\"0x%08"PFMT64x " (%d)\"]\n", n->addr, n->addr, n->addr, n->refs); r_list_foreach (n->children, iter2, n2) { r_cons_printf ("\"0x%08"PFMT64x"\" -> \"0x%08"PFMT64x "\" [color=\"red\"];\n", n->addr, n2->addr); } } r_cons_printf ("}\n"); } static int checkbpcallback(RCore *core) ; static int step_until(RCore *core, ut64 addr) { ut64 off = r_debug_reg_get (core->dbg, "pc"); if (off == 0LL) { eprintf ("Cannot 'drn pc'\n"); return R_FALSE; } if (addr == 0LL) { eprintf ("Cannot continue until address 0\n"); return R_FALSE; } do { r_debug_step (core->dbg, 1); if (checkbpcallback (core)) { eprintf ("Interrupted by a breakpoint\n"); break; } off = r_debug_reg_get (core->dbg, "pc"); // check breakpoint here } while (off != addr); return R_TRUE; } /* until end of frame */ static int step_until_eof(RCore *core) { ut64 off, now = r_debug_reg_get (core->dbg, "sp"); do { if (!r_debug_step (core->dbg, 1)) break; if (checkbpcallback (core)) { eprintf ("Interrupted by a breakpoint\n"); break; } off = r_debug_reg_get (core->dbg, "sp"); // check breakpoint here } while (off <= now); return R_TRUE; } static int step_line(RCore *core, int times) { char file[512], file2[512]; int find_meta, line = -1, line2 = -1; char *tmp_ptr = NULL; ut64 off = r_debug_reg_get (core->dbg, "pc"); if (off == 0LL) { eprintf ("Cannot 'drn pc'\n"); return R_FALSE; } file[0] = 0; file2[0] = 0; if (r_bin_addr2line (core->bin, off, file, sizeof (file), &line)) { eprintf ("--> 0x%08"PFMT64x" %s : %d\n", off, file, line); eprintf ("--> %s\n", r_file_slurp_line (file, line, 0)); find_meta = R_FALSE; } else { eprintf ("--> Stepping until dwarf line\n"); find_meta = R_TRUE; } do { r_debug_step (core->dbg, 1); if (checkbpcallback (core)) { eprintf ("Interrupted by a breakpoint\n"); break; } off = r_debug_reg_get (core->dbg, "pc"); if (!r_bin_addr2line (core->bin, off, file2, sizeof (file2), &line2)) { if (find_meta) continue; eprintf ("Cannot retrieve dwarf info at 0x%08"PFMT64x"\n", off); return R_FALSE; } } while (!strcmp (file, file2) && line == line2); eprintf ("--> 0x%08"PFMT64x" %s : %d\n", off, file2, line2); tmp_ptr = r_file_slurp_line (file2, line2, 0); eprintf ("--> %s\n", tmp_ptr); free (tmp_ptr); return R_TRUE; } static void cmd_debug_pid(RCore *core, const char *input) { const char *ptr; int pid, sig; switch (input[1]) { case 'k': /* stop, print, pass -- just use flags*/ /* XXX: not for threads? signal is for a whole process!! */ /* XXX: but we want fine-grained access to process resources */ pid = atoi (input+2); ptr = strchr (input, ' '); sig = ptr? atoi (ptr+1): 0; if (pid > 0) { eprintf ("Sending signal '%d' to pid '%d'\n", sig, pid); r_debug_kill (core->dbg, 0, R_FALSE, sig); } else eprintf ("cmd_debug_pid: Invalid arguments (%s)\n", input); break; case 'n': eprintf ("TODO: debug_fork: %d\n", r_debug_child_fork (core->dbg)); break; case 't': switch (input[2]) { case 'n': eprintf ("TODO: debug_clone: %d\n", r_debug_child_clone (core->dbg)); break; case '=': case ' ': r_debug_select (core->dbg, core->dbg->pid, (int) r_num_math (core->num, input+3)); break; default: r_debug_thread_list (core->dbg, core->dbg->pid); break; } break; case 'a': if (input[2]) { r_debug_attach (core->dbg, (int) r_num_math ( core->num, input+2)); } else r_debug_attach (core->dbg, core->file->desc->fd); r_debug_select (core->dbg, core->dbg->pid, core->dbg->tid); r_config_set_i (core->config, "dbg.swstep", (core->dbg->h && !core->dbg->h->canstep)); break; case 'f': r_debug_select (core->dbg, core->file->desc->fd, core->dbg->tid); break; case '=': r_debug_select (core->dbg, (int) r_num_math (core->num, input+2), core->dbg->tid); break; case '*': r_debug_pid_list (core->dbg, 0); break; case 'e': { int pid = (input[2] == ' ')? atoi(input+2): core->dbg->pid; char *exe = r_sys_pid_to_path (pid); if (exe) { r_cons_printf ("%s\n", exe); free (exe); } } break; case ' ': r_debug_pid_list (core->dbg, (int) r_num_math (core->num, input+2)); break; case '?': r_cons_printf ( "|Usage: dp Process commands\n" "| dp List current pid and childrens\n" "| dp List children of pid\n" "| dp* List all attachable pids\n" "| dp= Select pid\n" "| dpa Attach and select pid\n" "| dpe Show path to executable\n" "| dpf Attach to pid like file fd // HACK\n" "| dpk Send signal to process\n" "| dpn Create new process (fork)\n" "| dpnt Create new thread (clone)\n" "| dpt List threads of current pid\n" "| dpt List threads of process\n" "| dpt= Attach to thread\n"); break; default: eprintf ("Selected: %d %d\n", core->dbg->pid, core->dbg->tid); r_debug_pid_list (core->dbg, core->dbg->pid); break; } } static void cmd_debug_backtrace (RCore *core, const char *input) { RAnalOp analop; ut64 addr, len = r_num_math (core->num, input); if (len == 0) { r_bp_traptrace_list (core->dbg->bp); } else { ut64 oaddr = 0LL; eprintf ("Trap tracing 0x%08"PFMT64x"-0x%08"PFMT64x"\n", core->offset, core->offset+len); r_reg_arena_swap (core->dbg->reg, R_TRUE); r_bp_traptrace_reset (core->dbg->bp, R_TRUE); r_bp_traptrace_add (core->dbg->bp, core->offset, core->offset+len); r_bp_traptrace_enable (core->dbg->bp, R_TRUE); do { ut8 buf[32]; r_debug_continue (core->dbg); if (checkbpcallback (core)) { eprintf ("Interrupted by breakpoint\n"); break; } addr = r_debug_reg_get (core->dbg, "pc"); if (addr == 0LL) { eprintf ("pc=0\n"); break; } if (addr == oaddr) { eprintf ("pc=opc\n"); break; } oaddr = addr; /* XXX Bottleneck..we need to reuse the bytes read by traptrace */ // XXX Do asm.arch should define the max size of opcode? r_core_read_at (core, addr, buf, 32); // XXX longer opcodes? r_anal_op (core->anal, &analop, addr, buf, sizeof (buf)); } while (r_bp_traptrace_at (core->dbg->bp, addr, analop.size)); r_bp_traptrace_enable (core->dbg->bp, R_FALSE); } } static int cmd_debug_map(RCore *core, const char *input) { char file[128]; RListIter *iter; RDebugMap *map; ut64 addr = core->offset; switch (input[0]) { case '?': r_cons_printf ( "|Usage: dm Memory maps commands\n" "| dm List memory maps of target process\n" "| dm
Allocate bytes at
(anywhere if address is -1) in child process\n" "| dm* List memmaps in radare commands\n" "| dm-
Deallocate memory map of
\n" "| dmd [file] Dump current debug map region to a file (from-to.dmp) (see Sd)\n" "| dmi [addr|libname] [symname] List symbols of target lib\n" "| dmi* [addr|libname] [symname] List symbols of target lib in radare commands\n" "| dmj List memmaps in JSON format\n" "| dml Load contents of file into the current map region (see Sl)\n" "| dmp
Change page at
with , protection (rwx)\n" //"| dm rw- esp 9K set 9KB of the stack as read+write (no exec)\n" "|TODO: map files in process memory. (dmf file @ [addr])\n"); break; case 'p': if (input[1] == ' ') { int perms; char *p, *q; ut64 size, addr; p = strchr (input+2, ' '); if (p) { *p++ = 0; q = strchr (p, ' '); if (q) { *q++ = 0; addr = r_num_math (core->num, input+2); size = r_num_math (core->num, p); perms = r_str_rwx (q); eprintf ("(%s)(%s)(%s)\n", input+2, p, q); eprintf ("0x%08"PFMT64x" %d %o\n", addr, (int) size, perms); r_debug_map_protect (core->dbg, addr, size, perms); } else eprintf ("See dm?\n"); } else eprintf ("See dm?\n"); } else eprintf ("See dm?\n"); break; case 'd': r_debug_map_sync (core->dbg); // update process memory maps r_list_foreach (core->dbg->maps, iter, map) { if (addr >= map->addr && addr < map->addr_end) { ut8 *buf = malloc (map->size); #warning TODO: use mmap here. we need a portable implementation if (!buf) { eprintf ("Cannot allocate 0x%08"PFMT64x" bytes\n", map->size); return R_FALSE; } r_io_read_at (core->io, map->addr, buf, map->size); if (input[1]==' ' && input[2]) { snprintf (file, sizeof (file), "%s", input+2); } else snprintf (file, sizeof (file), "0x%08"PFMT64x"-0x%08"PFMT64x"-%s.dmp", map->addr, map->addr_end, r_str_rwx_i (map->perm)); if (!r_file_dump (file, buf, map->size)) { eprintf ("Cannot write '%s'\n", file); free (buf); return R_FALSE; } eprintf ("Dumped %d bytes into %s\n", (int)map->size, file); free (buf); return R_TRUE; } } eprintf ("No debug region found here\n"); return R_FALSE; case 'l': if (input[1] != ' ') { eprintf ("Usage: dml [file]\n"); return R_FALSE; } r_debug_map_sync (core->dbg); // update process memory maps r_list_foreach (core->dbg->maps, iter, map) { if (addr >= map->addr && addr < map->addr_end) { int sz; char *buf = r_file_slurp (input+2, &sz); #warning TODO: use mmap here. we need a portable implementation if (!buf) { eprintf ("Cannot allocate 0x%08"PFMT64x" bytes\n", map->size); return R_FALSE; } r_io_write_at (core->io, map->addr, (const ut8*)buf, sz); if (sz != map->size) eprintf ("File size differs from region size (%d vs %"PFMT64d")\n", sz, map->size); eprintf ("Loaded %d bytes into the map region at 0x%08"PFMT64x"\n", sz, map->addr); free (buf); return R_TRUE; } } eprintf ("No debug region found here\n"); return R_FALSE; case 'i': { // Move to a separate function RCoreBinFilter filter; const char *libname = NULL, *symname = NULL; char *ptr = strdup (r_str_trim_head ((char*)input+2)); int i; ut64 baddr; addr = 0LL; i = r_str_word_set0 (ptr); switch (i) { case 2: // get symname symname = r_str_word_get0 (ptr, 1); case 1: // get addr|libname addr = r_num_math (core->num, r_str_word_get0 (ptr, 0)); if (!addr) libname = r_str_word_get0 (ptr, 0); } r_debug_map_sync (core->dbg); // update process memory maps r_list_foreach (core->dbg->maps, iter, map) { if ((addr != -1 && (addr >= map->addr && addr < map->addr_end)) || (libname != NULL && (strstr (map->name, libname)))) { RBinObject *o = core->bin->cur->o; filter.offset = 0LL; filter.name = (char *)symname; baddr = o->baddr; o->baddr = map->addr; r_core_bin_info (core, R_CORE_BIN_ACC_SYMBOLS, (input[1]=='*'), R_TRUE, &filter, 0); o->baddr = baddr; break; } } free (ptr); } break; case ' ': { char *p; int size; p = strchr (input+2, ' '); if (p) { *p++ = 0; addr = r_num_math (core->num, input+1); size = r_num_math (core->num, p); r_debug_map_alloc(core->dbg, addr, size); } else { eprintf ("Usage: dm addr size\n"); return R_FALSE; } } break; case '-': addr = r_num_math (core->num, input+2); r_list_foreach (core->dbg->maps, iter, map) { if (addr >= map->addr && addr < map->addr_end) { r_debug_map_dealloc(core->dbg, map); r_debug_map_sync (core->dbg); return R_TRUE; } } eprintf ("The address doesn't match with any map.\n"); break; case '\0': case '*': case 'j': r_debug_map_sync (core->dbg); // update process memory maps r_debug_map_list (core->dbg, core->offset, input[0]); break; } return R_TRUE; } static void cmd_debug_reg(RCore *core, const char *str) { int size, i, type = R_REG_TYPE_GPR; int bits = (core->dbg->bits & R_SYS_BITS_64)? 64: 32; struct r_reg_item_t *r; const char *name; char *arg; switch (str[0]) { case '?': if (str[1]) { ut64 off; r_debug_reg_sync (core->dbg, -1, 0); //R_REG_TYPE_GPR, R_FALSE); off = r_debug_reg_get (core->dbg, str+1); // r = r_reg_get (core->dbg->reg, str+1, 0); // if (r == NULL) eprintf ("Unknown register (%s)\n", str+1); r_cons_printf ("0x%08"PFMT64x"\n", off); //r_reg_get_value (core->dbg->reg, r)); } else r_cons_printf ( "|Usage: dr Registers commands\n" "| dr Show 'gpr' registers\n" "| dr 16 Show 16 bit registers\n" "| dr 32 Show 32 bit registers\n" "| dr all Show all registers\n" "| dr Show flag registers\n" "| dr = Set register value\n" "| dr= Show registers in columns\n" "| dr? Show value of eax register\n" "| drb [type] Display hexdump of gpr arena (WIP)\n" "| drc [name] Related to conditional flag registers\n" "| drd Show only different registers\n" "| drn Get regname for pc,sp,bp,a0-3,zf,cf,of,sg\n" "| dro Show previous (old) values of registers\n" "| drp Load register metadata file\n" "| drp Display current register profile\n" "| drs? Stack register states\n" "| drt Show all register types\n" "| .dr* Include common register values in flags\n" "| .dr- Unflag all registers\n"); // TODO: 'drs' to swap register arenas and display old register valuez break; case 'b': { // WORK IN PROGRESS // DEBUG COMMAND int len; const ut8 *buf = r_reg_get_bytes (core->dbg->reg, R_REG_TYPE_GPR, &len); //r_print_hexdump (core->print, 0LL, buf, len, 16, 16); r_print_hexdump (core->print, 0LL, buf, len, 32, 4); } break; case 'c': // TODO: set flag values with drc zf=1 { RRegItem *r; const char *name = str+1; while (*name==' ') name++; if (*name && name[1]) { r = r_reg_cond_get (core->dbg->reg, name); if (r) { r_cons_printf ("%s\n", r->name); } else { int id = r_reg_cond_from_string (name); RRegFlags* rf = r_reg_cond_retrieve (core->dbg->reg, NULL); if (rf) { int o = r_reg_cond_bits (core->dbg->reg, id, rf); core->num->value = o; // ORLY? r_cons_printf ("%d\n", o); free (rf); } else eprintf ("unknown conditional or flag register\n"); } } else { RRegFlags *rf = r_reg_cond_retrieve (core->dbg->reg, NULL); if (rf) { r_cons_printf ("| s:%d z:%d c:%d o:%d p:%d\n", rf->s, rf->z, rf->c, rf->o, rf->p); if (*name=='=') { for (i=0; idbg->reg, i, rf)); } r_cons_newline (); } else { for (i=0; idbg->reg, i, rf), r_reg_cond_to_string (i)); } } free (rf); } } } break; case 'x': switch (str[1]) { case '-': r_debug_reg_sync (core->dbg, R_REG_TYPE_DRX, R_FALSE); r_debug_drx_unset (core->dbg, atoi (str+2)); r_debug_reg_sync (core->dbg, R_REG_TYPE_DRX, R_TRUE); break; case ' ': { char *s = strdup (str+2); char sl, n, rwx; int len; ut64 off; sl = r_str_word_set0 (s); if (sl == 4) { #define ARG(x) r_str_word_get0(s,x) n = (char)r_num_math (core->num, ARG(0)); off = r_num_math (core->num, ARG(1)); len = (int)r_num_math (core->num, ARG(2)); rwx = (char)r_str_rwx (ARG(3)); if (len== -1) { r_debug_reg_sync (core->dbg, R_REG_TYPE_DRX, R_FALSE); r_debug_drx_set (core->dbg, n, 0, 0, 0, 0); r_debug_reg_sync (core->dbg, R_REG_TYPE_DRX, R_TRUE); } else { r_debug_reg_sync (core->dbg, R_REG_TYPE_DRX, R_FALSE); r_debug_drx_set (core->dbg, n, off, len, rwx, 0); r_debug_reg_sync (core->dbg, R_REG_TYPE_DRX, R_TRUE); } } else eprintf ("|Usage: drx N [address] [length] [rwx]\n"); free (s); } break; case '\0': r_debug_reg_sync (core->dbg, R_REG_TYPE_DRX, R_FALSE); r_debug_drx_list (core->dbg); break; default: r_cons_printf ( "|Usage: drx Hardware breakpoints commands\n" "| drx List all (x86?) hardware breakpoints\n" "| drx
Modify hardware breakpoint\n" "| drx- Clear hardware breakpoint\n"); break; } break; case 's': switch (str[1]) { case '-': r_reg_arena_pop (core->dbg->reg); // restore debug registers if in debugger mode r_debug_reg_sync (core->dbg, 0, 1); break; case '+': r_reg_arena_push (core->dbg->reg); break; case '?': r_cons_printf ( "|Usage: drs Register states commands\n" "| drs List register stack\n" "| drs+ Push register state\n" "| drs- Pop register state\n"); break; default: r_cons_printf ("%d\n", r_list_length ( core->dbg->reg->regset[0].pool)); break; } break; case 'p': if (!str[1]) { if (core->dbg->reg->reg_profile_str) { //core->anal->reg = core->dbg->reg; r_cons_printf ("%s\n", core->dbg->reg->reg_profile_str); //r_cons_printf ("%s\n", core->anal->reg->reg_profile); } else eprintf ("No register profile defined. Try 'dr.'\n"); } else r_reg_set_profile (core->dbg->reg, str+2); break; case 't': for (i=0; (name=r_reg_get_type (i)); i++) r_cons_printf ("%s\n", name); break; case 'n': name = r_reg_get_name (core->dbg->reg, r_reg_get_name_idx (str+2)); if (name && *name) r_cons_printf ("%s\n", name); else eprintf ("Oops. try drn [pc|sp|bp|a0|a1|a2|a3|zf|sf|nf|of]\n"); break; case 'd': r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, bits, 3); // XXX detect which one is current usage break; case 'o': r_reg_arena_swap (core->dbg->reg, R_FALSE); r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, bits, 0); // XXX detect which one is current usage r_reg_arena_swap (core->dbg->reg, R_FALSE); break; case '=': if (r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE)) { r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, bits, 2); // XXX detect which one is current usage } //else eprintf ("Cannot retrieve registers from pid %d\n", core->dbg->pid); break; case '*': if (r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE)) r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, bits, '*'); break; case 'j': case '\0': if (r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE)) { r_debug_reg_list (core->dbg, R_REG_TYPE_GPR, bits, str[0]); } else eprintf ("Cannot retrieve registers from pid %d\n", core->dbg->pid); break; case ' ': arg = strchr (str+1, '='); if (arg) { *arg = 0; r = r_reg_get (core->dbg->reg, str+1, -1); //R_REG_TYPE_GPR); if (r) { r_cons_printf ("0x%08"PFMT64x" ->", str, r_reg_get_value (core->dbg->reg, r)); r_reg_set_value (core->dbg->reg, r, r_num_math (core->num, arg+1)); r_debug_reg_sync (core->dbg, -1, R_TRUE); r_cons_printf ("0x%08"PFMT64x"\n", r_reg_get_value (core->dbg->reg, r)); } else eprintf ("Unknown register '%s'\n", str+1); return; } size = atoi (str+1); if (size==0) { arg = strchr (str+1, ' '); if (arg && size==0) { *arg='\0'; size = atoi (arg); } else size = bits; type = r_reg_type_by_name (str+1); } if (type != R_REG_TYPE_LAST) { r_debug_reg_sync (core->dbg, type, R_FALSE); r_debug_reg_list (core->dbg, type, size, str[0]=='*'); } else eprintf ("cmd_debug_reg: Unknown type\n"); } } static int checkbpcallback(RCore *core) { ut64 pc = r_debug_reg_get (core->dbg, "pc"); RBreakpointItem *bpi = r_bp_get (core->dbg->bp, pc); if (bpi) { if (bpi->data) r_core_cmd (core, bpi->data, 0); return R_TRUE; } return R_FALSE; } static int bypassbp(RCore *core) { RBreakpointItem *bpi; ut64 addr; r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE); addr = r_debug_reg_get (core->dbg, "pc"); bpi = r_bp_get (core->dbg->bp, addr); if (!bpi) return R_FALSE; /* XXX 2 if libr/debug/debug.c:226 is enabled */ r_debug_step (core->dbg, 1); return R_TRUE; } static void static_debug_stop(void *u) { RDebug *dbg = (RDebug *)u; r_debug_stop (dbg); } static void r_core_cmd_bp(RCore *core, const char *input) { int i, hwbp = r_config_get_i (core->config, "dbg.hwbp"); RDebugFrame *frame; RListIter *iter; const char *p; RList *list; ut64 addr; switch (input[1]) { case 't': addr = UT64_MAX; if (input[2]==' ' && input[3]) addr = r_num_math (core->num, input+2); i = 0; list = r_debug_frames (core->dbg, addr); r_list_foreach (list, iter, frame) { r_cons_printf ("%d 0x%08"PFMT64x" %d\n", i++, frame->addr, frame->size); } r_list_purge (list); break; case '*': r_bp_list (core->dbg->bp, 1); break; case '\0': r_bp_list (core->dbg->bp, 0); break; case '-': if (input[2] == '*') { r_bp_del_all (core->dbg->bp); } else r_bp_del (core->dbg->bp, r_num_math (core->num, input+2)); break; case 'c': addr = r_num_math (core->num, input+2); RBreakpointItem *bpi = r_bp_get (core->dbg->bp, addr); if (bpi) { char *arg = strchr (input+2, ' '); if (arg) arg = strchr (arg+1, ' '); if (arg) { free (bpi->data); bpi->data = strdup (arg+1); } else { free (bpi->data); bpi->data = NULL; } } else eprintf ("No breakpoint defined at 0x%08"PFMT64x"\n", addr); break; case 's': addr = r_num_math (core->num, input+2); RBreakpointItem *bp = r_bp_get (core->dbg->bp, addr); if (bp) { //bp->enabled = !bp->enabled; r_bp_del (core->dbg->bp, addr); } else { if (hwbp) bp = r_bp_add_hw (core->dbg->bp, addr, 1, R_BP_PROT_EXEC); else bp = r_bp_add_sw (core->dbg->bp, addr, 1, R_BP_PROT_EXEC); if (!bp) eprintf ("Cannot set breakpoint (%s)\n", input+2); } r_bp_enable (core->dbg->bp, r_num_math (core->num, input+2), 0); break; case 'e': r_bp_enable (core->dbg->bp, r_num_math (core->num, input+2), 1); break; case 'd': r_bp_enable (core->dbg->bp, r_num_math (core->num, input+2), 0); break; case 'h': if (input[2]==' ') { if (!r_bp_use (core->dbg->bp, input+3)) eprintf ("Invalid name: '%s'.\n", input+3); } else r_bp_plugin_list (core->dbg->bp); break; case ' ': for (p=input+1; *p==' ';p++); if (*p == '-') { r_bp_del (core->dbg->bp, r_num_math (core->num, p+1)); } else { addr = r_num_math (core->num, input+2); if (hwbp) bp = r_bp_add_hw (core->dbg->bp, addr, 1, R_BP_PROT_EXEC); else bp = r_bp_add_sw (core->dbg->bp, addr, 1, R_BP_PROT_EXEC); if (!bp) eprintf ("Cannot set breakpoint (%s)\n", input+2); } break; case '?': default: r_cons_printf ( "|Usage: db Breakpoints commands\n" "| db List breakpoints\n" "| db sym.main Add breakpoint into sym.main\n" "| db
Add breakpoint\n" "| db -
Remove breakpoint\n" // "dbi 0x848 ecx=3 ; stop execution when condition matches\n" "| dbc
Run command when breakpoint is hit\n" "| dbd
Disable breakpoint\n" "| dbe
Enable breakpoint\n" "| dbh x86 Set/list breakpoint plugin handlers\n" "| dbs
Toggle breakpoint\n" "|Unrelated:\n" "| dbt [ebp] Debug backtrace\n"); break; } } static void r_core_debug_trace_calls (RCore *core) { int n = 0, t = core->dbg->trace->enabled; /*RGraphNode *gn;*/ core->dbg->trace->enabled = 0; r_graph_plant (core->dbg->graph); r_cons_break (static_debug_stop, core->dbg); r_reg_arena_swap (core->dbg->reg, R_TRUE); for (;;) { ut8 buf[32]; ut64 addr; RAnalOp aop; if (r_cons_singleton ()->breaked) break; r_debug_step (core->dbg, 1); r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE); addr = r_debug_reg_get (core->dbg, "pc"); r_io_read_at (core->io, addr, buf, sizeof (buf)); r_anal_op (core->anal, &aop, addr, buf, sizeof (buf)); eprintf (" %d %"PFMT64x"\r", n++, addr); switch (aop.type) { case R_ANAL_OP_TYPE_UCALL: // store regs // step into // get pc r_debug_step (core->dbg, 1); r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE); addr = r_debug_reg_get (core->dbg, "pc"); eprintf ("0x%08"PFMT64x" ucall. computation may fail\n", addr); r_graph_push (core->dbg->graph, addr, NULL); // TODO: push pc+aop.length into the call path stack break; case R_ANAL_OP_TYPE_CALL: r_graph_push (core->dbg->graph, addr, NULL); break; case R_ANAL_OP_TYPE_RET: #if 0 // TODO: we must store ret value for each call in the graph path to do this check r_debug_step (core->dbg, 1); r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE); addr = r_debug_reg_get (core->dbg, "pc"); // TODO: step into and check return address if correct // if not correct we are hijacking the control flow (exploit!) #endif /*gn =*/ r_graph_pop (core->dbg->graph); #if 0 if (addr != gn->addr) { eprintf ("Oops. invalid return address 0x%08"PFMT64x "\n0x%08"PFMT64x"\n", addr, gn->addr); } #endif break; } if (checkbpcallback (core)) { eprintf ("Interrupted by a breakpoint\n"); break; } } r_graph_traverse (core->dbg->graph); core->dbg->trace->enabled = t; r_cons_break_end(); } static void r_core_debug_kill (RCore *core, const char *input) { if (!input || *input=='?') { if (input && input[1]) { const char *signame, *arg = input+1; int signum = atoi (arg); if (signum>0) { signame = r_debug_signal_resolve_i (core->dbg, signum); if (signame) r_cons_printf ("%s\n", signame); } else { signum = r_debug_signal_resolve (core->dbg, arg); if (signum>0) r_cons_printf ("%d\n", signum); } } else eprintf ( "|Usage: dk Signal commands\n" "| dk List all signal handlers of child process\n" "| dk Send KILL signal to child\n" "| dk =1 Set signal handler for in child\n" "| dk? Name/signum resolver\n" "| dko sc On signal Skip and CONT (default stop, always trace)\n"); } else if (*input=='o') { char *p, *name = strdup (input+2); p = strchr (name, ' '); if (p) { int signum = atoi (name); *p++ = 0; // Actions: // - pass // - trace // - stop if (signum<1) signum = r_debug_signal_resolve (core->dbg, name); if (signum>0) { int sigopt = 0; if (strchr (p, 's')) sigopt |= R_DBG_SIGNAL_SKIP; if (strchr (p, 'c')) sigopt |= R_DBG_SIGNAL_CONT; r_debug_signal_setup (core->dbg, signum, sigopt); } else { eprintf ("Invalid signal\n"); } } else { eprintf ("|Usage: dko SIGNAL sc\n" "| 'SIGNAL' can be a number or a string that resolves with dk?..\n" "| 'sc' stands for SKIP and CONT\n"); } free (name); } else if (!*input) { r_debug_signal_list (core->dbg); #if 0 RListIter *iter; RDebugSignal *ds; eprintf ("TODO: list signal handlers of child\n"); RList *list = r_debug_kill_list (core->dbg); r_list_foreach (list, iter, ds) { // TODO: resolve signal name by number and show handler offset eprintf ("--> %d\n", ds->num); } r_list_free (list); #endif } else { int sig = atoi (input); char *p = strchr (input, '='); if (p) { r_debug_kill_setup (core->dbg, sig, r_num_math (core->num, p+1)); } else { r_debug_kill (core->dbg, core->dbg->pid, core->dbg->tid, sig); } } } static int cmd_debug(void *data, const char *input) { RCore *core = (RCore *)data; int i, times, sig, follow=0; ut64 addr; char *ptr; switch (input[0]) { case 't': switch (input[1]) { case '?': r_cons_printf ( "|Usage: dt Trace commands\n" "| dtc Trace call/ret\n" "| dtg Graph call/ret trace\n" "| dtr Reset traces (instruction//cals)\n"); break; case 'c': if (r_debug_is_dead (core->dbg)) eprintf ("No process to debug."); else r_core_debug_trace_calls (core); break; case 'g': dot_r_graph_traverse (core, core->dbg->graph); break; case 'r': r_graph_reset (core->dbg->graph); r_debug_trace_free (core->dbg); core->dbg->trace = r_debug_trace_new (); break; case '\0': r_debug_trace_list (core->dbg, -1); break; default: eprintf ("Wrong arg. See dt?\n"); break; } break; case 'd': switch (input[1]) { case '\0': r_debug_desc_list (core->dbg, 0); break; case '*': r_debug_desc_list (core->dbg, 1); break; case 's': // r_debug_desc_seek() break; case 'd': // r_debug_desc_dup() break; case 'r': // r_debug_desc_read() break; case 'w': // r_debug_desc_write() break; case '-': // close file //r_core_syscallf (core, "close", "%d", atoi (input+2)); r_core_cmdf (core, "dis close %d", atoi (input+2)); // TODO: run break; case ' ': // TODO: handle read, readwrite, append r_core_syscallf (core, "open", "%s, %d, %d", input+2, 2, 0644); // open file break; case '?': default: r_cons_printf ( "|Usage: dd Descriptors commands\n" "| dd List file descriptors\n" "| dd Open and map that file into the UI\n" "| dd- Close stdout fd\n" "| dd* List file descriptors (in radare commands)\n"); break; } break; case 's': times = atoi (input+2); if (times<1) times = 1; switch (input[1]) { case '?': r_cons_printf ( "|Usage: ds Step commands\n" "| ds Step one instruction\n" "| ds Step instructions\n" "| dsf Step until end of frame\n" "| dsi Continue until condition matches\n" "| dsl Step one source line\n" "| dsl Step source lines\n" "| dso Step over instructions\n" "| dsp Step into program (skip libs)\n" "| dss Skip step instructions\n" "| dsu
Step until address\n" ); break; case 'i': if (input[2] == ' ') { int n = 0; r_cons_break (static_debug_stop, core->dbg); do { if (r_cons_singleton ()->breaked) break; r_debug_step (core->dbg, 1); if (r_debug_is_dead (core->dbg)) break; if (checkbpcallback (core)) { eprintf ("Interrupted by a breakpoint\n"); break; } r_core_cmd0 (core, ".dr*"); n++; } while (!r_num_conditional (core->num, input+3)); eprintf ("Stopped after %d instructions\n", n); } else { eprintf ("Missing argument\n"); } break; case 'f': step_until_eof (core); break; case 'u': r_reg_arena_swap (core->dbg->reg, R_TRUE); step_until (core, r_num_math (core->num, input+2)); // XXX dupped by times break; case 'p': r_reg_arena_swap (core->dbg->reg, R_TRUE); for (i=0; idbg, R_REG_TYPE_GPR, R_FALSE); addr = r_debug_reg_get (core->dbg, "pc"); r_io_read_at (core->io, addr, buf, sizeof (buf)); r_anal_op (core->anal, &aop, addr, buf, sizeof (buf)); if (aop.type == R_ANAL_OP_TYPE_CALL) { RIOSection *s = r_io_section_vget (core->io, aop.jump); if (!s) { r_debug_step_over (core->dbg, times); continue; } } r_debug_step (core->dbg, 1); if (checkbpcallback (core)) { eprintf ("Interrupted by a breakpoint\n"); break; } } break; case 's': { ut64 addr = r_debug_reg_get (core->dbg, "pc"); r_reg_arena_swap (core->dbg->reg, R_TRUE); for (i=0; idbg, R_REG_TYPE_GPR, R_FALSE); r_io_read_at (core->io, addr, buf, sizeof (buf)); r_anal_op (core->anal, &aop, addr, buf, sizeof (buf)); if (aop.jump != UT64_MAX && aop.fail != UT64_MAX) { eprintf ("Don't know how to skip this instruction\n"); break; } addr += aop.size; } r_debug_reg_set (core->dbg, "pc", addr); } break; case 'o': r_reg_arena_swap (core->dbg->reg, R_TRUE); r_debug_step_over (core->dbg, times); if (checkbpcallback (core)) { eprintf ("Interrupted by a breakpoint\n"); break; } break; case 'l': r_reg_arena_swap (core->dbg->reg, R_TRUE); step_line (core, times); break; default: r_reg_arena_swap (core->dbg->reg, R_TRUE); r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE); r_debug_step (core->dbg, times); if (checkbpcallback (core)) { eprintf ("Interrupted by a breakpoint\n"); break; } } follow = r_config_get_i (core->config, "dbg.follow"); break; case 'b': r_core_cmd_bp (core, input); break; case 'H': eprintf ("TODO: transplant process\n"); break; case 'c': // TODO: we must use this for step 'ds' too maybe... r_cons_break (static_debug_stop, core->dbg); switch (input[1]) { case '?': eprintf( "|Usage: dc Execution continuation commands\n" "| dc? Show this help\n" "| dc Continue execution of all children\n" "| dc Continue execution of pid\n" "| dc[-pid] Stop execution of pid\n" "| dca [sym] [sym]. Continue at every hit on any given symbol\n" "| dcc Continue until call (use step into)\n" "| dcf Continue until fork (TODO)\n" "| dck Continue sending signal to process\n" "| dco Step over instructions\n" "| dcp Continue until program code (mapped io section)\n" "| dcr Continue until ret (uses step over)\n" "| dcs Continue until syscall\n" "| dct Traptrace from curseek to len, no argument to list\n" "| dcu [addr] Continue until address\n" "| dcu
[end] Continue until given address range\n" "|TODO: dcu/dcr needs dbg.untilover=true??\n" "|TODO: same for only user/libs side, to avoid steping into libs\n" "|TODO: support for threads?\n" ); break; case 'a': eprintf ("TODO: dca\n"); break; case 'c': r_reg_arena_swap (core->dbg->reg, R_TRUE); r_debug_continue_until_optype (core->dbg, R_ANAL_OP_TYPE_CALL, 0); checkbpcallback (core); break; case 'r': r_reg_arena_swap (core->dbg->reg, R_TRUE); r_debug_continue_until_optype (core->dbg, R_ANAL_OP_TYPE_RET, 1); checkbpcallback (core); break; case 'k': // select pid and r_debug_continue_kill (core->dbg, r_reg_arena_swap (core->dbg->reg, R_TRUE); ptr = strchr (input+3, ' '); if (ptr) { bypassbp (core); int old_pid = core->dbg->pid; int old_tid = core->dbg->tid; int pid = atoi (ptr+1); int tid = pid; // XXX *ptr = 0; r_debug_select (core->dbg, pid, tid); r_debug_continue_kill (core->dbg, atoi (input+2)); r_debug_select (core->dbg, old_pid, old_tid); } else r_debug_continue_kill (core->dbg, atoi (input+2)); checkbpcallback (core); break; case 's': if (input[2]==' ') { sig = r_num_math (core->num, input+3); if (sig <= 0) { sig = r_syscall_get_num (core->anal->syscall, input+3); if (sig == -1) { eprintf ("Unknown syscall number\n"); return 0; } } eprintf ("Running child until syscall %d\n", sig); r_reg_arena_swap (core->dbg->reg, R_TRUE); r_debug_continue_syscall (core->dbg, sig); checkbpcallback (core); } else eprintf ("|Usage: dcs [syscall-name-or-number]\n"); break; case 'p': { // XXX: this is very slow RIOSection *s; ut64 pc; int n = 0; int t = core->dbg->trace->enabled; core->dbg->trace->enabled = 0; r_cons_break (static_debug_stop, core->dbg); do { r_debug_step (core->dbg, 1); r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE); pc = r_debug_reg_get (core->dbg, "pc"); eprintf (" %d %"PFMT64x"\r", n++, pc); s = r_io_section_vget (core->io, pc); if (r_cons_singleton ()->breaked) break; } while (!s); eprintf ("\n"); core->dbg->trace->enabled = t; r_cons_break_end(); return 1; } case 'u': if (input[2] != ' ') { eprintf ("|Usage: dcu
\n"); return 1; } ptr = strchr (input+3, ' '); // TODO : handle ^C here if (ptr) { // TODO: put '\0' in *ptr to avoid ut64 from, to, pc; from = r_num_math (core->num, input+3); to = r_num_math (core->num, ptr+1); do { r_debug_step (core->dbg, 1); r_debug_reg_sync (core->dbg, R_REG_TYPE_GPR, R_FALSE); pc = r_debug_reg_get (core->dbg, "pc"); eprintf ("Continue 0x%08"PFMT64x" > 0x%08"PFMT64x" < 0x%08"PFMT64x"\n", from, pc, to); } while (pc < from || pc > to); return 1; } addr = r_num_math (core->num, input+2); if (addr) { eprintf ("Continue until 0x%08"PFMT64x"\n", addr); bypassbp (core); r_reg_arena_swap (core->dbg->reg, R_TRUE); r_bp_add_sw (core->dbg->bp, addr, 1, R_BP_PROT_EXEC); r_debug_continue (core->dbg); checkbpcallback (core); r_bp_del (core->dbg->bp, addr); } else eprintf ("Cannot continue until address 0\n"); break; case ' ': { int old_pid = core->dbg->pid; int pid = atoi (input+2); bypassbp (core); r_reg_arena_swap (core->dbg->reg, R_TRUE); r_debug_select (core->dbg, pid, core->dbg->tid); r_debug_continue (core->dbg); r_debug_select (core->dbg, old_pid, core->dbg->tid); checkbpcallback (core); } break; case 't': cmd_debug_backtrace (core, input+2); break; default: bypassbp (core); r_reg_arena_swap (core->dbg->reg, R_TRUE); r_debug_continue (core->dbg); checkbpcallback (core); } follow = r_config_get_i (core->config, "dbg.follow"); r_cons_break_end(); break; case 'm': cmd_debug_map (core, input+1); break; case 'r': cmd_debug_reg (core, input+1); //r_core_cmd (core, "|reg", 0); break; case 'p': cmd_debug_pid (core, input); break; case 'h': if (input[1]==' ') r_debug_use (core->dbg, input+2); else r_debug_plugin_list (core->dbg); break; case 'i': eprintf ("TODO: info\n"); break; case 'x': switch (input[1]) { case 'a': { RAsmCode *acode; r_asm_set_pc (core->assembler, core->offset); acode = r_asm_massemble (core->assembler, input+2); if (acode && *acode->buf_hex) { r_reg_arena_push (core->dbg->reg); r_debug_execute (core->dbg, acode->buf, acode->len, 0); r_reg_arena_pop (core->dbg->reg); r_asm_code_free (acode); } } break; case 's': // XXX: last byte fails (ret) should not be generated r_core_cmdf (core, "dir `gs %s`", input+2); break; case 'r': r_reg_arena_push (core->dbg->reg); if (input[2]==' ') { ut8 bytes[4096]; int bytes_len = r_hex_str2bin (input+2, bytes); r_debug_execute (core->dbg, bytes, bytes_len, 0); } r_reg_arena_pop (core->dbg->reg); break; case ' ': { ut8 bytes[4096]; int bytes_len = r_hex_str2bin (input+2, bytes); if (bytes_len>0) r_debug_execute (core->dbg, bytes, bytes_len, 0); } break; default: r_cons_printf ( "|Usage: dx Code injection commands\n" "| dx ... Inject opcodes\n" "| Example: dx 9090 Inject two x86 nop\n" "| \"dia mov eax,6;mov ebx,0;int 0x80\" Inject and restore state\n" "| dxr ... Inject opcodes and restore state\n" "| dxs write 1, 0x8048, 12 Syscall injection (see gs)\n"); break; } break; case 'o': r_core_file_reopen (core, input[1]? input+2: NULL, 0); break; case 'w': r_cons_break (static_debug_stop, core->dbg); for (;!r_cons_singleton ()->breaked;) { int pid = atoi (input+1); //int opid = core->dbg->pid = pid; int res = r_debug_kill (core->dbg, pid, 0, 0); if (!res) break; r_sys_usleep (200); } r_cons_break_end(); break; case 'k': r_core_debug_kill (core, input+1); break; default: r_cons_printf ( "|Usage: d Debug commands\n" "| db[?] Breakpoints commands\n" "| dbt Display backtrace\n" "| dc[?] Continue execution\n" "| dd[?] File descriptors (!fd in r1)\n" "| dh [handler] List or set debugger handler\n" "| dH [handler] Transplant process to a new handler\n" "| di[?] Show debugger backend information (See dh)\n" "| dk[?] List, send, get, set, signal handlers of child\n" "| dm[?] Show memory maps\n" "| do[?] Open process (reload, alias for 'oo')\n" "| dp[?] List, attach to process or thread id\n" "| dr[?] Cpu registers\n" "| ds[?] Step, over, source line\n" "| dt[?] Display instruction traces (dtr=reset)\n" "| dw Block prompt until pid dies\n" "| dx[?] Inject code on running process and execute it (See gs)\n"); break; } if (follow>0) { ut64 pc = r_debug_reg_get (core->dbg, "pc"); if ((pcoffset) || (pc > (core->offset+follow))) r_core_cmd0 (core, "sr pc"); } return 0; }