This is one of the first steps to improve analysis. This way we'll have one single place to change if we want to change the meaning of the "size" field. (size -> realsize)
2826 lines
80 KiB
C
2826 lines
80 KiB
C
/* radare - LGPL - Copyright 2009-2016 - pancake, nibble */
|
|
|
|
#include <r_types.h>
|
|
#include <r_list.h>
|
|
#include <r_flags.h>
|
|
#include <r_core.h>
|
|
#include <r_bin.h>
|
|
|
|
#include <string.h>
|
|
|
|
#define HASNEXT_FOREVER 1
|
|
|
|
#define in_function(fn,y) ((y) >= (fn)->addr && (y) < ((fn)->addr + r_anal_fcn_size (fn)))
|
|
|
|
#define HINTCMD_ADDR(hint,x,y) if(hint->x) \
|
|
r_cons_printf (y" @ 0x%"PFMT64x"\n", hint->x, hint->addr)
|
|
#define HINTCMD(hint,x,y,json) if(hint->x) \
|
|
r_cons_printf (y"%s", hint->x, json ? "" : "\n")
|
|
|
|
typedef struct {
|
|
RAnal *a;
|
|
int mode;
|
|
int count;
|
|
} HintListState;
|
|
|
|
static void add_string_ref (RCore *core, ut64 xref_to);
|
|
|
|
static void loganal(ut64 from, ut64 to, int depth) {
|
|
r_cons_clear_line (1);
|
|
eprintf ("0x%08"PFMT64x" > 0x%08"PFMT64x" %d\r", from, to, depth);
|
|
}
|
|
|
|
static RCore *mycore = NULL;
|
|
|
|
// XXX: copypaste from anal/data.c
|
|
#define MINLEN 1
|
|
static int is_string (const ut8 *buf, int size, int *len) {
|
|
int i;
|
|
if (size<1)
|
|
return 0;
|
|
if (size>3 && buf[0] && !buf[1] && buf[2] && !buf[3]) {
|
|
*len = 1; // XXX: TODO: Measure wide string length
|
|
return 2; // is wide
|
|
}
|
|
for (i=0; i<size; i++) {
|
|
if (!buf[i] && i>MINLEN) {
|
|
*len = i;
|
|
return 1;
|
|
}
|
|
if (buf[i]==10 || buf[i]==13 || buf[i]==9) {
|
|
continue;
|
|
}
|
|
if (buf[i] < 32 || buf[i] > 127) {
|
|
// not ascii text
|
|
return 0;
|
|
}
|
|
if (!IS_PRINTABLE (buf[i])) {
|
|
*len = i;
|
|
return 0;
|
|
}
|
|
}
|
|
*len = i;
|
|
return 1;
|
|
}
|
|
|
|
// Detect if there's code in the given address
|
|
// - falls in section named 'text'
|
|
// - section has exec bit, some const strings are in there
|
|
// - addr is in different section than core->offset
|
|
static bool iscodesection(RCore *core, ut64 addr) {
|
|
RIOSection *s = r_io_section_vget (core->io, addr);
|
|
if (!s) return false;
|
|
if (strstr (s->name, "text")) {
|
|
return true;
|
|
}
|
|
return false;
|
|
// BSS return (s && s->rwx & R_IO_WRITE)? 0: 1;
|
|
// Cstring return (s && s->rwx & R_IO_EXEC)? 1: 0;
|
|
}
|
|
|
|
static char *is_string_at (RCore *core, ut64 addr, int *olen) {
|
|
ut8 *str;
|
|
int ret, len = 0;
|
|
if (iscodesection (core, addr)) {
|
|
return NULL;
|
|
}
|
|
str = calloc (1024, 1);
|
|
r_io_read_at (core->io, addr, str, 1024);
|
|
str[1023] = 0;
|
|
// check if current section have no exec bit
|
|
ret = is_string (str, 1024, &len);
|
|
if (!ret || len < 1) {
|
|
ret = 0;
|
|
free (str);
|
|
len = -1;
|
|
} else if (olen) {
|
|
*olen = len;
|
|
}
|
|
return ret? (char *)str: NULL;
|
|
}
|
|
|
|
/* returns the R_ANAL_ADDR_TYPE_* of the address 'addr' */
|
|
R_API ut64 r_core_anal_address (RCore *core, ut64 addr) {
|
|
ut64 types = 0;
|
|
RRegSet *rs = NULL;
|
|
if (!core) return 0;
|
|
if (core->dbg && core->dbg->reg) {
|
|
rs = r_reg_regset_get (core->dbg->reg, R_REG_TYPE_GPR);
|
|
}
|
|
if (rs) {
|
|
RRegItem *r;
|
|
RListIter *iter;
|
|
r_list_foreach (rs->regs, iter, r) {
|
|
ut64 val = r_reg_getv (core->dbg->reg, r->name);
|
|
//r_cons_printf ("%s\n", r->name);
|
|
if (addr == val) {
|
|
types |= R_ANAL_ADDR_TYPE_REG;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (r_flag_get_i (core->flags, addr))
|
|
types |= R_ANAL_ADDR_TYPE_FLAG;
|
|
if (r_anal_get_fcn_in (core->anal, addr, 0))
|
|
types |= R_ANAL_ADDR_TYPE_FUNC;
|
|
// check registers
|
|
if (core->io && core->io->debug) {
|
|
RDebugMap *map;
|
|
RListIter *iter;
|
|
// use 'dm'
|
|
// XXX: this line makes r2 debugging MUCH slower
|
|
// r_debug_map_sync (core->dbg);
|
|
r_list_foreach (core->dbg->maps, iter, map) {
|
|
if (addr >= map->addr && addr < map->addr_end) {
|
|
if (map->name && map->name[0] == '/') {
|
|
if (core->io && core->io->desc && core->io->desc->name && \
|
|
!strcmp (map->name, core->io->desc->name)) {
|
|
types |= R_ANAL_ADDR_TYPE_PROGRAM;
|
|
} else {
|
|
types |= R_ANAL_ADDR_TYPE_LIBRARY;
|
|
}
|
|
}
|
|
if (map->perm & R_IO_EXEC)
|
|
types |= R_ANAL_ADDR_TYPE_EXEC;
|
|
if (map->perm & R_IO_READ)
|
|
types |= R_ANAL_ADDR_TYPE_READ;
|
|
if (map->perm & R_IO_WRITE)
|
|
types |= R_ANAL_ADDR_TYPE_WRITE;
|
|
// find function
|
|
if (map->name && strstr (map->name, "heap"))
|
|
types |= R_ANAL_ADDR_TYPE_HEAP;
|
|
if (map->name && strstr (map->name, "stack"))
|
|
types |= R_ANAL_ADDR_TYPE_STACK;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
int _rwx = -1;
|
|
RIOSection *ios;
|
|
RListIter *iter;
|
|
if (core->io) {
|
|
// sections
|
|
r_list_foreach (core->io->sections, iter, ios) {
|
|
if (addr >= ios->vaddr && addr < (ios->vaddr+ios->vsize)) {
|
|
// sections overlap, so we want to get the one with lower perms
|
|
if (_rwx != -1) {
|
|
_rwx = R_MIN (_rwx, ios->rwx);
|
|
} else {
|
|
_rwx = ios->rwx;
|
|
}
|
|
// TODO: we should identify which maps come from the program or other
|
|
//types |= R_ANAL_ADDR_TYPE_PROGRAM;
|
|
// find function those sections should be created by hand or esil init
|
|
if (strstr (ios->name, "heap"))
|
|
types |= R_ANAL_ADDR_TYPE_HEAP;
|
|
if (strstr (ios->name, "stack"))
|
|
types |= R_ANAL_ADDR_TYPE_STACK;
|
|
}
|
|
}
|
|
}
|
|
if (_rwx != -1) {
|
|
if (_rwx & R_IO_EXEC)
|
|
types |= R_ANAL_ADDR_TYPE_EXEC;
|
|
if (_rwx & R_IO_READ)
|
|
types |= R_ANAL_ADDR_TYPE_READ;
|
|
if (_rwx & R_IO_WRITE)
|
|
types |= R_ANAL_ADDR_TYPE_WRITE;
|
|
}
|
|
}
|
|
|
|
// check if it's ascii
|
|
if (addr != 0) {
|
|
int not_ascii = 0;
|
|
int i, failed_sequence, dir, on;
|
|
for (i=0; i<8; i++) {
|
|
ut8 n = (addr >> (i*8)) & 0xff;
|
|
if (n && !IS_PRINTABLE (n))
|
|
not_ascii = 1;
|
|
}
|
|
if (!not_ascii)
|
|
types |= R_ANAL_ADDR_TYPE_ASCII;
|
|
|
|
failed_sequence = 0;
|
|
dir = on = -1;
|
|
for (i=0; i<8; i++) {
|
|
ut8 n = (addr>> (i*8)) & 0xff;
|
|
if (on != -1) {
|
|
if (dir == -1)
|
|
dir = (n>on)? 1: -1;
|
|
if (n == on+dir) {
|
|
// ok
|
|
} else {
|
|
failed_sequence = 1;
|
|
break;
|
|
}
|
|
}
|
|
on = n;
|
|
}
|
|
if (!failed_sequence)
|
|
types |= R_ANAL_ADDR_TYPE_SEQUENCE;
|
|
}
|
|
return types;
|
|
}
|
|
|
|
/*this only autoname those function that start with fcn.* or sym.func.* */
|
|
R_API void r_core_anal_autoname_all_fcns(RCore *core) {
|
|
RListIter *it;
|
|
RAnalFunction *fcn;
|
|
r_list_foreach (core->anal->fcns, it, fcn) {
|
|
char *name = r_core_anal_fcn_autoname (core, fcn->addr, 0);
|
|
if (name && (!strncmp (fcn->name, "fcn.", 4) || !strncmp (fcn->name, "sym.func.", 9))) {
|
|
r_flag_rename (core->flags, r_flag_get (core->flags, fcn->name), name);
|
|
free (fcn->name);
|
|
fcn->name = name;
|
|
} else {
|
|
free (name);
|
|
}
|
|
}
|
|
}
|
|
/* suggest a name for the function at the address 'addr'.
|
|
* If dump is true, every strings associated with the function is printed */
|
|
R_API char *r_core_anal_fcn_autoname(RCore *core, ut64 addr, int dump) {
|
|
int use_getopt = 0;
|
|
int use_isatty = 0;
|
|
char *do_call = NULL;
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
if (fcn) {
|
|
RAnalRef *ref;
|
|
RListIter *iter;
|
|
r_list_foreach (fcn->refs, iter, ref) {
|
|
RFlagItem *f = r_flag_get_i (core->flags, ref->addr);
|
|
if (f) {
|
|
if (dump) {
|
|
r_cons_printf ("0x%08"PFMT64x" 0x%08"PFMT64x" %s\n", ref->at, ref->addr, f->name);
|
|
}
|
|
if (strstr (f->name, "isatty"))
|
|
use_isatty = 1;
|
|
if (strstr (f->name, "getopt"))
|
|
use_getopt = 1;
|
|
if (!strncmp (f->name, "sym.imp.", 8)) {
|
|
free (do_call);
|
|
do_call = strdup (f->name+8);
|
|
break;
|
|
} else if (!strncmp (f->name, "reloc.", 6)) {
|
|
free (do_call);
|
|
do_call = strdup (f->name+6);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// TODO: append counter if name already exists
|
|
if (use_getopt) {
|
|
RFlagItem *item = r_flag_get (core->flags, "main");
|
|
free (do_call);
|
|
// if referenced from entrypoint. this should be main
|
|
if (item && item->offset == addr)
|
|
return strdup ("main"); // main?
|
|
return strdup ("parse_args"); // main?
|
|
}
|
|
if (use_isatty) {
|
|
char *ret = r_str_newf ("sub.setup_tty_%s_%x", do_call, addr & 0xfff);
|
|
free (do_call);
|
|
return ret;
|
|
}
|
|
if (do_call) {
|
|
char *ret = r_str_newf ("sub.%s_%x", do_call, addr & 0xfff);
|
|
free (do_call);
|
|
return ret;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static ut64 *next_append (ut64 *next, int *nexti, ut64 v) {
|
|
next = realloc (next, sizeof (ut64) * (1 + *nexti));
|
|
next[*nexti] = v;
|
|
(*nexti)++;
|
|
|
|
return next;
|
|
}
|
|
|
|
static void r_anal_set_stringrefs(RCore *core, RAnalFunction *fcn) {
|
|
RListIter *iter;
|
|
RAnalRef *ref;
|
|
r_list_foreach (fcn->refs, iter, ref) {
|
|
if (ref->type == R_ANAL_REF_TYPE_DATA &&
|
|
r_bin_is_string(core->bin, ref->addr))
|
|
ref->type = R_ANAL_REF_TYPE_STRING;
|
|
}
|
|
}
|
|
|
|
static int r_anal_try_get_fcn(RCore *core, RAnalRef *ref, int fcndepth, int refdepth) {
|
|
ut8 *buf;
|
|
ut16 bufsz = 1000;
|
|
RIOSection *sec;
|
|
if (!refdepth) return 1;
|
|
sec = r_io_section_vget (core->io, ref->addr);
|
|
if (!sec) return 1;
|
|
buf = calloc (bufsz, 1);
|
|
if (!buf) {
|
|
eprintf ("Error: malloc (buf)\n");
|
|
return 0;
|
|
}
|
|
r_io_read_at (core->io, ref->addr, buf, bufsz);
|
|
|
|
if (sec->rwx & R_IO_EXEC &&
|
|
r_anal_check_fcn (core->anal, buf, bufsz, ref->addr, sec->vaddr, sec->vaddr + sec->vsize)) {
|
|
if (core->anal->limit) {
|
|
if (ref->addr < core->anal->limit->from || ref->addr > core->anal->limit->to) {
|
|
free(buf);
|
|
return 1;
|
|
}
|
|
}
|
|
r_core_anal_fcn (core, ref->addr, ref->at, ref->type, fcndepth-1);
|
|
|
|
} else {
|
|
ut64 offs, sz = core->anal->bits >> 3;
|
|
RAnalRef ref1;
|
|
ref1.type = R_ANAL_REF_TYPE_DATA;
|
|
ref1.at = ref->addr;
|
|
ref1.addr = 0;
|
|
for (offs = 0; offs < bufsz; offs += sz, ref1.at += sz) {
|
|
// XXX wtf endian
|
|
memcpy ((ut8*)&ref1.addr, buf + offs, sz);
|
|
r_anal_try_get_fcn (core, &ref1, fcndepth, refdepth-1);
|
|
}
|
|
}
|
|
|
|
free(buf);
|
|
return 1;
|
|
}
|
|
|
|
static int r_anal_analyze_fcn_refs(RCore *core, RAnalFunction *fcn, int depth) {
|
|
RListIter *iter, *tmp;
|
|
RAnalRef *ref;
|
|
|
|
r_list_foreach_safe (fcn->refs, iter, tmp, ref) {
|
|
if (ref->addr != UT64_MAX) {
|
|
switch (ref->type) {
|
|
case 'd':
|
|
if (core->anal->opt.followdatarefs) {
|
|
r_anal_try_get_fcn (core, ref, depth, 2);
|
|
}
|
|
break;
|
|
case R_ANAL_REF_TYPE_CODE:
|
|
case R_ANAL_REF_TYPE_CALL:
|
|
r_core_anal_fcn (core, ref->addr, ref->at, ref->type, depth-1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// TODO: fix memleak here, fcn not freed even though it is
|
|
// added in core->anal->fcns which is freed in r_anal_free()
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int core_anal_fcn(RCore *core, ut64 at, ut64 from, int reftype, int depth) {
|
|
int has_next = r_config_get_i (core->config, "anal.hasnext");
|
|
RAnalHint *hint;
|
|
ut8 *buf = NULL;
|
|
int i, nexti = 0;
|
|
ut64 *next = NULL;
|
|
int buflen, fcnlen;
|
|
RAnalFunction *fcn = r_anal_fcn_new ();
|
|
if (!fcn) {
|
|
eprintf ("Error: new (fcn)\n");
|
|
return false;
|
|
}
|
|
|
|
hint = r_anal_hint_get (core->anal, at);
|
|
if (hint && hint->bits == 16) {
|
|
// expand 16bit for function
|
|
fcn->bits = 16;
|
|
}
|
|
fcn->addr = at;
|
|
r_anal_fcn_set_size (fcn, 0);
|
|
fcn->name = r_str_newf ("fcn.%08"PFMT64x, at);
|
|
buf = malloc (core->anal->opt.bb_max_size);
|
|
if (!buf) {
|
|
eprintf ("Error: malloc (buf)\n");
|
|
goto error;
|
|
}
|
|
|
|
do {
|
|
RFlagItem *f;
|
|
RAnalRef *ref;
|
|
int delta = r_anal_fcn_size (fcn);
|
|
|
|
// XXX hack slow check io error
|
|
if ((buflen = r_io_read_at (core->io, at+delta, buf, 4) != 4)) {
|
|
eprintf ("read errro\n");
|
|
goto error;
|
|
}
|
|
// real read.
|
|
// this is unnecessary if its contiguous
|
|
buflen = r_io_read_at (core->io, at+delta, buf, core->anal->opt.bb_max_size);
|
|
if (core->io->va && !core->io->raw) {
|
|
if (!r_io_is_valid_offset (core->io, at+delta, !core->anal->opt.noncode)) {
|
|
goto error;
|
|
}
|
|
}
|
|
if (r_cons_singleton ()->breaked)
|
|
break;
|
|
fcnlen = r_anal_fcn (core->anal, fcn, at+delta, buf, buflen, reftype);
|
|
if (core->anal->opt.searchstringrefs) r_anal_set_stringrefs(core, fcn);
|
|
if (fcnlen<0) {
|
|
switch (fcnlen) {
|
|
case R_ANAL_RET_ERROR:
|
|
case R_ANAL_RET_NEW:
|
|
case R_ANAL_RET_DUP:
|
|
case R_ANAL_RET_END:
|
|
break;
|
|
default:
|
|
eprintf ("Oops. Negative function size at 0x%08"PFMT64x" (%d)\n",
|
|
at, fcnlen);
|
|
continue;
|
|
}
|
|
}
|
|
f = r_flag_get_i2 (core->flags, fcn->addr);
|
|
free (fcn->name);
|
|
if (f && *f->name) {
|
|
fcn->name = strdup (f->name);
|
|
} else {
|
|
f = r_flag_get_i (core->flags, fcn->addr);
|
|
if (f && *f->name) {
|
|
fcn->name = strdup (f->name);
|
|
} else {
|
|
fcn->name = r_str_newf ("fcn.%08"PFMT64x, fcn->addr);
|
|
}
|
|
}
|
|
|
|
if (fcnlen == R_ANAL_RET_ERROR ||
|
|
(fcnlen == R_ANAL_RET_END && r_anal_fcn_size (fcn) < 1)) { /* Error analyzing function */
|
|
if (core->anal->opt.followbrokenfcnsrefs)
|
|
r_anal_analyze_fcn_refs(core, fcn, depth);
|
|
goto error;
|
|
} else if (fcnlen == R_ANAL_RET_END) { /* Function analysis complete */
|
|
f = r_flag_get_i2 (core->flags, fcn->addr);
|
|
free (fcn->name);
|
|
if (f && f->name) { /* Check if it's already flagged */
|
|
fcn->name = strdup (f->name);
|
|
} else {
|
|
fcn->name = r_str_newf ("%s.%08"PFMT64x,
|
|
fcn->type == R_ANAL_FCN_TYPE_LOC? "loc":
|
|
fcn->type == R_ANAL_FCN_TYPE_SYM? "sym":
|
|
fcn->type == R_ANAL_FCN_TYPE_IMP? "imp": "fcn", fcn->addr);
|
|
/* Add flag */
|
|
r_flag_space_push (core->flags, "functions");
|
|
r_flag_set (core->flags, fcn->name,
|
|
fcn->addr, r_anal_fcn_size (fcn));
|
|
r_flag_space_pop (core->flags);
|
|
}
|
|
// XXX fixes overlined function ranges wtf // fcn->addr = at;
|
|
/* TODO: Dupped analysis, needs more optimization */
|
|
fcn->depth = 256;
|
|
r_core_anal_bb (core, fcn, fcn->addr, true);
|
|
// hack
|
|
if (fcn->depth == 0) {
|
|
eprintf ("Analysis depth reached at 0x%08"PFMT64x"\n", fcn->addr);
|
|
} else {
|
|
fcn->depth = 256 - fcn->depth;
|
|
}
|
|
|
|
/* New function: Add initial xref */
|
|
if (from != UT64_MAX) {
|
|
// We shuold not use fcn->xrefs .. because that should be only via api (on top of sdb)
|
|
// the concepts of refs and xrefs are a bit twisted in the old implementation
|
|
ref = r_anal_ref_new ();
|
|
if (!ref) {
|
|
eprintf ("Error: new (xref)\n");
|
|
goto error;
|
|
}
|
|
ref->addr = from;
|
|
ref->at = fcn->addr;
|
|
ref->type = reftype;
|
|
r_list_append (fcn->xrefs, ref);
|
|
// XXX this is creating dupped entries in the refs list with invalid reftypes, wtf?
|
|
r_anal_xrefs_set (core->anal, reftype, from, fcn->addr);
|
|
}
|
|
// XXX: this is wrong. See CID 1134565
|
|
r_anal_fcn_insert (core->anal, fcn);
|
|
if (has_next) {
|
|
ut64 addr = fcn->addr + r_anal_fcn_size (fcn);
|
|
RIOSection *sect = r_io_section_vget (core->io, addr);
|
|
// only get next if found on an executable section
|
|
if (!sect || (sect && sect->rwx & 1)) {
|
|
for (i = 0; i < nexti; i++) {
|
|
if (next[i] == addr)
|
|
break;
|
|
}
|
|
if (i == nexti) {
|
|
ut64 at = fcn->addr + r_anal_fcn_size (fcn);
|
|
while (true) {
|
|
const RAnalMetaItem *mi = r_meta_find (core->anal, at, R_META_TYPE_ANY, 0);
|
|
if (!mi) break;
|
|
at += mi->size;
|
|
}
|
|
// TODO: ensure next address is function after padding (nop or trap or wat)
|
|
// XXX noisy for test cases because we want to clear the stderr
|
|
r_cons_clear_line (1);
|
|
loganal (fcn->addr, at, 10000 - depth);
|
|
next = next_append (next, &nexti, at);
|
|
}
|
|
}
|
|
}
|
|
if (!r_anal_analyze_fcn_refs (core, fcn, depth)) goto error;
|
|
}
|
|
} while (fcnlen != R_ANAL_RET_END);
|
|
R_FREE (buf);
|
|
|
|
if (has_next) {
|
|
for (i = 0; i < nexti; i++) {
|
|
if (!next[i]) continue;
|
|
r_core_anal_fcn (core, next[i], from, 0, depth - 1);
|
|
}
|
|
free (next);
|
|
}
|
|
return true;
|
|
|
|
error:
|
|
free (buf);
|
|
// ugly hack to free fcn
|
|
if (fcn) {
|
|
if (r_anal_fcn_size (fcn) == 0 || fcn->addr == UT64_MAX) {
|
|
r_anal_fcn_free (fcn);
|
|
fcn = NULL;
|
|
} else {
|
|
// TODO: mark this function as not properly analyzed
|
|
if (!fcn->name) {
|
|
// XXX dupped code.
|
|
fcn->name = r_str_newf ("%s.%08"PFMT64x,
|
|
fcn->type == R_ANAL_FCN_TYPE_LOC? "loc":
|
|
fcn->type == R_ANAL_FCN_TYPE_SYM? "sym":
|
|
fcn->type == R_ANAL_FCN_TYPE_IMP? "imp": "fcn", at);
|
|
/* Add flag */
|
|
r_flag_space_push (core->flags, "functions");
|
|
r_flag_set (core->flags, fcn->name, at, r_anal_fcn_size (fcn));
|
|
r_flag_space_pop (core->flags);
|
|
}
|
|
r_anal_fcn_insert (core->anal, fcn);
|
|
}
|
|
if (fcn && has_next) {
|
|
ut64 newaddr = fcn->addr + r_anal_fcn_size (fcn);
|
|
RIOSection *sect = r_io_section_vget (core->io, newaddr);
|
|
if (!sect || (sect && (sect->rwx & 1))) {
|
|
next = next_append (next, &nexti, newaddr);
|
|
for (i = 0; i < nexti; i++) {
|
|
if (!next[i]) continue;
|
|
#if HASNEXT_FOREVER
|
|
r_core_anal_fcn (core, next[i], next[i], 0, 9999);
|
|
#else
|
|
r_core_anal_fcn (core, next[i], next[i], 0, depth - 1);
|
|
#endif
|
|
}
|
|
free (next);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/* decode and return the RANalOp at the address addr */
|
|
R_API RAnalOp* r_core_anal_op (RCore *core, ut64 addr) {
|
|
int len;
|
|
RAnalOp *op;
|
|
ut8 buf[128];
|
|
ut8 *ptr;
|
|
RAsmOp asmop;
|
|
|
|
op = R_NEW0 (RAnalOp);
|
|
if (!op) return NULL;
|
|
|
|
if (addr >= core->offset && addr + 16 < core->offset + core->blocksize) {
|
|
int delta = (addr - core->offset);
|
|
ptr = core->block + delta;
|
|
len = core->blocksize - delta;
|
|
if (len < 1) goto err_op;
|
|
} else {
|
|
if (r_io_read_at (core->io, addr, buf, sizeof (buf)) < 1) {
|
|
goto err_op;
|
|
}
|
|
ptr = buf;
|
|
len = sizeof (buf);
|
|
}
|
|
if (r_anal_op (core->anal, op, addr, ptr, len) < 1) goto err_op;
|
|
|
|
// decode instruction here
|
|
r_asm_set_pc (core->assembler, addr);
|
|
if (r_asm_disassemble (core->assembler, &asmop, ptr, len) > 0)
|
|
op->mnemonic = strdup (asmop.buf_asm);
|
|
return op;
|
|
|
|
err_op:
|
|
free (op);
|
|
return NULL;
|
|
}
|
|
|
|
static int cb(void *p, const char *k, const char *v) {
|
|
RAnalHint *hint;
|
|
HintListState *hls = p;
|
|
|
|
hint = r_anal_hint_from_string (hls->a, sdb_atoi (k+5), v);
|
|
// TODO: format using (mode)
|
|
switch (hls->mode) {
|
|
case 's':
|
|
r_cons_printf ("%s=%s\n", k, v);
|
|
case '*':
|
|
HINTCMD_ADDR (hint, arch, "aha %s");
|
|
HINTCMD_ADDR (hint, bits, "ahb %d");
|
|
HINTCMD_ADDR (hint, size, "ahs %d");
|
|
HINTCMD_ADDR (hint, opcode, "aho %s");
|
|
HINTCMD_ADDR (hint, syntax, "ahS %s");
|
|
HINTCMD_ADDR (hint, immbase, "ahi %d");
|
|
HINTCMD_ADDR (hint, esil, "ahe %s");
|
|
break;
|
|
case 'j':
|
|
r_cons_printf ("%s{\"from\":%"PFMT64d",\"to\":%"PFMT64d,
|
|
hls->count>0?",":"", hint->addr, hint->addr+hint->size);
|
|
HINTCMD (hint, arch, ",\"arch\":\"%s\"", true); // XXX: arch must not contain strange chars
|
|
HINTCMD (hint, bits, ",\"bits\":%d", true);
|
|
HINTCMD (hint, size, ",\"size\":%d", true);
|
|
HINTCMD (hint, opcode, ",\"opcode\":\"%s\"", true);
|
|
HINTCMD (hint, syntax, ",\"syntax\":\"%s\"", true);
|
|
HINTCMD (hint, immbase, ",\"immbase\":%d", true);
|
|
HINTCMD (hint, esil, ",\"esil\":\"%s\"", true);
|
|
HINTCMD (hint, ptr, ",\"ptr\":\"0x%"PFMT64x"x\"", true);
|
|
r_cons_printf ("}");
|
|
break;
|
|
default:
|
|
r_cons_printf (" 0x%08"PFMT64x" - 0x%08"PFMT64x"\n", hint->addr, hint->addr+hint->size);
|
|
HINTCMD (hint, arch, " arch='%s'", false);
|
|
HINTCMD (hint, bits, " bits=%d", false);
|
|
HINTCMD (hint, size, " size=%d", false);
|
|
HINTCMD (hint, opcode, " opcode='%s'", false);
|
|
HINTCMD (hint, syntax, " syntax='%s'", false);
|
|
HINTCMD (hint, immbase, " immbase=%d", false);
|
|
HINTCMD (hint, esil, " esil='%s'", false);
|
|
r_cons_newline ();
|
|
}
|
|
hls->count++;
|
|
free (hint);
|
|
return 1;
|
|
}
|
|
|
|
R_API void r_core_anal_hint_list (RAnal *a, int mode) {
|
|
HintListState hls = {};
|
|
hls.mode = mode;
|
|
hls.count = 0;
|
|
hls.a = a;
|
|
if (mode == 'j') r_cons_strcat ("[");
|
|
sdb_foreach (a->sdb_hints, cb, &hls);
|
|
if (mode == 'j') r_cons_strcat ("]\n");
|
|
}
|
|
|
|
static char *core_anal_graph_label(RCore *core, RAnalBlock *bb, int opts) {
|
|
int is_html = r_cons_singleton ()->is_html;
|
|
int is_json = opts & R_CORE_ANAL_JSON;
|
|
char cmd[1024], file[1024], *cmdstr = NULL, *filestr = NULL, *str = NULL;
|
|
int line = 0, oline = 0, idx = 0;
|
|
ut64 at;
|
|
|
|
if (opts & R_CORE_ANAL_GRAPHLINES) {
|
|
#if R_ANAL_BB_HAS_OPS
|
|
RAnalOp *opi;
|
|
RListIter *iter;
|
|
r_list_foreach (bb->ops, iter, opi) {
|
|
r_bin_addr2line (core->bin, opi->addr, file, sizeof (file)-1, &line);
|
|
#else
|
|
for (at = bb->addr; at < bb->addr + bb->size; at += 2) {
|
|
r_bin_addr2line (core->bin, at, file, sizeof (file)-1, &line);
|
|
#endif
|
|
if (line != 0 && line != oline && strcmp (file, "??")) {
|
|
filestr = r_file_slurp_line (file, line, 0);
|
|
if (filestr) {
|
|
int flen = strlen (filestr);
|
|
cmdstr = realloc (cmdstr, idx + flen + 8);
|
|
memcpy (cmdstr + idx, filestr, flen);
|
|
idx += flen;
|
|
if (is_json) {
|
|
strcpy (cmdstr + idx, "\\n");
|
|
} else if (is_html) {
|
|
strcpy (cmdstr + idx, "<br />");
|
|
} else {
|
|
strcpy (cmdstr + idx, "\\l");
|
|
}
|
|
free (filestr);
|
|
}
|
|
}
|
|
oline = line;
|
|
}
|
|
} else if (opts & R_CORE_ANAL_GRAPHBODY) {
|
|
r_cons_push ();
|
|
snprintf (cmd, sizeof (cmd), "pD %d @e:asm.comments=0 @ 0x%08"PFMT64x,
|
|
bb->size, bb->addr);
|
|
cmdstr = r_core_cmd_str (core, cmd);
|
|
r_cons_pop ();
|
|
}
|
|
if (cmdstr) {
|
|
str = r_str_escape_dot (cmdstr);
|
|
free (cmdstr);
|
|
if (!str)
|
|
return NULL;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
static char *palColorFor(const char *k) {
|
|
RCons *cons = r_cons_singleton ();
|
|
if (!cons) return NULL;
|
|
const char *c = r_cons_pal_get (k);
|
|
if (c) {
|
|
ut8 r = 0, g = 0, b = 0;
|
|
r_cons_rgb_parse (c, &r, &g, &b, NULL);
|
|
return r_cons_rgb_tostring (r, g, b);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int core_anal_graph_nodes(RCore *core, RAnalFunction *fcn, int opts) {
|
|
int is_html = r_cons_singleton ()->is_html;
|
|
int is_json = opts & R_CORE_ANAL_JSON;
|
|
int is_keva = opts & R_CORE_ANAL_KEYVALUE;
|
|
struct r_anal_bb_t *bbi;
|
|
RListIter *iter;
|
|
int left = 300;
|
|
int count = 0;
|
|
int nodes = 0;
|
|
int top = 0;
|
|
char *str;
|
|
Sdb *DB = NULL;
|
|
char *pal_jump = palColorFor ("graph.true");
|
|
char *pal_fail = palColorFor ("graph.false");
|
|
char *pal_trfa = palColorFor ("graph.trufae");
|
|
char *pal_curr = palColorFor ("graph.current");
|
|
char *pal_traced = palColorFor ("graph.traced");
|
|
char *pal_box4 = palColorFor ("graph.box4");
|
|
bool color_current = r_config_get_i (core->config, "graph.gv.current");
|
|
|
|
if (is_keva) {
|
|
char ns[64];
|
|
DB = sdb_ns (core->anal->sdb, "graph", 1);
|
|
snprintf (ns, sizeof (ns), "fcn.0x%08"PFMT64x, fcn->addr);
|
|
DB = sdb_ns (DB, ns, 1);
|
|
}
|
|
|
|
if (is_keva) {
|
|
char *ename = sdb_encode ((const ut8*)fcn->name, -1);
|
|
sdb_set (DB, "name", fcn->name, 0);
|
|
sdb_set (DB, "ename", ename, 0);
|
|
free (ename);
|
|
if (fcn->nargs > 0)
|
|
sdb_num_set (DB, "nargs", fcn->nargs, 0);
|
|
sdb_num_set (DB, "size", r_anal_fcn_size (fcn), 0);
|
|
if (fcn->stack > 0)
|
|
sdb_num_set (DB, "stack", fcn->stack, 0);
|
|
sdb_set (DB, "pos", "0,0", 0); // needs to run layout
|
|
sdb_set (DB, "type", r_anal_fcn_type_tostring (fcn->type), 0);
|
|
} else
|
|
if (is_json) {
|
|
// TODO: show vars, refs and xrefs
|
|
r_cons_printf ("{\"name\":\"%s\"", fcn->name);
|
|
r_cons_printf (",\"offset\":%"PFMT64d, fcn->addr);
|
|
r_cons_printf (",\"ninstr\":%"PFMT64d, fcn->ninstr);
|
|
r_cons_printf (",\"nargs\":%d", r_anal_var_count (core->anal, fcn, 'a'));
|
|
r_cons_printf (",\"locals\":%d", r_anal_var_count (core->anal, fcn, 'v'));
|
|
r_cons_printf (",\"size\":%d", r_anal_fcn_size (fcn));
|
|
r_cons_printf (",\"stack\":%d", fcn->stack);
|
|
r_cons_printf (",\"type\":%d", fcn->type); // TODO: output string
|
|
//r_cons_printf (",\"cc\":%d", fcn->call); // TODO: calling convention
|
|
if (fcn->dsc) r_cons_printf (",\"signature\":\"%s\"", fcn->dsc);
|
|
r_cons_printf (",\"blocks\":[");
|
|
}
|
|
r_list_foreach (fcn->bbs, iter, bbi) {
|
|
count ++;
|
|
if (is_keva) {
|
|
char key[128];
|
|
sdb_array_push_num (DB, "bbs", bbi->addr, 0);
|
|
snprintf (key, sizeof (key), "bb.0x%08"PFMT64x".size", bbi->addr);
|
|
sdb_num_set (DB, key, bbi->size, 0); // bb.<addr>.size=<num>
|
|
} else
|
|
if (is_json) {
|
|
RDebugTracepoint *t = r_debug_trace_get (core->dbg, bbi->addr);
|
|
ut8 *buf = malloc (bbi->size);
|
|
|
|
if (count>1)
|
|
r_cons_printf (",");
|
|
r_cons_printf ("{\"offset\":%"PFMT64d",\"size\":%"PFMT64d, bbi->addr, bbi->size);
|
|
if (bbi->jump != UT64_MAX)
|
|
r_cons_printf (",\"jump\":%"PFMT64d, bbi->jump);
|
|
if (bbi->fail != -1)
|
|
r_cons_printf (",\"fail\":%"PFMT64d, bbi->fail);
|
|
if (t) {
|
|
r_cons_printf (",\"trace\":{\"count\":%d,\"times\":%d}",
|
|
t->count, t->times);
|
|
}
|
|
r_cons_printf (",\"ops\":");
|
|
if (buf) {
|
|
r_io_read_at (core->io, bbi->addr, buf, bbi->size);
|
|
r_core_print_disasm_json (core, bbi->addr, buf, bbi->size, 0);
|
|
free (buf);
|
|
} else eprintf ("cannot allocate %d bytes\n", bbi->size);
|
|
r_cons_printf ("}");
|
|
continue;
|
|
}
|
|
if (bbi->jump != UT64_MAX) {
|
|
nodes++;
|
|
if (is_keva) {
|
|
char key[128];
|
|
char val[128];
|
|
snprintf (key, sizeof (key), "bb.0x%08"PFMT64x".to", bbi->addr);
|
|
if (bbi->fail != UT64_MAX) {
|
|
snprintf (val, sizeof (val), "0x%08"PFMT64x, bbi->jump);
|
|
} else {
|
|
snprintf (val, sizeof (val), "0x%08"PFMT64x ",0x%08"PFMT64x,
|
|
bbi->jump, bbi->fail);
|
|
}
|
|
// bb.<addr>.to=<jump>,<fail>
|
|
sdb_set (DB, key, val, 0);
|
|
} else if (is_html) {
|
|
r_cons_printf ("<div class=\"connector _0x%08"PFMT64x" _0x%08"PFMT64x"\">\n"
|
|
" <img class=\"connector-end\" src=\"img/arrow.gif\" /></div>\n",
|
|
bbi->addr, bbi->jump);
|
|
} else if (!is_json) {
|
|
//r_cons_printf ("\t\"0x%08"PFMT64x"_0x%08"PFMT64x"\" -> \"0x%08"PFMT64x"_0x%08"PFMT64x"\" "
|
|
// "[color=\"%s\"];\n", fcn->addr, bbi->addr, fcn->addr, bbi->jump,
|
|
// bbi->fail != -1 ? "green" : "blue");
|
|
r_cons_printf ("\t\"0x%08"PFMT64x"\" -> \"0x%08"PFMT64x"\" "
|
|
"[color=\"%s\"];\n", bbi->addr, bbi->jump,
|
|
bbi->fail != -1 ? pal_jump : pal_trfa);
|
|
}
|
|
}
|
|
if (bbi->fail != -1) {
|
|
nodes++;
|
|
if (is_html) {
|
|
r_cons_printf ("<div class=\"connector _0x%08"PFMT64x" _0x%08"PFMT64x"\">\n"
|
|
" <img class=\"connector-end\" src=\"img/arrow.gif\"/></div>\n",
|
|
bbi->addr, bbi->fail);
|
|
} else if (!is_keva) {
|
|
//r_cons_printf ("\t\"0x%08"PFMT64x"_0x%08"PFMT64x"\" -> \"0x%08"PFMT64x"_0x%08"PFMT64x"\" "
|
|
// "[color=\"red\"];\n", fcn->addr, bbi->addr, fcn->addr, bbi->fail);
|
|
r_cons_printf ("\t\"0x%08"PFMT64x"\" -> \"0x%08"PFMT64x"\" "
|
|
"[color=\"%s\"];\n", bbi->addr, bbi->fail, pal_fail);
|
|
}
|
|
}
|
|
if (bbi->switch_op) {
|
|
RAnalCaseOp *caseop;
|
|
RListIter *iter;
|
|
|
|
if (is_html) {
|
|
r_cons_printf ("<div class=\"connector _0x%08"PFMT64x" _0x%08"PFMT64x"\">\n"
|
|
" <img class=\"connector-end\" src=\"img/arrow.gif\"/></div>\n",
|
|
bbi->addr, bbi->fail);
|
|
} else if (!is_keva) {
|
|
//r_cons_printf ("\t\"0x%08"PFMT64x"_0x%08"PFMT64x"\" -> \"0x%08"PFMT64x"_0x%08"PFMT64x"\" "
|
|
// "[color=\"red\"];\n", fcn->addr, bbi->addr, fcn->addr, bbi->fail);
|
|
r_cons_printf ("\t\"0x%08"PFMT64x"\" -> \"0x%08"PFMT64x"\" "
|
|
"[color=\"%s\"];\n", pal_fail, bbi->addr, bbi->fail);
|
|
}
|
|
|
|
r_list_foreach (bbi->switch_op->cases, iter, caseop) {
|
|
nodes++;
|
|
if (is_keva) {
|
|
char key[128];
|
|
snprintf (key, sizeof (key),
|
|
"bb.0x%08"PFMT64x".switch.%"PFMT64d,
|
|
bbi->addr, caseop->value);
|
|
sdb_num_set (DB, key, caseop->jump, 0);
|
|
snprintf (key, sizeof (key),
|
|
"bb.0x%08"PFMT64x".switch", bbi->addr);
|
|
sdb_array_add_num (DB, key, caseop->value, 0);
|
|
} else if (is_html) {
|
|
r_cons_printf ("<div class=\"connector _0x%08"PFMT64x" _0x%08"PFMT64x"\">\n"
|
|
" <img class=\"connector-end\" src=\"img/arrow.gif\"/></div>\n",
|
|
caseop->addr, caseop->jump);
|
|
} else {
|
|
//r_cons_printf ("\t\"0x%08"PFMT64x"_0x%08"PFMT64x"\" -> \"0x%08"PFMT64x"_0x%08"PFMT64x"\" "
|
|
// "[color=\"red\"];\n", fcn->addr, caseop->addr, fcn->addr, caseop->jump);
|
|
r_cons_printf ("\t\"0x%08"PFMT64x"\" -> \"0x%08"PFMT64x"\" "
|
|
"[color=\"%s\"];\n", pal_fail, caseop->addr, caseop->jump);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((str = core_anal_graph_label (core, bbi, opts))) {
|
|
if (opts & R_CORE_ANAL_GRAPHDIFF) {
|
|
const char *difftype = bbi->diff? (\
|
|
bbi->diff->type==R_ANAL_DIFF_TYPE_MATCH? "lightgray":
|
|
bbi->diff->type==R_ANAL_DIFF_TYPE_UNMATCH? "yellow": "red"): "black";
|
|
const char *diffname = bbi->diff? (\
|
|
bbi->diff->type==R_ANAL_DIFF_TYPE_MATCH? "match":
|
|
bbi->diff->type==R_ANAL_DIFF_TYPE_UNMATCH? "unmatch": "new"): "unk";
|
|
if (is_keva) {
|
|
sdb_set (DB, "diff", diffname, 0);
|
|
sdb_set (DB, "label", str, 0);
|
|
} else if (!is_json) {
|
|
nodes++;
|
|
//r_cons_printf (" \"0x%08"PFMT64x"_0x%08"PFMT64x"\" [color=\"%s\","
|
|
// " label=\"%s\", URL=\"%s/0x%08"PFMT64x"\"]\n",
|
|
// fcn->addr, bbi->addr, difftype, str, fcn->name, bbi->addr);
|
|
r_cons_printf (" \"0x%08"PFMT64x"\" [color=\"%s\","
|
|
" label=\"%s\", URL=\"%s/0x%08"PFMT64x"\"]\n",
|
|
bbi->addr, difftype, str, fcn->name, bbi->addr);
|
|
}
|
|
} else {
|
|
if (is_html) {
|
|
nodes++;
|
|
r_cons_printf ("<p class=\"block draggable\" style=\""
|
|
"top: %dpx; left: %dpx; width: 400px;\" id=\""
|
|
"_0x%08"PFMT64x"\">\n%s</p>\n",
|
|
top, left, bbi->addr, str);
|
|
left = left? 0: 600;
|
|
if (!left) top += 250;
|
|
} else if (!is_json && !is_keva) {
|
|
bool current = r_anal_bb_is_in_offset (bbi, core->offset);
|
|
const char *label_color = bbi->traced
|
|
? pal_traced
|
|
: (current && color_current)
|
|
? pal_curr
|
|
: pal_box4;
|
|
nodes++;
|
|
//r_cons_printf (" \"0x%08"PFMT64x"_0x%08"PFMT64x"\" ["
|
|
// "URL=\"%s/0x%08"PFMT64x"\", color=\"%s\", label=\"%s\"]\n",
|
|
// fcn->addr, bbi->addr,
|
|
// fcn->name, bbi->addr,
|
|
// bbi->traced?"yellow":"lightgray", str);
|
|
r_cons_printf ("\t\"0x%08"PFMT64x"\" ["
|
|
"URL=\"%s/0x%08"PFMT64x"\", fillcolor=\"%s\", color=\"%s\", label=\"%s\"]\n",
|
|
bbi->addr, fcn->name, bbi->addr,
|
|
current? "yellow": "lightgray", label_color, str);
|
|
}
|
|
}
|
|
free (str);
|
|
}
|
|
}
|
|
if (is_json)
|
|
r_cons_printf ("]}");
|
|
free (pal_jump);
|
|
free (pal_fail);
|
|
free (pal_trfa);
|
|
return nodes;
|
|
}
|
|
|
|
/* analyze a RAnalBlock at the address at and add that to the fcn function. */
|
|
R_API int r_core_anal_bb(RCore *core, RAnalFunction *fcn, ut64 at, int head) {
|
|
struct r_anal_bb_t *bb, *bbi;
|
|
RListIter *iter;
|
|
ut64 jump, fail;
|
|
int rc = true;
|
|
ut8 *buf = NULL;
|
|
int ret = R_ANAL_RET_NEW, buflen, bblen = 0;
|
|
|
|
--fcn->depth;
|
|
if (fcn->depth <= 0) return false;
|
|
|
|
bb = r_anal_bb_new ();
|
|
if (!bb) return false;
|
|
|
|
if (core->anal->split) {
|
|
ret = r_anal_fcn_split_bb (core->anal, fcn, bb, at);
|
|
} else {
|
|
r_list_foreach (fcn->bbs, iter, bbi) {
|
|
if (at == bbi->addr)
|
|
ret = R_ANAL_RET_DUP;
|
|
}
|
|
}
|
|
if (ret == R_ANAL_RET_DUP) /* Dupped bb */
|
|
goto error;
|
|
|
|
if (ret == R_ANAL_RET_NEW) { /* New bb */
|
|
// XXX: use static buffer size of 512 or so
|
|
buf = malloc (core->anal->opt.bb_max_size);
|
|
if (!buf)
|
|
goto error;
|
|
|
|
do {
|
|
// check io error
|
|
if (r_io_read_at (core->io, at+bblen, buf, 4) != 4) // ETOOSLOW
|
|
goto error;
|
|
r_core_read_at (core, at+bblen, buf, core->anal->opt.bb_max_size);
|
|
if (!r_io_is_valid_offset (core->io, at+bblen, !core->anal->opt.noncode))
|
|
goto error;
|
|
buflen = core->anal->opt.bb_max_size;
|
|
bblen = r_anal_bb (core->anal, bb, at+bblen, buf, buflen, head);
|
|
if (bblen == R_ANAL_RET_ERROR ||
|
|
(bblen == R_ANAL_RET_END && bb->size < 1)) { /* Error analyzing bb */
|
|
goto error;
|
|
} else if (bblen == R_ANAL_RET_END) { /* bb analysis complete */
|
|
if (core->anal->split)
|
|
ret = r_anal_fcn_bb_overlaps (fcn, bb);
|
|
|
|
if (ret == R_ANAL_RET_NEW) {
|
|
r_list_append (fcn->bbs, bb);
|
|
fail = bb->fail;
|
|
jump = bb->jump;
|
|
if (fail != -1)
|
|
r_core_anal_bb (core, fcn, fail, false);
|
|
if (jump != -1)
|
|
r_core_anal_bb (core, fcn, jump, false);
|
|
}
|
|
}
|
|
} while (bblen != R_ANAL_RET_END);
|
|
free (buf);
|
|
return true;
|
|
}
|
|
goto fin;
|
|
error:
|
|
rc = false;
|
|
fin:
|
|
r_list_delete_data (fcn->bbs, bb);
|
|
r_anal_bb_free (bb);
|
|
free (buf);
|
|
return rc;
|
|
}
|
|
|
|
/* returns the address of the basic block that contains addr or UT64_MAX if
|
|
* there is no such basic block */
|
|
R_API ut64 r_core_anal_get_bbaddr(RCore *core, ut64 addr) {
|
|
RAnalBlock *bbi;
|
|
RAnalFunction *fcni;
|
|
RListIter *iter, *iter2;
|
|
r_list_foreach (core->anal->fcns, iter, fcni) {
|
|
r_list_foreach (fcni->bbs, iter2, bbi) {
|
|
if (addr >= bbi->addr && addr < bbi->addr+bbi->size) {
|
|
return bbi->addr;
|
|
}
|
|
}
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
/* seek basic block that contains address addr or just addr if there's no such
|
|
* basic block */
|
|
R_API int r_core_anal_bb_seek(RCore *core, ut64 addr) {
|
|
ut64 bbaddr = r_core_anal_get_bbaddr (core, addr);
|
|
if (bbaddr != UT64_MAX) {
|
|
addr = bbaddr;
|
|
}
|
|
return r_core_seek (core, addr, false);
|
|
}
|
|
|
|
R_API int r_core_anal_esil_fcn(RCore *core, ut64 at, ut64 from, int reftype, int depth) {
|
|
const char *esil;
|
|
RAnalOp *op;
|
|
while (1) {
|
|
// TODO: Implement the proper logic for doing esil analysis
|
|
op = r_core_anal_op (core, at);
|
|
if (!op)
|
|
break;
|
|
esil = R_STRBUF_SAFEGET (&op->esil);
|
|
eprintf ("0x%08"PFMT64x" %d %s\n", at, op->size, esil);
|
|
at += op->size;
|
|
// esilIsRet()
|
|
// esilIsCall()
|
|
// esilIsJmp()
|
|
r_anal_op_free (op);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// XXX: This function takes sometimes forever
|
|
/* analyze a RAnalFunction at the address 'at'.
|
|
* If the function has been already analyzed, it adds a
|
|
* reference to that fcn */
|
|
R_API int r_core_anal_fcn(RCore *core, ut64 at, ut64 from, int reftype, int depth) {
|
|
RAnalFunction *fcn;
|
|
RListIter *iter;
|
|
|
|
int use_esil = r_config_get_i (core->config, "anal.esil");
|
|
|
|
if (core->io->va && !core->io->raw) {
|
|
if (!r_io_is_valid_offset (core->io, at, !core->anal->opt.noncode)) {
|
|
// eprintf ("Invalid address: 0x%08"PFMT64x"\n", at);
|
|
return false;
|
|
}
|
|
}
|
|
if (r_config_get_i (core->config, "anal.a2f")) {
|
|
r_core_cmd0 (core, ".a2f");
|
|
return 0;
|
|
}
|
|
if (use_esil) {
|
|
return r_core_anal_esil_fcn (core, at, from, reftype, depth);
|
|
}
|
|
|
|
/* if there is an anal plugin and it wants to analyze the function itself,
|
|
* run it instead of the normal analysis */
|
|
if (core->anal->cur && core->anal->cur->analyze_fns) {
|
|
int result = R_ANAL_RET_ERROR;
|
|
result = core->anal->cur->analyze_fns (core->anal,
|
|
at, from, reftype, depth);
|
|
|
|
/* update the flags after running the analysis function of the plugin */
|
|
r_flag_space_push (core->flags, "functions");
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
r_flag_set (core->flags, fcn->name,
|
|
fcn->addr, r_anal_fcn_size (fcn));
|
|
}
|
|
r_flag_space_pop (core->flags);
|
|
return result;
|
|
}
|
|
|
|
if (from != UT64_MAX && at == 0) return false;
|
|
if (at == UT64_MAX || depth < 0) return false;
|
|
if (r_cons_singleton()->breaked) return false;
|
|
|
|
fcn = r_anal_get_fcn_in (core->anal, at, 0);
|
|
if (fcn) {
|
|
if (fcn->addr == at) return 0; // already analyzed function
|
|
if (r_anal_fcn_is_in_offset (fcn, from)) { // inner function
|
|
RAnalRef *ref;
|
|
|
|
// XXX: use r_anal-xrefs api and sdb
|
|
// If the xref is new, add it
|
|
// avoid dupes
|
|
r_list_foreach (fcn->xrefs, iter, ref)
|
|
if (from == ref->addr)
|
|
return true;
|
|
|
|
ref = r_anal_ref_new ();
|
|
if (!ref) {
|
|
eprintf ("Error: new (xref)\n");
|
|
return false;
|
|
}
|
|
ref->addr = from;
|
|
ref->at = at;
|
|
ref->type = reftype;
|
|
if (reftype == R_ANAL_REF_TYPE_DATA) {
|
|
// XXX HACK TO AVOID INVALID REFS
|
|
r_list_append (fcn->xrefs, ref);
|
|
} else {
|
|
free (ref);
|
|
}
|
|
// we should analyze and add code ref otherwise aaa != aac
|
|
if (from != UT64_MAX) {
|
|
// We shuold not use fcn->xrefs .. because that should be only via api (on top of sdb)
|
|
// the concepts of refs and xrefs are a bit twisted in the old implementation
|
|
ref = r_anal_ref_new ();
|
|
if (ref) {
|
|
ref->addr = from;
|
|
ref->at = fcn->addr;
|
|
ref->type = reftype;
|
|
r_list_append (fcn->xrefs, ref);
|
|
// XXX this is creating dupped entries in the refs list with invalid reftypes, wtf?
|
|
r_anal_xrefs_set (core->anal, reftype, from, fcn->addr);
|
|
} else eprintf ("Error: new (xref)\n");
|
|
}
|
|
return true;
|
|
} else {
|
|
// split function if overlaps
|
|
r_anal_fcn_resize (fcn, at - fcn->addr);
|
|
}
|
|
}
|
|
return core_anal_fcn (core, at, from, reftype, depth);
|
|
}
|
|
|
|
/* if addr is 0, remove all functions
|
|
* otherwise remove the function addr falls into */
|
|
R_API int r_core_anal_fcn_clean(RCore *core, ut64 addr) {
|
|
RAnalFunction *fcni;
|
|
RListIter *iter, *iter_tmp;
|
|
|
|
if (addr == 0) {
|
|
r_list_purge (core->anal->fcns);
|
|
if (!(core->anal->fcns = r_anal_fcn_list_new ()))
|
|
return false;
|
|
} else {
|
|
r_list_foreach_safe (core->anal->fcns, iter, iter_tmp, fcni) {
|
|
if (in_function (fcni, addr)) {
|
|
r_list_delete (core->anal->fcns, iter);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#define FMT_NO 0
|
|
#define FMT_GV 1
|
|
#define FMT_JS 2
|
|
R_API void r_core_anal_coderefs(RCore *core, ut64 addr, int fmt) {
|
|
RAnalFunction fakefr = {0};
|
|
const char *font = r_config_get (core->config, "graph.font");
|
|
int is_html = r_cons_singleton ()->is_html;
|
|
bool refgraph = r_config_get_i (core->config, "graph.refs");
|
|
int first, first2, showhdr = 0;
|
|
RListIter *iter, *iter2;
|
|
const int hideempty = 1;
|
|
const int usenames = 1;
|
|
RAnalFunction *fcni;
|
|
RAnalRef *fcnr;
|
|
|
|
ut64 from = r_config_get_i (core->config, "graph.from");
|
|
ut64 to = r_config_get_i (core->config, "graph.to");
|
|
|
|
if (fmt == 2)
|
|
r_cons_printf ("[");
|
|
first = 0;
|
|
r_list_foreach (core->anal->fcns, iter, fcni) {
|
|
if (from != UT64_MAX && addr < from) {
|
|
continue;
|
|
}
|
|
if (to != UT64_MAX && addr > to) {
|
|
continue;
|
|
}
|
|
if (addr != UT64_MAX && addr != fcni->addr) {
|
|
continue;
|
|
}
|
|
if (fmt == 0) {
|
|
r_cons_printf ("0x%08"PFMT64x"\n", fcni->addr);
|
|
} else if (fmt == 2) {
|
|
//r_cons_printf ("{\"name\":\"%s\", \"size\":%d,\"imports\":[", fcni->name, fcni->size);
|
|
if (hideempty && r_list_length (fcni->refs) == 0)
|
|
continue;
|
|
if (usenames)
|
|
r_cons_printf ("%s{\"name\":\"%s\", \"size\":%d,\"imports\":[",
|
|
first?",":"", fcni->name, r_anal_fcn_size (fcni));
|
|
else
|
|
r_cons_printf ("%s{\"name\":\"0x%08"PFMT64x"\", \"size\":%d,\"imports\":[",
|
|
first?",":"", fcni->addr, r_anal_fcn_size (fcni));
|
|
first = 1;
|
|
}
|
|
first2 = 0;
|
|
// TODO: maybe fcni->calls instead ?
|
|
r_list_foreach (fcni->refs, iter2, fcnr) {
|
|
RAnalFunction *fr = r_anal_get_fcn_in (core->anal, fcnr->addr, 0);
|
|
if (!fr) {
|
|
//eprintf ("Invalid reference from 0x%08"PFMT64x
|
|
// " to 0x%08"PFMT64x"\n", fcni->addr, fcnr->addr);
|
|
fr = &fakefr;
|
|
if (fr) {
|
|
free (fr->name);
|
|
fr->name = r_str_newf ("unk.0x%"PFMT64x, fcnr->addr);
|
|
}
|
|
}
|
|
if (!is_html && !showhdr) {
|
|
if (fmt == 1) {
|
|
const char * gv_edge = r_config_get (core->config, "graph.gv.edge");
|
|
const char * gv_node = r_config_get (core->config, "graph.gv.node");
|
|
const char * gv_grph = r_config_get (core->config, "graph.gv.graph");
|
|
if (!gv_edge || !*gv_edge)
|
|
gv_edge = "arrowhead=\"vee\"";
|
|
if (!gv_node || !*gv_node)
|
|
gv_node = "color=gray, style=filled shape=box";
|
|
if (!gv_grph || !*gv_grph)
|
|
gv_grph = "bgcolor=white";
|
|
r_cons_printf ("digraph code {\n"
|
|
"\tgraph [%s fontname=\"%s\"];\n"
|
|
"\tnode [%s];\n"
|
|
"\tedge [%s];\n", gv_grph, font, gv_node, gv_edge);
|
|
}
|
|
showhdr = 1;
|
|
}
|
|
// TODO: display only code or data refs?
|
|
RFlagItem *flag = r_flag_get_i (core->flags, fcnr->addr);
|
|
if (fmt == 1) {
|
|
if (flag && flag->name) {
|
|
r_cons_printf ("\t\"0x%08"PFMT64x"\" -> \"0x%08"PFMT64x"\" "
|
|
"[label=\"%s\" color=\"%s\" URL=\"%s/0x%08"PFMT64x"\"];\n",
|
|
fcni->addr, fcnr->addr, flag->name,
|
|
(fcnr->type==R_ANAL_REF_TYPE_CODE ||
|
|
fcnr->type==R_ANAL_REF_TYPE_CALL)?"green":"red",
|
|
flag->name, fcnr->addr);
|
|
r_cons_printf ("\t\"0x%08"PFMT64x"\" "
|
|
"[label=\"%s\""
|
|
" URL=\"%s/0x%08"PFMT64x"\"];\n",
|
|
fcnr->addr, flag->name,
|
|
flag->name, fcnr->addr);
|
|
}
|
|
} else if (fmt == 2) {
|
|
if (fr) {
|
|
if (!hideempty || (hideempty && r_list_length (fr->refs)>0)) {
|
|
if (usenames)
|
|
r_cons_printf ("%s\"%s\"", first2?",":"", fr->name);
|
|
else
|
|
r_cons_printf ("%s\"0x%08"PFMT64x"\"", first2?",":"", fr->addr);
|
|
first2 = 1;
|
|
}
|
|
}
|
|
} else {
|
|
if (refgraph || fcnr->type == 'C') {
|
|
// r_cons_printf ("agn 0x%08"PFMT64x"\n", fcnr->addr);
|
|
// r_cons_printf ("age 0x%08"PFMT64x" 0x%08"PFMT64x"\n", fcni->addr, fr->addr);
|
|
|
|
// TODO: avoid recreating nodes unnecessarily
|
|
r_cons_printf ("agn %s\n", fcni->name);
|
|
r_cons_printf ("agn %s\n", fr->name);
|
|
r_cons_printf ("age %s %s\n", fcni->name, fr->name);
|
|
} else {
|
|
r_cons_printf ("# - 0x%08"PFMT64x" (%c)\n", fcnr->addr, fcnr->type);
|
|
}
|
|
}
|
|
}
|
|
if (fmt==2) r_cons_printf ("]}");
|
|
}
|
|
if (showhdr && fmt==1)
|
|
r_cons_printf ("}\n");
|
|
if (fmt == 2)
|
|
r_cons_printf ("]\n");
|
|
}
|
|
|
|
static void fcn_list_bbs(RAnalFunction *fcn) {
|
|
RAnalBlock *bbi;
|
|
RListIter *iter;
|
|
|
|
r_list_foreach (fcn->bbs, iter, bbi) {
|
|
r_cons_printf ("afb+ 0x%08"PFMT64x" 0x%08"PFMT64x" %d ",
|
|
fcn->addr, bbi->addr, bbi->size);
|
|
r_cons_printf ("0x%08"PFMT64x" ", bbi->jump);
|
|
r_cons_printf ("0x%08"PFMT64x" ", bbi->fail);
|
|
if (bbi->type != R_ANAL_BB_TYPE_NULL) {
|
|
if ((bbi->type & R_ANAL_BB_TYPE_BODY))
|
|
r_cons_printf ("b");
|
|
if ((bbi->type & R_ANAL_BB_TYPE_FOOT))
|
|
r_cons_printf ("f");
|
|
if ((bbi->type & R_ANAL_BB_TYPE_HEAD))
|
|
r_cons_printf ("h");
|
|
if ((bbi->type & R_ANAL_BB_TYPE_LAST))
|
|
r_cons_printf ("l");
|
|
} else {
|
|
r_cons_printf ("n");
|
|
}
|
|
if (bbi->diff) {
|
|
if (bbi->diff->type == R_ANAL_DIFF_TYPE_MATCH)
|
|
r_cons_printf (" m");
|
|
else if (bbi->diff->type == R_ANAL_DIFF_TYPE_UNMATCH)
|
|
r_cons_printf (" u");
|
|
else
|
|
r_cons_printf (" n");
|
|
}
|
|
r_cons_printf ("\n");
|
|
}
|
|
}
|
|
|
|
R_API int r_core_anal_fcn_list_size(RCore *core) {
|
|
RAnalFunction *fcn;
|
|
RListIter *iter;
|
|
ut32 total = 0;
|
|
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
total += r_anal_fcn_size (fcn);
|
|
}
|
|
r_cons_printf ("%d\n", total);
|
|
return total;
|
|
}
|
|
|
|
static int cmpfcn(const void *_a, const void *_b) {
|
|
const RAnalFunction *_fcn1 = _a, *_fcn2 = _b;
|
|
return (_fcn1->addr > _fcn2->addr);
|
|
}
|
|
|
|
R_API int r_core_anal_fcn_list(RCore *core, const char *input, int rad) {
|
|
ut64 addr;
|
|
RListIter *iter, *iter2;
|
|
RAnalFunction *fcn;
|
|
RAnalRef *refi;
|
|
RAnalVar *vari;
|
|
int first, bbs, count = 0;
|
|
const char *lang;
|
|
bool demangle = r_config_get_i (core->config, "bin.demangle");
|
|
lang = demangle ? r_config_get (core->config, "bin.lang") : NULL;
|
|
bool use_color = r_config_get_i (core->config, "scr.color");
|
|
|
|
if (input && *input)
|
|
addr = r_num_math (core->num, input + 1);
|
|
else
|
|
addr = core->offset;
|
|
|
|
if (rad == 2) {
|
|
char *tmp, *name = NULL;
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
if (input && input[0] && input[1] && input[2]!='*' && !strncmp (fcn->name, "loc.", 4))
|
|
continue;
|
|
bbs = r_list_length (fcn->bbs);
|
|
name = strdup (fcn->name ? fcn->name : "");
|
|
if (demangle) {
|
|
tmp = r_bin_demangle (core->bin->cur, lang, name);
|
|
if (tmp) {
|
|
free (name);
|
|
name = tmp;
|
|
}
|
|
}
|
|
r_cons_printf ("0x%08"PFMT64x" %"PFMT64d" %d %s\n",
|
|
(ut64)fcn->addr, (ut64)r_anal_fcn_size (fcn),
|
|
(int)bbs, name);
|
|
R_FREE (name);
|
|
}
|
|
return true;
|
|
} else if (rad == 'j') {
|
|
r_cons_printf ("[");
|
|
}
|
|
if (rad == 'l') {
|
|
if (!r_list_empty (core->anal->fcns)) {
|
|
r_cons_printf ("%-11s %4s %5s %4s %11s range %-11s %s %s %s %s %s %s\n",
|
|
"address", "size", "nbbs", "cc", "min bound", "max bound",
|
|
"calls", "vars", "args", "xref", "frame", "name");
|
|
r_cons_printf ("%-11s %-4s %-5s %-4s %-11s ===== %-11s %s %s %s %s %s %s\n",
|
|
"===========", "====", "=====", "====", "===========", "===========",
|
|
"=====", "====", "====", "====", "=====", "====");
|
|
}
|
|
}
|
|
r_list_sort (core->anal->fcns, &cmpfcn);
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
int showFunc = 0;
|
|
if (input) {
|
|
showFunc = *input && (!strcmp (input, "$$"));
|
|
if (showFunc) {
|
|
showFunc = in_function(fcn, core->offset);
|
|
} else {
|
|
if (!strcmp (fcn->name, *input? input + 1: input))
|
|
showFunc = 1;
|
|
showFunc = in_function (fcn, addr);
|
|
}
|
|
} else {
|
|
showFunc = 1;
|
|
}
|
|
if (showFunc) {
|
|
char *tmp, *name = strdup (fcn->name ? fcn->name : "");
|
|
if (demangle) {
|
|
tmp = r_bin_demangle (core->bin->cur, lang, name);
|
|
if (tmp) {
|
|
free (name);
|
|
name = tmp;
|
|
}
|
|
}
|
|
count++;
|
|
if (rad == 'o' || rad == 'l') {
|
|
RListIter *callrefiter, *bbsiter;
|
|
RAnalBlock *bbi;
|
|
ut64 max = UT64_MIN;
|
|
ut64 min = UT64_MAX;
|
|
int noofCallRef = 0;
|
|
int noofRef = 0;
|
|
r_list_foreach (fcn->refs, callrefiter, refi) {
|
|
if (refi->type == R_ANAL_REF_TYPE_CALL) {
|
|
noofCallRef = noofCallRef + 1;
|
|
}
|
|
noofRef++;
|
|
}
|
|
r_list_foreach (fcn->bbs, bbsiter, bbi) {
|
|
if (max < (bbi->addr + bbi->size)) {
|
|
max = bbi->addr + bbi->size;
|
|
}
|
|
if (min > bbi->addr) {
|
|
min = bbi->addr;
|
|
}
|
|
}
|
|
char *msg;
|
|
int sz = r_anal_fcn_realsize (fcn);
|
|
if (r_anal_fcn_size (fcn) != sz) {
|
|
msg = r_str_newf ("%4d -> %-4d", r_anal_fcn_size (fcn), sz);
|
|
} else {
|
|
msg = r_str_newf ("%4d", r_anal_fcn_size (fcn));
|
|
}
|
|
if (rad == 'l') {
|
|
const char *color = "";
|
|
const char *color_end = "";
|
|
if (use_color) {
|
|
color_end = Color_RESET;
|
|
if (strstr (name, "sym.imp.")) {
|
|
color = Color_YELLOW;
|
|
} else if (strstr (name, "sym.")) {
|
|
color = Color_GREEN;
|
|
} else if (strstr (name, "sub.")) {
|
|
color = Color_MAGENTA;
|
|
}
|
|
}
|
|
r_cons_printf ("%s0x%08"PFMT64x" %4d %5d %4d 0x%08"PFMT64x" %5d 0x%08"PFMT64x" %5d %4d %4d %4d %5d %s%s\n",
|
|
color, fcn->addr,
|
|
r_anal_fcn_cc (fcn),
|
|
r_anal_fcn_realsize (fcn), r_list_length (fcn->bbs), min, r_anal_fcn_size (fcn), max, noofCallRef,
|
|
r_anal_var_count (core->anal, fcn, 'v'),
|
|
r_anal_var_count (core->anal, fcn, 'a'), noofRef, fcn->maxstack, name, color_end);
|
|
} else {
|
|
int sz = r_anal_fcn_realsize (fcn);
|
|
if (r_anal_fcn_size(fcn) == sz) {
|
|
free (msg);
|
|
msg = r_str_newf ("%-12d", r_anal_fcn_size (fcn));
|
|
} else {
|
|
free (msg);
|
|
msg = r_str_newf ("%-4d -> %-4d", r_anal_fcn_size (fcn), sz);
|
|
}
|
|
r_cons_printf ("0x%08"PFMT64x" %4d %4s %s\n",
|
|
fcn->addr, r_list_length (fcn->bbs), msg, name);
|
|
}
|
|
free (msg);
|
|
} else if (rad == 'q') {
|
|
r_cons_printf ("0x%08"PFMT64x" ", fcn->addr);
|
|
//fcn->addr, fcn->size, r_list_length (fcn->bbs), fcn->name);
|
|
} else if (rad == 'j') {
|
|
r_cons_printf ("%s{\"offset\":%"PFMT64d",\"name\":\"%s\",\"size\":%d",
|
|
count>1? ",":"", fcn->addr, name, r_anal_fcn_size (fcn));
|
|
r_cons_printf (",\"realsz\":%d", r_anal_fcn_realsize (fcn));
|
|
r_cons_printf (",\"cc\":%d", r_anal_fcn_cc (fcn));
|
|
r_cons_printf (",\"nbbs\":%d", r_list_length (fcn->bbs));
|
|
r_cons_printf (",\"calltype\":\"%s\"", r_anal_cc_type2str (fcn->call));
|
|
r_cons_printf (",\"type\":\"%s\"",
|
|
fcn->type==R_ANAL_FCN_TYPE_SYM?"sym":
|
|
fcn->type==R_ANAL_FCN_TYPE_IMP?"imp":"fcn");
|
|
if (fcn->type == R_ANAL_FCN_TYPE_FCN || fcn->type == R_ANAL_FCN_TYPE_SYM)
|
|
r_cons_printf (",\"diff\":\"%s\"",
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_MATCH?"MATCH":
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_UNMATCH?"UNMATCH":"NEW");
|
|
|
|
r_cons_printf (",\"callrefs\":[");
|
|
first = 1;
|
|
r_list_foreach (fcn->refs, iter2, refi)
|
|
if (refi->type == R_ANAL_REF_TYPE_CODE ||
|
|
refi->type == R_ANAL_REF_TYPE_CALL) {
|
|
r_cons_printf ("%s{\"addr\":%"PFMT64d",\"type\":\"%c\"}",
|
|
first?"":",",
|
|
refi->addr,
|
|
refi->type==R_ANAL_REF_TYPE_CALL?'C':'J');
|
|
first = 0;
|
|
}
|
|
|
|
r_cons_printf ("],\"datarefs\":[");
|
|
first = 1;
|
|
r_list_foreach (fcn->refs, iter2, refi)
|
|
if (refi->type == R_ANAL_REF_TYPE_DATA) {
|
|
r_cons_printf ("%s%"PFMT64d, first?"":",", refi->addr);
|
|
first = 0;
|
|
}
|
|
|
|
r_cons_printf ("],\"codexrefs\":[");
|
|
first = 1;
|
|
r_list_foreach (fcn->xrefs, iter2, refi)
|
|
if (refi->type == R_ANAL_REF_TYPE_CODE ||
|
|
refi->type == R_ANAL_REF_TYPE_CALL) {
|
|
r_cons_printf ("%s{\"addr\":%"PFMT64d",\"type\":\"%c\"}",
|
|
first?"":",",
|
|
refi->addr,
|
|
refi->type==R_ANAL_REF_TYPE_CALL?'C':'J');
|
|
first = 0;
|
|
}
|
|
|
|
r_cons_printf ("],\"dataxrefs\":[");
|
|
first = 1;
|
|
r_list_foreach (fcn->xrefs, iter2, refi)
|
|
if (refi->type == R_ANAL_REF_TYPE_DATA) {
|
|
r_cons_printf ("%s%"PFMT64d, first?"":",", refi->addr);
|
|
first = 0;
|
|
}
|
|
r_cons_printf ("]");
|
|
|
|
if (fcn->type == R_ANAL_FCN_TYPE_FCN || fcn->type == R_ANAL_FCN_TYPE_SYM) {
|
|
r_cons_printf (",\"difftype\":\"%s\"",
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_MATCH?"match":
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_UNMATCH?"unmatch":"new");
|
|
if (fcn->diff->addr != -1)
|
|
r_cons_printf (",\"diffaddr\":%"PFMT64d, fcn->diff->addr);
|
|
if (fcn->diff->name != NULL)
|
|
r_cons_printf (",\"diffname\":\"%s\"", fcn->diff->name);
|
|
}
|
|
r_cons_printf ("}");
|
|
} else if (rad) {
|
|
r_cons_printf ("f %s %d 0x%08"PFMT64x"\n", name, r_anal_fcn_size (fcn), fcn->addr);
|
|
r_cons_printf ("af+ 0x%08"PFMT64x" %d %s %c %c\n",
|
|
fcn->addr, r_anal_fcn_size (fcn), name,
|
|
fcn->type==R_ANAL_FCN_TYPE_LOC?'l':
|
|
fcn->type==R_ANAL_FCN_TYPE_SYM?'s':
|
|
fcn->type==R_ANAL_FCN_TYPE_IMP?'i':'f',
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_MATCH?'m':
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_UNMATCH?'u':'n');
|
|
r_cons_printf ("afC %s @ 0x%08"PFMT64x"\n",
|
|
r_anal_cc_type2str (fcn->call), fcn->addr);
|
|
if (fcn->folded)
|
|
r_cons_printf ("afF @ 0x%08"PFMT64x"\n", fcn->addr);
|
|
fcn_list_bbs (fcn);
|
|
/* show variables and arguments */
|
|
r_core_cmdf (core, "afa* @ 0x%"PFMT64x"\n", fcn->addr);
|
|
r_core_cmdf (core, "afv* @ 0x%"PFMT64x"\n", fcn->addr);
|
|
} else {
|
|
r_cons_printf ("#\n offset: 0x%08"PFMT64x"\n name: %s\n size: %"PFMT64d,
|
|
fcn->addr, name, (ut64)r_anal_fcn_size (fcn));
|
|
r_cons_printf ("\n realsz: %d", r_anal_fcn_realsize (fcn));
|
|
r_cons_printf ("\n stackframe: %d", fcn->maxstack);
|
|
r_cons_printf ("\n call-convention: %s", r_anal_cc_type2str (fcn->call));
|
|
r_cons_printf ("\n cyclomatic-complexity: %d", r_anal_fcn_cc (fcn));
|
|
r_cons_printf ("\n type: %s",
|
|
fcn->type==R_ANAL_FCN_TYPE_SYM?"sym":
|
|
fcn->type==R_ANAL_FCN_TYPE_IMP?"imp":"fcn");
|
|
if (fcn->type==R_ANAL_FCN_TYPE_FCN || fcn->type==R_ANAL_FCN_TYPE_SYM)
|
|
r_cons_printf (" [%s]",
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_MATCH?"MATCH":
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_UNMATCH?"UNMATCH":"NEW");
|
|
|
|
r_cons_printf ("\n num-bbs: %d", r_list_length (fcn->bbs));
|
|
r_cons_printf ("\n call-refs: ");
|
|
r_list_foreach (fcn->refs, iter2, refi)
|
|
if (refi->type == R_ANAL_REF_TYPE_CODE ||
|
|
refi->type == R_ANAL_REF_TYPE_CALL)
|
|
r_cons_printf ("0x%08"PFMT64x" %c ", refi->addr,
|
|
refi->type==R_ANAL_REF_TYPE_CALL?'C':'J');
|
|
|
|
r_cons_printf ("\n data-refs: ");
|
|
r_list_foreach (fcn->refs, iter2, refi)
|
|
if (refi->type == R_ANAL_REF_TYPE_DATA)
|
|
r_cons_printf ("0x%08"PFMT64x" ", refi->addr);
|
|
|
|
r_cons_printf ("\n code-xrefs: ");
|
|
r_list_foreach (fcn->xrefs, iter2, refi)
|
|
if (refi->type == R_ANAL_REF_TYPE_CODE ||
|
|
refi->type == R_ANAL_REF_TYPE_CALL)
|
|
r_cons_printf ("0x%08"PFMT64x" %c ", refi->addr,
|
|
refi->type==R_ANAL_REF_TYPE_CALL?'C':'J');
|
|
|
|
r_cons_printf ("\n data-xrefs: ");
|
|
r_list_foreach (fcn->xrefs, iter2, refi)
|
|
if (refi->type == R_ANAL_REF_TYPE_DATA)
|
|
r_cons_printf ("0x%08"PFMT64x" ", refi->addr);
|
|
|
|
if (fcn->type==R_ANAL_FCN_TYPE_FCN || fcn->type==R_ANAL_FCN_TYPE_SYM) {
|
|
r_cons_printf ("\n vars: %d", r_list_length (fcn->vars));
|
|
r_list_foreach (fcn->vars, iter2, vari) {
|
|
char *s = r_anal_type_to_str (core->anal, vari->type);
|
|
r_cons_printf ("\n %s %s @ 0x%02x", s, vari->name, vari->delta);
|
|
free (s);
|
|
}
|
|
r_cons_printf ("\n diff: type: %s",
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_MATCH?"match":
|
|
fcn->diff->type==R_ANAL_DIFF_TYPE_UNMATCH?"unmatch":"new");
|
|
if (fcn->diff->addr != -1)
|
|
r_cons_printf (" addr: 0x%"PFMT64x, fcn->diff->addr);
|
|
if (fcn->diff->name != NULL)
|
|
r_cons_printf (" function: %s",
|
|
fcn->diff->name);
|
|
}
|
|
r_cons_newline ();
|
|
}
|
|
R_FREE (name);
|
|
}
|
|
}
|
|
if (rad == 'q')
|
|
r_cons_newline ();
|
|
if (rad == 'j') {
|
|
r_cons_printf ("]\n");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static RList *recurse(RCore *core, RAnalBlock *from, RAnalBlock *dest);
|
|
|
|
static RList *recurse_bb(RCore *core, ut64 addr, RAnalBlock *dest) {
|
|
RAnalBlock *bb;
|
|
RList *ret;
|
|
bb = r_anal_bb_from_offset (core->anal, addr);
|
|
if (bb == dest) {
|
|
eprintf ("path found!");
|
|
return NULL;
|
|
}
|
|
ret = recurse (core, bb, dest);
|
|
if (ret) return ret;
|
|
return NULL;
|
|
}
|
|
|
|
static RList *recurse(RCore *core, RAnalBlock *from, RAnalBlock *dest) {
|
|
recurse_bb (core, from->jump, dest);
|
|
recurse_bb (core, from->fail, dest);
|
|
|
|
/* same for all calls */
|
|
// TODO: RAnalBlock must contain a linked list of calls
|
|
return NULL;
|
|
}
|
|
|
|
R_API void fcn_callconv (RCore *core, RAnalFunction *fcn) {
|
|
if (!core || !fcn || core->anal->opt.bb_max_size < 1) {
|
|
return;
|
|
}
|
|
ut8 *buf = calloc(1,core->anal->opt.bb_max_size);
|
|
RListIter *tmp = NULL;
|
|
RAnalBlock *bb = NULL;
|
|
int i;
|
|
if(!buf){
|
|
return;
|
|
}
|
|
r_list_foreach (fcn->bbs, tmp, bb) {
|
|
if (bb->size < 1) continue;
|
|
buf = realloc (buf, bb->size);
|
|
if (r_io_read_at (core->io, bb->addr, buf, bb->size) != bb->size) {
|
|
eprintf ("read error\n");
|
|
free(buf);
|
|
return;
|
|
}
|
|
for (i = 0 ; i < bb->n_op_pos ; i++) {
|
|
RAnalOp op = {0};
|
|
r_anal_op (core->anal, &op, 0, buf + bb->op_pos[i], bb->size - bb->op_pos[i]);
|
|
op.addr = bb->addr + bb->op_pos[i];
|
|
fill_args (core->anal, fcn, &op);
|
|
}
|
|
}
|
|
|
|
if (fcn->call == R_ANAL_CC_TYPE_FASTCALL) {
|
|
r_anal_var_add (core->anal, fcn->addr, 1, 0,'A', "int", 4,"arg_ecx");
|
|
r_anal_var_add (core->anal, fcn->addr, 1, 1,'A', "int", 4,"arg_edx");
|
|
}
|
|
free (buf);
|
|
return;
|
|
}
|
|
|
|
R_API RList* r_core_anal_graph_to(RCore *core, ut64 addr, int n) {
|
|
RAnalBlock *bb, *root = NULL, *dest = NULL;
|
|
RListIter *iter, *iter2;
|
|
RList *list2 = NULL, *list = NULL;
|
|
RAnalFunction *fcn;
|
|
|
|
r_list_foreach (core->anal->fcns, iter, fcn) {
|
|
if (!r_anal_fcn_is_in_offset (fcn, core->offset))
|
|
continue;
|
|
r_list_foreach (fcn->bbs, iter2, bb) {
|
|
if (r_anal_bb_is_in_offset (bb, addr)) {
|
|
dest = bb;
|
|
}
|
|
if (r_anal_bb_is_in_offset (bb, core->offset)) {
|
|
root = bb;
|
|
r_list_append (list, list2);
|
|
}
|
|
}
|
|
}
|
|
if (root && dest) {
|
|
if (dest == root) {
|
|
eprintf ("Source and destination are the same\n");
|
|
return NULL;
|
|
}
|
|
eprintf ("ROOT BB 0x%08"PFMT64x"\n", root->addr);
|
|
eprintf ("DEST BB 0x%08"PFMT64x"\n", dest->addr);
|
|
list = r_list_new ();
|
|
printf ("=> 0x%08"PFMT64x"\n", root->jump);
|
|
} else eprintf ("Unable to find source or destination basic block\n");
|
|
return list;
|
|
}
|
|
|
|
R_API int r_core_anal_graph(RCore *core, ut64 addr, int opts) {
|
|
ut64 from = r_config_get_i (core->config, "graph.from");
|
|
ut64 to = r_config_get_i (core->config, "graph.to");
|
|
const char *font = r_config_get (core->config, "graph.font");
|
|
int is_html = r_cons_singleton ()->is_html;
|
|
int is_json = opts & R_CORE_ANAL_JSON;
|
|
int is_keva = opts & R_CORE_ANAL_KEYVALUE;
|
|
int reflines, bytes, dwarf;
|
|
RAnalFunction *fcni;
|
|
RListIter *iter;
|
|
int nodes = 0;
|
|
int count = 0;
|
|
|
|
if (!addr) {
|
|
addr = core->offset;
|
|
}
|
|
if (r_list_empty (core->anal->fcns)) {
|
|
eprintf ("No functions to diff\n");
|
|
return false;
|
|
}
|
|
|
|
//opts |= R_CORE_ANAL_GRAPHBODY;
|
|
reflines = r_config_get_i (core->config, "asm.lines");
|
|
bytes = r_config_get_i (core->config, "asm.bytes");
|
|
dwarf = r_config_get_i (core->config, "asm.dwarf");
|
|
r_config_set_i (core->config, "asm.lines", 0);
|
|
r_config_set_i (core->config, "asm.bytes", 0);
|
|
r_config_set_i (core->config, "asm.dwarf", 0);
|
|
if (!is_html && !is_json && !is_keva) {
|
|
const char * gv_edge = r_config_get (core->config, "graph.gv.edge");
|
|
const char * gv_node = r_config_get (core->config, "graph.gv.node");
|
|
if (!gv_edge || !*gv_edge)
|
|
gv_edge = "arrowhead=\"vee\"";
|
|
if (!gv_node || !*gv_node) {
|
|
gv_node = "color=gray, style=filled shape=box";
|
|
}
|
|
r_cons_printf ("digraph code {\n"
|
|
"\tgraph [bgcolor=white fontsize=8 fontname=\"%s\"];\n"
|
|
"\tnode [%s];\n"
|
|
"\tedge [%s];\n", font, gv_node, gv_edge);
|
|
}
|
|
if (is_json)
|
|
r_cons_printf ("[");
|
|
r_list_foreach (core->anal->fcns, iter, fcni) {
|
|
if (fcni->type & (R_ANAL_FCN_TYPE_SYM | R_ANAL_FCN_TYPE_FCN)
|
|
&& (addr == 0 || in_function (fcni, addr))) {
|
|
if (!addr && (from != UT64_MAX && to != UT64_MAX)) {
|
|
if (fcni->addr < from || fcni->addr > to) {
|
|
continue;
|
|
}
|
|
}
|
|
if (is_json && count++>0) r_cons_printf (",");
|
|
nodes += core_anal_graph_nodes (core, fcni, opts);
|
|
if (addr != 0) break;
|
|
}
|
|
}
|
|
if (!nodes) {
|
|
if (!is_html && !is_json && !is_keva) {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, addr, 0);
|
|
r_cons_printf ("\t\"0x%08"PFMT64x"\";\n", fcn? fcn->addr: addr);
|
|
}
|
|
}
|
|
if (!is_keva && !is_html && !is_json) r_cons_printf ("}\n");
|
|
if (is_json)
|
|
r_cons_printf ("]\n");
|
|
r_config_set_i (core->config, "asm.lines", reflines);
|
|
r_config_set_i (core->config, "asm.bytes", bytes);
|
|
r_config_set_i (core->config, "asm.dwarf", dwarf);
|
|
return true;
|
|
}
|
|
|
|
static int core_anal_followptr(RCore *core, int type, ut64 at, ut64 ptr, ut64 ref, int code, int depth) {
|
|
ut64 dataptr;
|
|
int wordsize;
|
|
|
|
if (ptr == ref) {
|
|
if (code) {
|
|
r_anal_ref_add (core->anal, ref, at, type? type: 'c');
|
|
// r_cons_printf ("ax%c 0x%08"PFMT64x" 0x%08"PFMT64x"\n",
|
|
// type? type: 'c', (ut64)ref, (ut64)at);
|
|
} else {
|
|
r_anal_ref_add (core->anal, ref, at, 'd');
|
|
//r_cons_printf ("axd 0x%08"PFMT64x" 0x%08"PFMT64x"\n", (ut64)ref, (ut64)at);
|
|
}
|
|
return true;
|
|
}
|
|
if (depth < 1) {
|
|
return false;
|
|
}
|
|
wordsize = (int)(core->anal->bits / 8);
|
|
if ((dataptr = r_io_read_i (core->io, ptr, wordsize)) == -1) {
|
|
return false;
|
|
}
|
|
return core_anal_followptr (core, type, at, dataptr, ref, code, depth - 1);
|
|
}
|
|
|
|
#define OPSZ 8
|
|
R_API int r_core_anal_search(RCore *core, ut64 from, ut64 to, ut64 ref) {
|
|
ut8 *buf = (ut8 *)malloc (core->blocksize);
|
|
int ptrdepth = r_config_get_i (core->config, "anal.ptrdepth");
|
|
int ret, i, count = 0;
|
|
RAnalOp op = {0};
|
|
ut64 at;
|
|
char bckwrds, do_bckwrd_srch;
|
|
// TODO: get current section range here or gtfo
|
|
// ???
|
|
// XXX must read bytes correctly
|
|
do_bckwrd_srch = bckwrds = core->search->bckwrds;
|
|
if (buf == NULL) return -1;
|
|
r_io_use_desc (core->io, core->file->desc);
|
|
if (ref == 0LL) {
|
|
eprintf ("Null reference search is not supported\n");
|
|
free (buf);
|
|
return -1;
|
|
}
|
|
r_cons_break (NULL, NULL);
|
|
if (core->blocksize > OPSZ) {
|
|
if (bckwrds) {
|
|
if (from + core->blocksize > to) {
|
|
at = from;
|
|
do_bckwrd_srch = false;
|
|
} else {
|
|
at = to - core->blocksize;
|
|
}
|
|
} else {
|
|
at = from;
|
|
}
|
|
while ((!bckwrds && at < to) || bckwrds) {
|
|
eprintf ("\r[0x%08"PFMT64x"-0x%08"PFMT64x"]", at, to);
|
|
if (r_cons_is_breaked ())
|
|
break;
|
|
r_cons_break (NULL, NULL);
|
|
// TODO: this can be probably enhaced
|
|
ret = r_io_read_at (core->io, at, buf, core->blocksize);
|
|
if (ret != core->blocksize)
|
|
break;
|
|
for (i = bckwrds ? (core->blocksize - OPSZ - 1) : 0;
|
|
(!bckwrds && i < core->blocksize - OPSZ) || (bckwrds && i > 0);
|
|
bckwrds ? i-- : i++) {
|
|
if (r_cons_is_breaked ())
|
|
break;
|
|
r_cons_break (NULL, NULL);
|
|
r_anal_op_fini (&op);
|
|
if (!r_anal_op (core->anal, &op, at + i, buf + i,
|
|
core->blocksize - i)) {
|
|
continue;
|
|
}
|
|
switch (op.type) {
|
|
case R_ANAL_OP_TYPE_JMP:
|
|
case R_ANAL_OP_TYPE_CJMP:
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
case R_ANAL_OP_TYPE_CCALL:
|
|
if (op.jump != -1 &&
|
|
core_anal_followptr (core, 'C',
|
|
at + i, op.jump, ref,
|
|
true, 0)) {
|
|
count ++;
|
|
}
|
|
break;
|
|
case R_ANAL_OP_TYPE_UCJMP:
|
|
case R_ANAL_OP_TYPE_UJMP:
|
|
if (op.ptr != -1 &&
|
|
core_anal_followptr (core, 'c',
|
|
at + i, op.ptr, ref,
|
|
true ,1)) {
|
|
count ++;
|
|
}
|
|
break;
|
|
case R_ANAL_OP_TYPE_UCALL:
|
|
case R_ANAL_OP_TYPE_UCCALL:
|
|
if (op.ptr != -1 &&
|
|
core_anal_followptr (core, 'C',
|
|
at + i, op.ptr, ref,
|
|
true ,1)) {
|
|
count ++;
|
|
}
|
|
break;
|
|
default:
|
|
if (op.ptr != -1 &&
|
|
core_anal_followptr (core, 'd',
|
|
at + i, op.ptr, ref,
|
|
false, ptrdepth)) {
|
|
count ++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (bckwrds) {
|
|
if (!do_bckwrd_srch) break;
|
|
if (at > from + core->blocksize - OPSZ) {
|
|
at -= core->blocksize;
|
|
} else {
|
|
do_bckwrd_srch = false;
|
|
at = from;
|
|
}
|
|
} else {
|
|
at += core->blocksize - OPSZ;
|
|
}
|
|
}
|
|
} else {
|
|
eprintf ("error: block size too small\n");
|
|
}
|
|
r_cons_break_end ();
|
|
free (buf);
|
|
r_anal_op_fini (&op);
|
|
return count;
|
|
}
|
|
|
|
R_API int r_core_anal_search_xrefs(RCore *core, ut64 from, ut64 to, int rad) {
|
|
int cfg_debug = r_config_get_i (core->config, "cfg.debug");
|
|
bool cfg_anal_strings = r_config_get_i (core->config, "anal.strings");
|
|
ut8 *buf;
|
|
ut64 at;
|
|
int count = 0;
|
|
RAnalOp op = { 0 };
|
|
|
|
if (from == to) {
|
|
return -1;
|
|
}
|
|
if (from > to) {
|
|
eprintf ("Invalid range (0x%"PFMT64x
|
|
" >= 0x%"PFMT64x")\n", from, to);
|
|
return -1;
|
|
}
|
|
|
|
if (core->blocksize <= OPSZ) {
|
|
eprintf ("Error: block size too small\n");
|
|
return -1;
|
|
}
|
|
|
|
buf = (ut8 *)malloc (core->blocksize);
|
|
if (!buf) {
|
|
eprintf ("Error: cannot allocate a block\n");
|
|
return -1;
|
|
}
|
|
|
|
if (rad == 'j')
|
|
r_cons_printf ("{");
|
|
|
|
r_io_use_desc (core->io, core->file->desc);
|
|
r_cons_break (NULL, NULL);
|
|
at = from;
|
|
while (at < to && !r_cons_singleton()->breaked) {
|
|
int i, ret;
|
|
|
|
ret = r_io_read_at (core->io, at, buf, core->blocksize);
|
|
if (ret != core->blocksize && at+ret-OPSZ < to)
|
|
break;
|
|
|
|
i = 0;
|
|
while (at+i < to && i < ret-OPSZ) {
|
|
RAnalRefType type;
|
|
ut64 xref_from, xref_to;
|
|
|
|
if (r_cons_singleton()->breaked) {
|
|
break;
|
|
}
|
|
xref_from = at+i;
|
|
r_anal_op_fini (&op);
|
|
ret = r_anal_op (core->anal, &op, at+i, buf+i, core->blocksize-i);
|
|
i += (ret > 0) ? ret : 1;
|
|
if (ret <= 0 || at+i > to)
|
|
continue;
|
|
|
|
// Get reference type and target address
|
|
type = R_ANAL_REF_TYPE_NULL;
|
|
switch (op.type) {
|
|
case R_ANAL_OP_TYPE_JMP:
|
|
case R_ANAL_OP_TYPE_CJMP:
|
|
type = R_ANAL_REF_TYPE_CODE;
|
|
xref_to = op.jump;
|
|
break;
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
case R_ANAL_OP_TYPE_CCALL:
|
|
type = R_ANAL_REF_TYPE_CALL;
|
|
xref_to = op.jump;
|
|
break;
|
|
case R_ANAL_OP_TYPE_UJMP:
|
|
case R_ANAL_OP_TYPE_UCJMP:
|
|
type = R_ANAL_REF_TYPE_CODE;
|
|
xref_to = op.ptr;
|
|
break;
|
|
case R_ANAL_OP_TYPE_UCALL:
|
|
case R_ANAL_OP_TYPE_UCCALL:
|
|
type = R_ANAL_REF_TYPE_CALL;
|
|
xref_to = op.ptr;
|
|
break;
|
|
case R_ANAL_OP_TYPE_LOAD:
|
|
type = R_ANAL_REF_TYPE_DATA;
|
|
xref_to = op.ptr;
|
|
break;
|
|
default:
|
|
if (op.ptr != -1) {
|
|
type = R_ANAL_REF_TYPE_DATA;
|
|
xref_to = op.ptr;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Validate the reference. If virtual addressing is enabled, we
|
|
// allow only references to virtual addresses in order to reduce
|
|
// the number of false positives. In debugger mode, the reference
|
|
// must point to a mapped memory region.
|
|
if (type == R_ANAL_REF_TYPE_NULL)
|
|
continue;
|
|
if (!r_core_is_valid_offset (core, xref_to))
|
|
continue;
|
|
if (cfg_debug) {
|
|
if (!r_debug_map_get (core->dbg, xref_to))
|
|
continue;
|
|
} else if (core->io->va) {
|
|
RListIter *iter = NULL;
|
|
RIOSection *s;
|
|
r_list_foreach (core->io->sections, iter, s) {
|
|
if (xref_to >= s->vaddr && xref_to < s->vaddr + s->vsize) {
|
|
if (s->vaddr != 0)
|
|
break;
|
|
}
|
|
}
|
|
if (!iter)
|
|
continue;
|
|
}
|
|
|
|
if (!rad) {
|
|
if (cfg_anal_strings) {
|
|
int len = 0;
|
|
char *str_string = is_string_at (core, xref_to, &len);
|
|
if (str_string) {
|
|
r_name_filter (str_string, -1);
|
|
char *str_flagname = r_str_newf ("str.%s", str_string);
|
|
(void)r_flag_set (core->flags, str_flagname, xref_to, 1);
|
|
free (str_string);
|
|
}
|
|
if (len > 0) {
|
|
r_core_cmdf (core, "Cs %d @ 0x%"PFMT64x, len, xref_to);
|
|
}
|
|
}
|
|
// Add to SDB
|
|
r_anal_xrefs_set (core->anal, type, xref_from, xref_to);
|
|
} else if (rad == 'j') {
|
|
// Output JSON
|
|
if (count > 0) r_cons_printf (",");
|
|
r_cons_printf ("\"0x%"PFMT64x"\":\"0x%"PFMT64x"\"", xref_to, xref_from);
|
|
} else {
|
|
int len = 0;
|
|
// Display in radare commands format
|
|
char *cmd;
|
|
switch (type) {
|
|
case R_ANAL_REF_TYPE_CODE: cmd = "axc"; break;
|
|
case R_ANAL_REF_TYPE_CALL: cmd = "axC"; break;
|
|
case R_ANAL_REF_TYPE_DATA: cmd = "axd"; break;
|
|
default: cmd = "ax"; break;
|
|
}
|
|
r_cons_printf ("%s 0x%08"PFMT64x" 0x%08"PFMT64x"\n",
|
|
cmd, xref_to, xref_from);
|
|
if (cfg_anal_strings) {
|
|
char *str_flagname = is_string_at (core, xref_to, &len);
|
|
if (str_flagname) {
|
|
ut64 str_addr = xref_to;
|
|
r_name_filter (str_flagname, -1);
|
|
r_cons_printf ("f str.%s=0x%"PFMT64x"\n",
|
|
str_flagname, str_addr);
|
|
r_cons_printf ("Cs %d @ 0x%"PFMT64x"\n",
|
|
len, str_addr);
|
|
free (str_flagname);
|
|
}
|
|
}
|
|
}
|
|
|
|
count++;
|
|
}
|
|
|
|
at += i;
|
|
}
|
|
r_cons_break_end ();
|
|
free (buf);
|
|
r_anal_op_fini (&op);
|
|
|
|
if (rad == 'j')
|
|
r_cons_printf ("}\n");
|
|
|
|
return count;
|
|
}
|
|
|
|
R_API int r_core_anal_ref_list(RCore *core, int rad) {
|
|
r_anal_xrefs_list (core->anal, rad);
|
|
return 0;
|
|
}
|
|
|
|
R_API int r_core_anal_all(RCore *core) {
|
|
RList *list;
|
|
RListIter *iter;
|
|
RFlagItem *item;
|
|
RAnalFunction *fcni;
|
|
RBinAddr *binmain;
|
|
RBinAddr *entry;
|
|
RBinSymbol *symbol;
|
|
int depth = r_config_get_i (core->config, "anal.depth");
|
|
|
|
/* Analyze Functions */
|
|
/* Entries */
|
|
item = r_flag_get (core->flags, "entry0");
|
|
if (item) {
|
|
r_core_anal_fcn (core, item->offset, -1, R_ANAL_REF_TYPE_NULL, depth);
|
|
r_core_cmdf (core, "afn entry0 0x%08"PFMT64x, item->offset);
|
|
} else {
|
|
r_core_cmd0 (core, "af");
|
|
}
|
|
|
|
r_cons_break (NULL, NULL);
|
|
/* Main */
|
|
if ((binmain = r_bin_get_sym (core->bin, R_BIN_SYM_MAIN)) != NULL) {
|
|
ut64 addr = r_bin_get_vaddr (core->bin, binmain->paddr, binmain->vaddr);
|
|
r_core_anal_fcn (core, addr, -1, R_ANAL_REF_TYPE_NULL, depth);
|
|
}
|
|
if ((list = r_bin_get_entries (core->bin)) != NULL) {
|
|
r_list_foreach (list, iter, entry) {
|
|
ut64 addr = r_bin_get_vaddr (core->bin, entry->paddr, entry->vaddr);
|
|
r_core_anal_fcn (core, addr, -1, R_ANAL_REF_TYPE_NULL, depth);
|
|
}
|
|
}
|
|
/* Symbols (Imports are already analyzed by rabin2 on init) */
|
|
if ((list = r_bin_get_symbols (core->bin)) != NULL) {
|
|
r_list_foreach (list, iter, symbol) {
|
|
if (core->cons->breaked)
|
|
break;
|
|
if (symbol->type && (!strcmp (symbol->type, "FUNC"))) {
|
|
ut64 addr = r_bin_get_vaddr (core->bin, symbol->paddr,
|
|
symbol->vaddr);
|
|
r_core_anal_fcn (core, addr, -1,
|
|
R_ANAL_REF_TYPE_NULL, depth);
|
|
}
|
|
}
|
|
}
|
|
/* Set fcn type to R_ANAL_FCN_TYPE_SYM for symbols */
|
|
r_list_foreach (core->anal->fcns, iter, fcni) {
|
|
if (core->cons->breaked)
|
|
break;
|
|
fcn_callconv (core, fcni);
|
|
if (!strncmp (fcni->name, "sym.", 4) || !strncmp (fcni->name, "main", 4))
|
|
fcni->type = R_ANAL_FCN_TYPE_SYM;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
R_API void r_core_anal_setup_enviroment (RCore *core) {
|
|
char key[128], *str = NULL;
|
|
RListIter *iter;
|
|
RConfigNode *kv;
|
|
r_list_foreach (core->config->nodes, iter, kv) {
|
|
int kvlen = strlen (kv->name);
|
|
if (kvlen>=sizeof (key))
|
|
return;
|
|
strcpy (key, kv->name);
|
|
r_str_case (key, 1);
|
|
r_str_replace_char (key, '.', '_');
|
|
#define RANAL_PARSE_STRING_ONLY 1
|
|
#if RANAL_PARSE_STRING_ONLY
|
|
r_anal_type_define (core->anal, key, kv->value);
|
|
#else
|
|
if (kv->flags & CN_INT) {
|
|
r_anal_type_define_i (core->anal, key, kv->i_value);
|
|
} else if (kv->flags & CN_BOOL) {
|
|
r_anal_type_define (core->anal, key, kv->i_value? "": NULL);
|
|
} else r_anal_type_define (core->anal, key, kv->value);
|
|
#endif
|
|
}
|
|
r_anal_type_header (core->anal, str);
|
|
free (str);
|
|
}
|
|
|
|
R_API int r_core_anal_data (RCore *core, ut64 addr, int count, int depth) {
|
|
RAnalData *d;
|
|
ut64 dstaddr = 0LL;
|
|
ut8 *buf = core->block;
|
|
int len = core->blocksize;
|
|
int word = core->assembler->bits /8;
|
|
char *str;
|
|
int i, j;
|
|
|
|
count = R_MIN (count, len);
|
|
buf = malloc (len);
|
|
if (buf == NULL)
|
|
return false;
|
|
memset (buf, 0xff, len);
|
|
r_io_read_at (core->io, addr, buf, len);
|
|
buf[len-1] = 0;
|
|
|
|
for (i = j = 0; j<count; j++ ) {
|
|
if (i>=len) {
|
|
r_io_read_at (core->io, addr + i, buf, len);
|
|
addr += i;
|
|
i = 0;
|
|
continue;
|
|
}
|
|
d = r_anal_data (core->anal, addr + i, buf + i, len - i);
|
|
str = r_anal_data_to_string (d);
|
|
r_cons_printf ("%s\n", str);
|
|
|
|
if (d) {
|
|
switch (d->type) {
|
|
case R_ANAL_DATA_TYPE_POINTER:
|
|
r_cons_printf ("`- ");
|
|
dstaddr = r_mem_get_num (buf + i, word);
|
|
if (depth > 0)
|
|
r_core_anal_data (core, dstaddr, 1, depth - 1);
|
|
i += word;
|
|
break;
|
|
case R_ANAL_DATA_TYPE_STRING:
|
|
buf[len-1] = 0;
|
|
i += strlen ((const char*)buf + i) + 1;
|
|
break;
|
|
default:
|
|
i += (d->len > 3)? d->len: word;
|
|
break;
|
|
}
|
|
} else {
|
|
i += word;
|
|
}
|
|
free (str);
|
|
r_anal_data_free (d);
|
|
}
|
|
free (buf);
|
|
return true;
|
|
}
|
|
|
|
/* core analysis stats */
|
|
/* stats --- colorful bar */
|
|
R_API RCoreAnalStats* r_core_anal_get_stats (RCore *core, ut64 from, ut64 to, ut64 step) {
|
|
RFlagItem *f;
|
|
RAnalFunction *F;
|
|
RListIter *iter;
|
|
RCoreAnalStats *as = NULL;
|
|
int piece, as_size, blocks;
|
|
|
|
if (from == to) return NULL;
|
|
as = R_NEW0 (RCoreAnalStats);
|
|
if (!as) return NULL;
|
|
if (step<1) step = 1;
|
|
blocks = (to-from)/step;
|
|
as_size = (1+blocks) * sizeof (RCoreAnalStatsItem);
|
|
as->block = malloc (as_size);
|
|
if (!as->block) {
|
|
free (as);
|
|
return NULL;
|
|
}
|
|
memset (as->block, 0, as_size);
|
|
// iter all flags
|
|
r_list_foreach (core->flags->flags, iter, f) {
|
|
//if (f->offset+f->size < from) continue;
|
|
if (f->offset< from) continue;
|
|
if (f->offset > to) continue;
|
|
piece = (f->offset-from)/step;
|
|
as->block[piece].flags++;
|
|
}
|
|
|
|
r_list_foreach (core->anal->fcns, iter, F) {
|
|
if (F->addr< from) continue;
|
|
if (F->addr> to) continue;
|
|
piece = (F->addr-from)/step;
|
|
as->block[piece].functions++;
|
|
}
|
|
// iter all comments
|
|
// iter all symbols
|
|
// iter all imports
|
|
// iter all functions
|
|
// iter all strings
|
|
return as;
|
|
}
|
|
|
|
R_API void r_core_anal_stats_free (RCoreAnalStats *s) {
|
|
free (s);
|
|
}
|
|
|
|
R_API RList* r_core_anal_cycles (RCore *core, int ccl) {
|
|
ut64 addr = core->offset;
|
|
int depth = 0;
|
|
RAnalOp *op = NULL;
|
|
RAnalCycleFrame *prev = NULL, *cf = r_anal_cycle_frame_new ();
|
|
RAnalCycleHook *ch;
|
|
RList *hooks = r_list_new ();
|
|
while (cf && !core->cons->breaked) {
|
|
if ((op = r_core_anal_op (core, addr)) && (op->cycles) && (ccl > 0)) {
|
|
r_cons_clear_line (1);
|
|
eprintf ("%i -- ", ccl);
|
|
addr += op->size;
|
|
switch (op->type) {
|
|
case R_ANAL_OP_TYPE_JMP:
|
|
addr = op->jump;
|
|
ccl -= op->cycles;
|
|
loganal (op->addr, addr, depth);
|
|
break;
|
|
case R_ANAL_OP_TYPE_UJMP:
|
|
case R_ANAL_OP_TYPE_UCALL:
|
|
ch = R_NEW0 (RAnalCycleHook);
|
|
ch->addr = op->addr;
|
|
eprintf ("0x%08"PFMT64x" > ?\r", op->addr);
|
|
ch->cycles = ccl;
|
|
r_list_append (hooks, ch);
|
|
ch = NULL;
|
|
while (!ch && cf) {
|
|
ch = r_list_pop (cf->hooks);
|
|
if (ch) {
|
|
addr = ch->addr;
|
|
ccl = ch->cycles;
|
|
free (ch);
|
|
} else {
|
|
r_anal_cycle_frame_free (cf);
|
|
cf = prev;
|
|
if (cf)
|
|
prev = cf->prev;
|
|
}
|
|
}
|
|
break;
|
|
case R_ANAL_OP_TYPE_CJMP:
|
|
ch = R_NEW0 (RAnalCycleHook);
|
|
ch->addr = addr;
|
|
ch->cycles = ccl - op->failcycles;
|
|
r_list_push (cf->hooks, ch);
|
|
ch = NULL;
|
|
addr = op->jump;
|
|
loganal (op->addr, addr, depth);
|
|
break;
|
|
case R_ANAL_OP_TYPE_UCJMP:
|
|
case R_ANAL_OP_TYPE_UCCALL:
|
|
ch = R_NEW0 (RAnalCycleHook);
|
|
ch->addr = op->addr;
|
|
ch->cycles = ccl;
|
|
r_list_append (hooks, ch);
|
|
ch = NULL;
|
|
ccl -= op->failcycles;
|
|
eprintf ("0x%08"PFMT64x" > ?\r", op->addr);
|
|
break;
|
|
case R_ANAL_OP_TYPE_CCALL:
|
|
ch = R_NEW0 (RAnalCycleHook);
|
|
ch->addr = addr;
|
|
ch->cycles = ccl - op->failcycles;
|
|
r_list_push (cf->hooks, ch);
|
|
ch = NULL;
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
if (op->addr != op->jump) { //no selfies
|
|
cf->naddr = addr;
|
|
prev = cf;
|
|
cf = r_anal_cycle_frame_new ();
|
|
cf->prev = prev;
|
|
}
|
|
ccl -= op->cycles;
|
|
addr = op->jump;
|
|
loganal (op->addr, addr, depth);
|
|
break;
|
|
case R_ANAL_OP_TYPE_RET:
|
|
ch = R_NEW0 (RAnalCycleHook);
|
|
if (prev) {
|
|
ch->addr = prev->naddr;
|
|
ccl -= op->cycles;
|
|
ch->cycles = ccl;
|
|
r_list_push (prev->hooks, ch);
|
|
eprintf ("0x%08"PFMT64x" < 0x%08"PFMT64x"\r", prev->naddr, op->addr);
|
|
} else {
|
|
ch->addr = op->addr;
|
|
ch->cycles = ccl;
|
|
r_list_append (hooks, ch);
|
|
eprintf ("? < 0x%08"PFMT64x"\r", op->addr);
|
|
}
|
|
ch = NULL;
|
|
while (!ch && cf) {
|
|
ch = r_list_pop (cf->hooks);
|
|
if (ch) {
|
|
addr = ch->addr;
|
|
ccl = ch->cycles;
|
|
free (ch);
|
|
} else {
|
|
r_anal_cycle_frame_free (cf);
|
|
cf = prev;
|
|
if (cf)
|
|
prev = cf->prev;
|
|
}
|
|
}
|
|
break;
|
|
case R_ANAL_OP_TYPE_CRET:
|
|
ch = R_NEW0 (RAnalCycleHook);
|
|
if (prev) {
|
|
ch->addr = prev->naddr;
|
|
ch->cycles = ccl - op->cycles;
|
|
r_list_push (prev->hooks, ch);
|
|
eprintf ("0x%08"PFMT64x" < 0x%08"PFMT64x"\r", prev->naddr, op->addr);
|
|
} else {
|
|
ch->addr = op->addr;
|
|
ch->cycles = ccl - op->cycles;
|
|
r_list_append (hooks, ch);
|
|
eprintf ("? < 0x%08"PFMT64x"\r", op->addr);
|
|
}
|
|
ccl -= op->failcycles;
|
|
break;
|
|
default:
|
|
ccl -= op->cycles;
|
|
eprintf ("0x%08"PFMT64x"\r", op->addr);
|
|
break;
|
|
}
|
|
} else {
|
|
ch = R_NEW0 (RAnalCycleHook);
|
|
ch->addr = addr;
|
|
ch->cycles = ccl;
|
|
r_list_append (hooks, ch);
|
|
ch = NULL;
|
|
while (!ch && cf) {
|
|
ch = r_list_pop (cf->hooks);
|
|
if (ch) {
|
|
addr = ch->addr;
|
|
ccl = ch->cycles;
|
|
free (ch);
|
|
} else {
|
|
r_anal_cycle_frame_free (cf);
|
|
cf = prev;
|
|
if (cf)
|
|
prev = cf->prev;
|
|
}
|
|
}
|
|
}
|
|
r_anal_op_free (op);
|
|
}
|
|
if (core->cons->breaked) {
|
|
while (cf) {
|
|
ch = r_list_pop (cf->hooks);
|
|
while (ch) {
|
|
free (ch);
|
|
ch = r_list_pop (cf->hooks);
|
|
}
|
|
prev = cf->prev;
|
|
r_anal_cycle_frame_free (cf);
|
|
cf = prev;
|
|
}
|
|
}
|
|
return hooks;
|
|
}
|
|
|
|
R_API void r_core_anal_undefine (RCore *core, ut64 off) {
|
|
RAnalFunction *f;
|
|
r_anal_fcn_del_locs (core->anal, off);
|
|
f = r_anal_get_fcn_in (core->anal, off, 0);
|
|
if (f) {
|
|
if (!strncmp (f->name, "fcn.", 4)) {
|
|
r_flag_unset_name (core->flags, f->name);
|
|
}
|
|
r_meta_del (core->anal, R_META_TYPE_ANY, off, r_anal_fcn_size (f), "");
|
|
}
|
|
r_anal_fcn_del (core->anal, off);
|
|
}
|
|
|
|
/* Join function at addr2 into function at addr */
|
|
// addr use to be core->offset
|
|
R_API void r_core_anal_fcn_merge (RCore *core, ut64 addr, ut64 addr2) {
|
|
RListIter *iter;
|
|
ut64 min = 0;
|
|
ut64 max = 0;
|
|
int first = 1;
|
|
RAnalBlock *bb;
|
|
RAnalFunction *f1 = r_anal_get_fcn_at (core->anal, addr, 0);
|
|
RAnalFunction *f2 = r_anal_get_fcn_at (core->anal, addr2, 0);
|
|
if (!f1 || !f2) {
|
|
eprintf ("Cannot find function\n");
|
|
return;
|
|
}
|
|
// join all basic blocks from f1 into f2 if they are not
|
|
// delete f2
|
|
eprintf ("Merge 0x%08"PFMT64x" into 0x%08"PFMT64x"\n", addr, addr2);
|
|
r_list_foreach (f1->bbs, iter, bb) {
|
|
if (first) {
|
|
min = bb->addr;
|
|
max = bb->addr + bb->size;
|
|
first = 0;
|
|
} else {
|
|
if (bb->addr < min)
|
|
min = bb->addr;
|
|
if (bb->addr + bb->size > max)
|
|
max = bb->addr + bb->size;
|
|
}
|
|
}
|
|
r_list_foreach (f2->bbs, iter, bb) {
|
|
if (first) {
|
|
min = bb->addr;
|
|
max = bb->addr + bb->size;
|
|
first = 0;
|
|
} else {
|
|
if (bb->addr < min)
|
|
min = bb->addr;
|
|
if (bb->addr + bb->size > max)
|
|
max = bb->addr + bb->size;
|
|
}
|
|
r_list_append (f1->bbs, bb);
|
|
}
|
|
// TODO: import data/code/refs
|
|
// update size
|
|
f1->addr = R_MIN (addr, addr2);
|
|
r_anal_fcn_set_size (f1, max - min);
|
|
// resize
|
|
f2->bbs = NULL;
|
|
r_list_delete_data (core->anal->fcns, f2);
|
|
}
|
|
|
|
R_API void r_core_anal_auto_merge (RCore *core, ut64 addr) {
|
|
/* TODO: implement me */
|
|
}
|
|
|
|
|
|
static int myvalid(ut64 addr) {
|
|
if (addr <0x100)
|
|
return 0;
|
|
if (addr == UT32_MAX || addr == UT64_MAX)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int esilbreak_mem_write(RAnalEsil *esil, ut64 addr, const ut8 *buf, int len) {
|
|
/* do nothing */
|
|
return 1;
|
|
}
|
|
|
|
static ut64 esilbreak_last_read = UT64_MAX;
|
|
#if 0
|
|
static ut32 esilbreak_last_data = UT32_MAX;
|
|
#endif
|
|
|
|
static int esilbreak_mem_read(RAnalEsil *esil, ut64 addr, ut8 *buf, int len) {
|
|
ut8 str[128];
|
|
char cmd[128];
|
|
esilbreak_last_read = UT64_MAX;
|
|
if (myvalid (addr) && r_io_is_valid_offset (mycore->io, addr, 0)) {
|
|
ut32 refptr = 0;
|
|
if (r_io_read_at (mycore->io, addr, (ut8*)&refptr, sizeof (refptr)) != sizeof (refptr)) {
|
|
/* invalid read */
|
|
refptr = -1;
|
|
}
|
|
if (myvalid (refptr) && r_io_is_valid_offset (mycore->io, (ut64)refptr, 0)) {
|
|
esilbreak_last_read = addr;
|
|
snprintf (cmd, sizeof (cmd), "axd 0x%"PFMT64x" 0x%"PFMT64x,
|
|
(ut64)refptr, esil->address);
|
|
str[0] = 0;
|
|
if (r_io_read_at (mycore->io, refptr, str, sizeof (str)) < 1) {
|
|
eprintf ("Invalid read\n");
|
|
str[0] = 0;
|
|
}
|
|
str[sizeof(str)-1] = 0;
|
|
add_string_ref (mycore, refptr); //ut64 xref_to) {
|
|
#if 0
|
|
char cmd2[128];
|
|
int slen = 0;
|
|
if (is_string (str, sizeof (str)-1, &slen)) {
|
|
char *str2[256];
|
|
esilbreak_last_data = refptr;
|
|
snprintf (str2, sizeof (str2) - 1, "esilref: '%s'", str);
|
|
r_meta_set_string (mycore->anal, R_META_TYPE_COMMENT, esil->address, str2);
|
|
r_meta_set_string (mycore->anal, R_META_TYPE_COMMENT, refptr, str2);
|
|
if (refptr) {
|
|
snprintf (cmd2, sizeof (cmd2), "axd 0x%"PFMT64x" 0x%"PFMT64x,
|
|
esil->address, addr);
|
|
//eprintf ("%s\n", cmd2);
|
|
r_core_cmd0 (mycore, cmd2);
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
snprintf (cmd, sizeof (cmd), "axd 0x%"PFMT64x" 0x%"PFMT64x,
|
|
esil->address, addr);
|
|
}
|
|
//eprintf ("%s\n", cmd);
|
|
r_core_cmd0 (mycore, cmd);
|
|
}
|
|
return 0; // fallback
|
|
}
|
|
|
|
static bool esil_anal_stop = false;
|
|
static void cccb(void*u) {
|
|
esil_anal_stop = true;
|
|
eprintf ("^C\n");
|
|
}
|
|
|
|
static void add_string_ref (RCore *core, ut64 xref_to) {
|
|
int len = 0;
|
|
char *str_flagname;
|
|
if (xref_to == UT64_MAX || xref_to == 0LL) {
|
|
return;
|
|
}
|
|
str_flagname = is_string_at (core, xref_to, &len);
|
|
if (str_flagname) {
|
|
r_name_filter (str_flagname, -1);
|
|
char *flagname = sdb_fmt (0, "str.%s", str_flagname);
|
|
r_flag_space_push (core->flags, "strings");
|
|
r_flag_set (core->flags, flagname, xref_to, len);
|
|
r_flag_space_pop (core->flags);
|
|
r_meta_add (core->anal, 's', xref_to, xref_to + len, str_flagname);
|
|
//r_cons_printf ("Cs %d @ 0x%"PFMT64x"\n", len, xref_to);
|
|
free (str_flagname);
|
|
}
|
|
}
|
|
|
|
R_API void r_core_anal_esil (RCore *core, const char *str) {
|
|
bool cfg_anal_strings = r_config_get_i (core->config, "anal.strings");
|
|
RAnalEsil *ESIL = core->anal->esil;
|
|
const char *pcname;
|
|
RAsmOp asmop;
|
|
RAnalOp op;
|
|
ut8 *buf = NULL;
|
|
int i, iend;
|
|
int minopsize = 4; // XXX this depends on asm->mininstrsize
|
|
ut64 addr = core->offset;
|
|
ut64 end = 0LL;
|
|
ut64 cur;
|
|
|
|
mycore = core;
|
|
if (!strcmp (str, "?")) {
|
|
eprintf ("Usage: aae[f] [len] - analyze refs in function, section or len bytes with esil\n");
|
|
eprintf (" aae $SS @ $S - analyze the whole section\n");
|
|
return;
|
|
}
|
|
if (!strcmp (str, "f")) {
|
|
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
if (fcn) {
|
|
addr = fcn->addr;
|
|
end = fcn->addr + r_anal_fcn_size (fcn);
|
|
}
|
|
}
|
|
if (str[0] == ' ') {
|
|
end = addr + r_num_math (core->num, str+1);
|
|
} else {
|
|
RIOSection *sect = r_io_section_vget (core->io, addr);
|
|
if (sect) {
|
|
end = sect->vaddr + sect->size;
|
|
} else {
|
|
if (!end)
|
|
end = addr + core->blocksize;
|
|
}
|
|
}
|
|
iend = end - addr;
|
|
if (iend < 0) {
|
|
return;
|
|
}
|
|
buf = malloc (iend + 2);
|
|
if (buf == NULL) {
|
|
perror ("malloc");
|
|
return;
|
|
}
|
|
r_io_read_at (core->io, addr, buf, iend + 1);
|
|
if (!ESIL) {
|
|
r_core_cmd0 (core, "aei");
|
|
ESIL = core->anal->esil;
|
|
if (!ESIL) {
|
|
eprintf ("ESIL not initialized\n");
|
|
return;
|
|
}
|
|
}
|
|
ESIL->cb.hook_mem_read = &esilbreak_mem_read;
|
|
if (!core->io->cached) {
|
|
ESIL->cb.hook_mem_write = &esilbreak_mem_write;
|
|
}
|
|
//eprintf ("Analyzing ESIL refs from 0x%"PFMT64x" - 0x%"PFMT64x"\n", addr, end);
|
|
// TODO: backup/restore register state before/after analysis
|
|
pcname = r_reg_get_name (core->anal->reg, R_REG_NAME_PC);
|
|
if (!pcname || !*pcname) {
|
|
eprintf ("Cannot find program counter register in the current profile.\n");
|
|
return;
|
|
}
|
|
esil_anal_stop = false;
|
|
r_cons_break (cccb, core);
|
|
//r_cons_break (NULL, NULL);
|
|
for (i = 0; i < iend; i++) {
|
|
if (esil_anal_stop || r_cons_is_breaked ()) {
|
|
break;
|
|
}
|
|
r_cons_break (cccb, core);
|
|
cur = addr + i;
|
|
if (!r_anal_op (core->anal, &op, cur, buf + i, iend-i)) {
|
|
i += minopsize - 1;
|
|
}
|
|
r_asm_set_pc (core->assembler, cur);
|
|
//we need to check again i because buf+i may goes beyond its boundaries
|
|
//because of i+= minopsize - 1
|
|
if (i > iend) {
|
|
break;
|
|
}
|
|
if (r_asm_disassemble (core->assembler, &asmop, buf+i, iend-i) > 0) {
|
|
op.mnemonic = strdup (asmop.buf_asm);
|
|
}
|
|
if (op.size < 1) {
|
|
i++;
|
|
continue;
|
|
}
|
|
if (1) {
|
|
const char *esilstr = R_STRBUF_SAFEGET (&op.esil);
|
|
r_anal_esil_set_pc (ESIL, cur);
|
|
if (!esilstr || !*esilstr)
|
|
continue;
|
|
(void)r_anal_esil_parse (ESIL, esilstr);
|
|
// looks like ^C is handled by esil_parse !!!!
|
|
r_cons_break (cccb, core);
|
|
//r_anal_esil_dumpstack (ESIL);
|
|
r_anal_esil_stack_free (ESIL);
|
|
i += op.size - 1;
|
|
|
|
switch (op.type) {
|
|
case R_ANAL_OP_TYPE_LEA:
|
|
if (strcmp (core->anal->cpu, "arm")) {
|
|
if (cfg_anal_strings) {
|
|
r_anal_ref_add (core->anal, op.ptr, cur, 'd');
|
|
add_string_ref (core, op.ptr);
|
|
}
|
|
}
|
|
break;
|
|
case R_ANAL_OP_TYPE_ADD:
|
|
/* TODO: test if this is valid for other archs too */
|
|
if (core->anal->bits == 64 && !strcmp (core->anal->cpu, "arm")) {
|
|
ut64 dst = ESIL->cur;
|
|
r_anal_ref_add (core->anal, dst, cur, 'd');
|
|
} else if ((core->anal->bits == 32 && !strcmp (core->anal->cpu, "mips"))) {
|
|
ut64 dst = ESIL->cur;
|
|
|
|
if (!op.src[0] || !op.src[0]->reg || !op.src[0]->reg->name)
|
|
break;
|
|
|
|
if (!strcmp(op.src[0]->reg->name, "sp"))
|
|
break;
|
|
if (!strcmp(op.src[0]->reg->name, "zero"))
|
|
break;
|
|
|
|
|
|
if (dst > 0xffff && op.src[1] && (dst & 0xffff) == (op.src[1]->imm & 0xffff) &&
|
|
myvalid (dst) && r_io_is_valid_offset (mycore->io, dst, 0)) {
|
|
RFlagItem *f;
|
|
char *str;
|
|
|
|
r_anal_ref_add (core->anal, dst, cur, 'd');
|
|
if (cfg_anal_strings) {
|
|
add_string_ref (core, dst);
|
|
}
|
|
|
|
if ((f = r_flag_get_i2 (core->flags, dst))) {
|
|
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, cur, f->name);
|
|
} else if ((str = is_string_at (mycore, dst, NULL))) {
|
|
char *str2 = sdb_fmt (0, "esilref: '%s'", str);
|
|
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, cur, str2);
|
|
free (str);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case R_ANAL_OP_TYPE_LOAD:
|
|
{
|
|
ut64 dst = esilbreak_last_read;
|
|
if (myvalid (dst) && r_io_is_valid_offset (mycore->io, dst, 0)) {
|
|
r_anal_ref_add (core->anal, dst, cur, 'd');
|
|
if (cfg_anal_strings) {
|
|
add_string_ref (core, dst);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case R_ANAL_OP_TYPE_UJMP:
|
|
case R_ANAL_OP_TYPE_UCALL:
|
|
{
|
|
ut64 dst = core->anal->esil->jump_target;
|
|
if (dst == UT64_MAX) {
|
|
dst = r_reg_getv (core->anal->reg, pcname);
|
|
}
|
|
if (myvalid (dst) && r_io_is_valid_offset (mycore->io, dst, 0)) {
|
|
r_anal_ref_add (core->anal, dst, cur, 'c');
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
r_cons_break_end ();
|
|
}
|