/* radare2 - LGPL - Copyright 2009-2017 - pancake */ #include #include R_LIB_VERSION (r_bp); static struct r_bp_plugin_t *bp_static_plugins[] = { R_BP_STATIC_PLUGINS }; static void r_bp_item_free (RBreakpointItem *b) { free (b->name); free (b->bbytes); free (b->obytes); free (b->module_name); free (b->data); free (b->cond); free (b); } R_API RBreakpoint *r_bp_new() { int i; RBreakpointPlugin *static_plugin; RBreakpoint *bp = R_NEW0 (RBreakpoint); if (!bp) { return NULL; } bp->bps_idx_count = 16; bp->bps_idx = R_NEWS0 (RBreakpointItem*, bp->bps_idx_count); bp->stepcont = R_BP_CONT_NORMAL; bp->traces = r_bp_traptrace_new (); bp->cb_printf = (PrintfCallback)printf; bp->bps = r_list_newf ((RListFree)r_bp_item_free); bp->plugins = r_list_newf ((RListFree)free); bp->nhwbps = 0; for (i = 0; bp_static_plugins[i]; i++) { static_plugin = R_NEW (RBreakpointPlugin); memcpy (static_plugin, bp_static_plugins[i], sizeof (RBreakpointPlugin)); r_bp_plugin_add (bp, static_plugin); } memset (&bp->iob, 0, sizeof (bp->iob)); return bp; } R_API RBreakpoint *r_bp_free(RBreakpoint *bp) { r_list_free (bp->bps); r_list_free (bp->plugins); r_list_free (bp->traces); free (bp->bps_idx); free (bp); return NULL; } R_API int r_bp_get_bytes(RBreakpoint *bp, ut8 *buf, int len, int endian, int idx) { int i; struct r_bp_arch_t *b; if (bp->cur) { // find matching size breakpoint repeat: for (i = 0; i < bp->cur->nbps; i++) { b = &bp->cur->bps[i]; if (bp->cur->bps[i].bits) { if (bp->bits != bp->cur->bps[i].bits) { continue; } } if (bp->cur->bps[i].length == len && bp->cur->bps[i].endian == endian) { memcpy (buf, b->bytes, b->length); return b->length; } } if (len != 4) { len = 4; goto repeat; } /* if not found try to pad with the first one */ b = &bp->cur->bps[0]; if (len % b->length) { eprintf ("No matching bpsize\n"); return 0; } for (i = 0; i < len; i++) { memcpy (buf + i, b->bytes, b->length); } return b->length; } return 0; } R_API RBreakpointItem *r_bp_get_at(RBreakpoint *bp, ut64 addr) { RListIter *iter; RBreakpointItem *b; r_list_foreach(bp->bps, iter, b) { if (b->addr == addr) return b; } return NULL; } static inline bool inRange(RBreakpointItem *b, ut64 addr) { return (addr >= b->addr && addr < (b->addr + b->size)); } static inline bool matchProt(RBreakpointItem *b, int rwx) { return (!rwx || (rwx && b->rwx)); } R_API RBreakpointItem *r_bp_get_in(RBreakpoint *bp, ut64 addr, int rwx) { RBreakpointItem *b; RListIter *iter; r_list_foreach (bp->bps, iter, b) { // eprintf ("---ataddr--- 0x%08"PFMT64x" %d %d %x\n", b->addr, b->size, b->recoil, b->rwx); // Check addr within range and provided rwx matches (or null) if (inRange (b, addr) && matchProt (b, rwx)) { return b; } } return NULL; } R_API RBreakpointItem *r_bp_enable(RBreakpoint *bp, ut64 addr, int set, int count) { RBreakpointItem *b = r_bp_get_in (bp, addr, 0); if (b) { b->enabled = set; b->togglehits = count; return b; } return NULL; } R_API int r_bp_enable_all(RBreakpoint *bp, int set) { RListIter *iter; RBreakpointItem *b; r_list_foreach (bp->bps, iter, b) { b->enabled = set; } return true; } R_API int r_bp_stepy_continuation(RBreakpoint *bp) { // TODO: implement return bp->stepcont; } /* TODO: detect overlapping of breakpoints */ static RBreakpointItem *r_bp_add(RBreakpoint *bp, const ut8 *obytes, ut64 addr, int size, int hw, int rwx) { int ret; RBreakpointItem *b; if (addr == UT64_MAX || size < 1) { return NULL; } if (r_bp_get_in (bp, addr, rwx)) { eprintf ("Breakpoint already set at this address.\n"); return NULL; } b = r_bp_item_new (bp); b->addr = addr + bp->delta; b->size = size; b->enabled = true; b->rwx = rwx; b->hw = hw; // NOTE: for hw breakpoints there are no bytes to save/restore if (!hw) { b->bbytes = calloc (size + 16, 1); if (obytes) { b->obytes = malloc (size); memcpy (b->obytes, obytes, size); } else { b->obytes = NULL; } /* XXX: endian .. use bp->endian */ ret = r_bp_get_bytes (bp, b->bbytes, size, bp->endian, 0); if (ret != size) { eprintf ("Cannot get breakpoint bytes. No architecture selected?\n"); r_bp_item_free (b); return NULL; } b->recoil = ret; } bp->nbps++; r_list_append (bp->bps, b); return b; } R_API int r_bp_add_fault(RBreakpoint *bp, ut64 addr, int size, int rwx) { // TODO return false; } R_API RBreakpointItem* r_bp_add_sw(RBreakpoint *bp, ut64 addr, int size, int rwx) { RBreakpointItem *item; ut8 *bytes; if (size < 1) { size = 1; } if (!(bytes = calloc (1, size))) { return NULL; } memset (bytes, 0, size); if (bp->iob.read_at) { bp->iob.read_at (bp->iob.io, addr, bytes, size); } item = r_bp_add (bp, bytes, addr, size, R_BP_TYPE_SW, rwx); free (bytes); return item; } R_API RBreakpointItem* r_bp_add_hw(RBreakpoint *bp, ut64 addr, int size, int rwx) { return r_bp_add (bp, NULL, addr, size, R_BP_TYPE_HW, rwx); } R_API int r_bp_del_all(RBreakpoint *bp) { if (!r_list_empty (bp->bps)) { r_list_purge (bp->bps); return true; } return false; } R_API int r_bp_del(RBreakpoint *bp, ut64 addr) { RListIter *iter; RBreakpointItem *b; /* No _safe loop necessary because we return immediately after the delete. */ r_list_foreach (bp->bps, iter, b) { if (b->addr == addr) { r_list_delete (bp->bps, iter); return true; } } return false; } R_API int r_bp_set_trace(RBreakpoint *bp, ut64 addr, int set) { RBreakpointItem *b = r_bp_get_in (bp, addr, 0); if (b) { b->trace = set; return true; } return false; } R_API int r_bp_set_trace_all(RBreakpoint *bp, int set) { RListIter *iter; RBreakpointItem *b; r_list_foreach (bp->bps, iter, b) { b->trace = set; } return true; } // TODO: deprecate R_API int r_bp_list(RBreakpoint *bp, int rad) { int n = 0; RBreakpointItem *b; RListIter *iter; if (rad == 'j') bp->cb_printf ("["); //eprintf ("Breakpoint list:\n"); r_list_foreach (bp->bps, iter, b) { switch (rad) { case 0: bp->cb_printf ("0x%08"PFMT64x" - 0x%08"PFMT64x \ " %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' : '-', (b->rwx & R_BP_PROT_WRITE) ? 'w' : '-', (b->rwx & R_BP_PROT_EXEC) ? 'x' : '-', b->hw ? "hw": "sw", b->trace ? "trace" : "break", b->enabled ? "enabled" : "disabled", 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': case '*': // TODO: add command, tracing, enable, .. if (b->module_name) { bp->cb_printf ("dbm %s %"PFMT64d"\n", b->module_name, b->module_delta); } else { bp->cb_printf ("db 0x%08"PFMT64x"\n", b->addr); } //b->trace? "trace": "break", //b->enabled? "enabled": "disabled", // b->data? b->data: ""); break; case 'j': bp->cb_printf ("%s{\"addr\":%"PFMT64d",\"size\":%d," "\"prot\":\"%c%c%c\",\"hw\":%s," "\"trace\":%s,\"enabled\":%s," "\"data\":\"%s\"," "\"cond\":\"%s\"}", iter->p ? "," : "", b->addr, b->size, (b->rwx & R_BP_PROT_READ) ? 'r' : '-', (b->rwx & R_BP_PROT_WRITE) ? 'w' : '-', (b->rwx & R_BP_PROT_EXEC) ? 'x' : '-', b->hw ? "true" : "false", b->trace ? "true" : "false", b->enabled ? "true" : "false", r_str_get2 (b->data), r_str_get2 (b->cond)); break; } /* TODO: Show list of pids and trace points, conditionals */ n++; } if (rad == 'j') { bp->cb_printf ("]\n"); } return n; } R_API RBreakpointItem *r_bp_item_new (RBreakpoint *bp) { int i, j; /* find empty slot */ for (i = 0; i < bp->bps_idx_count; i++) { if (!bp->bps_idx[i]) { goto return_slot; } } /* allocate new slot */ bp->bps_idx_count += 16; // alocate space for 16 more bps bp->bps_idx = realloc (bp->bps_idx, bp->bps_idx_count * sizeof(RBreakpointItem*)); for (j = i; j < bp->bps_idx_count; j++) { bp->bps_idx[j] = NULL; } return_slot: /* empty slot */ return (bp->bps_idx[i] = R_NEW0 (RBreakpointItem)); } R_API RBreakpointItem *r_bp_get_index(RBreakpoint *bp, int idx) { if (idx >= 0 && idx < bp->bps_idx_count) { return bp->bps_idx[idx]; } return NULL; } R_API int r_bp_del_index(RBreakpoint *bp, int idx) { if (idx >= 0 && idx < bp->bps_idx_count) { r_list_delete_data (bp->bps, bp->bps_idx[idx]); free (bp->bps_idx[idx]); bp->bps_idx[idx] = NULL; return true; } return false; } R_API int r_bp_size(RBreakpoint *bp) { RBreakpointArch *bpa; int i, bpsize = 8; for (i = 0; bp->cur->bps[i].bytes; i++) { bpa = &bp->cur->bps[i]; if (bpa->bits && bpa->bits != bp->bits) { continue; } if (bpa->length < bpsize) { bpsize = bpa->length; } } return bpsize; }