R_API int r_debug_esil_stepi (RDebug *d) { RAnalOp op; ut8 obuf[64]; int ret = 1; dbg = d; if (!ESIL) { ESIL = r_anal_esil_new (R_TRUE); // TODO setup something? } r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE); opc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); dbg->iob.read_at (dbg->iob.io, opc, obuf, sizeof (obuf)); //dbg->iob.read_at (dbg->iob.io, npc, buf, sizeof (buf)); //dbg->anal->reg = dbg->reg; // hack ESIL->cb.hook_mem_read = &esilbreak_mem_read; ESIL->cb.hook_mem_write = &esilbreak_mem_write; ESIL->cb.hook_reg_read = &esilbreak_reg_read; ESIL->cb.hook_reg_write = &esilbreak_reg_write; if (prestep) { // required when a exxpression is like <= == .. // otherwise it will stop at the next instruction if (r_debug_step (dbg, 1)<1) { eprintf ("Step failed\n"); return 0; } r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE); // npc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); } if (r_anal_op (dbg->anal, &op, opc, obuf, sizeof (obuf))) { if (esilbreak_check_pc (dbg, opc)) { eprintf ("STOP AT 0x%08"PFMT64x"\n", opc); ret = 0; } else { r_anal_esil_set_pc (ESIL, opc); eprintf ("0x%08"PFMT64x" %s\n", opc, R_STRBUF_SAFEGET (&op.esil)); (void)r_anal_esil_parse (ESIL, R_STRBUF_SAFEGET (&op.esil)); //r_anal_esil_dumpstack (ESIL); r_anal_esil_stack_free (ESIL); ret = 1; } } if (!prestep) { if (ret && !has_match) { if (r_debug_step (dbg, 1)<1) { eprintf ("Step failed\n"); return 0; } r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE); // npc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); } } return ret; }
R_API int r_debug_continue_until_optype(RDebug *dbg, int type, int over) { int ret, n = 0; ut64 pc, buf_pc = 0; RAnalOp op; ut8 buf[DBG_BUF_SIZE]; if (r_debug_is_dead (dbg)) { return R_FALSE; } if (!dbg->anal || !dbg->reg) { eprintf ("Undefined pointer at dbg->anal\n"); return R_FALSE; } r_debug_step (dbg, 1); r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE); // Initial refill buf_pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); dbg->iob.read_at (dbg->iob.io, buf_pc, buf, sizeof (buf)); // step first, we dont want to check current optype for (;;) { r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE); pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); // Try to keep the buffer full if (pc - buf_pc > sizeof (buf)) { buf_pc = pc; dbg->iob.read_at (dbg->iob.io, buf_pc, buf, sizeof (buf)); } // Analyze the opcode if (!r_anal_op (dbg->anal, &op, pc, buf + (pc - buf_pc), sizeof (buf) - (pc - buf_pc))) { eprintf ("Decode error at %"PFMT64x"\n", pc); return R_FALSE; } if (op.type == type) break; // Step over and repeat ret = over ? r_debug_step_over (dbg, 1) : r_debug_step (dbg, 1); if (!ret) { eprintf ("r_debug_step: failed\n"); break; } n++; } return n; }
R_API int r_debug_step_over(RDebug *dbg, int steps) { RAnalOp op; ut8 buf[64]; int ret = -1; if (r_debug_is_dead (dbg)) return R_FALSE; if (dbg->h && dbg->h->step_over) { if (steps<1) steps = 1; while (steps--) if (!dbg->h->step_over (dbg)) return R_FALSE; return R_TRUE; } if (dbg->anal && dbg->reg) { ut64 pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); dbg->iob.read_at (dbg->iob.io, pc, buf, sizeof (buf)); r_anal_op (dbg->anal, &op, pc, buf, sizeof (buf)); if (op.type & R_ANAL_OP_TYPE_CALL || op.type & R_ANAL_OP_TYPE_UCALL) { ut64 bpaddr = pc + op.length; r_bp_add_sw (dbg->bp, bpaddr, 1, R_BP_PROT_EXEC); ret = r_debug_continue (dbg); r_bp_del (dbg->bp, bpaddr); } else { ret = r_debug_step (dbg, 1); } } else eprintf ("Undefined debugger backend\n"); return ret; }
R_API int r_debug_continue_syscalls(RDebug *dbg, int *sc, int n_sc) { int i, reg, ret = R_FALSE; if (!dbg || !dbg->h || r_debug_is_dead (dbg)) return R_FALSE; if (!dbg->h->contsc) { /* user-level syscall tracing */ r_debug_continue_until_optype (dbg, R_ANAL_OP_TYPE_SWI, 0); return show_syscall (dbg, "a0"); } if (!r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE)) { eprintf ("--> cannot read registers\n"); return -1; } { int err; reg = (int)r_debug_reg_get_err (dbg, "sn", &err); if (err) { eprintf ("Cannot find 'sn' register for current arch-os.\n"); return -1; } } for (;;) { if (r_cons_singleton()->breaked) break; #if __linux__ // step is needed to avoid dupped contsc results r_debug_step (dbg, 1); #endif dbg->h->contsc (dbg, dbg->pid, 0); // TODO handle return value // wait until continuation r_debug_wait (dbg); if (!r_debug_reg_sync (dbg, R_REG_TYPE_GPR, R_FALSE)) { eprintf ("--> cannot sync regs, process is probably dead\n"); return -1; } reg = show_syscall (dbg, "sn"); if (n_sc == -1) continue; if (n_sc == 0) { break; } for (i=0; i<n_sc; i++) { if (sc[i] == reg) return reg; } // TODO: must use r_core_cmd(as)..import code from rcore } return ret; }
R_API int r_debug_continue_until(RDebug *dbg, ut64 addr) { // TODO: use breakpoint+continue... more efficient RRegItem *ripc = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_PC], R_REG_TYPE_GPR); int n = 0; ut64 pc = r_reg_get_value (dbg->reg, ripc); while (pc != addr && !r_debug_is_dead (dbg)) { r_debug_step (dbg, 1); // TODO: obey breakpoints too? /* TODO: check if the debugger stops at the right address */ pc = r_reg_get_value (dbg->reg, ripc); n++; } return n; }
/* * replace breakpoints before we continue execution * * this is called from r_debug_step_hard or r_debug_continue_kill * * this is a trick process because of breakpoints/tracepoints. * * if a breakpoint was just hit, we need step over that instruction before * allowing the caller to proceed as desired. * * if the user wants to step, the single step here does the job. */ static int r_debug_recoil(RDebug *dbg, RDebugRecoilMode rc_mode) { /* if bp_addr is not set, we must not have actually hit a breakpoint */ if (!dbg->reason.bp_addr) { return r_debug_bps_enable (dbg); } /* don't do anything if we already are recoiling */ if (dbg->recoil_mode != R_DBG_RECOIL_NONE) { /* the first time recoil is called with swstep, we just need to * look up the bp and step past it. * the second time it's called, the new sw breakpoint should exist * so we just restore all except what we originally hit and reset. */ if (dbg->swstep) { if (!r_bp_restore_except (dbg->bp, true, dbg->reason.bp_addr)) { return false; } return true; } /* otherwise, avoid recursion */ return true; } /* we have entered recoil! */ dbg->recoil_mode = rc_mode; /* step over the place with the breakpoint and let the caller resume */ if (r_debug_step (dbg, 1) != 1) { return false; } /* when stepping away from a breakpoint during recoil in stepping mode, * the r_debug_bp_hit function tells us that it was called * innapropriately by setting bp_addr back to zero. however, recoil_mode * is still set. we use this condition to know not to proceed but * pretend as if we had. */ if (!dbg->reason.bp_addr && dbg->recoil_mode == R_DBG_RECOIL_STEP) { /* restore all sw breakpoints. we are about to step/continue so these need * to be in place. */ if (!r_bp_restore (dbg->bp, true)) { return false; } return true; } return r_debug_bps_enable (dbg); }
R_API int r_debug_continue_until(struct r_debug_t *dbg, ut64 addr) { // TODO: use breakpoint+continue... more efficient int n = 0; ut64 pc = 0; if (r_debug_is_dead (dbg)) return R_FALSE; do { if (pc !=0) r_debug_step (dbg, 1); n++; } while (pc != addr && !r_debug_is_dead (dbg)); return n; //struct r_debug_bp_t *bp = r_debug_bp_add (dbg, addr); //int ret = r_debug_continue(dbg); /* TODO: check if the debugger stops at the right address */ //r_debug_bp_del(dbg, bp); //return -1; }
R_API int r_debug_continue_kill(RDebug *dbg, int sig) { ut64 pc; int retwait, ret = R_FALSE; if (!dbg) return R_FALSE; #if __WINDOWS__ r_cons_break(w32_break_process, dbg); #endif repeat: if (r_debug_is_dead (dbg)) return R_FALSE; if (dbg->h && dbg->h->cont) { r_bp_restore (dbg->bp, R_TRUE); // set sw breakpoints ret = dbg->h->cont (dbg, dbg->pid, dbg->tid, sig); dbg->reason.signum = 0; retwait = r_debug_wait (dbg); #if __WINDOWS__ if (retwait != R_DEBUG_REASON_DEAD) { ret = dbg->tid; } #endif r_bp_restore (dbg->bp, R_FALSE); // unset sw breakpoints //r_debug_recoil (dbg); if (r_debug_recoil (dbg) || (dbg->reason.type == R_DEBUG_REASON_BREAKPOINT)) { /* check if cur bp demands tracing or not */ pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); RBreakpointItem *b = r_bp_get_at (dbg->bp, pc); if (b) { /* check if cur bp demands tracing or not */ if (b->trace) { eprintf("hit tracepoit at: %"PFMT64x"\n",pc); } else { eprintf("hit breakpoint at: %"PFMT64x"\n",pc); } if (dbg->trace->enabled) r_debug_trace_pc (dbg); // TODO: delegate this to RCore.bphit(RCore, RBreakopintItem) if (dbg->corebind.core && dbg->corebind.bphit) { dbg->corebind.bphit (dbg->corebind.core, b); } if (b->trace) { r_debug_step (dbg, 1); goto repeat; } } } #if 0 #if __UNIX__ /* XXX Uh? */ if (dbg->stop_all_threads && dbg->pid>0) r_sandbox_kill (dbg->pid, SIGSTOP); #endif #endif r_debug_select (dbg, dbg->pid, ret); sig = 0; // clear continuation after signal if needed if (retwait == R_DEBUG_REASON_SIGNAL && dbg->reason.signum != -1) { int what = r_debug_signal_what (dbg, dbg->reason.signum); if (what & R_DBG_SIGNAL_CONT) { sig = dbg->reason.signum; eprintf ("Continue into the signal %d handler\n", sig); goto repeat; } else if (what & R_DBG_SIGNAL_SKIP) { // skip signal. requires skipping one instruction ut8 buf[64]; RAnalOp op = {0}; ut64 pc = r_debug_reg_get (dbg, "pc"); dbg->iob.read_at (dbg->iob.io, pc, buf, sizeof (buf)); r_anal_op (dbg->anal, &op, pc, buf, sizeof (buf)); if (op.size>0) { const char *signame = r_debug_signal_resolve_i (dbg, dbg->reason.signum); r_debug_reg_set (dbg, "pc", pc+op.size); eprintf ("Skip signal %d handler %s\n", dbg->reason.signum, signame); goto repeat; } else { ut64 pc = r_debug_reg_get (dbg, "pc"); eprintf ("Stalled with an exception at 0x%08"PFMT64x"\n", pc); } } } } return ret; }
R_API int r_debug_step_over(RDebug *dbg, int steps) { RAnalOp op; ut64 buf_pc, pc; ut8 buf[DBG_BUF_SIZE]; int i; if (r_debug_is_dead (dbg)) return R_FALSE; if (steps < 1) steps = 1; if (dbg->h && dbg->h->step_over) { for (i = 0; i < steps; i++) if (!dbg->h->step_over (dbg)) return R_FALSE; return i; } if (!dbg->anal || !dbg->reg) return R_FALSE; // Initial refill buf_pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); dbg->iob.read_at (dbg->iob.io, buf_pc, buf, sizeof (buf)); for (i = 0; i < steps; i++) { pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); // Try to keep the buffer full if (pc - buf_pc > sizeof (buf)) { buf_pc = pc; dbg->iob.read_at (dbg->iob.io, buf_pc, buf, sizeof (buf)); } // Analyze the opcode if (!r_anal_op (dbg->anal, &op, pc, buf + (pc - buf_pc), sizeof (buf) - (pc - buf_pc))) { eprintf ("Decode error at %"PFMT64x"\n", pc); return R_FALSE; } // Skip over all the subroutine calls if (op.type == R_ANAL_OP_TYPE_CALL || op.type == R_ANAL_OP_TYPE_CCALL || op.type == R_ANAL_OP_TYPE_UCALL || op.type == R_ANAL_OP_TYPE_UCCALL) { // Use op.fail here instead of pc+op.size to enforce anal backends to fill in this field if (!r_debug_continue_until (dbg, op.fail)) { eprintf ("Could not step over call @ 0x%"PFMT64x"\n", pc); return R_FALSE; } } else if ((op.prefix & (R_ANAL_OP_PREFIX_REP | R_ANAL_OP_PREFIX_REPNE | R_ANAL_OP_PREFIX_LOCK))) { //eprintf ("REP: skip to next instruction...\n"); if (!r_debug_continue_until (dbg, pc+op.size)) { eprintf ("step over failed over rep\n"); return R_FALSE; } } else r_debug_step (dbg, 1); } return i; }