Add support for conditional breakpoints running r2 commands

This commit is contained in:
Tiago Gasiba 2016-09-01 19:11:46 +02:00 committed by radare
parent e16773367f
commit f0a2d8657d
10 changed files with 130 additions and 15 deletions

27
doc/debug Normal file
View file

@ -0,0 +1,27 @@
Conditional breakpoints
=========================
conditional breakpoints are implemented in the following way:
- when a breakpoint is hit, the condition is run as a normal command
- if the command returns a value different from zero, execution continue,
- otherwise, execution is stopped at the breakpoint
Examples of conditional breakpoints
======================================
1.) ignore breakpoint at address 0x4000ce for five times:
f times=5
(dec_times,f times=`?vi times-1,?= times)
db 0x4000ce
dbC 0x4000ce .(dec_times)
dc
2.) execute until rax==0x31c0 at address 0x4000ce
e cmd.hitinfo=0
(break_rax,f reg_rax=`dr rax`,f test=`?vi reg_rax-0x31c0`,?= test)
db 0x4000ce
dbC 0x4000ce .(break_rax)
dc
3.) perform a register tracing dump at address 0x4000ce
e cmd.hitinfo=0
(trace_rax,dr rax,?= 1)
db 0x4000ce
dbC 0x4000ce .(trace_rax)
dc > trace.txt

View file

@ -1,5 +1,18 @@
Examples of Macros
--------------------
NOTE: in radare2, do not add a space between the "," and the next
command otherwise you are in for pain...
1.) Hello, world
(hello,?e Hello World)
.(hello)
2.) Looping inside a macro
(loop_macro,f cnt=3,loop:,?e hello `?vi cnt`,f cnt=`?vi cnt-1`,?= cnt,?!(),.loop:)
.(loop_macro)
Backtrace implementation for x86-64:
------------------------------------
(backtrace,
aa

View file

@ -13,6 +13,8 @@ static void r_bp_item_free (RBreakpointItem *b) {
free (b->bbytes);
free (b->obytes);
free (b->module_name);
free (b->data);
free (b->cond);
free (b);
}
@ -252,7 +254,7 @@ R_API int r_bp_list(RBreakpoint *bp, int rad) {
switch (rad) {
case 0:
bp->cb_printf ("0x%08"PFMT64x" - 0x%08"PFMT64x \
" %d %c%c%c %s %s %s cmd=\"%s\" " \
" %d %c%c%c %s %s %s cmd=\"%s\" cond=\"%s\" " \
"name=\"%s\" module=\"%s\"\n",
b->addr, b->addr + b->size, b->size,
(b->rwx & R_BP_PROT_READ) ? 'r' : '-',
@ -261,9 +263,10 @@ R_API int r_bp_list(RBreakpoint *bp, int rad) {
b->hw ? "hw": "sw",
b->trace ? "trace" : "break",
b->enabled ? "enabled" : "disabled",
b->data ? b->data : "",
b->name ? b->name : "",
b->module_name ? b->module_name : "");
r_str_get2 (b->data),
r_str_get2 (b->cond),
r_str_get2 (b->name),
r_str_get2 (b->module_name));
break;
case 1:
case 'r':
@ -282,7 +285,8 @@ R_API int r_bp_list(RBreakpoint *bp, int rad) {
bp->cb_printf ("%s{\"addr\":%"PFMT64d",\"size\":%d,"
"\"prot\":\"%c%c%c\",\"hw\":%s,"
"\"trace\":%s,\"enabled\":%s,"
"\"data\":\"%s\"}",
"\"data\":\"%s\","
"\"cond\":\"%s\"}",
iter->p ? "," : "",
b->addr, b->size,
(b->rwx & R_BP_PROT_READ) ? 'r' : '-',
@ -291,7 +295,8 @@ R_API int r_bp_list(RBreakpoint *bp, int rad) {
b->hw ? "true" : "false",
b->trace ? "true" : "false",
b->enabled ? "true" : "false",
b->data ? b->data : "");
r_str_get2 (b->data),
r_str_get2 (b->cond));
break;
}
/* TODO: Show list of pids and trace points, conditionals */

View file

@ -1766,6 +1766,7 @@ static void r_core_cmd_bp(RCore *core, const char *input) {
"dbj", "", "List breakpoints in JSON format",
// "dbi", " 0x848 ecx=3", "stop execution when condition matches",
"dbc", " <addr> <cmd>", "Run command when breakpoint is hit",
"dbC", " <addr> <cmd>", "Set breakpoint condition on command",
"dbd", " <addr>", "Disable breakpoint",
"dbe", " <addr>", "Enable breakpoint",
"dbs", " <addr>", "Toggle breakpoint",
@ -2043,6 +2044,32 @@ static void r_core_cmd_bp(RCore *core, const char *input) {
eprintf ("Use: dbc [addr] [command]\n");
}
break;
case 'C': // "dbC"
if (input[2] == ' ') {
char *inp = strdup (input + 3);
if (inp) {
char *arg = strchr (inp, ' ');
if (arg) {
*arg++ = 0;
addr = r_num_math (core->num, inp);
bpi = r_bp_get_at (core->dbg->bp, addr);
if (bpi) {
free (bpi->cond);
bpi->cond = strdup (arg);
} else {
eprintf ("No breakpoint defined at 0x%08"PFMT64x"\n", addr);
}
} else {
eprintf ("Missing argument\n");
}
free (inp);
} else {
eprintf ("Cannot strdup. Your heap is fucked up\n");
}
} else {
eprintf ("Use: dbc [addr] [command]\n");
}
break;
case 's': // "dbs"
addr = r_num_math (core->num, input + 2);
bpi = r_bp_get_at (core->dbg->bp, addr);

View file

@ -79,6 +79,13 @@ static inline void __setsegoff(RConfig *cfg, const char *asmarch, int asmbits) {
r_config_set (cfg, "asm.segoff", r_str_bool (autoseg));
}
static int cb_debug_hitinfo(void *user, void *data) {
RCore *core = (RCore*) user;
RConfigNode *node = (RConfigNode*) data;
core->dbg->hitinfo = node->i_value;
return true;
}
static int cb_analeobjmp(void *user, void *data) {
RCore *core = (RCore*) user;
RConfigNode *node = (RConfigNode*) data;
@ -1727,6 +1734,7 @@ R_API int r_core_config_init(RCore *core) {
SETPREF("cmd.xterm", "xterm -bg black -fg gray -e", "xterm command to spawn with V@");
SETICB("cmd.depth", 10, &cb_cmddepth, "Maximum command depth");
SETPREF("cmd.bp", "", "Run when a breakpoint is hit");
SETICB("cmd.hitinfo", 1, &cb_debug_hitinfo, "Show info when a tracepoint/breakpoint is hit");
SETPREF("cmd.times", "", "Run when a command is repeated (number prefix)");
SETPREF("cmd.stack", "", "Command to display the stack in visual debug mode");
SETPREF("cmd.cprompt", "", "Column visual prompt commands");

42
libr/debug/debug.c Executable file → Normal file
View file

@ -1,6 +1,7 @@
/* radare - LGPL - Copyright 2009-2016 - pancake, jduck, TheLemonMan */
/* radare - LGPL - Copyright 2009-2016 - pancake, jduck, TheLemonMan, saucec0de */
#include <r_debug.h>
#include <r_core.h>
#include <signal.h>
#if __WINDOWS__
@ -95,8 +96,10 @@ static int r_debug_bp_hit(RDebug *dbg, RRegItem *pc_ri, ut64 pc, RBreakpointItem
dbg->reason.bp_addr = b->addr;
/* inform the user of what happened */
eprintf ("hit %spoint at: %"PFMT64x "\n",
b->trace ? "trace" : "break", pc);
if (dbg->hitinfo) {
eprintf ("hit %spoint at: %"PFMT64x "\n",
b->trace ? "trace" : "break", pc);
}
/* now that we've cleaned up after the breakpoint, call the other
* potential breakpoint handlers
@ -293,6 +296,7 @@ R_API RDebug *r_debug_new(int hard) {
dbg->num = r_num_new (r_debug_num_callback, dbg);
dbg->h = NULL;
dbg->threads = NULL;
dbg->hitinfo = 1;
/* TODO: needs a redesign? */
dbg->maps = r_debug_map_list_new ();
dbg->maps_user = r_debug_map_list_new ();
@ -545,13 +549,15 @@ R_API RDebugReasonType r_debug_stop_reason(RDebug *dbg) {
*
* Returns R_DEBUG_REASON_*
*/
R_API RDebugReasonType r_debug_wait(RDebug *dbg) {
R_API RDebugReasonType r_debug_wait(RDebug *dbg, RBreakpointItem **bp) {
RDebugReasonType reason = R_DEBUG_REASON_ERROR;
if (!dbg) {
return reason;
}
if (bp) {
*bp = NULL;
}
/* default to unknown */
dbg->reason.type = R_DEBUG_REASON_UNKNOWN;
if (r_debug_is_dead (dbg)) {
@ -599,6 +605,12 @@ R_API RDebugReasonType r_debug_wait(RDebug *dbg) {
/* if we hit a tracing breakpoint, we need to continue in
* whatever mode the user desired. */
if (dbg->corebind.core && b && b->cond) {
if (bp) {
*bp = b;
}
reason = R_DEBUG_REASON_COND;
}
if (b && b->trace) {
reason = R_DEBUG_REASON_TRACEPOINT;
}
@ -749,7 +761,7 @@ R_API int r_debug_step_hard(RDebug *dbg) {
if (!dbg->h->step (dbg)) {
return false;
}
reason = r_debug_wait (dbg);
reason = r_debug_wait (dbg, NULL);
/* TODO: handle better */
if (reason == R_DEBUG_REASON_ERROR) {
return false;
@ -871,6 +883,7 @@ R_API int r_debug_step_over(RDebug *dbg, int steps) {
R_API int r_debug_continue_kill(RDebug *dbg, int sig) {
RDebugReasonType reason, ret = false;
RBreakpointItem *bp = NULL;
if (!dbg) {
return false;
@ -893,7 +906,20 @@ repeat:
//XXX(jjd): why? //dbg->reason.signum = 0;
reason = r_debug_wait (dbg);
reason = r_debug_wait (dbg, &bp);
if (dbg->corebind.core) {
RCore *core = (RCore *)dbg->corebind.core;
RNum *num = core->num;
if (reason == R_DEBUG_REASON_COND) {
if (bp->cond && dbg->corebind.cmd) {
dbg->corebind.cmd (dbg->corebind.core, bp->cond);
}
if (num->value) {
goto repeat;
}
}
}
#if __WINDOWS__
if (reason != R_DEBUG_REASON_DEAD) {
@ -1113,7 +1139,7 @@ R_API int r_debug_continue_syscalls(RDebug *dbg, int *sc, int n_sc) {
#endif
dbg->h->contsc (dbg, dbg->pid, 0); // TODO handle return value
// wait until continuation
reason = r_debug_wait (dbg);
reason = r_debug_wait (dbg, NULL);
if (reason == R_DEBUG_REASON_DEAD || r_debug_is_dead (dbg)) {
break;
}

View file

@ -57,6 +57,7 @@ typedef struct r_bp_item_t {
ut8 *bbytes; /* breakpoint bytes */
int pids[R_BP_MAXPIDS];
char *data;
char *cond; /* used for conditional breakpoints */
} RBreakpointItem;
typedef int (*RBreakpointCallback)(RBreakpointItem *bp, int set, void *user);

View file

@ -12,6 +12,7 @@
#include <r_syscall.h>
#include "list.h"
#include <r_config.h>
#include "r_bind.h"
#ifdef __cplusplus
extern "C" {
@ -87,6 +88,7 @@ typedef enum {
R_DEBUG_REASON_SEGFAULT,
R_DEBUG_REASON_BREAKPOINT,
R_DEBUG_REASON_TRACEPOINT,
R_DEBUG_REASON_COND,
R_DEBUG_REASON_READERR,
R_DEBUG_REASON_STEP,
R_DEBUG_REASON_ABORT,
@ -186,6 +188,7 @@ typedef struct r_debug_tracepoint_t {
typedef struct r_debug_t {
char *arch;
int bits; /// XXX: MUST SET ///
int hitinfo;
int pid; /* selected process id */
int tid; /* selected thread id */
@ -347,7 +350,7 @@ R_API RDebugReasonType r_debug_stop_reason(RDebug *dbg);
R_API const char *r_debug_reason_to_string(int type);
/* wait for another event */
R_API RDebugReasonType r_debug_wait(RDebug *dbg);
R_API RDebugReasonType r_debug_wait(RDebug *dbg, RBreakpointItem **bp);
/* continuations */
R_API int r_debug_step(RDebug *dbg, int steps);

View file

@ -83,6 +83,7 @@ R_API int r_str_ccmp(const char *dst, const char *orig, int ch);
R_API int r_str_cmp(const char *dst, const char *orig, int len);
R_API int r_str_ccpy(char *dst, char *orig, int ch);
R_API const char *r_str_get(const char *str);
R_API const char *r_str_get2(const char *str);
R_API char *r_str_ndup(const char *ptr, int len);
R_API char *r_str_dup(char *ptr, const char *string);
R_API void *r_str_free(void *ptr);

View file

@ -782,6 +782,10 @@ R_API const char *r_str_get(const char *str) {
return str? str: nullstr_c;
}
R_API const char *r_str_get2(const char *str) {
return str? str: nullstr;
}
R_API char *r_str_ndup(const char *ptr, int len) {
char *out = malloc (len+1);
if (!out) return NULL;