diff --git a/libr/anal/anal.c b/libr/anal/anal.c index 133edaede93d0..c3a2df6a7bf6b 100644 --- a/libr/anal/anal.c +++ b/libr/anal/anal.c @@ -128,6 +128,98 @@ static void r_meta_item_free(void *_item) { } } +#if USE_NEW_ESIL +static bool anal_esil_mem_switch (void *mem, ut32 idx) { + RAnal *anal = mem; + if (!anal || !anal->iob.init) { + R_LOG_WARN ("anal->iob is not setup"); + return false; + } + return anal->iob.bank_use (anal->iob.io, idx); +} + +static bool anal_esil_mem_read (void *mem, ut64 addr, ut8 *buf, int len) { + RAnal *anal = mem; + if (!anal || !anal->iob.init) { + R_LOG_WARN ("anal->iob is not setup"); + return false; + } + return anal->iob.read_at (anal->iob.io, addr, buf, len); +} + +static bool anal_esil_mem_write (void *mem, ut64 addr, const ut8 *buf, int len) { + RAnal *anal = mem; + if (!anal || !anal->iob.init) { + R_LOG_WARN ("anal->iob is not setup"); + return false; + } + return anal->iob.write_at (anal->iob.io, addr, buf, len); +} + +REsilMemInterface anal_esil_mem_if = { + .mem_switch = anal_esil_mem_switch, + .mem_read = anal_esil_mem_read, + .mem_write = anal_esil_mem_write +}; + +static bool anal_esil_is_reg (void *user, const char *name) { + RRegItem *ri = r_reg_get (((RAnal *)user)->reg, name, -1); + if (!ri) { + return false; + } + r_unref (ri); + return true; +} + +static bool anal_esil_reg_read (void *user, const char *name, ut64 *val) { + RRegItem *ri = r_reg_get (((RAnal *)user)->reg, name, -1); + if (!ri) { + return false; + } + *val = r_reg_get_value (((RAnal *)user)->reg, ri); + r_unref (ri); + return true; +} + +static bool anal_esil_reg_write (void *user, const char *name, ut64 val) { + return r_reg_setv (((RAnal *)user)->reg, name, val); +} + +static ut32 anal_esil_reg_size (void *user, const char *name) { + RRegItem *ri = r_reg_get (((RAnal *)user)->reg, name, -1); + if (!ri) { + return 0; + } + const ut32 size = ri->size; + r_unref (ri); + return size; +} + +static bool anal_esil_reg_alias (void *user, const char *name, const char *alias) { + int alias_type = r_reg_alias_fromstring (alias); + if (alias_type < 0) { + return false; + } + return r_reg_alias_setname (((RAnal *)user)->reg, alias_type, name); +} + +static REsilRegInterface anal_esil_reg_if = { + .is_reg = anal_esil_is_reg, + .reg_read = anal_esil_reg_read, + .reg_write = anal_esil_reg_write, + .reg_size = anal_esil_reg_size, + .reg_alias = anal_esil_reg_alias +}; + +static bool anal_esil_set_bits (void *user, int bits) { + return r_anal_set_triplet ((RAnal *)user, NULL, NULL, bits); +} + +static REsilUtilInterface anal_esil_util_if = { + .set_bits = anal_esil_set_bits +}; +#endif + // Take nullable RArchConfig as argument? R_API RAnal *r_anal_new(void) { int i; @@ -176,7 +268,14 @@ R_API RAnal *r_anal_new(void) { anal->sdb_classes_attrs = sdb_ns (anal->sdb_classes, "attrs", 1); anal->zign_path = strdup (""); anal->cb_printf = (PrintfCallback) printf; +#if USE_NEW_ESIL + anal_esil_reg_if.reg = anal; + anal_esil_mem_if.mem = anal; + anal_esil_util_if.user = anal; + anal->esil = r_esil_new_ex (4096, 0, 1, &anal_esil_reg_if, &anal_esil_mem_if, &anal_esil_util_if); +#else anal->esil = r_esil_new (4096, 0, 1); +#endif anal->esil->anal = anal; (void)r_anal_pin_init (anal); (void)r_anal_xrefs_init (anal); diff --git a/libr/anal/esil_dfg.c b/libr/anal/esil_dfg.c index 74061f3094f75..11eafaf6e62d6 100644 --- a/libr/anal/esil_dfg.c +++ b/libr/anal/esil_dfg.c @@ -1551,7 +1551,11 @@ R_API RAnalEsilDFG *r_anal_esil_dfg_new(RAnal *anal, bool use_map_info, bool use free (dfg); return NULL; } +#if USE_NEW_ESIL + dfg->esil = r_esil_new_simple (1, anal->reg, &anal->iob); +#else dfg->esil = r_esil_new (4096, 0, 1); +#endif if (!dfg->esil) { r_reg_free (dfg->reg); free (dfg); @@ -1641,7 +1645,11 @@ R_API void r_anal_esil_dfg_free(RAnalEsilDFG *dfg) { R_API RAnalEsilDFG *r_anal_esil_dfg_expr(RAnal *anal, RAnalEsilDFG *R_NULLABLE dfg, const char *expr, bool use_map_info, bool use_maps) { R_RETURN_VAL_IF_FAIL (anal && expr, NULL); +#if USE_NEW_ESIL + REsil *esil = r_esil_new_simple (1, anal->reg, &anal->iob); +#else REsil *esil = r_esil_new (4096, 0, 1); +#endif if (!esil) { return NULL; } diff --git a/libr/anal/p/anal_tp.c b/libr/anal/p/anal_tp.c index ead04e72d2508..6a33fcf3f4183 100644 --- a/libr/anal/p/anal_tp.c +++ b/libr/anal/p/anal_tp.c @@ -1159,6 +1159,14 @@ static ut32 tt_reg_size(void *reg, const char *name) { return size; } +static bool tt_reg_alias(void *reg, const char *name, const char *alias) { + int alias_type = r_reg_alias_fromstring (alias); + if (alias_type < 0) { + return false; + } + return r_reg_alias_setname (reg, alias_type, name); +} + static bool tt_mem_read(void *mem, ut64 addr, ut8 *buf, int len) { TPState *tps = (TPState *)mem; if (tps->anal->iob.read_at) { @@ -1306,6 +1314,7 @@ static TPState *tps_init(RAnal *anal) { tps->reg_if.reg_read = tt_reg_read; tps->reg_if.reg_write = (REsilRegWrite)r_reg_setv; tps->reg_if.reg_size = tt_reg_size; + tps->reg_if.reg_alias = tt_reg_alias; tps->mem_if.mem = tps; tps->mem_if.mem_read = tt_mem_read; tps->mem_if.mem_write = tt_mem_write; @@ -1313,7 +1322,7 @@ static TPState *tps_init(RAnal *anal) { // todo: this probably needs some boundary checks r_reg_setv (reg, "SP", sp); r_reg_setv (reg, "BP", sp); - if (!r_esil_init (&tps->esil, 4096, false, anal->config->bits, &tps->reg_if, &tps->mem_if)) { + if (!r_esil_init (&tps->esil, 4096, false, anal->config->bits, &tps->reg_if, &tps->mem_if, NULL)) { r_reg_free (reg); if (anal->iob.fd_close) { anal->iob.fd_close (io, tps->stack_fd); diff --git a/libr/core/anal_tp.c b/libr/core/anal_tp.c new file mode 100644 index 0000000000000..c2937a4ccd040 --- /dev/null +++ b/libr/core/anal_tp.c @@ -0,0 +1,1597 @@ +/* radare - LGPL - Copyright 2016-2025 - oddcoder, sivaramaaa, pancake */ +/* type matching - type propagation */ + +#include +#define LOOP_MAX 10 + +typedef struct type_trace_change_reg_t { + int idx; + ut32 cc; + char *name; + ut64 data; + ut64 odata; +} TypeTraceRegChange; + +typedef struct type_trace_change_mem_t { + int idx; + ut32 cc; + ut64 addr; + ut8 data; + ut8 odata; +} TypeTraceMemChange; + +typedef struct { + const char *name; + ut64 value; + // TODO: size +} TypeTraceRegAccess; + +typedef struct { + char *data; + ut64 addr; + // TODO: size +} TypeTraceMemoryAccess; + +typedef struct { + union { + TypeTraceRegAccess reg; + TypeTraceMemoryAccess mem; + }; + bool is_write; + bool is_reg; +} TypeTraceAccess; + +typedef struct { + ut64 addr; + ut32 start; + ut32 end; // 1 past the end of the op for this index +} TypeTraceOp; + +static inline void tt_fini_access(TypeTraceAccess *access) { + if (access->is_reg) { + return; + } + free (access->mem.data); +} + +R_VEC_TYPE(VecTraceOp, TypeTraceOp); +R_VEC_TYPE_WITH_FINI(VecAccess, TypeTraceAccess, tt_fini_access); + +typedef struct { + VecTraceOp ops; + VecAccess accesses; + HtUU *loop_counts; +} TypeTraceDB; + +typedef struct type_trace_t { + TypeTraceDB db; + int idx; + ut32 cc; + int end_idx; + int cur_idx; + RReg *reg; + HtUP *registers; + HtUP *memory; + ut32 voy[4]; +} TypeTrace; + +#define CMP_REG_CHANGE(x, y) ((x) - ((TypeTraceRegChange *)y)->idx) +#define CMP_MEM_CHANGE(x, y) ((x) - ((TypeTraceMemChange *)y)->idx) + +static void update_trace_db_op(TypeTraceDB *db) { + const ut32 trace_op_len = VecTraceOp_length (&db->ops); + if (!trace_op_len) { + return; + } + TypeTraceOp *last = VecTraceOp_at (&db->ops, trace_op_len - 1); + if (!last) { + return; + } + const ut32 vec_idx = VecAccess_length (&db->accesses); + if (!vec_idx) { + R_LOG_ERROR ("Invalid access database"); + return; + } + last->end = vec_idx; // - 1; +} + +static void type_trace_voyeur_reg_read (void *user, const char *name, ut64 val) { + R_RETURN_IF_FAIL (user && name); + char *name_dup = strdup (name); + if (!name_dup) { + R_LOG_ERROR ("Failed to allocate(strdup) memory for storing access"); + return; + } + TypeTraceDB *db = user; + TypeTraceAccess *access = VecAccess_emplace_back (&db->accesses); + if (!access) { + free (name_dup); + R_LOG_ERROR ("Failed to allocate memory for storing access"); + return; + } + access->reg.name = name_dup; + access->reg.value = val; + access->is_reg = true; + access->is_write = false; + update_trace_db_op (db); +} + +static void add_reg_change(TypeTrace *trace, RRegItem *ri, ut64 data, ut64 odata) { + R_RETURN_IF_FAIL (trace && ri); + ut64 addr = ri->offset | (ri->arena << 16); + RVector *vreg = ht_up_find (trace->registers, addr, NULL); + if (R_UNLIKELY (!vreg)) { + vreg = r_vector_new (sizeof (TypeTraceRegChange), NULL, NULL); + if (R_UNLIKELY (!vreg)) { + R_LOG_ERROR ("creating a register vector"); + return; + } + ht_up_insert (trace->registers, addr, vreg); + } + TypeTraceRegChange reg = {trace->cur_idx, trace->cc++, + strdup (ri->name), data, odata}; + r_vector_push (vreg, ®); +} + +static void type_trace_voyeur_reg_write (void *user, const char *name, ut64 old, ut64 val) { + R_RETURN_IF_FAIL (user && name); + TypeTrace *trace = user; + RRegItem *ri = r_reg_get (trace->reg, name, -1); + if (!ri) { + return; + } + char *name_dup = strdup (name); + if (!name_dup) { + R_LOG_ERROR ("Failed to allocate(strdup) memory for storing access"); + goto fail_name_dup; + } + TypeTraceAccess *access = VecAccess_emplace_back (&trace->db.accesses); + if (!access) { + R_LOG_ERROR ("Failed to allocate memory for storing access"); + goto fail_emplace_back; + } + access->is_reg = true; + access->reg.name = name_dup; + access->reg.value = val; + access->is_write = true; + + add_reg_change (trace, ri, val, old); + update_trace_db_op (&trace->db); + r_unref (ri); + return; +fail_emplace_back: + free (name_dup); +fail_name_dup: + r_unref (ri); +} + +static void type_trace_voyeur_mem_read (void *user, ut64 addr, const ut8 *buf, int len) { + R_RETURN_IF_FAIL (user && buf && (len > 0)); + char *hexbuf = r_hex_bin2strdup (buf, len); //why? + if (!hexbuf) { + R_LOG_ERROR ("Failed to allocate(r_hex_bin2strdup) memory for storing access"); + return; + } + TypeTraceDB *db = user; + TypeTraceAccess *access = VecAccess_emplace_back (&db->accesses); + if (!access) { + free (hexbuf); + R_LOG_ERROR ("Failed to allocate memory for storing access"); + return; + } + access->is_reg = false; + access->mem.data = hexbuf; + access->mem.addr = addr; + access->is_write = false; + update_trace_db_op (db); +} + +static void type_trace_voyeur_mem_write (void *user, ut64 addr, const ut8 *old, const ut8 *buf, int len) { + R_RETURN_IF_FAIL (user && buf && (len > 0)); + char *hexbuf = r_hex_bin2strdup (buf, len); //why? + if (!hexbuf) { + R_LOG_ERROR ("Failed to allocate(r_hex_bin2strdup) memory for storing access"); + return; + } + TypeTrace *trace = user; + TypeTraceAccess *access = VecAccess_emplace_back (&trace->db.accesses); + if (!access) { + free (hexbuf); + R_LOG_ERROR ("Failed to allocate memory for storing access"); + return; + } + access->is_reg = false; + access->mem.data = hexbuf; + access->mem.addr = addr; + access->is_write = true; + ut32 i; + for (i = 0; i < len; i++) { + //adding each byte one by one is utterly stupid, typical gsoc crap + //ideally this would use a tree structure, that splits nodes when necessary + RVector *vmem = ht_up_find (trace->memory, addr, NULL); + if (!vmem) { + vmem = r_vector_new (sizeof (TypeTraceMemChange), NULL, NULL); + if (!vmem) { + R_LOG_ERROR ("creating a memory vector"); + break; + } + ht_up_insert (trace->memory, addr, vmem); + } + TypeTraceMemChange mem = {trace->idx, trace->cc++, addr, buf[i], old[i]}; + r_vector_push (vmem, &mem); + } + update_trace_db_op (&trace->db); +} + +static void htup_vector_free(HtUPKv *kv) { + if (kv) { + r_vector_free (kv->value); + } +} + +static void trace_db_init(TypeTraceDB *db) { + VecTraceOp_init (&db->ops); + VecAccess_init (&db->accesses); + db->loop_counts = ht_uu_new0 (); +} + +static bool type_trace_init(TypeTrace *trace, REsil *esil, RReg *reg) { + R_RETURN_VAL_IF_FAIL (trace && esil && reg, false); + *trace = (const TypeTrace){0}; + trace_db_init (&trace->db); + trace->registers = ht_up_new (NULL, htup_vector_free, NULL); + if (!trace->registers) { + goto fail_registers_ht; + } + trace->memory = ht_up_new (NULL, htup_vector_free, NULL); + if (!trace->memory) { + goto fail_memory_ht; + } + trace->voy[R_ESIL_VOYEUR_REG_READ] = r_esil_add_voyeur (esil, &trace->db, + type_trace_voyeur_reg_read, R_ESIL_VOYEUR_REG_READ); + if (R_UNLIKELY (trace->voy[R_ESIL_VOYEUR_REG_READ] == R_ESIL_VOYEUR_ERR)) { + goto fail_regr_voy; + } + trace->voy[R_ESIL_VOYEUR_REG_WRITE] = r_esil_add_voyeur (esil, trace, + type_trace_voyeur_reg_write, R_ESIL_VOYEUR_REG_WRITE); + if (R_UNLIKELY (trace->voy[R_ESIL_VOYEUR_REG_WRITE] == R_ESIL_VOYEUR_ERR)) { + goto fail_regw_voy; + } + trace->voy[R_ESIL_VOYEUR_MEM_READ] = r_esil_add_voyeur (esil, &trace->db, + type_trace_voyeur_mem_read, R_ESIL_VOYEUR_MEM_READ); + if (R_UNLIKELY (trace->voy[R_ESIL_VOYEUR_MEM_READ] == R_ESIL_VOYEUR_ERR)) { + goto fail_memr_voy; + } + trace->voy[R_ESIL_VOYEUR_MEM_WRITE] = r_esil_add_voyeur (esil, trace, + type_trace_voyeur_mem_write, R_ESIL_VOYEUR_MEM_WRITE); + if (R_UNLIKELY (trace->voy[R_ESIL_VOYEUR_MEM_WRITE] == R_ESIL_VOYEUR_ERR)) { + goto fail_memw_voy; + } + trace->reg = reg; + return true; +fail_memw_voy: + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_MEM_READ]); +fail_memr_voy: + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_REG_WRITE]); +fail_regw_voy: + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_REG_READ]); +fail_regr_voy: + ht_up_free (trace->memory); + trace->memory = NULL; +fail_memory_ht: + ht_up_free (trace->registers); + trace->registers = NULL; +fail_registers_ht: + return false; +} + +static ut64 type_trace_loopcount(TypeTrace *trace, ut64 addr) { + bool found = false; + const ut64 count = ht_uu_find (trace->db.loop_counts, addr, &found); + return found? count: 0; +} + +static void type_trace_loopcount_increment(TypeTrace *trace, ut64 addr) { + const ut64 count = type_trace_loopcount (trace, addr); + ht_uu_update (trace->db.loop_counts, addr, count + 1); +} + +//XXX: trace should be the only parameter +static void type_trace_fini(TypeTrace *trace, REsil *esil) { + R_RETURN_IF_FAIL (trace && esil); + VecTraceOp_fini (&trace->db.ops); + VecAccess_fini (&trace->db.accesses); + ht_uu_free (trace->db.loop_counts); + ht_up_free (trace->registers); + ht_up_free (trace->memory); + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_MEM_WRITE]); + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_MEM_READ]); + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_REG_WRITE]); + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_REG_READ]); + r_reg_free (trace->reg); + trace[0] = (const TypeTrace){0}; +} + +static bool type_trace_op(TypeTrace *trace, REsil *esil, RAnalOp *op) { + R_RETURN_VAL_IF_FAIL (trace && esil && op, false); + const char *expr = r_strbuf_get (&op->esil); + if (R_UNLIKELY (!expr || !strlen (expr))) { + R_LOG_WARN ("expr is empty or null"); + return false; + } + trace->cc = 0; + + RRegItem *ri = r_reg_get (trace->reg, "PC", -1); + if (ri) { + const bool suc = r_esil_reg_write_silent (esil, ri->name, op->addr + op->size); + r_unref (ri); + if (!suc) { + return false; + } + } + + TypeTraceOp *to = VecTraceOp_emplace_back (&trace->db.ops); + if (R_LIKELY (to)) { + ut32 vec_idx = VecAccess_length (&trace->db.accesses); + to->start = vec_idx; + to->end = vec_idx; + to->addr = op->addr; + } else { + R_LOG_WARN ("Couldn't allocate(emplace_back) trace op"); + //anything to do here? + } + const bool ret = r_esil_parse (esil, expr); + r_esil_stack_free (esil); + trace->idx++; + trace->end_idx++; // should be vector length? + return ret; +} + +#if 0 +static bool count_changes_above_idx_cb (void *user, const ut64 key, const void *val) { + RVector *vec = val; + if (R_UNLIKELY (r_vector_empty (vec))) { + return true; + } + ut64 *v = user; + const int idx = v[0] >> 32; + ut32 count = v[0] & UT32_MAX; + v[0] &= UT64_MAX ^ UT64_MAX; + ut32 i = r_vector_length (vec) - 1; + TypeTraceMemChange *change = r_vector_index_ptr (vec, i); + //idx is guaranteed to be at struct offset 0 for MemChange and RegChange, so this hack is fine + while (change->idx >= idx) { + count++; + if (!i) { + break; + } + i--; + change = r_vector_index_ptr (vec, i); + } + v[0] |= count; + return true; +} + +typedef struct { + int idx; + union { + TypeTraceRegChange *rc_ptr; + TypeTraceMemChange *mc_ptr; + void *data; + }; +} TTChangeCollector; + +static bool collect_reg_changes_cb (void *user, const ut64 key, const void *val) { + RVector *vec = val; + if (R_UNLIKELY (r_vector_empty (vec))) { + return true; + } + TTChangeCollector *cc = user; + ut32 i = r_vector_length (vec) - 1; + TypeTraceRegChange *rc = r_vector_index_ptr (vec, i); + while (rc->idx >= cc->idx) { + r_vector_remove_at (vec, i, cc->rc_ptr); + cc->rc_ptr++; + if (!i) { + return true; + } + i--; + } + return true; +} + +static bool collect_mem_changes_cb (void *user, const ut64 key, const void *val) { + RVector *vec = val; + if (R_UNLIKELY (r_vector_empty (vec))) { + return true; + } + TTChangeCollector *cc = user; + ut32 i = r_vector_length (vec) - 1; + TypeTraceMemChange *rc = r_vector_index_ptr (vec, i); + while (rc->idx >= cc->idx) { + r_vector_remove_at (vec, i, cc->mc_ptr); + cc->mc_ptr++; + if (!i) { + return true; + } + i--; + } + return true; +} + +static int sort_reg_changes_cb (const void *v0, const void *v1) { + const TypeTraceRegChange *a = v0; + const TypeTraceRegChange *b = v1; + if (a->idx == b->idx) { + return (int)b->cc - (int)a->cc; + } + return b->idx - a->idx; +} + +static int sort_mem_changes_cb (const void *v0, const void *v1) { + const TypeTraceMemChange *a = v0; + const TypeTraceMemChange *b = v1; + if (a->idx == b->idx) { + return (int)b->cc - (int)a->cc; + } + return b->idx - a->idx; +} + +static void type_trace_restore(TypeTrace *trace, REsil *esil, int idx) { + R_RETURN_IF_FAIL (trace && esil && (idx < trace->idx)); + ut64 v = ((ut64)idx) << 32; + ht_up_foreach (trace->registers, count_changes_above_idx_cb, &v); + ut32 c_num = v & UT32_MAX; + void *data = NULL; + if (c_num) { + data = R_NEWS (TypeTraceRegChange, c_num); + TTChangeCollector collector = {.idx = idx, .data = data}; + ht_up_foreach (trace->registers, collect_reg_changes_cb, &collector); + //sort collected reg changes so that the newest come first + qsort (data, c_num, sizeof (TypeTraceRegChange), sort_reg_changes_cb); + collector.data = data; + ut32 i = 0; + for (; i < c_num; i++) { + r_esil_reg_write_silent (esil, collector.rc_ptr[i].name, collector.rc_ptr[i].odata); + R_FREE (collector.rc_ptr[i].name); + } + } + v &= UT64_MAX ^ UT32_MAX; + ht_up_foreach (trace->memory, count_changes_above_idx_cb, &v); + if (data && (((v & UT32_MAX) * sizeof (TypeTraceMemChange)) > + (c_num * sizeof (TypeTraceRegChange)))) { + c_num = v & UT32_MAX; + void *new_data = realloc (data, sizeof (TypeTraceMemChange) * c_num); + if (!new_data) { + free (data); + return; + } + data = new_data; + } else { + c_num = v & UT32_MAX; + } + if (!c_num) { + free (data); + return; + } + if (R_UNLIKELY (!data)) { + data = R_NEWS (TypeTraceMemChange, c_num); + if (!data) { + return; + } + } + TTChangeCollector collector = {.idx = idx, .data = data}; + ht_up_foreach (trace->memory, collect_mem_changes_cb, &collector); + //sort collected mem changes so that the newest come first + qsort (data, c_num, sizeof (TypeTraceMemChange), sort_mem_changes_cb); + collector.data = data; + ut32 i = 0; + for (;i < c_num; i++) { + r_esil_mem_write_silent (esil, collector.mc_ptr[i].addr, &collector.rc_ptr[i].odata, 1); + } +} +#endif + +R_VEC_TYPE (RVecUT64, ut64); +R_VEC_TYPE (RVecBuf, ut8); + +typedef struct { + REsil esil; + TypeTrace tt; + ut64 stack_base; + RCore *core; + RReg *anal_reg; + int stack_fd; + ut32 stack_map; + RConfigHold *hc; + char *cfg_spec; + bool cfg_breakoninvalid; + bool cfg_chk_constraint; +} TPState; + +/// BEGIN /////////////////// esil trace helpers /////////////////////// + +static int etrace_index(TypeTrace *etrace) { + int len = VecTraceOp_length (&etrace->db.ops); + etrace->cur_idx = len; // > 0? len -1: 0; + return etrace->cur_idx; // VecTraceOp_length (&etrace->db.ops); +} + +static ut64 etrace_addrof(TypeTrace *etrace, ut32 idx) { + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); + return op? op->addr: 0; +} + +static ut64 etrace_memwrite_addr(TypeTrace *etrace, ut32 idx) { + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); + R_LOG_DEBUG ("memwrite %d %d", etrace->idx, idx); + if (op && op->start != op->end) { + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); + while (start <= end) { + if (!start->is_reg && start->is_write) { + return start->mem.addr; + } + start++; + } + } + return 0; +} + +static bool etrace_have_memread(TypeTrace *etrace, ut32 idx) { + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); + R_LOG_DEBUG ("memread %d %d", etrace->idx, idx); + if (op && op->start != op->end) { + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); + while (start <= end) { + if (!start->is_reg && !start->is_write) { + return true; + } + start++; + } + } + return false; +} + +static ut64 etrace_regread_value(TypeTrace *etrace, ut32 idx, const char *rname) { + R_LOG_DEBUG ("regread %d %d", etrace->idx, idx); + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); + if (op && op->start != op->end) { + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); + while (start <= end) { + if (start->is_reg && !start->is_write) { + if (!strcmp (rname, start->reg.name)) { + return start->reg.value; + } + } + start++; + } + } + return 0; +} + +static const char *etrace_regwrite(TypeTrace *etrace, ut32 idx) { + R_LOG_DEBUG ("regwrite %d %d", etrace->idx, idx); + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); + if (op && op->start != op->end) { + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); + while (start <= end) { + if (start->is_reg && start->is_write) { + return start->reg.name; + } + start++; + } + } + return NULL; +} + +/// END ///////////////////// esil trace helpers /////////////////////// + +static bool etrace_regwrite_contains(TypeTrace *etrace, ut32 idx, const char *rname) { + R_LOG_DEBUG ("regwrite contains %d %s", idx, rname); + R_RETURN_VAL_IF_FAIL (etrace && rname, false); + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); // AAA + 1); + if (op && op->start != op->end) { + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); + while (start <= end) { + if (start->is_reg && start->is_write) { + if (!strcmp (rname, start->reg.name)) { + return true; + } + } + start++; + } + } + return false; +} + +static bool type_pos_hit(TPState *tps, bool in_stack, int idx, int size, const char *place) { + R_LOG_DEBUG ("Type pos hit %d %d %d %s", in_stack, idx, size, place); + if (in_stack) { + ut64 sp = r_reg_getv (tps->tt.reg, "SP"); // XXX this is slow too and we can cache + const ut64 write_addr = etrace_memwrite_addr (&tps->tt, idx); // AAA -1 + return (write_addr == sp + size); + } + return place && etrace_regwrite_contains (&tps->tt, idx, place); +} + +static void var_rename(RAnal *anal, RAnalVar *v, const char *name, ut64 addr) { + if (!name || !v) { + return; + } + if (!*name || !strcmp (name , "...")) { + return; + } + bool is_default = (r_str_startswith (v->name, VARPREFIX) + || r_str_startswith (v->name, ARGPREFIX)); + if (*name == '*') { + name++; + } + // longer name tends to be meaningful like "src" instead of "s1" + if (!is_default && (strlen (v->name) > strlen (name))) { + return; + } + RAnalFunction *fcn = r_anal_get_fcn_in (anal, addr, 0); + if (fcn) { + r_anal_var_rename (v, name, false); + } +} + +static void var_retype(RAnal *anal, RAnalVar *var, const char *vname, const char *type, bool ref, bool pfx) { + R_LOG_DEBUG ("Var retype %s %s", var->name, type); + R_RETURN_IF_FAIL (anal && var && type); + // XXX types should be passed without spaces to trim + type = r_str_trim_head_ro (type); + // default type if none is provided + if (!*type) { + type = "int"; + } + bool is_ptr = (vname && *vname == '*'); + // removing this return makes 64bit vars become 32bit + if (r_str_startswith (type, "int") || (!is_ptr && !strcmp (type, "void"))) { + // default or void type + R_LOG_DEBUG ("DEFAULT NOT DOING THIS"); + return; + } + const char *expand = var->type; + if (!strcmp (var->type, "int32_t")) { + expand = "int"; + } else if (!strcmp (var->type, "uint32_t")) { + expand = "unsigned int"; + } else if (!strcmp (var->type, "uint64_t")) { + expand = "unsigned long long"; + } + const char *tmp = strstr (expand, "int"); + bool is_default = tmp; + if (!is_default && !r_str_startswith (var->type, "void")) { + // return since type is already propagated + // except for "void *", since "void *" => "char *" is possible + R_LOG_DEBUG ("not default NOT DOING A SHIT HERE"); + return; + } + RStrBuf *sb = r_strbuf_new (""); + if (pfx) { + if (is_default && !r_str_startswith (var->type, "signed")) { + r_strbuf_setf (sb, "%s %s", type, tmp); + } else { + r_strbuf_free (sb); + R_LOG_DEBUG ("THIS IS RETURN NOT DOING A SHIT HERE"); + return; + } + } else { + r_strbuf_set (sb, type); + } + if (r_str_startswith (r_strbuf_get (sb), "const ")) { + // Dropping const from type + //TODO: Inferring const type + r_strbuf_setf (sb, "%s", type + 6); + } + if (is_ptr) { + //type *ptr => type * + r_strbuf_append (sb, " *"); + } + if (ref) { + if (r_str_endswith (r_strbuf_get (sb), "*")) { // type * => type ** + r_strbuf_append (sb, "*"); + } else { // type => type * + r_strbuf_append (sb, " *"); + } + } + + char* tmp1 = r_strbuf_get (sb); + if (r_str_startswith (tmp1, "unsigned long long")) { + r_strbuf_set (sb, "uint64_t"); + } else if (r_str_startswith (tmp1, "unsigned")) { + r_strbuf_set (sb, "uint32_t"); + } else if (r_str_startswith (tmp1, "int")) { + r_strbuf_set (sb, "int32_t"); + } + r_anal_var_set_type (var, r_strbuf_get (sb)); + r_strbuf_free (sb); +} + +static void get_src_regname(RCore *core, ut64 addr, char *regname, int size) { + R_RETURN_IF_FAIL (core && regname && size > 0); + RAnal *anal = core->anal; + regname[0] = 0; + RAnalOp *op = r_core_anal_op (core, addr, R_ARCH_OP_MASK_VAL | R_ARCH_OP_MASK_ESIL); + if (!op || r_strbuf_is_empty (&op->esil)) { + r_anal_op_free (op); + return; + } + char *op_esil = r_strbuf_get (&op->esil); + char *tmp = strchr (op_esil, ','); + if (tmp) { + *tmp = '\0'; + } + RRegItem *ri = r_reg_get (anal->reg, op_esil, -1); + if (ri) { + const char *s = op_esil; + if ((anal->config->bits == 64) && (ri->size == 32)) { + const char *reg = r_reg_32_to_64 (anal->reg, op_esil); + if (reg) { + s = reg; + } + } + if (s) { + r_str_ncpy (regname, s, size); + } + R_LOG_DEBUG ("===================regitem %s", regname); + r_unref (ri); + } else { + R_LOG_DEBUG ("no regitem %s at 0x%"PFMT64x, op_esil, addr); + } + r_anal_op_free (op); +} + +static ut64 get_addr(TypeTrace *et, const char *regname, int idx) { + if (R_STR_ISEMPTY (regname)) { + return 0; + } + /// r_strf_var (query, 64, "%d.reg.read.%s", idx, regname); + // return r_num_math (NULL, sdb_const_get (trace, query, 0)); + return etrace_regread_value (et, idx, regname); +} + +static RAnalCondType cond_invert(RAnal *anal, RAnalCondType cond) { + switch (cond) { + case R_ANAL_CONDTYPE_LE: + return R_ANAL_CONDTYPE_GT; + case R_ANAL_CONDTYPE_LT: + return R_ANAL_CONDTYPE_GE; + case R_ANAL_CONDTYPE_GE: + return R_ANAL_CONDTYPE_LT; + case R_ANAL_CONDTYPE_GT: + return R_ANAL_CONDTYPE_LE; + case R_ANAL_CONDTYPE_AL: + return R_ANAL_CONDTYPE_NV; + case R_ANAL_CONDTYPE_NV: + return R_ANAL_CONDTYPE_AL; + case R_ANAL_CONDTYPE_EQ: + return R_ANAL_CONDTYPE_NE; + case R_ANAL_CONDTYPE_NE: + return R_ANAL_CONDTYPE_EQ; + default: + R_LOG_WARN ("unhandled condition for swapping %d", cond); + break; + } + return 0; // 0 is COND_ALways... + /* I haven't looked into it but I suspect that this might be confusing: + the opposite of any condition not in the list above is "always"? */ +} + +typedef const char* String; +R_VEC_TYPE (RVecString, String); // no fini, these are owned by SDB + +static bool parse_format(TPState *tps, const char *fmt, RVecString *vec) { + if (R_STR_ISEMPTY (fmt)) { + return false; + } + + Sdb *s = tps->core->anal->sdb_fmts; + char arr[32] = {0}; + const char *ptr = strchr (fmt, '%'); + while (ptr) { + ptr++; + // strip [width] specifier + while (isdigit (*ptr)) { + ptr++; + } + r_str_ncpy (arr, ptr, sizeof (arr) - 1); + char *tmp = arr; + while (isalpha (*tmp)) { + tmp++; + } + *tmp = '\0'; + r_strf_var (query, 128, "spec.%s.%s", tps->cfg_spec, arr); + const char *type = sdb_const_get (s, query, 0); // maybe better to return an owned pointer here? + if (type) { + RVecString_push_back (vec, &type); + } + // ptr = strchr (ptr + (tmp-arr), '%'); + ptr = strchr (ptr, '%'); + } + + return true; +} + +static void retype_callee_arg(RAnal *anal, const char *callee_name, bool in_stack, const char *place, int size, const char *type) { + R_LOG_DEBUG (">>> CALLE ARG"); + RAnalFunction *fcn = r_anal_get_function_byname (anal, callee_name); + if (!fcn) { + return; + } + if (in_stack) { + RAnalVar *var = r_anal_function_get_var (fcn, R_ANAL_VAR_KIND_BPV, size - fcn->bp_off + 8); + if (!var) { + return; + } + var_retype (anal, var, NULL, type, false, false); + } else { + RRegItem *item = r_reg_get (anal->reg, place, -1); + if (!item) { + return; + } + RAnalVar *rvar = r_anal_function_get_var (fcn, R_ANAL_VAR_KIND_REG, item->index); + if (!rvar) { + return; + } + char *t = strdup (type); + var_retype (anal, rvar, NULL, type, false, false); + RAnalVar *lvar = r_anal_var_get_dst_var (rvar); + if (lvar) { + var_retype (anal, lvar, NULL, t, false, false); + } + free (t); + } +} + +#define DEFAULT_MAX 3 +#define REGNAME_SIZE 10 +#define MAX_INSTR 5 + +/** + * type match at a call instruction inside another function + * + * \param fcn_name name of the callee + * \param addr addr of the call instruction + * \param baddr addr of the caller function + * \param cc cc of the callee + * \param prev_idx index in the esil trace + * \param userfnc whether the callee is a user function (affects propagation direction) + * \param caddr addr of the callee + */ +static void type_match(TPState *tps, char *fcn_name, ut64 addr, ut64 baddr, const char* cc, + int prev_idx, bool userfnc, ut64 caddr) { + RAnal *anal = tps->core->anal; + TypeTrace *et = &tps->tt; + Sdb *TDB = anal->sdb_types; + const int idx = etrace_index (et) -1; + const bool verbose = r_config_get_b (tps->core->config, "anal.types.verbose"); // XXX + bool stack_rev = false, in_stack = false, format = false; + R_LOG_DEBUG ("type_match %s %"PFMT64x" %"PFMT64x" %s %d", fcn_name, addr, baddr, cc, prev_idx); + + if (!fcn_name || !cc) { + return; + } + int i, j, pos = 0, size = 0, max = r_type_func_args_count (TDB, fcn_name); + int lastarg = ST32_MAX; + const char *place = r_anal_cc_arg (anal, cc, lastarg, -1); + r_cons_break_push (NULL, NULL); + + if (place && !strcmp (place, "stack_rev")) { + stack_rev = true; + } + place = r_anal_cc_arg (anal, cc, 0, -1); + if (place && r_str_startswith (place, "stack")) { + in_stack = true; + } + if (verbose && r_str_startswith (fcn_name, "sym.imp.")) { + R_LOG_WARN ("Missing function definition for '%s'", fcn_name + 8); + } + if (!max) { + max = in_stack? DEFAULT_MAX : r_anal_cc_max_arg (anal, cc); + } + // TODO: if function takes more than 7 args is usually bad analysis + if (max > 7) { + max = DEFAULT_MAX; + } + + RVecString types; + RVecString_init (&types); + const int bytes = anal->config->bits / 8; + const ut32 opmask = R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_VAL; + for (i = 0; i < max; i++) { + int arg_num = stack_rev ? (max - 1 - i) : i; + char *type = NULL; + const char *name = NULL; + R_LOG_DEBUG ("ARG NUM %d %d %d", i, arg_num, format); + if (format) { + if (RVecString_empty (&types)) { + break; + } + const String *type_ = RVecString_at (&types, pos++); + type = type_ ? R_STR_DUP (*type_) : NULL; + R_LOG_DEBUG ("TYPE (%s)", type); + } else { + type = r_type_func_args_type (TDB, fcn_name, arg_num); + name = r_type_func_args_name (TDB, fcn_name, arg_num); + } + if (!type && !userfnc) { + R_LOG_DEBUG ("NO TYPE AND NO USER FUNK"); + continue; + } + if (!in_stack) { + // XXX: param arg_num must be fixed to support floating point register + // before this change place could be null + R_LOG_DEBUG ("not in stack"); + const char *p = r_anal_cc_arg (anal, cc, arg_num, -1); + if (p && r_str_startswith (p, "stack")) { + in_stack = true; + place = p; + } + place = p; + } + char regname[REGNAME_SIZE] = {0}; + ut64 xaddr = UT64_MAX; + bool memref = false; + bool cmt_set = false; + bool res = false; + // Backtrace instruction from source sink to prev source sink + ///// eprintf ("ii %d %d\n", j, prev_idx); + for (j = idx; j >= prev_idx; j--) { + // r_strf_var (k, 32, "%d.addr", j); + // ut64 instr_addr = sdb_num_get (trace, k, 0); + ut64 instr_addr = etrace_addrof (et, j); + R_LOG_DEBUG ("0x%08"PFMT64x" back traceing %d", instr_addr, j); + if (instr_addr < baddr) { + break; + } + RAnalOp *op = r_core_anal_op (tps->core, instr_addr, opmask); + if (!op) { + r_anal_op_free (op); + break; + } + RAnalOp *next_op = r_core_anal_op (tps->core, instr_addr + op->size, opmask); + if (!next_op || (j != idx && (next_op->type == R_ANAL_OP_TYPE_CALL || next_op->type == R_ANAL_OP_TYPE_JMP))) { + r_anal_op_free (op); + r_anal_op_free (next_op); + break; + } + RAnalVar *var = r_anal_get_used_function_var (anal, op->addr); + if (op->type == R_ANAL_OP_TYPE_MOV && etrace_have_memread (et, j)) { + memref = ! (!memref && var && (var->kind != R_ANAL_VAR_KIND_REG)); + } + // Match type from function param to instr + if (type_pos_hit (tps, in_stack, j, size, place)) { + R_LOG_DEBUG ("InHit"); + if (!cmt_set && type && name) { + char *ms = r_str_newf ("%s%s%s", type, r_str_endswith (type, "*") ? "" : " ", name); + r_meta_set_string (anal, R_META_TYPE_VARTYPE, instr_addr, ms); + free (ms); + cmt_set = true; + if ((op->ptr && op->ptr != UT64_MAX) && !strcmp (name, "format")) { + RFlagItem *f = r_flag_get_by_spaces (tps->core->flags, false, op->ptr, R_FLAGS_FS_STRINGS, NULL); + if (f) { + char formatstr[0x200]; + int read = r_io_nread_at (tps->core->io, f->addr, (ut8 *)formatstr, R_MIN (sizeof (formatstr) - 1, f->size)); + if (read > 0) { + formatstr[read] = '\0'; + RVecString_clear (&types); + if (parse_format (tps, formatstr, &types)) { + max += RVecString_length (&types); + } + format = true; + } + } + } + } + if (var) { + R_LOG_DEBUG ("retype var %s", name); + if (!userfnc) { + // not a userfunction, propagate the callee's arg types into our function's vars + var_retype (anal, var, name, type, memref, false); + var_rename (anal, var, name, addr); + } else { + // callee is a userfunction, propagate our variable's type into the callee's args + retype_callee_arg (anal, fcn_name, in_stack, place, size, var->type); + } + res = true; + } else { + get_src_regname (tps->core, instr_addr, regname, sizeof (regname)); + xaddr = get_addr (et, regname, j); + } + } + // Type propagate by following source reg + if (!res && *regname && etrace_regwrite_contains (et, j, regname)) { + if (var) { + if (!userfnc) { + // not a userfunction, propagate the callee's arg types into our function's vars + var_retype (anal, var, name, type, memref, false); + var_rename (anal, var, name, addr); + } else { + // callee is a userfunction, propagate our variable's type into the callee's args + retype_callee_arg (anal, fcn_name, in_stack, place, size, var->type); + } + res = true; + } else { + switch (op->type) { + case R_ANAL_OP_TYPE_MOV: + case R_ANAL_OP_TYPE_PUSH: + get_src_regname (tps->core, instr_addr, regname, sizeof (regname)); + break; + case R_ANAL_OP_TYPE_LEA: + case R_ANAL_OP_TYPE_LOAD: + case R_ANAL_OP_TYPE_STORE: + res = true; + break; + } + } + } else if (var && res && (xaddr && xaddr != UT64_MAX)) { // Type progation using value + char tmp[REGNAME_SIZE] = {0}; + get_src_regname (tps->core, instr_addr, tmp, sizeof (tmp)); + ut64 ptr = get_addr (et, tmp, j); + if (ptr == xaddr) { + var_retype (anal, var, name, r_str_get_fail (type, "int"), memref, false); + } + } + r_anal_op_free (op); + r_anal_op_free (next_op); + } + size += bytes; + free (type); + } + RVecString_fini (&types); + r_cons_break_pop (); +} + +static int bb_cmpaddr(const void *_a, const void *_b) { + const RAnalBlock *a = _a, *b = _b; + return a->addr > b->addr? 1: (a->addr < b->addr? -1: 0); +} + +static void tps_fini(TPState *tps) { + R_RETURN_IF_FAIL (tps); + tps->core->anal->reg = tps->anal_reg; + type_trace_fini (&tps->tt, &tps->esil); + r_esil_fini (&tps->esil); + r_io_fd_close (tps->core->io, tps->stack_fd); + free (tps->cfg_spec); + r_config_hold_restore (tps->hc); + r_config_hold_free (tps->hc); + free (tps); +} + +static bool tt_is_reg(void *reg, const char *name) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return false; + } + r_unref (ri); + return true; +} + +static bool tt_reg_read(void *reg, const char *name, ut64 *val) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return false; + } + *val = r_reg_get_value ((RReg *)reg, ri); + r_unref (ri); + return true; +} + +static ut32 tt_reg_size(void *reg, const char *name) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return 0; + } + ut32 size = ri->size; + r_unref (ri); + return size; +} + +static REsilRegInterface type_trace_reg_if = { + .is_reg = tt_is_reg, + .reg_read = tt_reg_read, + .reg_write = (REsilRegWrite)r_reg_setv, + .reg_size = tt_reg_size, + // .reg_alias = default_reg_alias +}; + +static bool tt_mem_read (void *mem, ut64 addr, ut8 *buf, int len) { + TPState *tps = (TPState *)mem; + return r_io_read_at (tps->core->io, addr, buf, len); +} + +// ensures type trace esil engine only writes to it's designated stack map. +// writes outside of that itv will be assumed as valid and return true. +// this function assumes, that stack map has highest priority, +// or does not overlap with any other map. +static bool tt_mem_write (void *mem, ut64 addr, const ut8 *buf, int len) { + TPState *tps = (TPState *)mem; + RIOMap *map = r_io_map_get (tps->core->io, tps->stack_map); + RInterval itv = {addr, len}; + if (!r_itv_overlap (map->itv, itv)) { + return true; + } + itv = r_itv_intersect (map->itv, itv); + return r_io_write_at (tps->core->io, itv.addr, &buf[itv.addr - addr], (int)itv.size); +} + +static REsilMemInterface type_trace_mem_if = { + .mem_read = tt_mem_read, + .mem_write = tt_mem_write +}; + +//XXX: this name is wrong +static TPState *tps_init(RCore *core) { + R_RETURN_VAL_IF_FAIL (core && core->io && core->anal && core->anal->esil, NULL); + TPState *tps = R_NEW0 (TPState); + RConfig *cfg = core->config; + tps->core = core; + int align = r_arch_info (core->anal->arch, R_ARCH_INFO_DATA_ALIGN); + align = R_MAX (r_arch_info (core->anal->arch, R_ARCH_INFO_CODE_ALIGN), align); + align = R_MAX (align, 1); + tps->stack_base = r_config_get_i (core->config, "esil.stack.addr"); + ut64 stack_size = r_config_get_i (core->config, "esil.stack.size"); + //ideally this all would happen in a dedicated temporal io bank + if (!r_io_map_locate (core->io, &tps->stack_base, stack_size, align)) { + free (tps); + return NULL; + } + char *uri = r_str_newf ("malloc://0x%"PFMT64x, stack_size); + if (!uri) { + free (tps); + return NULL; + } + tps->stack_fd = r_io_fd_open (core->io, uri, R_PERM_RW, 0); + free (uri); + RIOMap *map = r_io_map_add (core->io, tps->stack_fd, R_PERM_RW, 0, tps->stack_base, stack_size); + if (!map) { + r_io_fd_close (core->io, tps->stack_fd); + free (tps); + return NULL; + } + //XXX: r_reg_clone should be invoked in type_trace_init + RReg *reg = r_reg_clone (core->anal->reg); + if (!reg) { + r_io_fd_close (core->io, tps->stack_fd); + free (tps); + return NULL; + } + tps->stack_map = map->id; + //todo fix addrsize + type_trace_reg_if.reg = reg; + type_trace_mem_if.mem = tps; + ut64 sp = tps->stack_base + stack_size - (stack_size % align) - align * 8; + //todo: this probably needs some boundary checks + r_reg_setv (reg, "SP", sp); + r_reg_setv (reg, "BP", sp); + if (!r_esil_init (&tps->esil, 4096, false, 64, &type_trace_reg_if, &type_trace_mem_if)) { + r_reg_free (reg); + r_io_fd_close (core->io, tps->stack_fd); + free (tps); + return NULL; + } + if (!type_trace_init (&tps->tt, &tps->esil, reg)) { + r_esil_fini (&tps->esil); + r_reg_free (reg); + r_io_fd_close (core->io, tps->stack_fd); + free (tps); + return NULL; + } +//XXX: HACK + tps->anal_reg = core->anal->reg; + tps->esil.anal = core->anal; + core->anal->reg = reg; + tps->hc = r_config_hold_new (cfg); + tps->cfg_spec = strdup (r_config_get (cfg, "anal.types.spec")); + tps->cfg_breakoninvalid = r_config_get_b (cfg, "esil.breakoninvalid"); + tps->cfg_chk_constraint = r_config_get_b (cfg, "anal.types.constraint"); + r_config_hold (tps->hc, "dbg.follow", NULL); + r_config_set_i (cfg, "dbg.follow", 0); + return tps; +} + +R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { + R_RETURN_IF_FAIL (core && core->anal && fcn); + + // const int op_tions = R_ARCH_OP_MASK_BASIC ;//| R_ARCH_OP_MASK_VAL | R_ARCH_OP_MASK_ESIL | R_ARCH_OP_MASK_HINT; + const int op_tions = R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_HINT | R_ARCH_OP_MASK_ESIL; + RAnalBlock *bb; + RListIter *it; + RAnalOp aop = {0}; + bool resolved = false; + RAnal *anal = core->anal; + Sdb *TDB = anal->sdb_types; + int ret; + const int mininstrsz = r_anal_archinfo (anal, R_ARCH_INFO_MINOP_SIZE); + const int minopcode = R_MAX (1, mininstrsz); + int cur_idx, prev_idx = 0; + TPState *tps = tps_init (core); + if (!tps) { + return; + } + + tps->tt.cur_idx = 0; + const bool be = R_ARCH_CONFIG_IS_BIG_ENDIAN (core->rasm->config); + char *fcn_name = NULL; + char *ret_type = NULL; + bool str_flag = false; + bool prop = false; + bool prev_var = false; + char prev_type[256] = {0}; + const char *prev_dest = NULL; + char *ret_reg = NULL; + r_cons_break_push (NULL, NULL); + RVecBuf buf; + RVecBuf_init (&buf); + RVecUT64 bblist; + RVecUT64_init (&bblist); + RAnalOp *next_op = R_NEW0 (RAnalOp); + r_list_sort (fcn->bbs, bb_cmpaddr); // TODO: The algorithm can be more accurate if blocks are followed by their jmp/fail, not just by address + int retries = 2; +repeat: + if (retries < 0) { + tps_fini (tps); + return; + } + RVecUT64_clear (&bblist); + size_t bblist_size = r_list_length (fcn->bbs); // TODO: Use ut64 + RVecUT64_reserve (&bblist, bblist_size); + // TODO: add a dependency graph out of it, maybe just saving the depth index is enough so we save and restore the state on each level + r_list_foreach (fcn->bbs, it, bb) { + RVecUT64_push_back (&bblist, &bb->addr); + } + int i, j; + TypeTrace *etrace = &tps->tt; + for (j = 0; j < bblist_size; j++) { + const ut64 bbat = *RVecUT64_at (&bblist, j); + bb = r_anal_get_block_at (core->anal, bbat); + if (!bb) { + R_LOG_WARN ("basic block at 0x%08"PFMT64x" was removed during analysis", bbat); + retries--; + goto repeat; + } + ut64 bb_addr = bb->addr; + ut64 bb_size = bb->size; + const ut64 buf_size = bb->size + 32; + if (!RVecBuf_reserve (&buf, buf_size)) { + break; + } + ut8 *buf_ptr = R_VEC_START_ITER (&buf); + if (r_io_read_at (core->io, bb_addr, buf_ptr, bb_size) < 1) { + break; + } + ut64 addr = bb_addr; + for (i = 0; i < bb_size;) { + if (r_cons_is_breaked ()) { + goto out_function; + } + // XXX fail sometimes + /// addr = bb_addr + i; + r_reg_setv (etrace->reg, "PC", addr); + ut64 bb_left = bb_size - i; + if ((addr >= bb_addr + bb_size) || (addr < bb_addr)) { + // stop emulating this bb if pc is outside the basic block boundaries + break; + } + ret = r_anal_op (anal, &aop, addr, buf_ptr + i, bb_left, op_tions); + if (ret <= 0) { + i += minopcode; + addr += minopcode; + r_reg_setv (etrace->reg, "PC", addr); + r_anal_op_fini (&aop); + continue; + } + const int loop_count = type_trace_loopcount (etrace, addr); +#if 1 + if (loop_count > LOOP_MAX || aop.type == R_ANAL_OP_TYPE_RET) { + r_anal_op_fini (&aop); + break; + } +#endif + type_trace_loopcount_increment (etrace, addr); + r_reg_setv (etrace->reg, "PC", addr + aop.size); + if (!r_anal_op_nonlinear (aop.type)) { // skip jmp/cjmp/trap/ret/call ops +//this shit probably needs further refactoring. i hate this code + if (aop.type == R_ANAL_OP_TYPE_ILL || aop.type == R_ANAL_OP_TYPE_UNK) { + if (tps->cfg_breakoninvalid) { + R_LOG_ERROR ("step failed at 0x%08"PFMT64x, addr); + r_anal_op_fini (&aop); + retries = -1; + goto repeat; + } + goto bla; + } + if ((type_trace_op (etrace, &tps->esil, &aop)) && tps->cfg_breakoninvalid) { + R_LOG_ERROR ("step failed at 0x%08"PFMT64x, addr); + retries--; + goto repeat; + } + } +bla: +#if 1 + // XXX this code looks wrong and slow maybe is not needed + // maybe the basic block is gone after the step + if (i < bblist_size) { + bb = r_anal_get_block_at (core->anal, bb_addr); + if (!bb) { + R_LOG_WARN ("basic block at 0x%08"PFMT64x" was removed during analysis", *RVecUT64_at (&bblist, i)); + retries--; + goto repeat; + } + } +#endif + bool userfnc = false; + cur_idx = etrace_index (etrace) - 1; + if (cur_idx < 0) { + cur_idx = 0; + } + tps->tt.cur_idx = etrace_index (etrace); + RAnalVar *var = r_anal_get_used_function_var (anal, aop.addr); + + // XXX this is analyzing the same op twice wtf this is so wrong +#if 0 + RAnalOp *next_op = r_core_anal_op (core, addr + ret, R_ARCH_OP_MASK_BASIC); // | _VAL ? +#else + if (i + aop.size < bb_size) { + r_anal_op_fini (next_op); + // int ret2 = r_anal_op (anal, next_op, addr + ret, buf_ptr + i + ret, bb_left - ret, op_tions); + int ret2 = r_anal_op (anal, next_op, addr + ret, buf_ptr + i + ret, bb_left - ret, R_ARCH_OP_MASK_BASIC); + if (ret2 < 1) { + r_anal_op_fini (&aop); + break; + } + } else { + r_anal_op_fini (next_op); + } +#endif + + ut32 type = aop.type & R_ANAL_OP_TYPE_MASK; + if (aop.type == R_ANAL_OP_TYPE_CALL || aop.type & R_ANAL_OP_TYPE_UCALL) { + char *full_name = NULL; + ut64 callee_addr = UT64_MAX; + if (aop.type == R_ANAL_OP_TYPE_CALL) { + RAnalFunction *fcn_call = r_anal_get_fcn_in (anal, aop.jump, -1); + if (fcn_call) { + full_name = fcn_call->name; + callee_addr = fcn_call->addr; + } + } else if (aop.ptr != UT64_MAX) { + RFlagItem *flag = r_flag_get_by_spaces (core->flags, false, aop.ptr, R_FLAGS_FS_IMPORTS, NULL); + if (flag && flag->realname) { + full_name = flag->realname; + callee_addr = aop.ptr; + } + } + if (full_name) { + if (r_type_func_exist (TDB, full_name)) { + fcn_name = strdup (full_name); + } else { + fcn_name = r_type_func_guess (TDB, full_name); + } + if (!fcn_name) { + fcn_name = strdup (full_name); + userfnc = true; + } + const char* Cc = r_anal_cc_func (anal, fcn_name); + R_LOG_DEBUG ("CC can %s %s", Cc, fcn_name); + if (Cc && r_anal_cc_exist (anal, Cc)) { + char *cc = strdup (Cc); + type_match (tps, fcn_name, addr, bb->addr, cc, prev_idx, userfnc, callee_addr); + // prev_idx = tps->tt.cur_idx; + prev_idx = etrace->cur_idx; + R_FREE (ret_type); + const char *rt = r_type_func_ret (TDB, fcn_name); + if (rt) { + ret_type = strdup (rt); + } + R_FREE (ret_reg); + const char *rr = r_anal_cc_ret (anal, cc); + if (rr) { + ret_reg = strdup (rr); + } + resolved = false; + free (cc); + } + if (!strcmp (fcn_name, "__stack_chk_fail")) { + // r_strf_var (query, 32, "%d.addr", cur_idx - 1); + // ut64 mov_addr = sdb_num_get (trace, query, 0); + // cur_idx = tps->tt.cur_idx - 2; + cur_idx = etrace->cur_idx - 2; + // eprintf (Color_GREEN"ADDROF %d\n"Color_RESET, cur_idx); + ut64 mov_addr = etrace_addrof (etrace, cur_idx); + RAnalOp *mop = r_core_anal_op (core, mov_addr, R_ARCH_OP_MASK_VAL | R_ARCH_OP_MASK_BASIC); + if (mop) { + RAnalVar *mopvar = r_anal_get_used_function_var (anal, mop->addr); + ut32 vt = mop->type & R_ANAL_OP_TYPE_MASK; + if (vt == R_ANAL_OP_TYPE_MOV) { + var_rename (anal, mopvar, "canary", addr); + } + } + r_anal_op_free (mop); + } + free (fcn_name); + } + } else if (!resolved && ret_type && ret_reg) { + // Forward propgation of function return type + char src[REGNAME_SIZE] = {0}; + // r_strf_var (query, 32, "%d.reg.write", cur_idx); + // const char *cur_dest = sdb_const_get (trace, query, 0); + // sdb_const_get (trace, query, 0); + // cur_idx = tps->tt.cur_idx - 1; + cur_idx = etrace->cur_idx - 1; + const char *cur_dest = etrace_regwrite (etrace, cur_idx); + get_src_regname (core, aop.addr, src, sizeof (src)); + if (ret_reg && *src && strstr (ret_reg, src)) { + if (var && aop.direction == R_ANAL_OP_DIR_WRITE) { + var_retype (anal, var, NULL, ret_type, false, false); + resolved = true; + } else if (type == R_ANAL_OP_TYPE_MOV) { + R_FREE (ret_reg); + if (cur_dest) { + ret_reg = strdup (cur_dest); + } + } + } else if (cur_dest) { + char *foo = strdup (cur_dest); + char *tmp = strchr (foo, ','); + if (tmp) { + *tmp++ = '\0'; + } + if (ret_reg && (strstr (ret_reg, foo) || (tmp && strstr (ret_reg, tmp)))) { + resolved = true; + } else if (type == R_ANAL_OP_TYPE_MOV && (next_op && next_op->type == R_ANAL_OP_TYPE_MOV)) { + // Progate return type passed using pointer + // int *ret; *ret = strlen(s); + // TODO: memref check , dest and next src match + char nsrc[REGNAME_SIZE] = {0}; + get_src_regname (core, next_op->addr, nsrc, sizeof (nsrc)); + if (ret_reg && *nsrc && strstr (ret_reg, nsrc) && var && aop.direction == R_ANAL_OP_DIR_READ) { + var_retype (anal, var, NULL, ret_type, true, false); + } + } + free (foo); + } + } + // Type propagation using instruction access pattern + if (var) { + bool sign = false; + if ((type == R_ANAL_OP_TYPE_CMP) && next_op) { + if (next_op->sign) { + sign = true; + } else { + // cmp [local_ch], rax ; jb + var_retype (anal, var, NULL, "unsigned", false, true); + } + } + // cmp [local_ch], rax ; jge + if (sign || aop.sign) { + var_retype (anal, var, NULL, "signed", false, true); + } + // lea rax , str.hello ; mov [local_ch], rax; + // mov rdx , [local_4h] ; mov [local_8h], rdx; + if (prev_dest && (type == R_ANAL_OP_TYPE_MOV || type == R_ANAL_OP_TYPE_STORE)) { + char reg[REGNAME_SIZE] = {0}; + get_src_regname (core, addr, reg, sizeof (reg)); + bool match = strstr (prev_dest, reg); + if (str_flag && match) { + var_retype (anal, var, NULL, "const char *", false, false); + } + if (prop && match && prev_var) { + var_retype (anal, var, NULL, prev_type, false, false); + } + } + if (tps->cfg_chk_constraint && var && (type == R_ANAL_OP_TYPE_CMP && aop.disp != UT64_MAX) + && next_op && next_op->type == R_ANAL_OP_TYPE_CJMP) { + bool jmp = false; + RAnalOp *jmp_op = {0}; + ut64 jmp_addr = next_op->jump; + RAnalBlock *jmpbb = r_anal_function_bbget_in (anal, fcn, jmp_addr); + RAnalBlock jbb = {0}; + if (jmpbb) { + // the bb can be invalidated in the loop below, causing + // a crash, so we copy that into a stack ghosty struct + jbb.addr = jmpbb->addr; + jbb.size = jmpbb->size; + } + + // Check exit status of jmp branch + for (i = 0; i < MAX_INSTR; i++) { + jmp_op = r_core_anal_op (core, jmp_addr, R_ARCH_OP_MASK_BASIC); + if (!jmp_op) { + r_anal_op_free (jmp_op); + r_anal_op_fini (&aop); + break; + } + if ((jmp_op->type == R_ANAL_OP_TYPE_RET && r_anal_block_contains (&jbb, jmp_addr)) + || jmp_op->type == R_ANAL_OP_TYPE_CJMP) { + jmp = true; + r_anal_op_free (jmp_op); + r_anal_op_fini (&aop); + break; + } + jmp_addr += jmp_op->size; + r_anal_op_free (jmp_op); + } + RAnalVarConstraint constr = { + .cond = jmp? cond_invert (anal, next_op->cond): next_op->cond, + .val = aop.val + }; + r_anal_var_add_constraint (var, &constr); + } + } + prev_var = (var && aop.direction == R_ANAL_OP_DIR_READ); + str_flag = false; + prop = false; + prev_dest = NULL; + switch (type) { + case R_ANAL_OP_TYPE_MOV: + case R_ANAL_OP_TYPE_LEA: + case R_ANAL_OP_TYPE_LOAD: + if (aop.ptr && aop.refptr && aop.ptr != UT64_MAX) { + if (type == R_ANAL_OP_TYPE_LOAD) { + ut8 sbuf[256] = {0}; + r_io_read_at (core->io, aop.ptr, sbuf, sizeof (sbuf) - 1); + ut64 ptr = r_read_ble (sbuf, be, aop.refptr * 8); + if (ptr && ptr != UT64_MAX) { + RFlagItem *f = r_flag_get_by_spaces (core->flags, false, ptr, R_FLAGS_FS_STRINGS, NULL); + if (f) { + str_flag = true; + } + } + } else if (r_flag_exist_at (core->flags, "str", 3, aop.ptr)) { + str_flag = true; + } + } + // mov dword [local_4h], str.hello; + if (var && str_flag) { + var_retype (anal, var, NULL, "const char *", false, false); + } + prev_dest = etrace_regwrite (etrace, cur_idx); + if (var) { + r_str_ncpy (prev_type, var->type, sizeof (prev_type) - 1); + prop = true; + } + } + i += ret; + addr += ret; + // XXX its slow to analyze 2 instructions for every instruction :facepalm: we can reuse + r_anal_op_fini (next_op); + r_anal_op_fini (&aop); + } + } + R_FREE (next_op); + RVecBuf_fini (&buf); + RVecUT64_fini (&bblist); + + // Type propagation for register based args + RList *list = r_anal_var_list (anal, fcn, R_ANAL_VAR_KIND_REG); + RAnalVar *rvar; + RListIter *iter; + r_list_foreach (list, iter, rvar) { + RAnalVar *lvar = r_anal_var_get_dst_var (rvar); + RRegItem *i = r_reg_index_get (anal->reg, rvar->delta); + if (i && lvar) { + // Propagate local var type = to => register-based var + var_retype (anal, rvar, NULL, lvar->type, false, false); + // Propagate local var type <= from = register-based var + var_retype (anal, lvar, NULL, rvar->type, false, false); + } + } + r_list_free (list); +out_function: + R_FREE (ret_reg); + R_FREE (ret_type); + r_anal_op_fini (&aop); + r_cons_break_pop (); + RVecBuf_fini (&buf); + RVecUT64_fini (&bblist); + tps_fini (tps); +} diff --git a/libr/core/cconfig.c b/libr/core/cconfig.c index 2c7148889796d..bd98ea521ad5e 100644 --- a/libr/core/cconfig.c +++ b/libr/core/cconfig.c @@ -486,9 +486,15 @@ static bool cb_arch_platform(void *user, void *data) { static bool cb_archbits(void *user, void *data) { R_RETURN_VAL_IF_FAIL (user && data, false); - RCore *core = (RCore *)user; + RCore *core = user; +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif RConfigNode *node = (RConfigNode *)data; r_arch_set_bits (core->anal->arch, node->i_value); +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif return true; } @@ -501,10 +507,13 @@ static bool cb_archbits_getter(RCore *core, RConfigNode *node) { } static bool cb_archendian(void *user, void *data) { - RCore *core = (RCore *)user; - RConfigNode *node = (RConfigNode *)data; + RCore *core = user; + RConfigNode *node = data; R_RETURN_VAL_IF_FAIL (node && core && core->anal && core->anal->arch, false); if (!strcmp (node->value, "big") || !strcmp (node->value, "bigswap")) { +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif r_arch_set_endian (core->anal->arch, R_SYS_ENDIAN_BIG); RConfigNode *be = r_config_node_get (core->config, "cfg.bigendian"); if (be) { @@ -512,9 +521,15 @@ static bool cb_archendian(void *user, void *data) { be->value = strdup ("true"); be->i_value = 1; } +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif return true; } if (!strcmp (node->value, "little") || !strcmp (node->value, "littleswap")) { +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif r_arch_set_endian (core->anal->arch, R_SYS_ENDIAN_LITTLE); RConfigNode *be = r_config_node_get (core->config, "cfg.bigendian"); if (be) { @@ -522,9 +537,15 @@ static bool cb_archendian(void *user, void *data) { be->value = strdup ("false"); be->i_value = 0; } +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif return true; } if (!strcmp (node->value, "middle")) { +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif r_arch_set_endian (core->anal->arch, R_SYS_ENDIAN_MIDDLE); RConfigNode *be = r_config_node_get (core->config, "cfg.bigendian"); if (be) { @@ -532,6 +553,9 @@ static bool cb_archendian(void *user, void *data) { be->value = strdup ("false"); be->i_value = 0; } +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif return true; } return false; @@ -735,7 +759,13 @@ static bool cb_asmcpu(void *user, void *data) { #endif return 0; } +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif r_arch_config_set_cpu (core->rasm->config, node->value); +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif const int v = r_anal_archinfo (core->anal, R_ARCH_INFO_CODE_ALIGN); if (v >= 0) { core->anal->config->codealign = v; @@ -1376,12 +1406,21 @@ static bool cb_bigendian(void *user, void *data) { core->dbg->bp->endian = isbig; } core->rasm->config->endian = endianType; +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif r_arch_set_endian (core->anal->arch, endianType); +<<<<<<< HEAD RConfigNode *ae = r_config_node_get (core->config, "arch.endian"); if (ae) { free (ae->value); ae->value = strdup (node->i_value? "big": "little"); } +======= +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif +>>>>>>> 4f529a73c9 (Initialize core_esil on core init and autoupdate on arch config changes) return true; } @@ -2102,6 +2141,15 @@ static bool cb_gotolimit(void *user, void *data) { return true; } +static bool cb_esilmaxbacksteps(void *user, void *data) { + RCore *core = user; + RConfigNode *node = data; + if (core->esil.reg) { //hack + r_core_esil_set_max_stepback (core, node->i_value); + } + return true; +} + static bool cb_esilverbose(void *user, void *data) { RCore *core = user; RConfigNode *node = data; @@ -2356,12 +2404,18 @@ R_API bool r_core_esil_cmd(REsil *esil, const char *cmd, ut64 a1, ut64 a2) { } static bool cb_cmd_esil_ioer(void *user, void *data) { - RCore *core = (RCore *)user; - RConfigNode *node = (RConfigNode *)data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->esil.cmd_ioer); + core->esil.cmd_ioer = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->cmd_ioer); core->anal->esil->cmd_ioer = strdup (node->value); +#endif } return true; } @@ -2389,12 +2443,18 @@ static bool cb_cmd_esil_intr(void *user, void *data) { } static bool cb_mdevrange(void *user, void *data) { - RCore *core = (RCore *)user; - RConfigNode *node = (RConfigNode *)data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->esil.mdev_range); + core->esil.mdev_range = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->mdev_range); core->anal->esil->mdev_range = strdup (node->value); +#endif } return true; } @@ -2410,34 +2470,52 @@ static bool cb_cmd_esil_pin(void *user, void *data) { } static bool cb_cmd_esil_step(void *user, void *data) { - RCore *core = (RCore *)user; - RConfigNode *node = (RConfigNode *)data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->esil.cmd_step); + core->esil.cmd_step = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->cmd_step); core->anal->esil->cmd_step = strdup (node->value); +#endif } return true; } static bool cb_cmd_esil_step_out(void *user, void *data) { - RCore *core = (RCore *)user; - RConfigNode *node = (RConfigNode *)data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->esil.cmd_step_out); + core->esil.cmd_step_out = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->cmd_step_out); core->anal->esil->cmd_step_out = strdup (node->value); +#endif } return true; } static bool cb_cmd_esil_mdev(void *user, void *data) { - RCore *core = (RCore *)user; - RConfigNode *node = (RConfigNode *)data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->esil.cmd_mdev); + core->esil.cmd_mdev = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->cmd_mdev); core->anal->esil->cmd_mdev = strdup (node->value); +#endif } return true; } @@ -4030,6 +4108,7 @@ R_API int r_core_config_init(RCore *core) { SETB ("dbg.glibc.demangle", "false", "demangle linked-lists pointers introduced in glibc 2.32"); SETB ("esil.prestep", "true", "step before esil evaluation in `de` commands"); SETI ("esil.maxsteps", 0, "If !=0 defines the maximum amount of steps to perform on aesu/aec/.."); + SETICB ("esil.maxbacksteps", 256, &cb_esilmaxbacksteps, "esil back step capacity"); SETS ("esil.fillstack", "", "initialize ESIL stack with (random, debruijn, sequence, zeros, ...)"); SETICB ("esil.verbose", 0, &cb_esilverbose, "show ESIL verbose level (0, 1, 2)"); SETICB ("esil.gotolimit", core->anal->esil_goto_limit, &cb_gotolimit, "maximum number of gotos per ESIL expression"); diff --git a/libr/core/cmd_anal.inc.c b/libr/core/cmd_anal.inc.c index 23f2a2802b70b..e1a4d6bafedd6 100644 --- a/libr/core/cmd_anal.inc.c +++ b/libr/core/cmd_anal.inc.c @@ -2362,6 +2362,28 @@ R_API char *cmd_syscall_dostr(RCore *core, st64 n, ut64 addr) { r_syscall_item_free (item); return r_str_append (res, ")"); } +#if USE_NEW_ESIL +static bool mw(int *ec, ut64 addr, const ut8 *old, const ut8 *buf, int len) { + *ec += (len * 2); + return true; +} + +#if 0 +static bool rw(void *null, const char *regname, ut64 old, ut64 num) { + return true; +} + +static bool rr(void *null, const char *regname, ut64 num) { + return true; +} +#endif + +static bool mr(int *ec, ut64 addr, const ut8 *buf, int len) { + *ec += len; + return true; +} + +#else static bool mw(REsil *esil, ut64 addr, const ut8 *buf, int len) { int *ec = (int*)esil->user; @@ -2382,12 +2404,24 @@ static bool mr(REsil *esil, ut64 addr, ut8 *buf, int len) { *ec += len; return true; } +#endif static int esil_cost(RCore *core, ut64 addr, const char *expr) { if (R_STR_ISEMPTY (expr)) { return 0; } int ec = 0; +#if USE_NEW_ESIL + REsil *e = r_esil_new_simple (0, core->anal->reg, &core->anal->iob); + e->anal = core->anal; //XXX + //preserve regs? enforce ro mem access? + r_esil_add_voyeur (e, &ec, mr, R_ESIL_VOYEUR_MEM_READ); + r_esil_add_voyeur (e, &ec, mw, R_ESIL_VOYEUR_MEM_WRITE); +#if 0 + r_esil_add_voyeur (e, NULL, rr, R_ESIL_VOYEUR_REG_READ); + r_esil_add_voyeur (e, NULL, rw, R_ESIL_VOYEUR_REG_WRITE); +#endif +#else REsil *e = r_esil_new (256, 0, 0); r_esil_setup (e, core->anal, false, false, false); e->user = &ec; @@ -2395,6 +2429,7 @@ static int esil_cost(RCore *core, ut64 addr, const char *expr) { e->cb.mem_write = mw; e->cb.reg_write = rw; e->cb.reg_read = rr; +#endif r_esil_parse (e, expr); r_esil_free (e); return ec; @@ -2456,22 +2491,36 @@ static void val_tojson(PJ *pj, RAnalValue *val) { pj_end (pj); } - +#if USE_NEW_ESIL +static bool mw2(void *null, ut64 addr, const ut8 *old, const ut8 *buf, int len) { +#else static bool mw2(REsil *esil, ut64 addr, const ut8 *buf, int len) { +#endif R_LOG_INFO ("WRITE 0x%08"PFMT64x" %d", addr, len); return true; } +#if USE_NEW_ESIL +static bool mr2(void *null, ut64 addr, const ut8 *buf, int len) { +#else static bool mr2(REsil *esil, ut64 addr, ut8 *buf, int len) { +#endif R_LOG_INFO ("READ 0x%08"PFMT64x" %d", addr, len); return true; } static void esilmemrefs(RCore *core, const char *expr) { +#if USE_NEW_ESIL + REsil *e = r_esil_new_simple (0, core->anal->reg, &core->anal->iob); + r_esil_add_voyeur (e, NULL, mw2, R_ESIL_VOYEUR_MEM_WRITE); + r_esil_add_voyeur (e, NULL, mr2, R_ESIL_VOYEUR_MEM_READ); + e->anal = core->anal; //XXX +#else REsil *e = r_esil_new (256, 0, 0); r_esil_setup (e, core->anal, false, false, false); e->cb.mem_read = mr2; e->cb.mem_write = mw2; +#endif r_esil_parse (e, expr); r_esil_free (e); } @@ -2489,12 +2538,6 @@ static void core_anal_bytes(RCore *core, const ut8 *buf, int len, int nops, int ut64 addr; PJ *pj = NULL; int totalsize = 0; - REsil *esil = r_esil_new (256, 0, 0); - r_esil_setup (esil, core->anal, false, false, false); - esil->user = &core; - esil->cb.mem_read = mr; - esil->cb.mem_write = mw; - // Variables required for setting up ESIL to REIL conversion if (use_color) { color = core->cons->context->pal.label; } @@ -2979,7 +3022,6 @@ static void core_anal_bytes(RCore *core, const ut8 *buf, int len, int nops, int r_cons_println (core->cons, pj_string (pj)); pj_free (pj); } - r_esil_free (esil); } static int bb_cmp(const void *a, const void *b) { @@ -7086,6 +7128,78 @@ static bool is_steporeable(int type) { return false; } +#if USE_NEW_ESIL +R_API int r_core_esil_step(RCore *core, ut64 until_addr, const char *until_expr, ut64 *prev_addr, bool stepOver) { + const bool is_x86 = r_str_startswith (r_config_get (core->config, "asm.arch"), "x86"); + const bool breakoninvalid = r_config_get_b (core->config, "esil.breakoninvalid"); + const int esiltimeout = r_config_get_i (core->config, "esil.timeout"); + int ret = true; + ut64 startTime = 0; + + if (esiltimeout > 0) { + startTime = r_time_now_mono (); + } + ut64 addr = r_reg_getv (core->esil.reg, "PC"); + r_cons_break_push (core->cons, NULL, NULL); + while (addr != until_addr) { + if (esiltimeout > 0) { + ut64 elapsedTime = r_time_now_mono () - startTime; + elapsedTime >>= 20; + if (elapsedTime >= esiltimeout) { + R_LOG_INFO ("[ESIL] Timeout exceeded"); + ret = false; + break; + } + } + RAnalOp op; + if (until_expr || stepOver) { + r_anal_op_init (&op); + ut8 buf[64]; + r_io_read_at (core->io, addr, buf, 64); + r_anal_op_set_bytes (&op, addr, buf, 64); + r_arch_decode (core->anal->arch, &op, R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_ESIL); + } + if (until_expr) { + RAnalHint *hint = r_anal_hint_get (core->anal, addr); + if (hint && hint->esil) { + r_strbuf_set (&op.esil, hint->esil); + } + if (!strcmp (r_strbuf_get (&op.esil), until_expr)) { + r_anal_op_fini (&op); + break; + } + } + if (prev_addr) { + prev_addr[0] = addr; + } + if (stepOver && is_steporeable (op.type)) { + if (addr % R_MAX (r_arch_info (core->anal->arch, R_ARCH_INFO_CODE_ALIGN), 1)) { + if (core->esil.cmd_trap) { + r_core_cmd0 (core, core->esil.cmd_trap); + } + r_anal_op_fini (&op); + goto out; + } + r_reg_setv (core->esil.reg, "PC", op.addr + op.size); + r_anal_op_fini (&op); + continue; + } + if (until_expr || stepOver) { + r_anal_op_fini (&op); + } + if (!r_core_esil_single_step (core)) { + ret = false; + break; + } + addr = r_reg_getv (core->esil.reg, "PC"); + } +out: + r_cons_break_pop (core->cons); + return ret; +} + +#else + R_API int r_core_esil_step(RCore *core, ut64 until_addr, const char *until_expr, ut64 *prev_addr, bool stepOver) { #define SET_PC_BOTH(core, val) do { \ r_reg_setv ((core)->anal->reg, "PC", (val)); \ @@ -7403,6 +7517,17 @@ R_API int r_core_esil_step(RCore *core, ut64 until_addr, const char *until_expr, r_cons_break_pop (core->cons); return tail_return_value; } +#endif + +#if USE_NEW_ESIL +R_API bool r_core_esil_step_back(RCore *core) { + R_RETURN_VAL_IF_FAIL (core && core->io && core->esil.reg && + r_list_length (&core->esil.stepback), false); + r_core_esil_stepback (core); + return true; +} + +#else R_API bool r_core_esil_step_back(RCore *core) { R_RETURN_VAL_IF_FAIL (core && core->anal, false); @@ -7419,6 +7544,7 @@ R_API bool r_core_esil_step_back(RCore *core) { } return false; } +#endif static void cmd_address_info(RCore *core, const char *addrstr, int fmt) { ut64 addr = R_STR_ISEMPTY (addrstr)? core->addr: r_num_math (core->num, addrstr); @@ -8387,6 +8513,7 @@ static void cmd_debug_stack_init(RCore *core, int argc, char **argv, char **envp } R_IPI void cmd_aei(RCore *core) { +//TODO use core_esil here REsil *esil = esil_new_setup (core); if (esil) { r_esil_free (core->anal->esil); @@ -8438,6 +8565,16 @@ R_IPI int core_type_by_addr(RCore *core, ut64 addr) { return type; } +#if USE_NEW_ESIL +static void regwrite_voy(void *user, const char *name, ut64 val) { + RCore *core = user; + const int type = core_type_by_addr (core, val); + if (type == -1) { + return; + } + r_anal_xrefs_set (core->anal, core->anal->esil->addr, val, type); +} +#else static bool regwrite_hook(REsil *esil, const char *name, ut64 *val) { RCore *core = esil->user; int type = core_type_by_addr (core, *val); @@ -8446,6 +8583,7 @@ static bool regwrite_hook(REsil *esil, const char *name, ut64 *val) { } return false; } +#endif R_API void r_core_anal_esil_function(RCore *core, ut64 addr) { RListIter *iter; @@ -8458,10 +8596,14 @@ R_API void r_core_anal_esil_function(RCore *core, ut64 addr) { if (!sdb_const_get (core->sdb, "aeim.fd", 0)) { r_core_cmd_call (core, "aeim"); // should be set by default imho } +#if USE_NEW_ESIL + const ut32 vid = r_esil_add_voyeur (core->anal->esil, core, regwrite_voy, R_ESIL_VOYEUR_REG_WRITE); +#else void *u = core->anal->esil->user; core->anal->esil->user = core; void *p = core->anal->esil->cb.hook_reg_write; core->anal->esil->cb.hook_reg_write = regwrite_hook; +#endif RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, R_ANAL_FCN_TYPE_FCN | R_ANAL_FCN_TYPE_SYM); const ut64 old_pc = r_reg_getv (core->anal->reg, "PC"); @@ -8526,8 +8668,12 @@ R_API void r_core_anal_esil_function(RCore *core, ut64 addr) { } else { R_LOG_ERROR ("Cannot find function at 0x%08" PFMT64x, addr); } +#if USE_NEW_ESIL + r_esil_del_voyeur (core->anal->esil, vid); +#else core->anal->esil->cb.hook_reg_write = p; core->anal->esil->user = u; +#endif r_reg_setv (core->anal->reg, "PC", old_pc); } diff --git a/libr/core/cmd_search.inc.c b/libr/core/cmd_search.inc.c index 08a6c1443934e..c177146bbbfd3 100644 --- a/libr/core/cmd_search.inc.c +++ b/libr/core/cmd_search.inc.c @@ -1868,8 +1868,88 @@ static int r_core_search_rop(RCore *core, RInterval search_itv, int opt, const c return result; } +#if USE_NEW_ESIL +static bool search_esil_mem_read (void *mem, ut64 addr, ut8 *buf, int len) { + RCore *core = mem; + if (!addr && r_config_get_b (core->config, "esil.nonull")) { + return false; + } + return r_io_read_at (core->io, addr, buf, len); +} + +static bool search_esil_mem_write_ro (void *mem, ut64 addr, const ut8 *buf, int len) { + RCore *core = mem; + if (!addr && r_config_get_b (core->config, "esil.nonull")) { + return false; + } + RIORegion region; + if (!r_io_get_region_at (core->io, ®ion, addr)) { + //maybe check voidwrites config here + return true; + } + if (!(region.perm & R_PERM_W)) { + return false; + } + if (r_itv_contain (region.itv, addr + len - 1)) { + return true; + } + return search_esil_mem_write_ro (mem, r_itv_end (region.itv), + NULL, addr + len - r_itv_end (region.itv)); //no need to pass buf, because this is RO mode +} + +REsilMemInterface search_esil_mem_if = { + .mem_read = search_esil_mem_read, + .mem_write = search_esil_mem_write_ro, +}; + +static bool search_esil_is_reg (void *reg, const char *name) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return false; + } + r_unref (ri); + return true; +} + +static bool search_esil_reg_read (void *reg, const char *name, ut64 *val) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return false; + } + *val = r_reg_get_value ((RReg *)reg, ri); + r_unref (ri); + return true; +} + +static ut32 search_esil_reg_size (void *reg, const char *name) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return 0; + } + const ut32 size = ri->size; + r_unref (ri); + return size; +} + +static bool search_esil_reg_alias (void *reg, const char *name, const char *alias) { + int alias_type = r_reg_alias_fromstring (alias); + if (alias_type < 0) { + return false; + } + return r_reg_alias_setname (reg, alias_type, name); +} + +REsilRegInterface search_esil_reg_if = { + .is_reg = search_esil_is_reg, + .reg_read = search_esil_reg_read, + .reg_write = (REsilRegWrite)r_reg_setv, + .reg_size = search_esil_reg_size, + .reg_alias = search_esil_reg_alias, +}; +#endif + static bool esil_addrinfo(REsil *esil) { - RCore *core = (RCore *) esil->cb.user; + RCore *core = esil->user; ut64 num = 0; char *src = r_esil_pop (esil); if (src && *src && r_esil_get_parm (esil, src, &num)) { @@ -1904,26 +1984,43 @@ static void do_esil_search(RCore *core, struct search_parameters *param, const c r_core_cmd_help (core, help_msg_slash_esil); return; } - const unsigned int addrsize = r_config_get_i (core->config, "esil.addr.size"); + const ut32 addrsize = r_config_get_i (core->config, "esil.addr.size"); const int iotrap = r_config_get_i (core->config, "esil.iotrap"); - int stacksize = r_config_get_i (core->config, "esil.stack.size"); - const int nonull = r_config_get_i (core->config, "esil.nonull"); - const int romem = r_config_get_i (core->config, "esil.romem"); - const int stats = r_config_get_i (core->config, "esil.stats"); - if (stacksize < 16) { - stacksize = 16; + const int stacksize = R_MAX (r_config_get_i (core->config, "esil.stack.size"), 16); +#if USE_NEW_ESIL + search_esil_reg_if.user = core->anal->reg; + search_esil_mem_if.user = core; + REsil stack_located_esil = {0}; + if (!r_esil_init (&stack_located_esil, stacksize, iotrap, addrsize, + &search_esil_reg_if, &search_esil_mem_if, NULL)) { + R_LOG_ERROR ("Cannot initialize search esil instance"); + return; + } + if (!r_reg_arena_push (core->anal->reg)) { + r_esil_fini (&stack_located_esil); + R_LOG_ERROR ("Cannot push reg arena instance"); + return; } + REsil *esil = &stack_located_esil; +#else + const int nonull = r_config_get_i (core->config, "esil.nonull"); +// const int romem = r_config_get_i (core->config, "esil.romem"); +// const int stats = r_config_get_i (core->config, "esil.stats"); REsil *esil = r_esil_new (stacksize, iotrap, addrsize); if (!esil) { R_LOG_ERROR ("Cannot create an esil instance"); return; } +// r_esil_setup (esil, core->anal, romem, stats, nonull); + r_esil_setup (esil, core->anal, true, false, nonull); +#endif + esil->user = core; //this is fine, because no arch plugin custom ops are registered r_esil_set_op (esil, "$$", esil_address, 0, 1, R_ESIL_OP_TYPE_UNKNOWN, "current address"); - esil->cb.user = core; + /* hook addrinfo */ + r_esil_set_op (esil, "AddrInfo", esil_addrinfo, 1, 1, R_ESIL_OP_TYPE_UNKNOWN, NULL); // TODO:? cmd_aei (core); RIOMap *map; RListIter *iter; - r_esil_setup (esil, core->anal, romem, stats, nonull); r_list_foreach (param->boundaries, iter, map) { bool hit_happens = false; size_t hit_combo = 0; @@ -1931,10 +2028,7 @@ static void do_esil_search(RCore *core, struct search_parameters *param, const c ut64 nres, addr; ut64 from = r_io_map_begin (map); ut64 to = r_io_map_end (map); - /* hook addrinfo */ - r_esil_set_op (esil, "AddrInfo", esil_addrinfo, 1, 1, R_ESIL_OP_TYPE_UNKNOWN, NULL); - /* hook addrinfo */ - r_esil_setup (esil, core->anal, 1, 0, nonull); + //r_esil_setup (esil, core->anal, 1, 0, nonull); r_esil_stack_free (esil); esil->verbose = 0; @@ -1960,7 +2054,12 @@ static void do_esil_search(RCore *core, struct search_parameters *param, const c R_LOG_INFO ("Breaked at 0x%08"PFMT64x, addr); break; } +#if USE_NEW_ESIL + const char *pc_name = r_reg_alias_getname (core->anal->reg, R_REG_ALIAS_PC); + r_reg_setv (core->anal->reg, pc_name, core->addr); +#else r_esil_set_pc (esil, addr); +#endif if (!r_esil_parse (esil, input + 2)) { // XXX: return value doesnt seems to be correct here R_LOG_ERROR ("Cannot parse esil (%s)", input + 2); @@ -2017,7 +2116,12 @@ static void do_esil_search(RCore *core, struct search_parameters *param, const c if (param->outmode == R_MODE_JSON) { pj_end (param->pj); } +#if USE_NEW_ESIL + r_esil_fini (&stack_located_esil); + r_reg_arena_pop (core->anal->reg); +#else r_esil_free (esil); +#endif } #define MAXINSTR 8 @@ -2108,29 +2212,20 @@ static void do_syscall_search(RCore *core, struct search_parameters *param) { RAnalOp aop = {0}; int i, ret, bsize = R_MAX (64, core->blocksize); int kwidx = core->search->n_kws; - RIOMap* map; + RIOMap *map; RListIter *iter; const int mininstrsz = r_anal_archinfo (core->anal, R_ARCH_INFO_MINOP_SIZE); const int minopcode = R_MAX (1, mininstrsz); - REsil *esil; int align = core->search->align; - int stacksize = r_config_get_i (core->config, "esil.stack.depth"); - int iotrap = r_config_get_i (core->config, "esil.iotrap"); - unsigned int addrsize = r_config_get_i (core->config, "esil.addr.size"); const bool isx86 = r_str_startswith (r_config_get (core->config, "asm.arch"), "x86"); - if (!(esil = r_esil_new (stacksize, iotrap, addrsize))) { - return; - } int *previnstr = calloc (MAXINSTR + 1, sizeof (int)); if (!previnstr) { - r_esil_free (esil); return; } ut8 *buf = malloc (bsize); if (!buf) { R_LOG_ERROR ("Cannot allocate %d byte(s)", bsize); - r_esil_free (esil); free (previnstr); return; } @@ -2303,7 +2398,6 @@ static void do_syscall_search(RCore *core, struct search_parameters *param) { pj_end (param->pj); } r_core_seek (core, oldoff, true); - r_esil_free (esil); r_cons_break_pop (core->cons); free (buf); free (esp32); diff --git a/libr/core/core.c b/libr/core/core.c index 33ba9c6ce0d87..58b3fa4fd11dc 100644 --- a/libr/core/core.c +++ b/libr/core/core.c @@ -2672,6 +2672,9 @@ R_API bool r_core_init(RCore *core) { core->io->cb_printf = r_cons_gprintf; core->dbg->cb_printf = r_cons_gprintf; core->dbg->ev = core->ev; +#if USE_NEW_ESIL + r_core_esil_init (core); +#endif r_core_config_init (core); core->print->reg = core->anal->reg; core->print->get_register = r_reg_get; @@ -2779,6 +2782,12 @@ R_API void r_core_fini(RCore *c) { free (c->sessionfile); r_lib_free (c->lib); r_event_free (c->ev); + /* + r_unref (c->anal->config); + */ +#if USE_NEW_ESIL + r_core_esil_fini (&c->esil); +#endif if (c->anal->esil) { c->anal->esil->anal = NULL; } diff --git a/libr/core/core_esil.c b/libr/core/core_esil.c index 12ca2b7c448b4..7cdd054070eb4 100644 --- a/libr/core/core_esil.c +++ b/libr/core/core_esil.c @@ -1,4 +1,4 @@ -/* radare - LGPL - Copyright 2024 - condret */ +/* radare - LGPL - Copyright 2024 - 2025 - condret */ #define R_LOG_ORIGIN "core.esil" #include @@ -57,16 +57,25 @@ static ut32 core_esil_reg_size (void *core, const char *name) { if (!ri) { return 0; } - int size = ri->size; + const ut32 size = ri->size; r_unref (ri); return size; } +static bool core_esil_reg_alias (void *core, const char *name, const char *alias) { + int alias_type = r_reg_alias_fromstring (alias); + if (alias_type < 0) { + return false; + } + return r_reg_alias_setname (((RCore *)core)->esil.reg, alias_type, name); +} + static REsilRegInterface core_esil_reg_if = { .is_reg = core_esil_is_reg, .reg_read = core_esil_reg_read, .reg_write = core_esil_reg_write, - .reg_size = core_esil_reg_size + .reg_size = core_esil_reg_size, + .reg_alias = core_esil_reg_alias }; static bool core_esil_mem_switch (void *core, ut32 idx) { @@ -85,6 +94,11 @@ static bool core_esil_mem_read (void *core, ut64 addr, ut8 *buf, int len) { if (!addr && c->esil.cfg & R_CORE_ESIL_NONULL) { return false; } + + if (c->esil.cmd_mdev && c->esil.mdev_range && r_str_range_in (c->esil.mdev_range, addr)) { + r_core_cmdf (c, "%s %"PFMT64d" 0", c->esil.cmd_mdev, c->esil.old_pc); + return c->num->value; + } return r_io_read_at (c->io, addr, buf, len); } @@ -100,6 +114,10 @@ static bool core_esil_mem_write (void *core, ut64 addr, const ut8 *buf, int len) if (!addr && c->esil.cfg & R_CORE_ESIL_NONULL) { return false; } + if (c->esil.cmd_mdev && c->esil.mdev_range && r_str_range_in (c->esil.mdev_range, addr)) { + r_core_cmdf (c, "%s %"PFMT64d" 1", c->esil.cmd_mdev, c->esil.old_pc); + return c->num->value; + } if (c->esil.cfg & R_CORE_ESIL_RO) { RIORegion region; if (!r_io_get_region_at (c->io, ®ion, addr)) { @@ -124,6 +142,15 @@ static REsilMemInterface core_esil_mem_if = { .mem_write = core_esil_mem_write }; +static bool core_esil_set_bits(void *core, int bits) { + r_config_set_i (((RCore *)core)->config, "asm.bits", bits); + return true; +} + +static REsilUtilInterface core_esil_util_if = { + .set_bits = core_esil_set_bits +}; + static void core_esil_voyeur_trap_revert_reg_write (void *user, const char *name, ut64 old, ut64 val) { RCoreEsil *cesil = user; @@ -139,6 +166,9 @@ static void core_esil_voyeur_trap_revert_mem_write (void *user, ut64 addr, if (!(cesil->cfg & R_CORE_ESIL_TRAP_REVERT)) { return; } + if (cesil->cfg & R_CORE_ESIL_RO) { + return; + } int i; for (i = 0; i < len; i++) { //TODO: optimize this after breaking @@ -147,16 +177,52 @@ static void core_esil_voyeur_trap_revert_mem_write (void *user, ut64 addr, } } +static void core_esil_voyeur_trap_revert_set_bits (void *user, int bits) { + RCore *core = user; + RCoreEsil *cesil = &core->esil; + if (!(cesil->cfg & R_CORE_ESIL_TRAP_REVERT)) { + return; + } + ut32 old_bits = r_config_get_i (core->config, "asm.bits"); + r_strbuf_prependf (&cesil->trap_revert, "%d,BITS,", old_bits); +} + +static void core_esil_voyeur_trap_revert_reg_alias (void *user, const char *name, const char *alias) { + RCoreEsil *cesil = user; + if (!(cesil->cfg & R_CORE_ESIL_TRAP_REVERT)) { + return; + } + int alias_type = r_reg_alias_fromstring (alias); + if (alias_type < 0) { + return; + } + const char *old_name = r_reg_alias_getname (cesil->reg, alias_type); + if (!old_name) { + return; + } + r_strbuf_prependf (&cesil->trap_revert, "%s,%s,r=,", old_name, alias); +} + +static void core_esil_stepback_free (void *data) { + if (data) { + RCoreEsilStepBack *cesb = data; + free (cesb->expr); + free (data); + } +} + R_API bool r_core_esil_init(RCore *core) { R_RETURN_VAL_IF_FAIL (core && core->io, false); + core->esil = (const RCoreEsil){0}; core->esil.reg = r_reg_new (); if (!core->esil.reg) { return false; } core_esil_reg_if.reg = core; core_esil_mem_if.mem = core; + core_esil_util_if.user = core; if (!r_esil_init (&core->esil.esil, 4096, false, 64, - &core_esil_reg_if, &core_esil_mem_if)) { + &core_esil_reg_if, &core_esil_mem_if, &core_esil_util_if)) { goto init_fail; } if (!r_esil_set_op (&core->esil.esil, "TODO", core_esil_op_todo, 0, 0, @@ -170,6 +236,11 @@ R_API bool r_core_esil_init(RCore *core) { core_esil_voyeur_trap_revert_reg_write, R_ESIL_VOYEUR_REG_WRITE); core->esil.tr_mem = r_esil_add_voyeur (&core->esil.esil, &core->esil, core_esil_voyeur_trap_revert_mem_write, R_ESIL_VOYEUR_MEM_WRITE); + core->esil.tr_bits = r_esil_add_voyeur (&core->esil.esil, core, + core_esil_voyeur_trap_revert_set_bits, R_ESIL_VOYEUR_SET_BITS); + core->esil.tr_reg_alias = r_esil_add_voyeur (&core->esil.esil, core, + core_esil_voyeur_trap_revert_reg_alias, R_ESIL_VOYEUR_REG_ALIAS); + core->esil.stepback.free = core_esil_stepback_free; return true; op_fail: r_esil_fini (&core->esil.esil); @@ -212,18 +283,107 @@ R_API void r_core_esil_unload_arch(RCore *core) { core->anal->arch->esil = arch_esil; } -R_API void r_core_esil_single_step(RCore *core) { - R_RETURN_IF_FAIL (core && core->anal && core->anal->arch && core->io && core->esil.reg); +R_API bool r_core_esil_run_expr_at(RCore *core, const char *expr, ut64 addr) { + R_RETURN_VAL_IF_FAIL (expr && core && core->anal && core->anal->arch && core->io && core->esil.reg, false); const char *pc_name = r_reg_alias_getname (core->esil.reg, R_REG_ALIAS_PC); if (!pc_name) { R_LOG_ERROR ("CoreEsil reg profile has no pc register"); - return; + return false; } ut64 pc; if (!r_esil_reg_read_silent (&core->esil.esil, pc_name, &pc, NULL)) { R_LOG_ERROR ("Couldn't read from PC register"); - return; + return false; + } + if ((core->esil.cfg & R_CORE_ESIL_TRAP_REVERT_CONFIG) || core->esil.max_stepback) { + core->esil.cfg |= R_CORE_ESIL_TRAP_REVERT; + r_strbuf_initf (&core->esil.trap_revert, + "0x%"PFMT64x",%s,:=", pc, pc_name); + } else { + core->esil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; + } + core->esil.old_pc = pc; + r_esil_reg_write_silent (&core->esil.esil, pc_name, addr); + if (r_esil_parse (&core->esil.esil, expr)) { + if (core->esil.cfg & R_CORE_ESIL_TRAP_REVERT) { + if (core->esil.max_stepback) { + if (core->esil.max_stepback > r_list_length (&core->esil.stepback)) { + RCoreEsilStepBack *cesb = R_NEW (RCoreEsilStepBack); + if (!cesb) { + R_LOG_WARN ("RCoreEsilStepBack allocation failed"); + r_strbuf_fini (&core->esil.trap_revert); + } else { + if (!r_list_push (&core->esil.stepback, cesb)) { + R_LOG_WARN ("Pushing RCoreEsilStepBack failed"); + } else { + cesb->expr = r_strbuf_drain_nofree (&core->esil.trap_revert); + cesb->addr = core->esil.old_pc; + } + } + } else { + //this is like r_list_pop_head + r_list_push, + //but without expensive calls to malloc and free + RListIter *iter = core->esil.stepback.head; + iter->p->n = NULL; + core->esil.stepback.head = iter->p; + iter->p = NULL; + iter->n = core->esil.stepback.tail; + core->esil.stepback.tail->p = iter; + core->esil.stepback.tail = iter; + RCoreEsilStepBack *cesb = iter->data; + free (cesb->expr); + cesb->expr = r_strbuf_drain_nofree (&core->esil.trap_revert); + cesb->addr = core->esil.old_pc; + } + } else { + r_strbuf_fini (&core->esil.trap_revert); + } + } + return true; } + if (core->esil.cfg & R_CORE_ESIL_TRAP_REVERT) { + //disable trap_revert voyeurs + core->esil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; + char *expr = r_strbuf_drain_nofree (&core->esil.trap_revert); + //revert all changes + r_esil_parse (&core->esil.esil, expr); + free (expr); + } else { + r_esil_reg_write_silent (&core->esil.esil, pc_name, core->esil.old_pc); + } + if (core->esil.cmd_trap) { + r_core_cmd0 (core, core->esil.cmd_trap); + } + switch (core->esil.esil.trap_code) { + case R_ANAL_TRAP_WRITE_ERR: + case R_ANAL_TRAP_READ_ERR: + if (core->esil.cmd_ioer) { + r_core_cmdf (core, "%s %"PFMT64d" 0", core->esil.cmd_ioer, + core->esil.old_pc); + } + break; + } + return false; +} + +R_API bool r_core_esil_single_step(RCore *core) { + R_RETURN_VAL_IF_FAIL (core && core->anal && core->anal->arch && core->io && core->esil.reg, false); + const char *pc_name = r_reg_alias_getname (core->esil.reg, R_REG_ALIAS_PC); + if (!pc_name) { + R_LOG_ERROR ("CoreEsil reg profile has no pc register"); + return false; + } + ut64 pc; + if (!r_esil_reg_read_silent (&core->esil.esil, pc_name, &pc, NULL)) { + R_LOG_ERROR ("Couldn't read from PC register"); + return false; + } + ut32 trap_code = R_ANAL_TRAP_UNALIGNED; + const int align = R_MAX (1, r_arch_info (core->anal->arch, R_ARCH_INFO_CODE_ALIGN)); + if (pc % align) { + goto trap; + } + trap_code = R_ANAL_TRAP_READ_ERR; //check if pc is in mapped rx area, //or in case io is pa //check if pc is within desc and desc is at least readable @@ -235,6 +395,7 @@ R_API void r_core_esil_single_step(RCore *core) { (!core->io->va && !(region.perm & R_PERM_R))) { goto trap; } + trap_code = R_ANAL_TRAP_NONE; int max_opsize = R_MIN (64, r_arch_info (core->anal->arch, R_ARCH_INFO_MAXOP_SIZE)); if (R_UNLIKELY (max_opsize < 1)) { @@ -244,12 +405,12 @@ R_API void r_core_esil_single_step(RCore *core) { ut8 buf[64]; if (R_UNLIKELY (!r_io_read_at (core->io, pc, buf, max_opsize))) { R_LOG_ERROR ("Couldn't read data to decode from 0x%"PFMT64x, pc); - return; + return false; } // intentionally not using r_anal_op here, because this function is a fucking fever dream RArchSession *as = r_ref (core->anal->arch->session); if (!as) { - return; + return false; } RAnalOp op; r_anal_op_init (&op); @@ -259,7 +420,7 @@ R_API void r_core_esil_single_step(RCore *core) { R_LOG_ERROR ("COuldn't decode instruction at 0x%"PFMT64x, pc); r_anal_op_fini (&op); r_unref (as); - return; + return false; } RAnalHint *hint = r_anal_hint_get (core->anal, pc); if (hint) { @@ -285,32 +446,74 @@ R_API void r_core_esil_single_step(RCore *core) { if (!r_itv_contain (region.itv, pc + op.size)) { goto op_trap; } - if (core->esil.cfg & R_CORE_ESIL_TRAP_REVERT_CONFIG) { + if ((core->esil.cfg & R_CORE_ESIL_TRAP_REVERT_CONFIG) || core->esil.max_stepback) { core->esil.cfg |= R_CORE_ESIL_TRAP_REVERT; r_strbuf_initf (&core->esil.trap_revert, "0x%"PFMT64x",%s,:=", pc, pc_name); } else { core->esil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; - core->esil.old_pc = pc; } + core->esil.old_pc = pc; pc += op.size; char *expr = r_strbuf_drain_nofree (&op.esil); - r_anal_op_fini (&op); r_esil_reg_write_silent (&core->esil.esil, pc_name, pc); + r_anal_op_fini (&op); + if (core->esil.cmd_step) { + r_core_cmdf (core, "%s %"PFMT64d" 0", core->esil.cmd_step, core->esil.old_pc); + if (core->num->value) { + free (expr); + goto skip; + } + } const bool suc = r_esil_parse (&core->esil.esil, expr); free (expr); if (suc) { +skip: if (core->esil.cfg & R_CORE_ESIL_TRAP_REVERT) { - r_strbuf_fini (&core->esil.trap_revert); + if (core->esil.max_stepback) { + if (core->esil.max_stepback > r_list_length (&core->esil.stepback)) { + RCoreEsilStepBack *cesb = R_NEW (RCoreEsilStepBack); + if (!cesb) { + R_LOG_WARN ("RCoreEsilStepBack allocation failed"); + r_strbuf_fini (&core->esil.trap_revert); + } else { + if (!r_list_push (&core->esil.stepback, cesb)) { + R_LOG_WARN ("Pushing RCoreEsilStepBack failed"); + } else { + cesb->expr = r_strbuf_drain_nofree (&core->esil.trap_revert); + cesb->addr = core->esil.old_pc; + } + } + } else { + //this is like r_list_pop_head + r_list_push, + //but without expensive calls to malloc and free + RListIter *iter = core->esil.stepback.head; + iter->p->n = NULL; + core->esil.stepback.head = iter->p; + iter->p = NULL; + iter->n = core->esil.stepback.tail; + core->esil.stepback.tail->p = iter; + core->esil.stepback.tail = iter; + RCoreEsilStepBack *cesb = iter->data; + free (cesb->expr); + cesb->expr = r_strbuf_drain_nofree (&core->esil.trap_revert); + cesb->addr = core->esil.old_pc; + } + } else { + r_strbuf_fini (&core->esil.trap_revert); + } } r_unref (as); - return; + if (core->esil.cmd_step_out) { + r_core_cmdf (core, "%s %"PFMT64d" 0", core->esil.cmd_step_out, core->esil.old_pc); + } + return true; } + trap_code = core->esil.esil.trap_code; if (core->esil.cfg & R_CORE_ESIL_TRAP_REVERT) { //disable trap_revert voyeurs core->esil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; char *expr = r_strbuf_drain_nofree (&core->esil.trap_revert); - //TODO: check trap codes here //revert all changes r_esil_parse (&core->esil.esil, expr); free (expr); @@ -318,7 +521,6 @@ R_API void r_core_esil_single_step(RCore *core) { } //restore pc r_esil_reg_write_silent (&core->esil.esil, pc_name, core->esil.old_pc); - //TODO: check trap codes here goto trap; op_trap: r_unref (as); @@ -327,13 +529,44 @@ R_API void r_core_esil_single_step(RCore *core) { if (core->esil.cmd_trap) { r_core_cmd0 (core, core->esil.cmd_trap); } - return; + switch (trap_code) { + case R_ANAL_TRAP_WRITE_ERR: + case R_ANAL_TRAP_READ_ERR: + if (core->esil.cmd_ioer) { + r_core_cmdf (core, "%s %"PFMT64d" 0", core->esil.cmd_ioer, + core->esil.old_pc); + } + break; + } + return false; +} + +R_API void r_core_esil_stepback(RCore *core) { + R_RETURN_IF_FAIL (core && core->io && core->esil.reg); + if (!r_list_length (&core->esil.stepback)) { + //not an error + return; + } + RCoreEsilStepBack *cesb = r_list_pop (&core->esil.stepback); + core->esil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; + r_esil_parse (&core->esil.esil, cesb->expr); + core_esil_stepback_free (cesb); +} + +R_API void r_core_esil_set_max_stepback(RCore *core, ut32 max_stepback) { + R_RETURN_IF_FAIL (core && core->esil.stepback.free); + core->esil.max_stepback = max_stepback; + while (r_list_length (&core->esil.stepback) > max_stepback) { + core_esil_stepback_free (r_list_pop_head (&core->esil.stepback)); + } } R_API void r_core_esil_fini(RCoreEsil *cesil) { R_RETURN_IF_FAIL (cesil); r_esil_del_voyeur (&cesil->esil, cesil->tr_reg); r_esil_del_voyeur (&cesil->esil, cesil->tr_mem); + r_esil_del_voyeur (&cesil->esil, cesil->tr_bits); + r_esil_del_voyeur (&cesil->esil, cesil->tr_reg_alias); r_esil_fini (&cesil->esil); r_strbuf_fini (&cesil->trap_revert); if (cesil->reg) { @@ -348,5 +581,6 @@ R_API void r_core_esil_fini(RCoreEsil *cesil) { R_FREE (cesil->cmd_todo); R_FREE (cesil->cmd_ioer); R_FREE (cesil->mdev_range); + r_list_purge (&cesil->stepback); cesil->esil = (const REsil){0}; } diff --git a/libr/core/vmenus.c b/libr/core/vmenus.c index 107a208eaef72..f7dcca394c386 100644 --- a/libr/core/vmenus.c +++ b/libr/core/vmenus.c @@ -225,10 +225,16 @@ R_API bool r_core_visual_esil(RCore *core, const char *input) { } RCons *cons = core->cons; r_reg_arena_push (core->anal->reg); +#if USE_NEW_ESIL + REsil *esil = r_esil_new_simple (addrsize, core->anal->reg, &core->anal->iob); + const char *pc_name = r_reg_alias_getname (core->anal->reg, R_REG_ALIAS_PC); + r_reg_setv (core->anal->reg, pc_name, core->addr); +#else REsil *esil = r_esil_new (20, 0, addrsize); r_esil_setup (esil, core->anal, false, false, false); // esil->anal = core->anal; r_esil_set_pc (esil, core->addr); +#endif char *expr = NULL; bool refresh = false; for (;;) { @@ -332,19 +338,29 @@ R_API bool r_core_visual_esil(RCore *core, const char *input) { case 'P': x = 0; r_esil_free (esil); - esil = r_esil_new (20, 0, addrsize); - esil->anal = core->anal; r_core_cmd0 (core, "so+1"); +#if USE_NEW_ESIL + esil = r_esil_new_simple (addrsize, core->anal->reg, &core->anal->iob); + r_reg_setv (core->anal->reg, pc_name, core->addr); +#else + esil = r_esil_new (20, 0, addrsize); r_esil_set_pc (esil, core->addr); +#endif + esil->anal = core->anal; break; case 'N': case 'p': x = 0; r_esil_free (esil); - esil = r_esil_new (20, 0, addrsize); - esil->anal = core->anal; r_core_cmd0 (core, "so-1"); +#if USE_NEW_ESIL + esil = r_esil_new_simple (addrsize, core->anal->reg, &core->anal->iob); + r_reg_setv (core->anal->reg, pc_name, core->addr); +#else + esil = r_esil_new (20, 0, addrsize); r_esil_set_pc (esil, core->addr); +#endif + esil->anal = core->anal; break; case '=': { // TODO: edit diff --git a/libr/esil/esil.c b/libr/esil/esil.c index c7c2a901edf03..a4cf05ecf78de 100644 --- a/libr/esil/esil.c +++ b/libr/esil/esil.c @@ -42,14 +42,16 @@ R_API REsil *r_esil_new(int stacksize, int iotrap, unsigned int addrsize) { r_esil_plugins_init (esil); esil->addrmask = r_num_genmask (addrsize - 1); esil->trace = r_esil_trace_new (esil); +#if USE_NEW_ESIL == 0 int stats = 1; r_esil_stats (esil, NULL, stats); +#endif r_esil_setup_ops (esil); return esil; } -R_API bool r_esil_init(REsil *esil, int stacksize, bool iotrap, - ut32 addrsize, REsilRegInterface *reg_if, REsilMemInterface *mem_if) { +R_API bool r_esil_init(REsil *esil, int stacksize, bool iotrap, ut32 addrsize, + REsilRegInterface *reg_if, REsilMemInterface *mem_if, REsilUtilInterface *R_NULLABLE util_if) { R_RETURN_VAL_IF_FAIL (esil && reg_if && reg_if->is_reg && reg_if->reg_read && reg_if->reg_write && reg_if->reg_size && mem_if && mem_if->mem_read && mem_if->mem_write && (stacksize > 2), false); @@ -73,6 +75,7 @@ R_API bool r_esil_init(REsil *esil, int stacksize, bool iotrap, if (r_id_storage_init (&esil->voyeur[i], 0, MAX_VOYEURS)) { continue; } + R_LOG_ERROR ("voyeur init failed"); do { r_id_storage_fini (&esil->voyeur[i]); i--; @@ -87,6 +90,9 @@ R_API bool r_esil_init(REsil *esil, int stacksize, bool iotrap, esil->addrmask = r_num_genmask (addrsize - 1); esil->reg_if = *reg_if; esil->mem_if = *mem_if; + if (util_if) { + esil->util_if = *util_if; + } return true; voyeur_fail: r_esil_plugins_fini (esil); @@ -100,10 +106,10 @@ R_API bool r_esil_init(REsil *esil, int stacksize, bool iotrap, } R_API REsil *r_esil_new_ex(int stacksize, bool iotrap, ut32 addrsize, - REsilRegInterface *reg_if, REsilMemInterface *mem_if) { + REsilRegInterface *reg_if, REsilMemInterface *mem_if, REsilUtilInterface *R_NULLABLE util_if) { REsil *esil = R_NEW0 (REsil); if (R_UNLIKELY (!r_esil_init (esil, stacksize, iotrap, - addrsize, reg_if, mem_if))) { + addrsize, reg_if, mem_if, util_if))) { free (esil); return NULL; } @@ -139,12 +145,20 @@ static ut32 default_reg_size(void *reg, const char *name) { return rsize; } +static bool default_reg_alias(void *reg, const char *name, const char *alias) { + const int kind = r_reg_alias_fromstring (alias); + if (kind < 0) { + return false; + } + return r_reg_alias_setname (reg, kind, name); +} + static REsilRegInterface simple_reg_if = { .is_reg = default_is_reg, .reg_read = default_reg_read, .reg_write = (REsilRegWrite)r_reg_setv, .reg_size = default_reg_size, - // .reg_alias = default_reg_alias + .reg_alias = default_reg_alias, }; R_API REsil *r_esil_new_simple(ut32 addrsize, void *reg, void *iob) { @@ -153,7 +167,7 @@ R_API REsil *r_esil_new_simple(ut32 addrsize, void *reg, void *iob) { simple_reg_if.reg = reg; REsilMemInterface simple_mem_if = {{bnd->io}, (REsilMemSwitch)bnd->bank_use, (REsilMemRead)bnd->read_at, (REsilMemWrite)bnd->write_at}; - return r_esil_new_ex (4096, false, addrsize, &simple_reg_if, &simple_mem_if); + return r_esil_new_ex (4096, false, addrsize, &simple_reg_if, &simple_mem_if, NULL); } R_API ut32 r_esil_add_voyeur(REsil *esil, void *user, void *vfn, REsilVoyeurType vt) { @@ -161,8 +175,10 @@ R_API ut32 r_esil_add_voyeur(REsil *esil, void *user, void *vfn, REsilVoyeurType switch (vt) { case R_ESIL_VOYEUR_REG_READ: case R_ESIL_VOYEUR_REG_WRITE: + case R_ESIL_VOYEUR_REG_ALIAS: case R_ESIL_VOYEUR_MEM_READ: case R_ESIL_VOYEUR_MEM_WRITE: + case R_ESIL_VOYEUR_SET_BITS: case R_ESIL_VOYEUR_OP: break; default: @@ -189,8 +205,10 @@ R_API void r_esil_del_voyeur(REsil *esil, ut32 vid) { switch (vt) { case R_ESIL_VOYEUR_REG_READ: case R_ESIL_VOYEUR_REG_WRITE: + case R_ESIL_VOYEUR_REG_ALIAS: case R_ESIL_VOYEUR_MEM_READ: case R_ESIL_VOYEUR_MEM_WRITE: + case R_ESIL_VOYEUR_SET_BITS: case R_ESIL_VOYEUR_OP: break; default: @@ -308,6 +326,55 @@ R_API bool r_esil_mem_read_silent(REsil *esil, ut64 addr, ut8 *buf, int len) { return false; } +R_API bool r_esil_mem_read(REsil *esil, ut64 addr, ut8 *buf, int len) { +#if USE_NEW_ESIL + R_RETURN_VAL_IF_FAIL (buf && esil && esil->mem_if.mem_read, false); + addr &= esil->addrmask; +#else + R_RETURN_VAL_IF_FAIL (buf && esil, false); + addr &= esil->addrmask; + bool ret = false; + if (esil->cb.hook_mem_read) { + ret = esil->cb.hook_mem_read (esil, addr, buf, len); + } +#endif + if (!alignCheck (esil, addr)) { + esil->trap = R_ANAL_TRAP_READ_ERR; + esil->trap_code = addr; + return false; + } +#if USE_NEW_ESIL + if (R_UNLIKELY (!r_esil_mem_read_silent (esil, addr, buf, len))) { + return false; + } + ut32 i; + if (!r_id_storage_get_lowest (&esil->voyeur[R_ESIL_VOYEUR_MEM_READ], &i)) { + return true; + } + do { + REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_VOYEUR_MEM_READ], i); + voy->mem_read (voy->user, addr, buf, len); + } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_VOYEUR_MEM_READ], &i)); + return true; +#else + if (!ret && esil->cb.mem_read) { + if (ret = esil->cb.mem_read (esil, addr, buf, len), (!ret && esil->iotrap)) { + esil->trap = R_ANAL_TRAP_READ_ERR; + esil->trap_code = addr; + } + } + IFDBG { + size_t i; + eprintf ("0x%08" PFMT64x " R> ", addr); + for (i = 0; i < len; i++) { + eprintf ("%02x", buf[i]); + } + eprintf ("\n"); + } + return ret; +#endif +} + static bool internal_esil_mem_write(REsil *esil, ut64 addr, const ut8 *buf, int len) { R_RETURN_VAL_IF_FAIL (esil && esil->anal, false); bool ret = false; @@ -396,9 +463,9 @@ R_API bool r_esil_mem_write(REsil *esil, ut64 addr, const ut8 *buf, int len) { return true; } do { - REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_OP_TYPE_MEM_WRITE], i); + REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], i); voy->mem_write (voy->user, addr, o.buf, buf, len); - } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_OP_TYPE_MEM_WRITE], &i)); + } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], &i)); return true; } o.ptr = R_NEWS (ut8, len); @@ -410,12 +477,15 @@ R_API bool r_esil_mem_write(REsil *esil, ut64 addr, const ut8 *buf, int len) { esil->trap = R_ANAL_TRAP_NONE; } if (R_UNLIKELY (!r_esil_mem_write_silent (esil, addr, buf, len))) { + free (o.ptr); return false; } - do { - REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_OP_TYPE_MEM_WRITE], i); - voy->mem_write (voy->user, addr, o.ptr, buf, len); - } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_OP_TYPE_MEM_WRITE], &i)); + if (!r_id_storage_get_lowest (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], &i)) { + do { + REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], i); + voy->mem_write (voy->user, addr, o.ptr, buf, len); + } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], &i)); + } free (o.ptr); return true; #else @@ -538,9 +608,14 @@ R_API char *r_esil_pop(REsil *esil) { } static int not_a_number(REsil *esil, const char *str) { +#if USE_NEW_ESIL + R_RETURN_VAL_IF_FAIL (esil && str && esil->reg_if.is_reg, R_ESIL_PARM_INVALID); + if (esil->reg_if.is_reg (esil->reg_if.reg, str)) { +#else RRegItem *ri = r_reg_get (esil->anal->reg, str, -1); if (ri) { r_unref (ri); +#endif return R_ESIL_PARM_REG; } return R_ESIL_PARM_INVALID; @@ -688,6 +763,38 @@ R_API bool r_esil_reg_read_silent(REsil *esil, const char *name, ut64 *val, ut32 return true; } +R_API bool r_esil_reg_alias(REsil *esil, const char *name, const char *alias) { + R_RETURN_VAL_IF_FAIL (esil && name && alias, false); + if (!esil->reg_if.reg_alias) { + R_LOG_WARN ("Cannot set reg alias; .reg_alias was not setup for this Esil"); + return false; + } + ut32 i; + if (r_id_storage_get_lowest (&esil->voyeur[R_ESIL_VOYEUR_REG_ALIAS], &i)) { + do { + REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_VOYEUR_REG_ALIAS], i); + voy->reg_alias (voy->user, name, alias); + } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_VOYEUR_REG_ALIAS], &i)); + } + return esil->reg_if.reg_alias (esil->reg_if.reg, name, alias); +} + +R_API bool r_esil_set_bits(REsil *esil, int bits) { + R_RETURN_VAL_IF_FAIL (esil, false); + if (!esil->util_if.set_bits) { + R_LOG_WARN ("Cannot set bits; REsilUtilInterface was not setup for this Esil"); + return false; + } + ut32 i; + if (r_id_storage_get_lowest (&esil->voyeur[R_ESIL_VOYEUR_SET_BITS], &i)) { + do { + REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_VOYEUR_SET_BITS], i); + voy->set_bits (voy->user, bits); + } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_VOYEUR_SET_BITS], &i)); + } + return esil->util_if.set_bits (esil->util_if.user, bits); +} + R_API const char *r_esil_trapstr(int type) { switch (type) { case R_ANAL_TRAP_READ_ERR: @@ -1094,7 +1201,9 @@ R_API bool r_esil_setup(REsil *esil, RAnal *anal, bool romem, bool stats, bool n esil->cb.mem_write = internal_esil_mem_write; } r_esil_mem_ro (esil, romem); +#if USE_NEW_ESIL == 0 r_esil_stats (esil, NULL, stats); +#endif r_esil_setup_ops (esil); // Try arch esil init cb first, then anal as fallback diff --git a/libr/esil/esil_ops.c b/libr/esil/esil_ops.c index 8cdcd843967a2..b4f8596189c6b 100644 --- a/libr/esil/esil_ops.c +++ b/libr/esil/esil_ops.c @@ -25,56 +25,6 @@ R_IPI bool alignCheck(REsil *esil, ut64 addr); -/// XXX R2_600 - must be internal imho -R_API bool r_esil_mem_read(REsil *esil, ut64 addr, ut8 *buf, int len) { -#if USE_NEW_ESIL - R_RETURN_VAL_IF_FAIL (buf && esil && esil->mem_if.mem_read, false); - addr &= esil->addrmask; -#else - R_RETURN_VAL_IF_FAIL (buf && esil, false); - addr &= esil->addrmask; - bool ret = false; - if (esil->cb.hook_mem_read) { - ret = esil->cb.hook_mem_read (esil, addr, buf, len); - } -#endif - if (!alignCheck (esil, addr)) { - esil->trap = R_ANAL_TRAP_READ_ERR; - esil->trap_code = addr; - return false; - } -#if USE_NEW_ESIL - if (R_UNLIKELY (!r_esil_mem_read_silent (esil, addr, buf, len))) { - return false; - } - ut32 i; - if (!r_id_storage_get_lowest (&esil->voyeur[R_ESIL_VOYEUR_MEM_READ], &i)) { - return true; - } - do { - REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_VOYEUR_MEM_READ], i); - voy->mem_read (voy->user, addr, buf, len); - } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_VOYEUR_MEM_READ], &i)); - return true; -#else - if (!ret && esil->cb.mem_read) { - if (ret = esil->cb.mem_read (esil, addr, buf, len), (!ret && esil->iotrap)) { - esil->trap = R_ANAL_TRAP_READ_ERR; - esil->trap_code = addr; - } - } - IFDBG { - size_t i; - eprintf ("0x%08" PFMT64x " R> ", addr); - for (i = 0; i < len; i++) { - eprintf ("%02x", buf[i]); - } - eprintf ("\n"); - } - return ret; -#endif -} - static bool r_esil_fire_trap(REsil *esil, int trap_type, int trap_code) { R_RETURN_VAL_IF_FAIL (esil, false); if (esil->cmd && R_STR_ISNOTEMPTY (esil->cmd_trap)) { @@ -597,10 +547,7 @@ static bool esil_trap(REsil *esil) { static bool esil_bits(REsil *esil) { ut64 s; if (popRN (esil, &s)) { - if (esil->anal && esil->anal->coreb.setArchBits) { - esil->anal->coreb.setArchBits (esil->anal->coreb.core, NULL, s); - } - return true; + return r_esil_set_bits (esil, (int)s); } R_LOG_DEBUG ("esil_bits: missing parameters in stack"); return false; @@ -697,6 +644,20 @@ static bool esil_cmp(REsil *esil) { #if 1 // needed for COSMAC +#if USE_NEW_ESIL +static bool esil_regalias(REsil *esil) { + R_RETURN_VAL_IF_FAIL (esil, false); + char *dst = r_esil_pop (esil); + char *src = r_esil_pop (esil); + bool ret = false; + if (src && dst) { + ret = r_esil_reg_alias (esil, src, dst); + } + free (dst); + free (src); + return ret; +} +#else static bool esil_regalias(REsil *esil) { R_RETURN_VAL_IF_FAIL (esil, false); ut64 num; @@ -715,6 +676,7 @@ static bool esil_regalias(REsil *esil) { return ret; } #endif +#endif #if 0 x86 documentation: diff --git a/libr/esil/esil_toc.c b/libr/esil/esil_toc.c index 6c916f88e1378..2f00feba0ecb8 100644 --- a/libr/esil/esil_toc.c +++ b/libr/esil/esil_toc.c @@ -183,7 +183,11 @@ static void esil2c_free(REsilC *user) { free (user); } +#if USE_NEW_ESIL +static bool esil2c_mw(void *null, ut64 addr, const ut8 *old, const ut8 *buf, int len) { +#else static bool esil2c_mw(REsil *esil, ut64 addr, const ut8 *buf, int len) { +#endif R_LOG_TODO ("poke%d 0x%08"PFMT64x" %d", len, addr, *buf); return true; } @@ -198,8 +202,13 @@ static void esil2c_setup(REsil *esil) { REsilC *user = R_NEW (REsilC); esil->user = user; esil->verbose = true; // r_config_get_b (core->config, "esil.verbose"); +#if USE_NEW_ESIL + r_esil_add_voyeur (esil, NULL, esil2c_mw, R_ESIL_VOYEUR_MEM_WRITE); + r_esil_add_voyeur (esil, NULL, esil2c_mr, R_ESIL_VOYEUR_MEM_READ); +#else esil->cb.mem_read = esil2c_mr; esil->cb.mem_write = esil2c_mw; +#endif r_esil_set_op (esil, "=", esil2c_eq, 0, 2, R_ESIL_OP_TYPE_REG_WRITE, NULL); r_esil_set_op (esil, ":=", esil2c_eq, 0, 2, R_ESIL_OP_TYPE_REG_WRITE, NULL); r_esil_set_op (esil, "-", esil2c_sub, 1, 2, R_ESIL_OP_TYPE_REG_WRITE, NULL); @@ -221,14 +230,21 @@ static void esil2c_setup(REsil *esil) { R_API REsilC *r_esil_toc_new(RAnal *anal, const int bits) { R_RETURN_VAL_IF_FAIL (anal, NULL); REsilC *ec = R_NEW0 (REsilC); - int ss = 16 * 1024; - REsil *esil = r_esil_new (ss, 0, bits); - if (esil) { - esil2c_setup (esil); - ec->anal = anal; - ec->esil = esil; - } else { - R_FREE (ec); + if (ec) { +#if USE_NEW_ESIL + REsil *esil = r_esil_new_simple (bits, anal->reg, &anal->iob); + esil->anal = anal; +#else + int ss = 16 * 1024; + REsil *esil = r_esil_new (ss, 0, bits); +#endif + if (esil) { + esil2c_setup (esil); + ec->anal = anal; + ec->esil = esil; + } else { + R_FREE (ec); + } } return ec; } diff --git a/libr/include/r_core.h b/libr/include/r_core.h index 6e9914cb73da1..8a78f64b55595 100644 --- a/libr/include/r_core.h +++ b/libr/include/r_core.h @@ -309,23 +309,30 @@ typedef struct { int y; } VisualMark; +typedef struct r_core_esil_stepback_t { + char *expr; + ut64 addr; +} RCoreEsilStepBack; + typedef struct r_core_esil_t { REsil esil; - union { - RStrBuf trap_revert; - ut64 old_pc; - }; + RList stepback; + RStrBuf trap_revert; + ut64 old_pc; ut32 tr_reg; ut32 tr_mem; + ut32 tr_bits; + ut32 tr_reg_alias; RReg *reg; - char *cmd_step; // command to run before a step is performed - char *cmd_step_out; // command to run after a step is performed - char *cmd_intr; // command to run when an interrupt occurs - char *cmd_trap; // command to run when a trap occurs - char *cmd_mdev; // command to run when an memory mapped device address is used - char *cmd_todo; // command to run when esil expr contains TODO - char *cmd_ioer; // command to run when esil fails to IO - char *mdev_range; // string containing the r_str_range to match for read/write accesses + char *cmd_step; // command to run before a step is performed + char *cmd_step_out; // command to run after a step is performed + char *cmd_intr; // command to run when an interrupt occurs + char *cmd_trap; // command to run when a trap occurs + char *cmd_mdev; // command to run when an memory mapped device address is used + char *cmd_todo; // command to run when esil expr contains TODO + char *cmd_ioer; // command to run when esil fails to IO + char *mdev_range; // string containing the r_str_range to match for read/write accesses + ut32 max_stepback; ut8 cfg; } RCoreEsil; @@ -787,7 +794,10 @@ R_API bool r_core_esil_init(RCore *core); R_API void r_core_esil_fini(RCoreEsil *cesil); R_API void r_core_esil_load_arch(RCore *core); R_API void r_core_esil_unload_arch(RCore *core); -R_API void r_core_esil_single_step(RCore *core); +R_API bool r_core_esil_run_expr_at(RCore *core, const char *expr, ut64 addr); +R_API bool r_core_esil_single_step(RCore *core); +R_API void r_core_esil_stepback(RCore *core); //replacement for r_core_esil_step_back; rename later +R_API void r_core_esil_set_max_stepback(RCore *core, ut32 max_stepback); // both do the same, we should get rid of one of them R_API bool r_core_bin_raise(RCore *core, ut32 bfid); diff --git a/libr/include/r_esil.h b/libr/include/r_esil.h index 41765030cc12a..2a9aba4fa0324 100644 --- a/libr/include/r_esil.h +++ b/libr/include/r_esil.h @@ -14,7 +14,7 @@ extern "C" { #endif -#define USE_NEW_ESIL 0 +#define USE_NEW_ESIL 1 #define esilprintf(op, fmt, ...) r_strbuf_setf (&op->esil, fmt, ##__VA_ARGS__) // only flags that affect control flow @@ -153,7 +153,7 @@ typedef bool (*REsilIsReg)(void *reg, const char *name); typedef bool (*REsilRegRead)(void *reg, const char *name, ut64 *val); typedef bool (*REsilRegWrite)(void *reg, const char *name, ut64 val); typedef ut32 (*REsilRegSize)(void *reg, const char *name); -// typedef bool (*REsilRegAlias)(void *reg, int kind, const char *name); +typedef bool (*REsilRegAlias)(void *reg, const char *name, const char *alias); typedef struct r_esil_register_interface_t { union { @@ -164,13 +164,22 @@ typedef struct r_esil_register_interface_t { REsilRegRead reg_read; REsilRegWrite reg_write; REsilRegSize reg_size; - // REsilRegAlias reg_alias; + REsilRegAlias reg_alias; } REsilRegInterface; +typedef bool (*REsilSetBits)(void *user, int bits); + +typedef struct r_esil_util_interface_t { + void *user; + REsilSetBits set_bits; +} REsilUtilInterface; + typedef void (*REsilVoyeurRegRead)(void *user, const char *name, ut64 val); typedef void (*REsilVoyeurRegWrite)(void *user, const char *name, ut64 old, ut64 val); +typedef void (*REsilVoyeurRegAlias)(void *user, const char *name, const char *alias); typedef void (*REsilVoyeurMemRead)(void *user, ut64 addr, const ut8 *buf, int len); typedef void (*REsilVoyeurMemWrite)(void *user, ut64 addr, const ut8 *old, const ut8 *buf, int len); +typedef void (*REsilVoyeurSetBits)(void *user, int bits); typedef void (*REsilVoyeurOp)(void *user, const char *op); typedef struct r_esil_voyeur_t { @@ -178,8 +187,10 @@ typedef struct r_esil_voyeur_t { union { REsilVoyeurRegRead reg_read; REsilVoyeurRegWrite reg_write; + REsilVoyeurRegAlias reg_alias; REsilVoyeurMemRead mem_read; REsilVoyeurMemWrite mem_write; + REsilVoyeurSetBits set_bits; REsilVoyeurOp op; void *vfn; }; @@ -188,15 +199,17 @@ typedef struct r_esil_voyeur_t { typedef enum { R_ESIL_VOYEUR_REG_READ = 0, R_ESIL_VOYEUR_REG_WRITE, + R_ESIL_VOYEUR_REG_ALIAS, R_ESIL_VOYEUR_MEM_READ, R_ESIL_VOYEUR_MEM_WRITE, + R_ESIL_VOYEUR_SET_BITS, R_ESIL_VOYEUR_OP, R_ESIL_VOYEUR_LAST, - R_ESIL_VOYEUR_HIGH_MASK = 0x7, + R_ESIL_VOYEUR_HIGH_MASK = 0xf, R_ESIL_VOYEUR_ERR = UT32_MAX, } REsilVoyeurType; -#define VOYEUR_TYPE_BITS 3 +#define VOYEUR_TYPE_BITS 4 #define VOYEUR_SHIFT_LEFT ((sizeof (ut32) << 3) - VOYEUR_TYPE_BITS) #define VOYEUR_TYPE_MASK ((ut32)R_ESIL_VOYEUR_HIGH_MASK << VOYEUR_SHIFT_LEFT) #define MAX_VOYEURS (UT32_MAX ^ VOYEUR_TYPE_MASK) @@ -251,6 +264,7 @@ typedef struct r_esil_t { REsilTrace *trace; REsilRegInterface reg_if; REsilMemInterface mem_if; + REsilUtilInterface util_if; RIDStorage voyeur[R_ESIL_VOYEUR_LAST]; REsilCallbacks cb; REsilCallbacks ocb; @@ -300,9 +314,9 @@ typedef struct r_esil_active_plugin_t { R_API REsil *r_esil_new(int stacksize, int iotrap, unsigned int addrsize); R_API bool r_esil_init(REsil *esil, int stacksize, bool iotrap, - ut32 addrsize, REsilRegInterface *reg_if, REsilMemInterface *mem_if); + ut32 addrsize, REsilRegInterface *reg_if, REsilMemInterface *mem_if, REsilUtilInterface *util_if); R_API REsil *r_esil_new_ex(int stacksize, bool iotrap, ut32 addrsize, - REsilRegInterface *reg_if, REsilMemInterface *mem_if); + REsilRegInterface *reg_if, REsilMemInterface *mem_if, REsilUtilInterface *R_NULLABLE util_if); //this should replace existing r_esil_new R_API REsil *r_esil_new_simple(ut32 addrsize, void *reg, void *iob); //R_API REsil *r_esil_new_simple(ut32 addrsize, struct r_reg_t *reg, struct r_io_bind_t *iob); @@ -326,6 +340,8 @@ R_API bool r_esil_reg_read(REsil *esil, const char *regname, ut64 *val, ut32 *si R_API bool r_esil_reg_read_silent(REsil *esil, const char *name, ut64 *val, ut32 *size); R_API bool r_esil_reg_write(REsil *esil, const char *name, ut64 val); R_API bool r_esil_reg_write_silent(REsil *esil, const char *dst, ut64 val); +R_API bool r_esil_reg_alias(REsil *esil, const char *name, const char *alias); +R_API bool r_esil_set_bits(REsil *esil, int bits); R_API bool r_esil_pushnum(REsil *esil, ut64 num); R_API bool r_esil_push(REsil *esil, const char *str); #if R2_590 diff --git a/test/unit/test_esil_dfg_filter.c b/test/unit/test_esil_dfg_filter.c index 0cb3fb7820412..8e7f0772a3cc1 100644 --- a/test/unit/test_esil_dfg_filter.c +++ b/test/unit/test_esil_dfg_filter.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include "minunit.h" @@ -8,7 +10,13 @@ bool test_filter_regs(void) { r_anal_use (anal, "x86"); r_anal_set_bits (anal, 32); r_anal_set_reg_profile (anal, NULL); +#if USE_NEW_ESIL + RIO *io = r_io_new (); + r_io_bind (io, &anal->iob); + REsil *esil = r_esil_new_simple (1, anal->reg, &anal->iob); +#else REsil *esil = r_esil_new (4096, 0, 1); +#endif esil->anal = anal; // create expected results @@ -43,6 +51,9 @@ bool test_filter_regs(void) { r_anal_esil_dfg_free (dfg); r_esil_free (esil); r_anal_free (anal); +#if USE_NEW_ESIL + r_io_free (io); +#endif mu_assert ("filtering for ax failed", ax == filtered_ax); mu_assert ("filtering for ah failed", ah == filtered_ah); @@ -56,6 +67,11 @@ bool test_lemon_const_folder(void) { r_anal_set_bits (anal, 32); r_anal_set_reg_profile (anal, NULL); +#if USE_NEW_ESIL + RIO *io = r_io_new (); + r_io_bind (io, &anal->iob); +#endif + RAnalEsilDFG *dfg = r_anal_esil_dfg_expr (anal, NULL, "4,!,3,ebx,:=,!,1,+,eax,:=", false, false); r_anal_esil_dfg_fold_const (anal, dfg); RStrBuf *filtered = r_anal_esil_dfg_filter (dfg, "eax"); @@ -63,6 +79,9 @@ bool test_lemon_const_folder(void) { r_strbuf_free (filtered); r_anal_esil_dfg_free (dfg); r_anal_free (anal); +#if USE_NEW_ESIL + r_io_free (io); +#endif mu_assert_true (cmp_result, "esil dfg const folding is broken"); mu_end;