/* radare - LGPL - Copyright 2015 - pancake */ #include #if 0 /* debugesil performs step into + esil conditionals */ ESIL conditionals can be used to detect when a specific address is accessed, or a register. Those esil conditionals must be evaluated every iteration to ensure the register values are updated. Think in DebugESIL as software-watchpoints. [read|write|exec]-[reg|mem] [expression] de rw reg eax de-* # expression can be a number or a range (if .. is found) # The <=, >=, ==, <, > comparisons are also supported #endif typedef struct { int rwx; int dev; char *expr; } EsilBreak; // TODO: Kill those globals RDebug *dbg = NULL; static int has_match = 0; static int prestep = 1; // TODO: make it configurable static ut64 opc = 0; RList *esil_watchpoints = NULL; #define EWPS esil_watchpoints #define ESIL dbg->anal->esil static int exprmatch (RDebug *dbg, ut64 addr, const char *expr) { char *e = strdup (expr); char *p = strstr (e, ".."); ut64 a,b; int ret = 0; if (p) { *p = 0; p += 2; a = r_num_math (dbg->num, e); b = r_num_math (dbg->num, p); if (a=a && addr <= b) ret = 1; } else { if (addr >=b && addr <= a) ret = 1; } } else { a = r_num_math (dbg->num, e); if (addr == a) ret = 1; } has_match = ret; free (e); return ret; } static int esilbreak_check_pc (RDebug *dbg, ut64 pc) { EsilBreak *ew; RListIter *iter; if (!pc) pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]); r_list_foreach (EWPS, iter, ew) { if (ew->rwx & R_IO_EXEC) { if (exprmatch (dbg, pc, ew->expr)) return 1; } } return 0; } static int esilbreak_mem_read(RAnalEsil *esil, ut64 addr, ut8 *buf, int len) { EsilBreak *ew; RListIter *iter; eprintf (Color_GREEN"MEM READ 0x%"PFMT64x"\n"Color_RESET, addr); r_list_foreach (EWPS, iter, ew) { if (ew->rwx & R_IO_READ && ew->dev == 'm') { if (exprmatch (dbg, addr, ew->expr)) { has_match = 1; return 1; } } } return 0; // fallback } static int esilbreak_mem_write(RAnalEsil *esil, ut64 addr, const ut8 *buf, int len) { EsilBreak *ew; RListIter *iter; eprintf (Color_RED"MEM WRTE 0x%"PFMT64x"\n"Color_RESET, addr); r_list_foreach (EWPS, iter, ew) { if (ew->rwx & R_IO_WRITE && ew->dev == 'm') { if (exprmatch (dbg, addr, ew->expr)) { has_match = 1; return 1; } } } return 1; // fallback } static int esilbreak_reg_read(RAnalEsil *esil, const char *regname, ut64 *num, int *size) { EsilBreak *ew; RListIter *iter; if (regname[0]>='0' && regname[0]<='9') { //eprintf (Color_CYAN"IMM READ %s\n"Color_RESET, regname); return 0; } eprintf (Color_YELLOW"REG READ %s\n"Color_RESET, regname); r_list_foreach (EWPS, iter, ew) { if (ew->rwx & R_IO_READ && ew->dev == 'r') { // XXX: support array of regs in expr if (!strcmp (regname, ew->expr)) { has_match = 1; return 1; } } } return 0; // fallback } static int exprtoken(RDebug *dbg, char *s, const char *sep, char **o) { char *p = strstr (s, sep); if (p) { *p = 0; p += strlen (sep); *o = p; return 1; } *o = NULL; return 0; } static int exprmatchreg (RDebug *dbg, const char *regname, const char *expr) { int ret = 0; char *p; char *s = strdup (expr); if (!strcmp (regname, s)) { ret = 1; } else { #define CURVAL 0){}r_str_trim(s);if (!strcmp(regname,s) && regval ut64 regval = r_debug_reg_get_err (dbg, regname, NULL); if (exprtoken (dbg, s, ">=", &p)) { if (CURVAL >= r_num_math (dbg->num, p)) ret = 1; } else if (exprtoken (dbg, s, "<=", &p)) { if (CURVAL <= r_num_math (dbg->num, p)) ret = 1; } else if (exprtoken (dbg, s, "==", &p)) { if (CURVAL <= r_num_math (dbg->num, p)) ret = 1; } else if (exprtoken (dbg, s, "<", &p)) { if (CURVAL < r_num_math (dbg->num, p)) ret = 1; } else if (exprtoken (dbg, s, ">", &p)) { if (CURVAL > r_num_math (dbg->num, p)) ret = 1; } else if (exprtoken (dbg, s, " ", &p)) { r_str_trim (s); if (!strcmp (regname, s)) { ut64 num = r_num_math (dbg->num, p); ret = exprmatch (dbg, num, s); } } else { if (!strcmp (regname, s)) { ret = 1; } } } free (s); return ret; } static int esilbreak_reg_write(RAnalEsil *esil, const char *regname, ut64 num) { EsilBreak *ew; RListIter *iter; if (regname[0]>='0' && regname[0]<='9') { // wtf this should never happen //eprintf (Color_BLUE"IMM WRTE %s\n"Color_RESET, regname); return 0; } eprintf (Color_MAGENTA"REG WRTE %s 0x%"PFMT64x"\n"Color_RESET, regname, num); r_list_foreach (EWPS, iter, ew) { if (ew->rwx & R_IO_WRITE && ew->dev == 'r') { // XXX: support array of regs in expr if (exprmatchreg (dbg, regname, ew->expr)) { has_match = 1; return 1; } } } return 1; // fallback } R_API void r_debug_esil_prestep (RDebug *d, int p) { prestep = p; } 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 (32, R_TRUE); // TODO setup something? } if (!ESIL) return 0; 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 ut64 r_debug_esil_step(RDebug *dbg, ut32 count) { count++; has_match = 0; r_cons_break (NULL, NULL); do { if (r_cons_is_breaked ()) break; if (has_match) { eprintf ("EsilBreak match at 0x%08"PFMT64x"\n", opc); break; } if (count>0) { count--; if (!count) { //eprintf ("Limit reached\n"); break; } } } while (r_debug_esil_stepi (dbg)); r_cons_break_end (); return opc; } R_API ut64 r_debug_esil_continue (RDebug *dbg) { return r_debug_esil_step (dbg, UT32_MAX); } static void ewps_free(EsilBreak *ew) { free (ew->expr); ew->expr = NULL; free (ew); } R_API int r_debug_esil_watch_empty(RDebug *dbg) { return r_list_empty (EWPS); } R_API void r_debug_esil_watch(RDebug *dbg, int rwx, int dev, const char *expr) { if (!EWPS) { EWPS = r_list_new (); EWPS->free = (RListFree)ewps_free; } EsilBreak *ew = R_NEW0 (EsilBreak); ew->rwx = rwx; ew->dev = dev; ew->expr = strdup (expr); r_list_append (EWPS, ew); } R_API void r_debug_esil_watch_reset(RDebug *dbg) { r_list_free (EWPS); EWPS = NULL; } R_API void r_debug_esil_watch_list(RDebug *dbg) { EsilBreak *ew; RListIter *iter; r_list_foreach (EWPS, iter, ew) { dbg->cb_printf ("de %s %c %s\n", r_str_rwx_i (ew->rwx), ew->dev, ew->expr); } }