Add support for conditional breakpoints running r2 commands
This commit is contained in:
parent
e16773367f
commit
f0a2d8657d
10 changed files with 130 additions and 15 deletions
27
doc/debug
Normal file
27
doc/debug
Normal 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
|
||||
13
doc/macros
13
doc/macros
|
|
@ -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
|
||||
|
|
|
|||
17
libr/bp/bp.c
17
libr/bp/bp.c
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
42
libr/debug/debug.c
Executable file → Normal 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue