mirror of
https://github.com/radareorg/radare2.git
synced 2024-12-12 07:26:42 +00:00
2279 lines
63 KiB
C
2279 lines
63 KiB
C
/* radare - LGPL - Copyright 2009-2016 - pancake */
|
|
|
|
static int preludecnt = 0;
|
|
static int searchflags = 0;
|
|
static int searchshow = 0;
|
|
static int searchhits = 0;
|
|
static int maplist = 0;
|
|
static int maxhits = 0;
|
|
static int json = 0;
|
|
static int first_hit = true;
|
|
static const char *cmdhit = NULL;
|
|
static const char *searchprefix = NULL;
|
|
static unsigned int searchcount = 0;
|
|
|
|
struct search_parameters {
|
|
RList *boundaries;
|
|
const char *mode;
|
|
ut64 from;
|
|
ut64 to;
|
|
bool inverse;
|
|
bool crypto_search;
|
|
bool bckwrds;
|
|
bool do_bckwrd_srch;
|
|
bool use_mread;
|
|
bool aes_search;
|
|
bool rsa_search;
|
|
};
|
|
|
|
struct endlist_pair {
|
|
int instr_offset;
|
|
int delay_size;
|
|
};
|
|
|
|
static int search_hash(RCore *core, const char *hashname, const char *hashstr, ut32 minlen, ut32 maxlen) {
|
|
RIOMap *map;
|
|
ut8 *buf;
|
|
int i, j;
|
|
RList *list;
|
|
RListIter *iter;
|
|
|
|
list = r_core_get_boundaries_ok (core);
|
|
if (!list) {
|
|
eprintf ("Invalid boundaries\n");
|
|
goto hell;
|
|
}
|
|
if (!minlen || minlen == UT32_MAX) {
|
|
minlen = core->blocksize;
|
|
}
|
|
if (!maxlen || maxlen == UT32_MAX) {
|
|
maxlen = minlen;
|
|
}
|
|
|
|
for (j = minlen; j<=maxlen; j++) {
|
|
ut32 len = j;
|
|
eprintf ("Searching %s for %d byte length.\n", hashname, j);
|
|
r_list_foreach (list, iter, map) {
|
|
ut64 from = map->from;
|
|
ut64 to = map->to;
|
|
st64 bufsz;
|
|
if (from > to) {
|
|
eprintf ("Invalid range (from > to)\n");
|
|
continue;
|
|
}
|
|
bufsz = to - from;
|
|
if (len > bufsz) {
|
|
eprintf ("Hash length is bigger than range 0x%"PFMT64x"\n", from);
|
|
continue;
|
|
}
|
|
buf = malloc (bufsz);
|
|
if (!buf) {
|
|
eprintf ("Cannot allocate %"PFMT64d" bytes\n", bufsz);
|
|
goto hell;
|
|
}
|
|
eprintf ("Search in range 0x%08"PFMT64x" and 0x%08"PFMT64x"\n", from, to);
|
|
int blocks = (int)(to-from-len);
|
|
eprintf ("Carving %d blocks...\n", blocks);
|
|
(void)r_io_read_at (core->io, from, buf, bufsz);
|
|
for (i = 0; (from+i+len)<to; i++) {
|
|
char *s = r_hash_to_string (NULL, hashname, buf+i, len);
|
|
if (!(i%5)) eprintf ("%d\r", i);
|
|
if (!s) {
|
|
eprintf ("Hash fail\n");
|
|
break;
|
|
}
|
|
//eprintf ("0x%08"PFMT64x" %s\n", from+i, s);
|
|
if (!strcmp (s, hashstr)) {
|
|
eprintf ("Found at 0x%"PFMT64x"\n", from+i);
|
|
r_cons_printf ("f hash.%s.%s = 0x%"PFMT64x"\n",
|
|
hashname, hashstr, from+i);
|
|
free (s);
|
|
free (buf);
|
|
r_list_free (list);
|
|
return 1;
|
|
}
|
|
free (s);
|
|
}
|
|
free (buf);
|
|
}
|
|
}
|
|
r_list_free (list);
|
|
eprintf ("No hashes found\n");
|
|
return 0;
|
|
hell:
|
|
r_list_free (list);
|
|
return -1;
|
|
}
|
|
|
|
static void cmd_search_bin(RCore *core, ut64 from, ut64 to) {
|
|
RBinPlugin *plug;
|
|
ut8 buf[1024];
|
|
int size, sz = sizeof (buf);
|
|
|
|
r_cons_break (NULL, NULL);
|
|
while (from <to) {
|
|
if (r_cons_singleton()->breaked)
|
|
break;
|
|
r_io_read_at (core->io, from, buf, sz);
|
|
plug = r_bin_get_binplugin_by_bytes (core->bin, buf, sz);
|
|
if (plug) {
|
|
r_cons_printf ("0x%08"PFMT64x" %s\n",
|
|
from, plug->name);
|
|
// TODO: load the bin and calculate its size
|
|
if (plug->size) {
|
|
r_bin_load_io_at_offset_as_sz (core->bin,
|
|
core->file->desc, 0, 0, 0, core->offset,
|
|
plug->name, 4096);
|
|
size = plug->size (core->bin->cur);
|
|
if (size>0)
|
|
r_cons_printf ("size %d\n", size);
|
|
}
|
|
}
|
|
from ++;
|
|
}
|
|
r_cons_break_end ();
|
|
}
|
|
|
|
static int cmd_search_value_in_range(RCore *core, ut64 from, ut64 to, ut64 vmin, ut64 vmax, int vsize) {
|
|
int i, match, align = core->search->align, hitctr = 0;
|
|
ut8 buf[4096];
|
|
const int sz = sizeof (buf);
|
|
ut64 v64;
|
|
ut32 v32;
|
|
ut16 v16;
|
|
#define cbhit(y) r_cons_printf ("f hit0_%d = 0x%"PFMT64x"\n", hitctr, y); hitctr++
|
|
if (vmin >= vmax) {
|
|
eprintf ("Error: vmin must be lower than vmax\n");
|
|
return -1;
|
|
}
|
|
while (from < to) {
|
|
memset (buf, 0, sz); // probably unnecessary
|
|
(void)r_io_read_at (core->io, from, buf, sz);
|
|
for (i=0; i<sizeof (buf)-vsize; i++) {
|
|
void *v = (buf+i);
|
|
if (align && (from+i)%4)
|
|
continue;
|
|
match = false;
|
|
switch (vsize) {
|
|
case 1: match = (buf[i]>=vmin && buf[i]<=vmax); break;
|
|
case 2: v16 = *((ut16*)(v)); match = (v16>=vmin && v16<=vmax); break;
|
|
case 4: v32 = *((ut32 *)(v)); match = (v32>=vmin && v32<=vmax); break;
|
|
case 8: v64 = *((ut64 *)(v)); match = (v64>=vmin && v64<=vmax); break;
|
|
default: eprintf ("Unknown vsize\n"); return -1;
|
|
}
|
|
if (match)
|
|
cbhit (from+i);
|
|
}
|
|
from += sz;
|
|
}
|
|
return hitctr;
|
|
}
|
|
|
|
static int __prelude_cb_hit(RSearchKeyword *kw, void *user, ut64 addr) {
|
|
RCore *core = (RCore *)user;
|
|
int depth = r_config_get_i (core->config, "anal.depth");
|
|
//eprintf ("ap: Found function prelude %d at 0x%08"PFMT64x"\n", preludecnt, addr);
|
|
searchhits ++; //= kw->count+1;
|
|
r_core_anal_fcn (core, addr, -1, R_ANAL_REF_TYPE_NULL, depth);
|
|
preludecnt++;
|
|
return true;
|
|
}
|
|
|
|
R_API int r_core_search_prelude(RCore *core, ut64 from, ut64 to, const ut8 *buf,
|
|
int blen, const ut8 *mask, int mlen) {
|
|
int ret;
|
|
ut64 at;
|
|
ut8 *b = (ut8 *)malloc (core->blocksize);
|
|
// TODO: handle sections ?
|
|
r_search_reset (core->search, R_SEARCH_KEYWORD);
|
|
r_search_kw_add (core->search,
|
|
r_search_keyword_new (buf, blen, mask, mlen, NULL));
|
|
r_search_begin (core->search);
|
|
r_search_set_callback (core->search, &__prelude_cb_hit, core);
|
|
preludecnt = 0;
|
|
for (at = from; at < to; at += core->blocksize) {
|
|
if (r_cons_singleton ()->breaked)
|
|
break;
|
|
ret = r_io_read_at (core->io, at, b, core->blocksize);
|
|
if (ret != core->blocksize)
|
|
break;
|
|
if (r_search_update (core->search, &at, b, ret) == -1) {
|
|
eprintf ("search: update read error at 0x%08"PFMT64x"\n", at);
|
|
break;
|
|
}
|
|
}
|
|
free (b);
|
|
return preludecnt;
|
|
}
|
|
|
|
static int count_functions (RCore *core) {
|
|
return r_list_length (core->anal->fcns);
|
|
}
|
|
|
|
R_API int r_core_search_preludes(RCore *core) {
|
|
int ret = -1;
|
|
const char *prelude = r_config_get (core->config, "anal.prelude");
|
|
const char *arch = r_config_get (core->config, "asm.arch");
|
|
int bits = r_config_get_i (core->config, "asm.bits");
|
|
ut64 from = core->offset;
|
|
ut64 to = core->offset+0xffffff; // hacky!
|
|
int fc0, fc1;
|
|
|
|
fc0 = count_functions (core);
|
|
if (prelude && *prelude) {
|
|
ut8 *kw = malloc (strlen (prelude)+1);
|
|
int kwlen = r_hex_str2bin (prelude, kw);
|
|
ret = r_core_search_prelude (core, from, to, kw, kwlen, NULL, 0);
|
|
free (kw);
|
|
} else if (strstr (arch, "ppc")) {
|
|
ret = r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\x7c\x08\x02\xa6", 4, NULL, 0);
|
|
} else if (strstr (arch, "arm")) {
|
|
switch (bits) {
|
|
case 16:
|
|
ret = r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\xf0\xb5", 2, NULL, 0);
|
|
break;
|
|
case 32:
|
|
ret = r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\x00\x48\x2d\xe9", 4, NULL, 0);
|
|
break;
|
|
case 64:
|
|
r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\xf6\x57\xbd\xa9", 4, NULL, 0);
|
|
r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\xfd\x7b\xbf\xa9", 4, NULL, 0);
|
|
r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\xfc\x6f\xbe\xa9", 4, NULL, 0);
|
|
break;
|
|
default:
|
|
eprintf ("ap: Unsupported bits: %d\n", bits);
|
|
}
|
|
} else if (strstr (arch, "mips")) {
|
|
ret = r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\x27\xbd\x00", 3, NULL, 0);
|
|
} else if (strstr (arch, "x86")) {
|
|
switch (bits) {
|
|
case 32:
|
|
r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\x55\x89\xe5", 3, NULL, 0);
|
|
r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\x55\x8b\xec", 3, NULL, 0);
|
|
break;
|
|
case 64:
|
|
r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\x55\x48\x89\xe5", 4, NULL, 0);
|
|
r_core_search_prelude (core, from, to,
|
|
(const ut8 *)"\x55\x48\x8b\xec", 4, NULL, 0);
|
|
break;
|
|
default:
|
|
eprintf ("ap: Unsupported bits: %d\n", bits);
|
|
}
|
|
} else eprintf ("ap: Unsupported asm.arch and asm.bits\n");
|
|
fc1 = count_functions (core);
|
|
eprintf ("Analyzed %d functions based on preludes\n", fc1 - fc0);
|
|
return ret;
|
|
}
|
|
|
|
static int __cb_hit(RSearchKeyword *kw, void *user, ut64 addr) {
|
|
RCore *core = (RCore *)user;
|
|
ut64 base_addr = 0;
|
|
|
|
if (!core) {
|
|
eprintf ("Error: Callback has an invalid RCore.\n");
|
|
return false;
|
|
}
|
|
if (maxhits && searchhits >= maxhits) {
|
|
//eprintf ("Error: search.maxhits reached.\n");
|
|
return false;
|
|
}
|
|
|
|
searchhits ++; ///= kw->count+1;
|
|
if (searchcount) {
|
|
if (!--searchcount) {
|
|
//eprintf ("\nsearch stop: search.count reached\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (searchshow && kw && kw->keyword_length > 0) {
|
|
int len, i, extra, mallocsize;
|
|
ut32 buf_sz = kw->keyword_length;
|
|
ut8 *buf = malloc (buf_sz);
|
|
char *s = NULL, *str = NULL, *p = NULL;
|
|
extra = (json) ? 3 : 1;
|
|
switch (kw->type) {
|
|
case R_SEARCH_KEYWORD_TYPE_STRING:
|
|
str = malloc (kw->keyword_length + 20);
|
|
r_core_read_at (core, base_addr + addr,
|
|
(ut8*)str, kw->keyword_length);
|
|
p = r_str_utf16_encode (str, kw->keyword_length);
|
|
s = r_str_newf ("\"%s\"", p);
|
|
free (p);
|
|
break;
|
|
default:
|
|
len = kw->keyword_length; // 8 byte context
|
|
mallocsize = (len*2)+extra;
|
|
str = (len > 0xffff)? NULL: malloc (mallocsize);
|
|
if (str) {
|
|
p = str;
|
|
memset (str, 0, len);
|
|
r_core_read_at (core, base_addr + addr, buf, kw->keyword_length);
|
|
if (json) {
|
|
strcpy (str, "0x");
|
|
p = str + 2;
|
|
}
|
|
for (i=0; i<len; i++) {
|
|
sprintf (p, "%02x", buf[i]);
|
|
p += 2;
|
|
}
|
|
*p = 0;
|
|
} else eprintf ("Cannot allocate %d\n", mallocsize);
|
|
s = str;
|
|
str = NULL;
|
|
break;
|
|
}
|
|
|
|
if (json) {
|
|
if (!first_hit) r_cons_printf (",");
|
|
r_cons_printf ("{\"offset\": %"PFMT64d",\"id:\":%d,\"data\":\"%s\"}",
|
|
base_addr + addr, kw->kwidx, s);
|
|
} else {
|
|
r_cons_printf ("0x%08"PFMT64x" %s%d_%d %s\n",
|
|
base_addr + addr, searchprefix, kw->kwidx, kw->count, s);
|
|
}
|
|
free (s);
|
|
free (buf);
|
|
free (str);
|
|
} else if (kw) {
|
|
if (json) {
|
|
if (!first_hit) r_cons_printf(",");
|
|
r_cons_printf ("{\"offset\": %"PFMT64d",\"id:\":%d,\"len\":%d}",
|
|
base_addr + addr, kw->kwidx, kw->keyword_length);
|
|
} else {
|
|
if (searchflags)
|
|
r_cons_printf ("%s%d_%d\n", searchprefix, kw->kwidx, kw->count);
|
|
else r_cons_printf ("f %s%d_%d %d 0x%08"PFMT64x"\n", searchprefix,
|
|
kw->kwidx, kw->count, kw->keyword_length, base_addr + addr);
|
|
}
|
|
}
|
|
if (first_hit)
|
|
first_hit = false;
|
|
if (searchflags) {
|
|
const char *flag = sdb_fmt (0, "%s%d_%d", searchprefix, kw->kwidx, kw->count);
|
|
r_flag_set (core->flags, flag, base_addr + addr, kw->keyword_length, 1);
|
|
}
|
|
if (!strnull (cmdhit)) {
|
|
ut64 here = core->offset;
|
|
r_core_seek (core, base_addr + addr, true);
|
|
r_core_cmd (core, cmdhit, 0);
|
|
r_core_seek (core, here, true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int c = 0;
|
|
|
|
static inline void print_search_progress(ut64 at, ut64 to, int n) {
|
|
if ((++c%64) || (json))
|
|
return;
|
|
if (r_cons_singleton()->columns<50)
|
|
eprintf ("\r[ ] 0x%08"PFMT64x" hits = %d \r%s",
|
|
at, n, (c%2)?"[ #]":"[# ]");
|
|
else eprintf ("\r[ ] 0x%08"PFMT64x" < 0x%08"PFMT64x" hits = %d \r%s",
|
|
at, to, n, (c%2)?"[ #]":"[# ]");
|
|
}
|
|
|
|
R_API RList *r_core_get_boundaries_prot(RCore *core, int protection, const char *mode, ut64 *from, ut64 *to) {
|
|
RList *list = NULL;
|
|
if (!strcmp (mode, "block")) {
|
|
*from = core->offset;
|
|
*to = core->offset + core->blocksize;
|
|
} else if (!strcmp (mode, "io.maps")) {
|
|
*from = *to = 0;
|
|
return core->io->maps;
|
|
} else if (!strcmp (mode, "io.maps.range")) {
|
|
RListIter *iter;
|
|
RIOMap *m;
|
|
*from = *to = 0;
|
|
list = r_list_newf (free);
|
|
r_list_foreach (core->io->maps, iter, m) {
|
|
if (!*from) {
|
|
*from = m->from;
|
|
*to = m->to;
|
|
continue;
|
|
}
|
|
if ((m->from < *from) && m->from)
|
|
*from = m->from;
|
|
if (m->to > *to)
|
|
*to = m->to;
|
|
}
|
|
if (*to == 0LL || *to == UT64_MAX || *to == UT32_MAX)
|
|
*to = r_io_size (core->io);
|
|
} else if (!strcmp (mode, "file")) {
|
|
if (core->io->va) {
|
|
RListIter *iter;
|
|
RIOSection *s;
|
|
*from = *to = 0;
|
|
r_list_foreach (core->io->sections, iter, s) {
|
|
if (!*from) {
|
|
*from = s->vaddr;
|
|
*to = s->vaddr+s->vsize;
|
|
continue;
|
|
}
|
|
if (((s->vaddr) < *from) && s->vaddr)
|
|
*from = s->vaddr;
|
|
if ((s->vaddr+s->vsize) > *to)
|
|
*to = s->vaddr+s->vsize;
|
|
}
|
|
if (*to == 0LL || *to == UT64_MAX || *to == UT32_MAX)
|
|
*to = r_io_size (core->io);
|
|
} else {
|
|
RIOMap *map = r_io_map_get (core->io, core->offset);
|
|
*from = core->offset;
|
|
*to = r_io_size (core->io) + (map? map->to:0);
|
|
if (*from > *to) {
|
|
*from = 0;
|
|
}
|
|
}
|
|
} else if (!strcmp (mode, "io.section")) {
|
|
if (core->io->va) {
|
|
RListIter *iter;
|
|
RIOSection *s;
|
|
*from = *to = core->offset;
|
|
r_list_foreach (core->io->sections, iter, s) {
|
|
if (*from >= s->offset && *from < (s->offset+s->size)) {
|
|
*from = s->vaddr;
|
|
*to = s->vaddr+s->vsize;
|
|
break;
|
|
}
|
|
if (*from >= s->vaddr && *from < (s->vaddr+s->vsize)) {
|
|
*to = s->vaddr+s->size;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
*from = core->offset;
|
|
*to = r_io_size (core->io);
|
|
}
|
|
} else if (!strcmp (mode, "anal.fcn") || !strcmp (mode, "anal.bb")) {
|
|
if (core->io->va) {
|
|
RAnalFunction *f = r_anal_get_fcn_in (core->anal, core->offset,
|
|
R_ANAL_FCN_TYPE_FCN|R_ANAL_FCN_TYPE_SYM);
|
|
if (f) {
|
|
*from = f->addr;
|
|
*to = f->addr + f->size;
|
|
|
|
/* Search only inside the basic block */
|
|
if (!strcmp (mode, "anal.bb")) {
|
|
RListIter *iter;
|
|
struct r_anal_bb_t *bb;
|
|
|
|
r_list_foreach (f->bbs, iter, bb) {
|
|
*from = core->offset;
|
|
if ((*from >= bb->addr) && (*from < (bb->addr+bb->size))) {
|
|
*from = bb->addr;
|
|
*to = bb->addr + bb->size;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
eprintf("WARNING: search.in = ( anal.bb | anal.fcn )" \
|
|
"requires to seek into a valid function\n");
|
|
*from = core->offset;
|
|
*to = core->offset+1;
|
|
}
|
|
} else {
|
|
*from = core->offset;
|
|
*to = r_io_size (core->io);
|
|
}
|
|
} else if (!strncmp (mode, "io.sections", sizeof("io.sections")-1)) {
|
|
if (core->io->va) {
|
|
int mask = 0;
|
|
RIOMap *map;
|
|
RListIter *iter;
|
|
RIOSection *s;
|
|
|
|
if (!strcmp (mode, "io.sections.exec")) mask = R_IO_EXEC;
|
|
if (!strcmp (mode, "io.sections.write")) mask = R_IO_WRITE;
|
|
|
|
r_list_foreach (core->io->sections, iter, s) {
|
|
if (!mask || (s->rwx & mask)) {
|
|
if (!list) {
|
|
list = r_list_newf (free);
|
|
maplist = true;
|
|
}
|
|
map = R_NEW0 (RIOMap);
|
|
if (!map) {
|
|
eprintf ("RIOMap allocation failed\n");
|
|
break;
|
|
}
|
|
map->fd = s->fd;
|
|
map->from = s->vaddr;
|
|
map->to = s->vaddr + s->size;
|
|
map->flags = s->rwx;
|
|
map->delta = 0;
|
|
if (!(map->flags & protection)) {
|
|
R_FREE (map);
|
|
continue;
|
|
}
|
|
r_list_append (list, map);
|
|
}
|
|
}
|
|
} else {
|
|
*from = core->offset;
|
|
*to = r_io_size (core->io);
|
|
}
|
|
} else if (!strncmp (mode, "dbg.", 4)) {
|
|
if (core->io->debug) {
|
|
int mask = 0;
|
|
int add = 0;
|
|
int heap = false;
|
|
int stack = false;
|
|
int all = false;
|
|
RListIter *iter;
|
|
RDebugMap *map;
|
|
|
|
r_debug_map_sync (core->dbg);
|
|
|
|
if (!strcmp (mode, "dbg.map")) {
|
|
*from = *to = core->offset;
|
|
r_list_foreach (core->dbg->maps, iter, map) {
|
|
if (*from >= map->addr && *from < map->addr_end) {
|
|
*from = map->addr;
|
|
*to = map->addr_end;
|
|
}
|
|
}
|
|
} else {
|
|
if (!strcmp (mode, "dbg.maps")) all = true;
|
|
if (!strcmp (mode, "dbg.maps.exec")) mask = R_IO_EXEC;
|
|
if (!strcmp (mode, "dbg.maps.write")) mask = R_IO_WRITE;
|
|
if (!strcmp (mode, "dbg.heap")) heap = true;
|
|
if (!strcmp (mode, "dbg.stack")) stack = true;
|
|
|
|
r_list_foreach (core->dbg->maps, iter, map) {
|
|
add = (stack && strstr(map->name, "stack"))? 1: 0;
|
|
if (!add && (heap && (map->perm & R_IO_WRITE)) && strstr (map->name, "heap")) {
|
|
add = 1;
|
|
}
|
|
if ((mask && (map->perm & mask)) || add || all) {
|
|
if (!list) {
|
|
list = r_list_newf (free);
|
|
maplist = true;
|
|
}
|
|
RIOMap *nmap = R_NEW0 (RIOMap);
|
|
if (!nmap) break;
|
|
nmap->fd = core->io->desc->fd;
|
|
nmap->from = map->addr;
|
|
nmap->to = map->addr_end;
|
|
nmap->flags = map->perm;
|
|
nmap->delta = 0;
|
|
r_list_append (list, nmap);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
*from = core->offset;
|
|
*to = r_io_size (core->io);
|
|
}
|
|
} else {
|
|
//if (!strcmp (mode, "raw")) {
|
|
/* obey temporary seek if defined '/x 8080 @ addr:len' */
|
|
if (core->tmpseek) {
|
|
*from = core->offset;
|
|
*to = core->offset + core->blocksize;
|
|
} else {
|
|
// TODO: repeat last search doesnt works for /a
|
|
*from = r_config_get_i (core->config, "search.from");
|
|
if (*from == UT64_MAX)
|
|
*from = core->offset;
|
|
*to = r_config_get_i (core->config, "search.to");
|
|
if (*to == UT64_MAX) {
|
|
if (core->io->va) {
|
|
/* TODO: section size? */
|
|
} else {
|
|
*to = r_io_desc_size (core->io, core->file->desc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
// XXX: deprecate and use _ok function only
|
|
R_API RList *r_core_get_boundaries (RCore *core, const char *mode, ut64 *from, ut64 *to) {
|
|
return r_core_get_boundaries_prot (core, R_IO_EXEC|R_IO_WRITE|R_IO_READ, mode, from, to);
|
|
}
|
|
|
|
static ut64 findprevopsz(RCore *core, ut64 addr, ut8 *buf) {
|
|
ut8 i;
|
|
RAnalOp aop;
|
|
|
|
for (i=0; i<16; i++) {
|
|
if (r_anal_op (core->anal, &aop, addr-i, buf-i, 32-i)) {
|
|
if (aop.size < 1)
|
|
return UT64_MAX;
|
|
if (i == aop.size) {
|
|
switch (aop.type) {
|
|
case R_ANAL_OP_TYPE_ILL:
|
|
case R_ANAL_OP_TYPE_TRAP:
|
|
case R_ANAL_OP_TYPE_RET:
|
|
case R_ANAL_OP_TYPE_UCALL:
|
|
case R_ANAL_OP_TYPE_CJMP:
|
|
case R_ANAL_OP_TYPE_UJMP:
|
|
case R_ANAL_OP_TYPE_JMP:
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
return UT64_MAX;
|
|
}
|
|
return addr-i;
|
|
}
|
|
}
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
static bool is_end_gadget(const RAnalOp* aop, const ut8 crop) {
|
|
switch (aop->type) {
|
|
case R_ANAL_OP_TYPE_TRAP:
|
|
case R_ANAL_OP_TYPE_RET:
|
|
case R_ANAL_OP_TYPE_UCALL:
|
|
case R_ANAL_OP_TYPE_UJMP:
|
|
case R_ANAL_OP_TYPE_JMP:
|
|
case R_ANAL_OP_TYPE_CALL:
|
|
return true;
|
|
}
|
|
if (crop) { //if conditional jumps, calls and returns should be used for the gadget-search too
|
|
switch (aop->type) {
|
|
case R_ANAL_OP_TYPE_CJMP:
|
|
case R_ANAL_OP_TYPE_UCJMP:
|
|
case R_ANAL_OP_TYPE_CCALL:
|
|
case R_ANAL_OP_TYPE_UCCALL:
|
|
case R_ANAL_OP_TYPE_CRET: //i'm a condret
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//TODO: follow unconditional jumps
|
|
static RList* construct_rop_gadget(RCore *core, ut64 addr, ut8 *buf, int idx,
|
|
const char* grep, int regex, RList* rx_list, struct endlist_pair *end_gadget,
|
|
RList* badstart, int* max_count) {
|
|
int endaddr = end_gadget->instr_offset;
|
|
int branch_delay = end_gadget->delay_size;
|
|
RAsmOp asmop;
|
|
const char* start = NULL, *end = NULL;
|
|
char* grep_str = NULL;
|
|
RCoreAsmHit *hit = NULL;
|
|
RList *hitlist = r_core_asm_hit_list_new ();
|
|
ut8 nb_instr = 0;
|
|
const ut8 max_instr = r_config_get_i (core->config, "rop.len");
|
|
bool valid = false;
|
|
int grep_find;
|
|
int search_hit;
|
|
RRegex* rx = NULL;
|
|
RList /*<intptr_t>*/ *localbadstart = r_list_new();
|
|
RListIter *iter;
|
|
void* p;
|
|
int count = 0;
|
|
|
|
if (*max_count == 0) {
|
|
r_list_free (localbadstart);
|
|
r_list_free (hitlist);
|
|
return NULL;
|
|
}
|
|
if (grep) {
|
|
start = grep;
|
|
end = strstr (grep, ";");
|
|
if (!end) { // We filter on a single opcode, so no ";"
|
|
end = start + strlen (grep);
|
|
}
|
|
grep_str = calloc (1, end - start + 1);
|
|
strncpy (grep_str, start, end - start);
|
|
if (regex) {
|
|
// get the first regexp.
|
|
if (r_list_length (rx_list) > 0) {
|
|
rx = r_list_get_n(rx_list, count++);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r_list_contains (badstart, (void*)(intptr_t)idx)) {
|
|
valid = false;
|
|
goto ret;
|
|
}
|
|
while (nb_instr < max_instr) {
|
|
r_list_append (localbadstart, (void*)(intptr_t)idx);
|
|
r_asm_set_pc (core->assembler, addr);
|
|
if (!r_asm_disassemble (core->assembler, &asmop, buf+idx, 15))
|
|
goto ret;
|
|
if (!strncasecmp (asmop.buf_asm, "invalid", strlen ("invalid")) ||
|
|
!strncasecmp (asmop.buf_asm, ".byte", strlen (".byte"))) {
|
|
valid = false;
|
|
goto ret;
|
|
}
|
|
|
|
hit = r_core_asm_hit_new ();
|
|
hit->addr = addr;
|
|
hit->len = asmop.size;
|
|
r_list_append (hitlist, hit);
|
|
|
|
//Move on to the next instruction
|
|
idx += asmop.size;
|
|
addr += asmop.size;
|
|
if (rx) {
|
|
//grep_find = r_regex_exec (rx, asmop.buf_asm, 0, 0, 0);
|
|
grep_find = !r_regex_match (grep, "e", asmop.buf_asm);
|
|
search_hit = (end && grep && (grep_find < 1));
|
|
} else {
|
|
search_hit = (end && grep && strstr (asmop.buf_asm, grep_str));
|
|
}
|
|
|
|
//Handle (possible) grep
|
|
if (search_hit) {
|
|
if (end[0] == ';') { // fields are semicolon-separated
|
|
start = end + 1; // skip the ;
|
|
end = strstr (start, ";");
|
|
end = end?end: start + strlen(start); //latest field?
|
|
free (grep_str);
|
|
grep_str = calloc (1, end - start + 1);
|
|
strncpy (grep_str, start, end - start);
|
|
} else end = NULL;
|
|
if (regex) rx = r_list_get_n(rx_list, count++);
|
|
}
|
|
|
|
if (endaddr <= (idx - asmop.size)) {
|
|
valid = (endaddr == idx - asmop.size);
|
|
goto ret;
|
|
}
|
|
nb_instr++;
|
|
}
|
|
ret:
|
|
free (grep_str);
|
|
if (regex && rx) {
|
|
r_list_free (hitlist);
|
|
r_list_free (localbadstart);
|
|
return NULL;
|
|
}
|
|
if (!valid || (grep && end)) {
|
|
r_list_free (hitlist);
|
|
r_list_free (localbadstart);
|
|
return NULL;
|
|
}
|
|
r_list_foreach (localbadstart, iter, p) {
|
|
r_list_append (badstart, p);
|
|
}
|
|
r_list_free (localbadstart);
|
|
// If our arch has bds then we better be including them
|
|
if (branch_delay && r_list_length(hitlist) < (1 + branch_delay)) {
|
|
r_list_free(hitlist);
|
|
return NULL;
|
|
}
|
|
*max_count = *max_count - 1;
|
|
return hitlist;
|
|
}
|
|
|
|
static void print_rop (RCore *core, RList *hitlist, char mode, bool *json_first) {
|
|
const char *otype;
|
|
RCoreAsmHit *hit = NULL;
|
|
RListIter *iter;
|
|
char *buf_asm;
|
|
unsigned int size = 0;
|
|
RAnalOp analop = {0};
|
|
RAsmOp asmop;
|
|
int colorize = r_config_get_i (core->config, "scr.color");
|
|
int rop_comments = r_config_get_i (core->config, "rop.comments");
|
|
|
|
switch (mode) {
|
|
case 'j':
|
|
//Handle comma between gadgets
|
|
if (!*json_first) r_cons_strcat (",");
|
|
else *json_first = false;
|
|
|
|
r_cons_printf ("{\"opcodes\":[");
|
|
r_list_foreach (hitlist, iter, hit) {
|
|
ut8 *buf = malloc (hit->len);
|
|
r_core_read_at (core, hit->addr, buf, hit->len);
|
|
r_asm_set_pc (core->assembler, hit->addr);
|
|
r_asm_disassemble (core->assembler, &asmop, buf, hit->len);
|
|
r_anal_op (core->anal, &analop, hit->addr, buf, hit->len);
|
|
size += hit->len;
|
|
r_cons_printf ("{\"offset\":%"PFMT64d",\"size\":%d,"
|
|
"\"opcode\":\"%s\",\"type\":\"%s\"}%s",
|
|
hit->addr, hit->len, asmop.buf_asm,
|
|
r_anal_optype_to_string (analop.type),
|
|
iter->n?",":"");
|
|
free (buf);
|
|
}
|
|
if (hit)
|
|
r_cons_printf ("],\"retaddr\":%"PFMT64d",\"size\":%d}", hit->addr, size);
|
|
break;
|
|
case 'l':
|
|
// Print gadgets in a 'linear manner', each sequence
|
|
// on one line.
|
|
r_cons_printf ("0x%08"PFMT64x":",
|
|
((RCoreAsmHit *)hitlist->head->data)->addr);
|
|
r_list_foreach (hitlist, iter, hit) {
|
|
ut8 *buf = malloc (hit->len);
|
|
r_core_read_at (core, hit->addr, buf, hit->len);
|
|
r_asm_set_pc (core->assembler, hit->addr);
|
|
r_asm_disassemble (core->assembler, &asmop, buf, hit->len);
|
|
if (colorize) {
|
|
buf_asm = r_print_colorize_opcode (asmop.buf_asm,
|
|
core->cons->pal.reg, core->cons->pal.num);
|
|
r_cons_printf (" %s%s;", buf_asm, Color_RESET);
|
|
free (buf_asm);
|
|
} else {
|
|
r_cons_printf (" %s;", asmop.buf_asm);
|
|
}
|
|
free (buf);
|
|
}
|
|
break;
|
|
default:
|
|
// Print gadgets with new instruction on a new line.
|
|
r_list_foreach (hitlist, iter, hit) {
|
|
char *comment = rop_comments ?r_meta_get_string (core->anal,
|
|
R_META_TYPE_COMMENT, hit->addr) : NULL;
|
|
if (hit->len<0) {
|
|
eprintf ("Invalid hit length here\n");
|
|
continue;
|
|
}
|
|
ut8 *buf = malloc (1 + hit->len);
|
|
buf[hit->len] = 0;
|
|
r_core_read_at (core, hit->addr, buf, hit->len);
|
|
r_asm_set_pc (core->assembler, hit->addr);
|
|
r_asm_disassemble (core->assembler, &asmop, buf, hit->len);
|
|
r_anal_op (core->anal, &analop, hit->addr, buf, hit->len);
|
|
if (colorize) {
|
|
buf_asm = r_print_colorize_opcode (asmop.buf_asm,
|
|
core->cons->pal.reg, core->cons->pal.num);
|
|
otype = r_print_color_op_type (core->print, analop.type);
|
|
if (comment) {
|
|
r_cons_printf (" 0x%08"PFMT64x" %18s%s %s%s ; %s\n",
|
|
hit->addr, asmop.buf_hex, otype, buf_asm, Color_RESET, comment);
|
|
} else {
|
|
r_cons_printf (" 0x%08"PFMT64x" %18s%s %s%s\n",
|
|
hit->addr, asmop.buf_hex, otype, buf_asm, Color_RESET);
|
|
}
|
|
free (buf_asm);
|
|
} else {
|
|
if (comment) {
|
|
r_cons_printf (" 0x%08"PFMT64x" %18s %s ; %s\n",
|
|
hit->addr, asmop.buf_hex, asmop.buf_asm, comment);
|
|
} else {
|
|
r_cons_printf (" 0x%08"PFMT64x" %18s %s\n",
|
|
hit->addr, asmop.buf_hex, asmop.buf_asm);
|
|
}
|
|
}
|
|
free (buf);
|
|
}
|
|
}
|
|
if (mode != 'j') r_cons_newline ();
|
|
}
|
|
|
|
R_API RList* r_core_get_boundaries_ok(RCore *core) {
|
|
const char *searchin;
|
|
ut8 prot;
|
|
ut64 from, to;
|
|
ut64 __from, __to;
|
|
RList *list;
|
|
if (!core) return NULL;
|
|
prot = r_config_get_i (core->config, "rop.nx") ?
|
|
R_IO_READ|R_IO_WRITE|R_IO_EXEC : R_IO_EXEC;
|
|
searchin = r_config_get (core->config, "search.in");
|
|
|
|
from = core->offset;
|
|
to = core->offset + core->blocksize;
|
|
|
|
__from = r_config_get_i (core->config, "search.from");
|
|
__to = r_config_get_i (core->config, "search.to");
|
|
if (__from != UT64_MAX) from = __from;
|
|
if (__to != UT64_MAX) to = __to;
|
|
|
|
if (!strncmp (searchin, "dbg.", 4) \
|
|
|| !strncmp (searchin, "io.sections", 11) \
|
|
|| prot & R_IO_EXEC) /* always true */ {
|
|
list = r_core_get_boundaries_prot (core,
|
|
prot, searchin, &from, &to);
|
|
} else list = NULL;
|
|
if (!list) {
|
|
RIOMap *map = R_NEW0 (RIOMap);
|
|
if (!map) {
|
|
eprintf ("Cannot allocate map\n");
|
|
return NULL;
|
|
}
|
|
map->fd = core->io->desc->fd;
|
|
map->from = from;
|
|
map->to = to;
|
|
list = r_list_newf (free);
|
|
r_list_append (list, map);
|
|
}
|
|
return list;
|
|
}
|
|
static int r_core_search_rop(RCore *core, ut64 from, ut64 to, int opt, const char *grep, int regexp) {
|
|
const ut8 crop = r_config_get_i (core->config, "rop.conditional"); //decide if cjmp, cret, and ccall should be used too for the gadget-search
|
|
const ut8 subchain = r_config_get_i (core->config, "rop.subchains");
|
|
const ut8 max_instr = r_config_get_i (core->config, "rop.len");
|
|
const ut8 prot = r_config_get_i (core->config, "rop.nx") ? R_IO_READ|R_IO_WRITE|R_IO_EXEC : R_IO_EXEC;
|
|
const char *smode = r_config_get (core->config, "search.in");
|
|
const char *arch = r_config_get (core->config, "asm.arch");
|
|
int max_count = r_config_get_i(core->config, "search.count");
|
|
ut64 search_from = r_config_get_i (core->config, "search.from");
|
|
ut64 search_to = r_config_get_i (core->config, "search.to");
|
|
int i=0, end=0, mode=0, increment=1, ret;
|
|
RList/*<endlist_pair>*/ *end_list = r_list_newf(free);
|
|
RList/*<intptr_t>*/ *badstart = r_list_new();
|
|
RList/*<RRegex>*/ *rx_list = NULL;
|
|
RList/*<RIOMap>*/ *list = NULL;
|
|
int align = core->search->align;
|
|
RListIter *itermap = NULL;
|
|
char* tok, *gregexp = NULL;
|
|
char* grep_arg = NULL;
|
|
bool json_first = true;
|
|
RRegex* rx = NULL;
|
|
int delta = 0;
|
|
ut8 *buf;
|
|
RIOMap *map;
|
|
RAsmOp asmop;
|
|
|
|
if (max_count == 0) {
|
|
max_count = -1;
|
|
}
|
|
if (max_instr <= 1) {
|
|
r_list_free (badstart);
|
|
r_list_free (end_list);
|
|
eprintf ("ROP length (rop.len) must be greater than 1.\n");
|
|
if (max_instr == 1) {
|
|
eprintf ("For rop.len = 1, use /c to search for single "
|
|
"instructions. See /c? for help.\n");
|
|
}
|
|
return false;
|
|
}
|
|
if (search_from == UT64_MAX) {
|
|
search_from = 0;
|
|
}
|
|
|
|
if (!strcmp (arch, "mips")) // MIPS has no jump-in-the-middle
|
|
increment = 4;
|
|
else if (!strcmp (arch, "arm")) // ARM has no jump-in-the-middle
|
|
increment = r_config_get_i(core->config, "asm.bits")==16?2:4;
|
|
else if (!strcmp (arch, "avr")) // AVR is halfword aligned.
|
|
increment = 2;
|
|
|
|
//Options, like JSON, linear, ...
|
|
grep_arg = strchr (grep, ' ');
|
|
if (*grep) {
|
|
if (grep_arg) {
|
|
mode = *(grep_arg - 1);
|
|
grep = grep_arg;
|
|
} else {
|
|
mode = *grep;
|
|
++grep;
|
|
}
|
|
}
|
|
|
|
if (*grep==' ') // grep mode
|
|
for (++grep; *grep==' '; grep++);
|
|
else grep = NULL;
|
|
|
|
// Deal with the grep guy.
|
|
if (grep && regexp) {
|
|
if (!rx_list) rx_list = r_list_newf (free);
|
|
gregexp = strdup (grep);
|
|
tok = strtok (gregexp, ";");
|
|
while (tok) {
|
|
rx = r_regex_new (tok, "");
|
|
r_list_append (rx_list, rx);
|
|
tok = strtok (NULL, ";");
|
|
}
|
|
}
|
|
|
|
maxhits = r_config_get_i (core->config, "search.maxhits");
|
|
if (!strncmp (smode, "dbg.", 4) \
|
|
|| !strncmp (smode, "io.sections", 11) \
|
|
|| prot & R_IO_EXEC) {
|
|
list = r_core_get_boundaries_prot (core, prot, smode, &from, &to);
|
|
} else list = NULL;
|
|
|
|
if (!list) {
|
|
map = R_NEW0 (RIOMap);
|
|
if (!map) {
|
|
eprintf ("Cannot allocate map\n");
|
|
free (gregexp);
|
|
r_list_free (rx_list);
|
|
r_list_free (end_list);
|
|
r_list_free (badstart);
|
|
r_list_free (list);
|
|
return false;
|
|
}
|
|
map->fd = core->io->desc->fd;
|
|
map->from = from;
|
|
map->to = to;
|
|
list = r_list_newf (free);
|
|
r_list_append (list, map);
|
|
maplist = true;
|
|
}
|
|
|
|
if (json)
|
|
r_cons_printf ("[");
|
|
|
|
r_cons_break (NULL, NULL);
|
|
r_list_foreach (list, itermap, map) {
|
|
from = map->from;
|
|
to = map->to;
|
|
if (to > search_to) {
|
|
if (to < from) {
|
|
continue;
|
|
}
|
|
to = search_to;
|
|
}
|
|
if (from < search_from) {
|
|
if (search_from > to) {
|
|
continue;
|
|
}
|
|
from = search_from;
|
|
}
|
|
|
|
if (from>to) {
|
|
eprintf ("Invalid range 0x%"PFMT64x" - 0x%"PFMT64x"\n", from, to);
|
|
continue;
|
|
}
|
|
delta = to - from;
|
|
if (delta < 1) {
|
|
delta = from - to;
|
|
if (delta < 1) {
|
|
free (gregexp);
|
|
r_list_free (rx_list);
|
|
r_list_free (end_list);
|
|
r_list_free (badstart);
|
|
r_list_free (list);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
buf = calloc (1, delta);
|
|
if (!buf) {
|
|
free (gregexp);
|
|
r_list_free (rx_list);
|
|
r_list_free (end_list);
|
|
r_list_free (badstart);
|
|
r_list_free (list);
|
|
return -1;
|
|
}
|
|
(void)r_io_read_at (core->io, from, buf, delta);
|
|
|
|
// Find the end gadgets.
|
|
for (i = 0; i+32 < delta; i += increment) {
|
|
RAnalOp end_gadget = {0};
|
|
// Disassemble one.
|
|
if (r_anal_op (core->anal, &end_gadget, from+i, buf+i,
|
|
delta-i) <= 0) {
|
|
continue;
|
|
}
|
|
if (is_end_gadget (&end_gadget, crop)) {
|
|
struct endlist_pair *epair;
|
|
if (maxhits && r_list_length (end_list) >= maxhits) {
|
|
// limit number of high level rop gadget results
|
|
break;
|
|
}
|
|
epair = R_NEW0 (struct endlist_pair);
|
|
// If this arch has branch delay slots, add the next instr as well
|
|
if (end_gadget.delay) {
|
|
epair->instr_offset = i+increment;
|
|
epair->delay_size = end_gadget.delay;
|
|
r_list_append (end_list, (void*)(intptr_t)epair);
|
|
} else {
|
|
epair->instr_offset = (intptr_t)i;
|
|
epair->delay_size = end_gadget.delay;
|
|
r_list_append (end_list, (void*)epair);
|
|
}
|
|
}
|
|
if (r_cons_singleton()->breaked)
|
|
break;
|
|
// Right now we have a list of all of the end/stop gadgets.
|
|
// We can just construct gadgets from a little bit before them.
|
|
}
|
|
r_list_reverse (end_list);
|
|
// If we have no end gadgets, just skip all of this search nonsense.
|
|
if (r_list_length (end_list) > 0) {
|
|
int prev;
|
|
int next, ropdepth;
|
|
// Get the depth of rop search, should just be max_instr
|
|
// instructions, x86 and friends are weird length instructions, so
|
|
// we'll just assume 15 byte instructions.
|
|
ropdepth = increment == 1 ?
|
|
max_instr * 15 /* wow, x86 is long */ :
|
|
max_instr * increment;
|
|
if (r_cons_singleton()->breaked)
|
|
break;
|
|
struct endlist_pair *end_gadget = (struct endlist_pair *)r_list_pop(end_list);
|
|
next = end_gadget->instr_offset;
|
|
prev = 0;
|
|
// Start at just before the first end gadget.
|
|
for (i = next - ropdepth; i < (delta - 15 /* max insn size */) && max_count != 0; i+=increment) {
|
|
if (i <0) i = 0;
|
|
if (i < prev) i = prev;
|
|
if (r_cons_singleton()->breaked)
|
|
break;
|
|
if (i >= next) {
|
|
// We've exhausted the first end-gadget section,
|
|
// move to the next one.
|
|
if (r_list_get_n (end_list, 0)) {
|
|
prev = i;
|
|
free(end_gadget);
|
|
end_gadget = (struct endlist_pair *)r_list_pop(end_list);
|
|
next = end_gadget->instr_offset;
|
|
i = next - ropdepth;
|
|
if (i <0) i = 0;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (i >= end) { // read by chunk of 4k
|
|
r_core_read_at (core, from+i, buf+i,
|
|
R_MIN ((delta-i), 4096));
|
|
end = i + 2048;
|
|
}
|
|
ret = r_asm_disassemble (core->assembler, &asmop, buf+i, delta-i);
|
|
if (ret) {
|
|
RList * hitlist;
|
|
r_asm_set_pc (core->assembler, from+i);
|
|
hitlist = construct_rop_gadget (core,
|
|
from+i, buf, i, grep, regexp,
|
|
rx_list, end_gadget, badstart, &max_count);
|
|
if (!hitlist)
|
|
continue;
|
|
if (align && (0 != ((from + i) % align))) {
|
|
continue;
|
|
}
|
|
if (json) mode = 'j';
|
|
if ((mode == 'l') && subchain) {
|
|
do {
|
|
print_rop (core, hitlist, mode, &json_first);
|
|
hitlist->head = hitlist->head->n;
|
|
} while (hitlist->head->n);
|
|
} else {
|
|
print_rop (core, hitlist, mode, &json_first);
|
|
}
|
|
}
|
|
if (increment != 1)
|
|
i = next;
|
|
}
|
|
}
|
|
r_list_purge (badstart);
|
|
free (buf);
|
|
}
|
|
if (r_cons_singleton ()->breaked)
|
|
eprintf ("\n");
|
|
r_cons_break_end ();
|
|
|
|
if (json)
|
|
r_cons_printf ("]\n");
|
|
|
|
r_list_free (list);
|
|
r_list_free (rx_list);
|
|
r_list_free (end_list);
|
|
r_list_free (badstart);
|
|
free (gregexp);
|
|
|
|
return true;
|
|
}
|
|
|
|
static int esil_addrinfo(RAnalEsil *esil) {
|
|
RCore *core = (RCore*)esil->cb.user;
|
|
ut64 num = 0;
|
|
char *src = r_anal_esil_pop (esil);
|
|
if (src && *src && r_anal_esil_get_parm (esil, src, &num)) {
|
|
num = r_core_anal_address (core, num);
|
|
r_anal_esil_pushnum (esil, num);
|
|
} else {
|
|
// error. empty stack?
|
|
return 0;
|
|
}
|
|
free (src);
|
|
return 1;
|
|
}
|
|
|
|
static void do_esil_search(RCore *core, struct search_parameters *param, const char *input) {
|
|
const int hit_combo_limit = r_config_get_i (core->config, "search.esilcombo");
|
|
RSearchKeyword kw = {0};
|
|
searchhits = 0;
|
|
if (input[1]==' ') {
|
|
const int kwidx = r_config_get_i (core->config, "search.kwidx");
|
|
const int iotrap = r_config_get_i (core->config, "esil.iotrap");
|
|
const int stacksize = r_config_get_i (core->config, "esil.stacksize");
|
|
int hit_happens = 0;
|
|
int hit_combo = 0;
|
|
char *res;
|
|
ut64 nres, addr = param->from;
|
|
r_cons_break (NULL, NULL);
|
|
if (!core->anal->esil)
|
|
core->anal->esil = r_anal_esil_new (stacksize, iotrap);
|
|
if (!core->anal->esil)
|
|
return;
|
|
/* hook addrinfo */
|
|
core->anal->esil->cb.user = core;
|
|
r_anal_esil_set_op (core->anal->esil, "AddrInfo", esil_addrinfo);
|
|
/* hook addrinfo */
|
|
r_anal_esil_setup (core->anal->esil, core->anal, 1, 0);
|
|
r_anal_esil_stack_free (core->anal->esil);
|
|
core->anal->esil->debug = 0;
|
|
for (; addr<param->to; addr++) {
|
|
if (core->search->align) {
|
|
if ((addr % core->search->align)) {
|
|
continue;
|
|
}
|
|
}
|
|
#if 0
|
|
// we need a way to retrieve info from a speicif address, and make it accessible from the esil search
|
|
// maybe we can just do it like this: 0x804840,AddressType,3,&, ... bitmask
|
|
// executable = 1
|
|
// writable = 2
|
|
// inprogram
|
|
// instack
|
|
// inlibrary
|
|
// inheap
|
|
r_anal_esil_set_op (core->anal->esil, "AddressInfo", esil_search_address_info);
|
|
#endif
|
|
if (r_cons_singleton ()->breaked) {
|
|
eprintf ("Breaked at 0x%08"PFMT64x"\n", addr);
|
|
break;
|
|
}
|
|
r_anal_esil_set_pc (core->anal->esil, addr);
|
|
if (!r_anal_esil_parse (core->anal->esil, input+2)) {
|
|
// XXX: return value doesnt seems to be correct here
|
|
eprintf ("Cannot parse esil (%s)\n", input+2);
|
|
break;
|
|
}
|
|
hit_happens = false;
|
|
res = r_anal_esil_pop (core->anal->esil);
|
|
if (r_anal_esil_get_parm (core->anal->esil, res, &nres)) {
|
|
if (nres) {
|
|
if (!__cb_hit (&kw, core, addr))
|
|
break;
|
|
//eprintf (" HIT AT 0x%"PFMT64x"\n", addr);
|
|
kw.type = 0; //R_SEARCH_TYPE_ESIL;
|
|
kw.kwidx = kwidx;
|
|
kw.count++;
|
|
eprintf ("Hits: %d\r", kw.count);
|
|
kw.keyword_length = 0;
|
|
hit_happens = true;
|
|
}
|
|
} else {
|
|
eprintf ("Cannot parse esil (%s)\n", input+2);
|
|
r_anal_esil_stack_free (core->anal->esil);
|
|
free (res);
|
|
break;
|
|
}
|
|
r_anal_esil_stack_free (core->anal->esil);
|
|
free (res);
|
|
|
|
if (hit_happens) {
|
|
hit_combo ++;
|
|
if (hit_combo > hit_combo_limit) {
|
|
eprintf ("Hit combo limit reached. Stopping search. Use f-\n");
|
|
break;
|
|
}
|
|
} else {
|
|
hit_combo = 0;
|
|
}
|
|
}
|
|
r_config_set_i (core->config, "search.kwidx", kwidx +1);
|
|
r_cons_break_end ();
|
|
} else eprintf ("Usage: /E [esil-expr]\n");
|
|
r_cons_clear_line (1);
|
|
}
|
|
|
|
static void do_anal_search(RCore *core, struct search_parameters *param, const char *input) {
|
|
ut64 at;
|
|
ut8 *buf;
|
|
RAnalOp aop;
|
|
int chk_family = 0;
|
|
int i, ret, bsize = core->blocksize;
|
|
int kwidx = core->search->n_kws; //(int)r_config_get_i (core->config, "search.kwidx")-1;
|
|
int maxhits, match, count = 0;
|
|
if (bsize<64)
|
|
bsize=64;
|
|
if (!strncmp (param->mode, "dbg.", 4) || !strncmp(param->mode, "io.sections", 11))
|
|
param->boundaries = r_core_get_boundaries (core, param->mode, ¶m->from, ¶m->to);
|
|
else param->boundaries = NULL;
|
|
|
|
if (*input=='f') {
|
|
chk_family = 1;
|
|
input++;
|
|
}
|
|
if (*input=='?') {
|
|
r_cons_printf ("Usage: /A%s [type]\n", chk_family?"f":"");
|
|
for (i=0; i<64; i++) {
|
|
const char *str;
|
|
if (chk_family) str = r_anal_op_family_to_string (i);
|
|
else str = r_anal_optype_to_string (i);
|
|
if (chk_family && atoi (str))
|
|
break;
|
|
if (!str || !*str) break;
|
|
if (!strcmp (str, "undefined"))
|
|
continue;
|
|
r_cons_printf ("%s\n", str);
|
|
}
|
|
return;
|
|
}
|
|
buf = malloc (bsize);
|
|
maxhits = (int)r_config_get_i (core->config, "search.count");
|
|
r_cons_break (NULL, NULL);
|
|
for (i=0, at = param->from; at < param->to; at++,i++) {
|
|
if (r_cons_singleton()->breaked)
|
|
break;
|
|
if (i>=(bsize-32)) {
|
|
i = 0;
|
|
}
|
|
if (i==0) {
|
|
r_core_read_at (core, at, buf, bsize);
|
|
}
|
|
ret = r_anal_op (core->anal, &aop, at, buf+i, bsize-i);
|
|
if (ret) {
|
|
match = 0;
|
|
if (chk_family) {
|
|
const char *fam = r_anal_op_family_to_string (aop.family);
|
|
if (fam)
|
|
if (!*input || strstr (input, fam)) {
|
|
match = 1;
|
|
r_cons_printf ("0x%08"PFMT64x" - %d %s\n", at, ret, fam);
|
|
}
|
|
} else {
|
|
const char *type = r_anal_optype_to_string (aop.type);
|
|
if (type)
|
|
if (!*input || strstr (input, type)) {
|
|
match = 1;
|
|
r_cons_printf ("0x%08"PFMT64x" - %d %s\n", at, ret, type);
|
|
}
|
|
}
|
|
if (match) {
|
|
if (*input && searchflags) {
|
|
char flag[64];
|
|
snprintf (flag, sizeof (flag), "%s%d_%d",
|
|
searchprefix, kwidx, count);
|
|
r_flag_set (core->flags, flag, at, ret, 1);
|
|
}
|
|
count++;
|
|
if (maxhits && count >= maxhits)
|
|
break;
|
|
}
|
|
if (core->search->align>0) {
|
|
i += core->search->align -1;
|
|
at += core->search->align -1;
|
|
} else {
|
|
// skip instruction
|
|
i += ret - 1; //aop.size-1;
|
|
at += ret -1;
|
|
}
|
|
}
|
|
}
|
|
r_cons_break_end ();
|
|
free (buf);
|
|
}
|
|
|
|
static void do_asm_search(RCore *core, struct search_parameters *param, const char *input) {
|
|
RCoreAsmHit *hit;
|
|
RListIter *iter, *itermap;
|
|
int count = 0, maxhits = 0;
|
|
int kwidx = core->search->n_kws; //(int)r_config_get_i (core->config, "search.kwidx")-1;
|
|
RList *hits;
|
|
RIOMap *map;
|
|
int regexp = input[1] == '/';
|
|
char *end_cmd = strstr (input, " ");
|
|
int outmode;
|
|
if (!end_cmd) {
|
|
outmode = input[1];
|
|
} else {
|
|
outmode = *(end_cmd - 1);
|
|
}
|
|
if (outmode != 'j')
|
|
json = 0;
|
|
|
|
if (!strncmp (param->mode, "dbg.", 4) || !strncmp(param->mode, "io.sections", 11))
|
|
param->boundaries = r_core_get_boundaries (core, param->mode, ¶m->from, ¶m->to);
|
|
else
|
|
param->boundaries = NULL;
|
|
|
|
maxhits = (int)r_config_get_i (core->config, "search.count");
|
|
|
|
if (!param->boundaries) {
|
|
map = R_NEW0 (RIOMap);
|
|
map->fd = core->io->desc->fd;
|
|
map->from = param->from;
|
|
map->to = param->to;
|
|
param->boundaries = r_list_newf (free);
|
|
r_list_append (param->boundaries, map);
|
|
maplist = true;
|
|
}
|
|
|
|
if (json) r_cons_printf ("[");
|
|
r_cons_break (NULL, NULL);
|
|
r_list_foreach (param->boundaries, itermap, map) {
|
|
param->from = map->from;
|
|
param->to = map->to;
|
|
if (r_cons_singleton()->breaked)
|
|
break;
|
|
|
|
if (maxhits && count >= maxhits)
|
|
break;
|
|
|
|
if (outmode == 0) hits = NULL;
|
|
else hits = r_core_asm_strsearch (core, input+2,
|
|
param->from, param->to, maxhits, regexp);
|
|
if (hits) {
|
|
r_list_foreach (hits, iter, hit) {
|
|
if (r_cons_singleton()->breaked)
|
|
break;
|
|
switch (outmode) {
|
|
case 'j':
|
|
if (count > 0) r_cons_printf (",");
|
|
r_cons_printf (
|
|
"{\"offset\":%"PFMT64d",\"len\":%d,\"code\":\"%s\"}",
|
|
hit->addr, hit->len, hit->code);
|
|
break;
|
|
case '*':
|
|
r_cons_printf ("f %s%d_%i = 0x%08"PFMT64x"\n",
|
|
searchprefix, kwidx, count, hit->addr);
|
|
break;
|
|
default:
|
|
r_cons_printf ("0x%08"PFMT64x" # %i: %s\n",
|
|
hit->addr, hit->len, hit->code);
|
|
break;
|
|
}
|
|
if (searchflags) {
|
|
const char *flagname = sdb_fmt (0, "%s%d_%d", searchprefix, kwidx, count);
|
|
r_flag_set (core->flags, flagname, hit->addr, hit->len, 1);
|
|
}
|
|
count++;
|
|
}
|
|
r_list_purge (hits);
|
|
free (hits);
|
|
}
|
|
}
|
|
if (json) r_cons_printf ("]");
|
|
r_cons_break_end ();
|
|
|
|
if (maplist) {
|
|
param->boundaries->free = free;
|
|
r_list_free (param->boundaries);
|
|
param->boundaries = NULL;
|
|
}
|
|
}
|
|
|
|
static void do_string_search(RCore *core, struct search_parameters *param) {
|
|
ut64 at;
|
|
ut8 *buf;
|
|
int ret;
|
|
|
|
if (json) r_cons_printf("[");
|
|
int oraise = core->io->raised;
|
|
int bufsz;
|
|
RListIter *iter;
|
|
RIOMap *map;
|
|
if (!searchflags && !json)
|
|
r_cons_printf ("fs hits\n");
|
|
core->search->inverse = param->inverse;
|
|
searchcount = r_config_get_i (core->config, "search.count");
|
|
if (searchcount)
|
|
searchcount++;
|
|
if (core->search->n_kws>0 || param->crypto_search) {
|
|
RSearchKeyword aeskw;
|
|
if (param->crypto_search) {
|
|
memset (&aeskw, 0, sizeof (aeskw));
|
|
aeskw.keyword_length = 31;
|
|
}
|
|
/* set callback */
|
|
/* TODO: handle last block of data */
|
|
/* TODO: handle ^C */
|
|
/* TODO: launch search in background support */
|
|
// REMOVE OLD FLAGS r_core_cmdf (core, "f-%s*", r_config_get (core->config, "search.prefix"));
|
|
r_search_set_callback (core->search, &__cb_hit, core);
|
|
cmdhit = r_config_get (core->config, "cmd.hit");
|
|
r_cons_break (NULL, NULL);
|
|
// XXX required? imho nor_io_set_fd (core->io, core->file->fd);
|
|
if (!param->boundaries) {
|
|
RIOMap *map = R_NEW0 (RIOMap);
|
|
map->fd = core->io->desc->fd;
|
|
map->from = param->from;
|
|
map->to = param->to;
|
|
param->boundaries = r_list_newf (free);
|
|
r_list_append (param->boundaries, map);
|
|
maplist = true;
|
|
}
|
|
buf = (ut8 *)malloc (core->blocksize);
|
|
bufsz = core->blocksize;
|
|
r_list_foreach (param->boundaries, iter, map) {
|
|
int fd;
|
|
param->from = map->from;
|
|
param->to = map->to;
|
|
searchhits = 0;
|
|
|
|
r_io_raise (core->io, map->fd);
|
|
fd = core->io->raised;
|
|
if (fd == -1 && core->io->desc) {
|
|
fd = core->io->desc->fd;
|
|
}
|
|
if (!json) {
|
|
RSearchKeyword *kw = r_list_first (core->search->kws);
|
|
eprintf ("Searching %d bytes in [0x%"PFMT64x"-0x%"PFMT64x"]\n",
|
|
kw? kw->keyword_length: 0, param->from, param->to);
|
|
}
|
|
if (r_sandbox_enable (0)) {
|
|
if ((param->to - param->from) > 1024*64) {
|
|
eprintf ("Sandbox restricts search range\n");
|
|
break;
|
|
}
|
|
}
|
|
if (param->bckwrds) {
|
|
if (param->to < param->from + bufsz) {
|
|
at = param->from;
|
|
param->do_bckwrd_srch = false;
|
|
} else at = param->to - bufsz;
|
|
} else at = param->from;
|
|
/* bckwrds = false -> normal search -> must be at < to
|
|
bckwrds search -> check later */
|
|
for (; ( !param->bckwrds && at < param->to ) || param->bckwrds ;) {
|
|
print_search_progress (at, param->to, searchhits);
|
|
if (r_cons_singleton ()->breaked) {
|
|
eprintf ("\n\n");
|
|
break;
|
|
}
|
|
//ret = r_core_read_at (core, at, buf, bufsz);
|
|
// ret = r_io_read_at (core->io, at, buf, bufsz);
|
|
if (param->use_mread) {
|
|
// what about a config var to choose which io api to use?
|
|
ret = r_io_mread (core->io, fd, at, buf, bufsz);
|
|
} else {
|
|
r_io_seek (core->io, at, R_IO_SEEK_SET);
|
|
ret = r_io_read (core->io, buf, bufsz);
|
|
}
|
|
/*
|
|
if (ignorecase) {
|
|
int i;
|
|
for (i=0; i<bufsz; i++)
|
|
buf[i] = tolower (buf[i]);
|
|
}
|
|
*/
|
|
if (ret <1)
|
|
break;
|
|
if (param->crypto_search) {
|
|
int delta = 0;
|
|
if (param->aes_search)
|
|
delta = r_search_aes_update (core->search, at, buf, ret);
|
|
else if (param->rsa_search)
|
|
delta = r_search_rsa_update (core->search, at, buf, ret);
|
|
if (delta != -1) {
|
|
if (!r_search_hit_new (core->search, &aeskw, at+delta)) {
|
|
break;
|
|
}
|
|
aeskw.count++;
|
|
}
|
|
} else if (r_search_update (core->search, &at, buf, ret) == -1) {
|
|
//eprintf ("search: update read error at 0x%08"PFMT64x"\n", at);
|
|
break;
|
|
}
|
|
if (param->bckwrds) {
|
|
if (!param->do_bckwrd_srch) {
|
|
break;
|
|
}
|
|
if (at > param->from + bufsz) {
|
|
at -= bufsz;
|
|
} else {
|
|
param->do_bckwrd_srch = false;
|
|
at = param->from;
|
|
}
|
|
} else {
|
|
at += bufsz;
|
|
}
|
|
}
|
|
print_search_progress (at, param->to, searchhits);
|
|
r_cons_break_end ();
|
|
r_cons_clear_line (1);
|
|
core->num->value = searchhits;
|
|
if (searchflags && (searchcount>0) && !json) {
|
|
eprintf ("hits: %d %s%d_0 .. %s%d_%d\n",
|
|
searchhits,
|
|
searchprefix, core->search->n_kws-1,
|
|
searchprefix, core->search->n_kws-1, searchcount-1);
|
|
} else if (!json) {
|
|
eprintf ("hits: %d\n", searchhits);
|
|
}
|
|
if (!r_list_empty (core->search->kws)) {
|
|
RListIter *iter;
|
|
RSearchKeyword *kw;
|
|
r_list_foreach (core->search->kws, iter, kw) {
|
|
kw->kwidx++;
|
|
}
|
|
}
|
|
}
|
|
free (buf);
|
|
if (maplist) {
|
|
param->boundaries->free = free;
|
|
r_list_free (param->boundaries);
|
|
param->boundaries = NULL;
|
|
}
|
|
r_io_raise (core->io, oraise);
|
|
} else eprintf ("No keywords defined\n");
|
|
|
|
/* Crazy party counter (kill me please) */
|
|
if ((searchhits == 0 ) && (core->search->n_kws > 0))
|
|
core->search->n_kws--;
|
|
|
|
if (json) r_cons_printf("]");
|
|
}
|
|
|
|
static int cmd_search(void *data, const char *input) {
|
|
struct search_parameters param;
|
|
bool dosearch = false;
|
|
int i, len, ret = true;
|
|
RCore *core = (RCore *)data;
|
|
int ignorecase = false;
|
|
int param_offset = 2;
|
|
char *inp;
|
|
ut64 n64, __from, __to;
|
|
ut32 n32;
|
|
ut16 n16;
|
|
ut8 n8;
|
|
if (!core || !core->io || !core->io->desc) {
|
|
eprintf ("Can't search if we don't have an open file.\n");
|
|
return false;
|
|
}
|
|
if (core->in_search) {
|
|
eprintf ("Can't search from within a search.\n");
|
|
return false;
|
|
}
|
|
if (input[0] == '/') {
|
|
if (core->lastsearch) {
|
|
input = core->lastsearch;
|
|
} else {
|
|
eprintf ("No previous search done\n");
|
|
return false;
|
|
}
|
|
} else {
|
|
free (core->lastsearch);
|
|
core->lastsearch = strdup (input);
|
|
}
|
|
|
|
core->in_search = true;
|
|
r_flag_space_push (core->flags, "searches");
|
|
param.from = param.to = 0;
|
|
param.inverse = false;
|
|
param.crypto_search = false;
|
|
param.bckwrds = false;
|
|
param.do_bckwrd_srch = false;
|
|
param.aes_search = false;
|
|
param.rsa_search = false;
|
|
param.use_mread = false;
|
|
param.do_bckwrd_srch = false;
|
|
|
|
c = 0;
|
|
json = false;
|
|
first_hit = true;
|
|
//core->search->n_kws = 0;
|
|
maplist = false;
|
|
__from = r_config_get_i (core->config, "search.from");
|
|
__to = r_config_get_i (core->config, "search.to");
|
|
|
|
searchshow = r_config_get_i (core->config, "search.show");
|
|
param.mode = r_config_get (core->config, "search.in");
|
|
param.boundaries = r_core_get_boundaries (core, param.mode,
|
|
¶m.from, ¶m.to);
|
|
param.use_mread = (!strcmp (param.mode, "maps"))? 1: 0;
|
|
|
|
if (__from != UT64_MAX) param.from = __from;
|
|
if (__to != UT64_MAX) param.to = __to;
|
|
/*
|
|
this introduces a bug until we implement backwards search
|
|
for all search types
|
|
if (__to < __from) {
|
|
eprintf ("Invalid search range. Check 'e search.{from|to}'\n");
|
|
return false;
|
|
}
|
|
since the backward search will be implemented soon I'm not gonna stick
|
|
checks for every case in switch // jjdredd
|
|
remove when everything is done
|
|
*/
|
|
|
|
core->search->align = r_config_get_i (core->config, "search.align");
|
|
searchflags = r_config_get_i (core->config, "search.flags");
|
|
//TODO: handle section ranges if from&&to==0
|
|
/*
|
|
section = r_io_section_vget (core->io, core->offset);
|
|
if (section) {
|
|
from += section->vaddr;
|
|
//fin = ini + s->size;
|
|
}
|
|
*/
|
|
maxhits = r_config_get_i (core->config, "search.maxhits");
|
|
searchprefix = r_config_get (core->config, "search.prefix");
|
|
core->search->overlap = r_config_get_i (core->config, "search.overlap");
|
|
// TODO: get ranges from current IO section
|
|
/* XXX: Think how to get the section ranges here */
|
|
if (param.from == 0LL) param.from = core->offset;
|
|
if (param.to == 0LL) param.to = UT32_MAX; // XXX?
|
|
|
|
/* we don't really care what's bigger bc there's a flag for backward search
|
|
from now on 'from' and 'to' represent only the search boundaries, not
|
|
search direction */
|
|
if (core->io->va) {
|
|
__from = R_MIN (param.from, param.to);
|
|
param.to = R_MAX (param.from, param.to);
|
|
param.from = __from;
|
|
} else {
|
|
ut64 rawsize = r_io_size (core->io);
|
|
param.from = R_MIN (param.from, rawsize);
|
|
param.to = R_MIN (param.to, rawsize);
|
|
}
|
|
core->search->bckwrds = false;
|
|
|
|
if (param.from == param.to) {
|
|
eprintf ("WARNING from == to?\n");
|
|
}
|
|
/* Quick & dirty check for json output */
|
|
if (input[0] && (input[1] == 'j') && (input[0] != ' ')) {
|
|
json = true;
|
|
param_offset++;
|
|
}
|
|
|
|
reread:
|
|
switch (*input) {
|
|
case '!':
|
|
input++;
|
|
param.inverse = true;
|
|
goto reread;
|
|
case 'B':
|
|
cmd_search_bin (core, param.from, param.to);
|
|
break;
|
|
case 'b':
|
|
if (*(++input) == '?'){
|
|
eprintf ("Usage: /b<command> [value] backward search, see '/?'\n");
|
|
goto beach;
|
|
}
|
|
core->search->bckwrds = param.bckwrds = param.do_bckwrd_srch = true;
|
|
/* if backward search and __to wasn't specified
|
|
search from the beginning */
|
|
if ((unsigned int)param.to == UT32_MAX){
|
|
param.to = param.from;
|
|
param.from = 0;
|
|
}
|
|
goto reread;
|
|
case 'P':
|
|
{
|
|
// print the offset of the Previous opcode
|
|
ut8 buf[64];
|
|
ut64 off = core->offset;
|
|
r_core_read_at (core, off-16, buf, 32);
|
|
off = findprevopsz (core, off, buf + 16);
|
|
if (json) r_cons_printf ("[%"PFMT64u"]", off);
|
|
else r_cons_printf ("0x%08"PFMT64x"\n", off);
|
|
}
|
|
break;
|
|
case 'R':
|
|
if (input[1]=='?') {
|
|
const char* help_msg[] = {
|
|
"Usage: /R", "", "Search for ROP gadgets",
|
|
"/R", " [filter-by-string]" , "Show gadgets",
|
|
"/R/", " [filter-by-regexp]" , "Show gadgets [regular expression]",
|
|
"/Rl", " [filter-by-string]" , "Show gadgets in a linear manner",
|
|
"/R/l", " [filter-by-regexp]" , "Show gadgets in a linear manner [regular expression]",
|
|
"/Rj", " [filter-by-string]", "JSON output",
|
|
"/R/j", " [filter-by-regexp]", "JSON output [regular expression]",
|
|
NULL};
|
|
r_core_cmd_help (core, help_msg);
|
|
} else if (input[1] == '/') {
|
|
r_core_search_rop (core, param.from, param.to, 0, input+1, 1);
|
|
} else r_core_search_rop (core, param.from, param.to, 0, input+1, 0);
|
|
goto beach;
|
|
case 'r': // "/r"
|
|
if (input[param_offset - 1] == ' ') {
|
|
r_core_anal_search (core, param.from, param.to,
|
|
r_num_math (core->num, input+2));
|
|
} else {
|
|
r_core_anal_search (core, param.from, param.to,
|
|
core->offset);
|
|
}
|
|
break;
|
|
case 'A':
|
|
switch (input[1]) {
|
|
case 'f':
|
|
case '?':
|
|
case ' ':
|
|
do_anal_search (core, ¶m, input+1);
|
|
break;
|
|
default:
|
|
do_anal_search (core, ¶m, "?");
|
|
break;
|
|
}
|
|
dosearch = false;
|
|
break;
|
|
case 'a': if (input[1]) {
|
|
char *kwd = r_core_asm_search (core, input+param_offset,
|
|
param.from, param.to);
|
|
if (kwd) {
|
|
r_search_reset (core->search, R_SEARCH_KEYWORD);
|
|
r_search_set_distance (core->search, (int)
|
|
r_config_get_i (core->config, "search.distance"));
|
|
r_search_kw_add (core->search,
|
|
r_search_keyword_new_hexmask (kwd, NULL));
|
|
r_search_begin (core->search);
|
|
free (kwd);
|
|
dosearch = true;
|
|
} else {
|
|
ret = false;
|
|
goto beach;
|
|
}
|
|
} break;
|
|
case 'C': {
|
|
dosearch = true;
|
|
param.crypto_search = true;
|
|
switch (input[1]) {
|
|
case 'a':
|
|
param.aes_search = true;
|
|
break;
|
|
case 'r':
|
|
param.rsa_search = true;
|
|
break;
|
|
default:{
|
|
dosearch = false;
|
|
param.crypto_search = false;
|
|
const char* help_msg[] = {
|
|
"Usage: /C", "", "Search for crypto materials",
|
|
"/Ca", "" , "Search for AES keys",
|
|
"/Cr", "", "Search for private RSA keys",
|
|
NULL};
|
|
r_core_cmd_help (core, help_msg);
|
|
}
|
|
}
|
|
} break;
|
|
case 'm': // "/m"
|
|
dosearch = false;
|
|
if (input[1]==' ' || input[1]=='\0') {
|
|
int ret;
|
|
const char *file = input[1]? input+2: NULL;
|
|
ut64 addr = param.from;
|
|
r_cons_break (NULL, NULL);
|
|
for (; addr<param.to; addr++) {
|
|
if (r_cons_singleton ()->breaked)
|
|
break;
|
|
ret = r_core_magic_at (core, file, addr, 99, false);
|
|
if (ret == -1) {
|
|
// something went terribly wrong.
|
|
break;
|
|
}
|
|
addr += ret-1;
|
|
}
|
|
r_cons_clear_line (1);
|
|
r_cons_break_end ();
|
|
} else eprintf ("Usage: /m [file]\n");
|
|
r_cons_clear_line (1);
|
|
break;
|
|
case 'p': // "/p"
|
|
{
|
|
if (input[param_offset-1]) {
|
|
int ps = atoi (input+param_offset);
|
|
if (ps>1) {
|
|
r_cons_break (NULL, NULL);
|
|
r_search_pattern_size (core->search, ps);
|
|
r_search_pattern (core->search, param.from, param.to);
|
|
r_cons_break_end ();
|
|
break;
|
|
}
|
|
}
|
|
eprintf ("Invalid pattern size (must be >0)\n");
|
|
}
|
|
break;
|
|
case 'V':
|
|
// TODO: add support for json
|
|
{
|
|
int err = 1, vsize = atoi (input+1);
|
|
if (vsize && input[2] && input[3]) {
|
|
ut64 vmin = r_num_math (core->num, input+2);
|
|
char *w = strchr (input+3, ' ');
|
|
if (w) {
|
|
ut64 vmax = r_num_math (core->num, w);
|
|
if (vsize>0) {
|
|
err = 0;
|
|
(void)cmd_search_value_in_range (core,
|
|
param.from, param.to, vmin, vmax, vsize);
|
|
}
|
|
}
|
|
}
|
|
if (err)
|
|
eprintf ("Usage: /V[1|2|4|8] [minval] [maxval]\n");
|
|
}
|
|
break;
|
|
case 'v':
|
|
if (input[1]){
|
|
if (input[2] == 'j') {
|
|
json = true;
|
|
param_offset++;
|
|
}
|
|
}
|
|
r_search_reset (core->search, R_SEARCH_KEYWORD);
|
|
r_search_set_distance (core->search, (int)
|
|
r_config_get_i (core->config, "search.distance"));
|
|
switch (input[1]) {
|
|
case '8':
|
|
if (input[param_offset]){
|
|
n64 = r_num_math (core->num, input+param_offset);
|
|
r_mem_copyendian ((ut8*)&n64, (const ut8*)&n64,
|
|
8, !core->assembler->big_endian);
|
|
r_search_kw_add (core->search,
|
|
r_search_keyword_new ((const ut8*)&n64, 8, NULL, 0, NULL));
|
|
}
|
|
else eprintf ("Usage: /v8 value\n");
|
|
break;
|
|
case '1':
|
|
if (input[param_offset]){
|
|
n8 = (ut8)r_num_math (core->num, input+param_offset);
|
|
r_search_kw_add (core->search,
|
|
r_search_keyword_new ((const ut8*)&n8, 1, NULL, 0, NULL));
|
|
}
|
|
else eprintf ("Usage: /v1 value\n");
|
|
break;
|
|
case '2':
|
|
if (input[param_offset]){
|
|
n16 = (ut16)r_num_math (core->num, input+param_offset);
|
|
r_mem_copyendian ((ut8*)&n16, (ut8*)&n16,
|
|
2, !core->assembler->big_endian);
|
|
r_search_kw_add (core->search,
|
|
r_search_keyword_new ((const ut8*)&n16, 2, NULL, 0, NULL));
|
|
}
|
|
else eprintf ("Usage: /v2 value\n");
|
|
break;
|
|
default: // default size
|
|
case '4':
|
|
if (input[param_offset-1]) {
|
|
if (input[param_offset]){
|
|
n32 = (ut32)r_num_math (core->num, input+param_offset);
|
|
r_mem_copyendian ((ut8*)&n32, (const ut8*)&n32,
|
|
4, !core->assembler->big_endian);
|
|
r_search_kw_add (core->search,
|
|
r_search_keyword_new ((const ut8*)&n32, 4, NULL, 0, NULL));
|
|
}
|
|
}
|
|
else eprintf ("Usage: /v4 value\n");
|
|
break;
|
|
}
|
|
// TODO: Add support for /v4 /v8 /v2
|
|
r_search_begin (core->search);
|
|
dosearch = true;
|
|
break;
|
|
case 'w': /* search wide string, includes ignorecase search functionality (/wi cmd)! */
|
|
if (input[1]) {
|
|
if (input[2]) {
|
|
if (input[1] == 'j' || input[2] == 'j') json = true;
|
|
if (input[1] == 'i' || input[2] == 'i') ignorecase = true;
|
|
}
|
|
|
|
if (input[1+json+ignorecase] == ' ') {
|
|
int strstart, len;
|
|
const char *p2;
|
|
char *p, *str;
|
|
strstart = 2+json+ignorecase;
|
|
len = strlen (input+strstart);
|
|
str = malloc ((len+1)*2);
|
|
for (p2=input+strstart, p=str; *p2; p+=2, p2++) {
|
|
if (ignorecase)
|
|
p[0] = tolower((const unsigned char)*p2);
|
|
else
|
|
p[0] = *p2;
|
|
p[1] = 0;
|
|
}
|
|
r_search_reset (core->search, R_SEARCH_KEYWORD);
|
|
r_search_set_distance (core->search, (int)
|
|
r_config_get_i (core->config, "search.distance"));
|
|
RSearchKeyword *skw;
|
|
skw = r_search_keyword_new ((const ut8*)str, len*2, NULL, 0, NULL);
|
|
free (str);
|
|
if (skw) {
|
|
skw->icase = ignorecase;
|
|
r_search_kw_add (core->search, skw);
|
|
r_search_begin (core->search);
|
|
dosearch = true;
|
|
} else {
|
|
eprintf ("Invalid keyword\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'i':
|
|
if (input[param_offset-1]!= ' ') {
|
|
eprintf ("Missing ' ' after /i\n");
|
|
ret = false;
|
|
goto beach;
|
|
}
|
|
ignorecase = true;
|
|
case 'j':
|
|
if (input[0] =='j') json = true;
|
|
/* pass-thru */
|
|
case ' ': /* search string */
|
|
inp = strdup (input+1+ignorecase+json);
|
|
len = r_str_unescape (inp);
|
|
if (!json) {
|
|
eprintf ("Searching %d bytes from 0x%08"PFMT64x" to 0x%08"PFMT64x": ",
|
|
len, param.from, param.to);
|
|
for (i=0; i<len; i++) eprintf ("%02x ", (ut8)inp[i]);
|
|
eprintf ("\n");
|
|
}
|
|
r_search_reset (core->search, R_SEARCH_KEYWORD);
|
|
r_search_set_distance (core->search, (int)
|
|
r_config_get_i (core->config, "search.distance"));
|
|
{
|
|
RSearchKeyword *skw;
|
|
skw = r_search_keyword_new ((const ut8*)inp, len, NULL, 0, NULL);
|
|
free (inp);
|
|
if (skw) {
|
|
skw->icase = ignorecase;
|
|
skw->type = R_SEARCH_KEYWORD_TYPE_STRING;
|
|
r_search_kw_add (core->search, skw);
|
|
} else {
|
|
eprintf ("Invalid keyword\n");
|
|
break;
|
|
}
|
|
}
|
|
r_search_begin (core->search);
|
|
dosearch = true;
|
|
break;
|
|
case 'e': /* match regexp */
|
|
if (input[1]) {
|
|
RSearchKeyword *kw;
|
|
kw = r_search_keyword_new_regexp (input + param_offset, NULL);
|
|
if (!kw) {
|
|
eprintf("Invalid regexp specified\n");
|
|
break;
|
|
}
|
|
r_search_reset (core->search, R_SEARCH_REGEXP);
|
|
r_search_set_distance (core->search, (int)
|
|
r_config_get_i (core->config, "search.distance"));
|
|
r_search_kw_add (core->search, kw);
|
|
r_search_begin (core->search);
|
|
dosearch = true;
|
|
} else eprintf ("Missing regex\n");
|
|
break;
|
|
case 'E':
|
|
if (core->io && core->io->debug)
|
|
r_debug_map_sync (core->dbg);
|
|
do_esil_search (core, ¶m, input);
|
|
goto beach;
|
|
case 'd': /* search delta key */
|
|
if (input[1]) {
|
|
r_search_reset (core->search, R_SEARCH_DELTAKEY);
|
|
r_search_kw_add (core->search,
|
|
r_search_keyword_new_hexmask (input+param_offset, NULL));
|
|
r_search_begin (core->search);
|
|
dosearch = true;
|
|
} else eprintf ("Missing delta\n");
|
|
break;
|
|
case '#':
|
|
{
|
|
char *p, *arg = r_str_chop (strdup (input+1));
|
|
p = strchr (arg, ' ');
|
|
if (p) {
|
|
*p++ = 0;
|
|
if (*arg=='?') {
|
|
eprintf ("Usage: /#md5 [hash] [datalen]\n");
|
|
} else {
|
|
ut32 min = UT32_MAX;
|
|
ut32 max = UT32_MAX;
|
|
char *pmax, *pmin = strchr (p, ' ');
|
|
if (pmin) {
|
|
*pmin++ = 0;
|
|
pmax = strchr (pmin, ' ');
|
|
if (pmax) {
|
|
*pmax++ = 0;
|
|
max = r_num_math (core->num, pmax);
|
|
}
|
|
min = r_num_math (core->num, pmin);
|
|
}
|
|
search_hash (core, arg, p, min, max);
|
|
}
|
|
} else {
|
|
eprintf ("Missing hash\n");
|
|
}
|
|
free (arg);
|
|
}
|
|
break;
|
|
case 'x': /* search hex */
|
|
if (input[1]=='?') {
|
|
const char* help_msg[] = {
|
|
"Usage:", "/x [hexpairs]:[binmask]", "Search in memory",
|
|
"/x ", "9090cd80", "search for those bytes",
|
|
"/x ", "9090cd80:ffff7ff0", "search with binary mask",
|
|
NULL};
|
|
r_core_cmd_help (core, help_msg);
|
|
} else {
|
|
RSearchKeyword *kw;
|
|
char *s, *p = strdup (input+json+2);
|
|
r_search_reset (core->search, R_SEARCH_KEYWORD);
|
|
r_search_set_distance (core->search, (int)
|
|
r_config_get_i (core->config, "search.distance"));
|
|
s = strchr (p, ':');
|
|
if (s) {
|
|
*s++ = 0;
|
|
kw = r_search_keyword_new_hex (p, s, NULL);
|
|
} else {
|
|
kw = r_search_keyword_new_hexmask (p, NULL);
|
|
}
|
|
if (kw) {
|
|
r_search_kw_add (core->search, kw);
|
|
//eprintf ("Searching %d bytes...\n", kw->keyword_length);
|
|
r_search_begin (core->search);
|
|
dosearch = true;
|
|
} else {
|
|
eprintf ("no keyword\n");
|
|
}
|
|
free (p);
|
|
}
|
|
break;
|
|
case 'c': /* search asm */
|
|
if (input[1] == '?') {
|
|
const char* help_msg[] = {
|
|
"Usage:", "/c [inst]", " Search for asm",
|
|
"/c ", "instr", "search for instruction 'instr'",
|
|
"/c/ ", "instr", "search for instruction that matches regexp 'instr'",
|
|
"/c ", "instr1;instr2", "search for instruction 'instr1' followed by 'instr2'",
|
|
"/c/ ", "instr1;instr2", "search for regex instruction 'instr1' followed by regex 'instr2'",
|
|
"/cj ", "instr", "json output",
|
|
"/c/j ", "instr", "regex search with json output",
|
|
"/c* ", "instr", "r2 command output",
|
|
NULL};
|
|
r_core_cmd_help (core, help_msg);
|
|
}
|
|
do_asm_search (core, ¶m, input);
|
|
dosearch = 0;
|
|
break;
|
|
case '+':
|
|
if (input[1]==' ') {
|
|
// TODO: support /+j
|
|
char *buf = malloc (strlen (input)*2);
|
|
char *str = strdup (input+2);
|
|
int ochunksize;
|
|
int i, len, chunksize = r_config_get_i (core->config, "search.chunk");
|
|
if (chunksize<1) {
|
|
chunksize = core->assembler->bits / 8;
|
|
}
|
|
len = r_str_unescape (str);
|
|
ochunksize = chunksize = R_MIN (len, chunksize);
|
|
eprintf ("Using chunksize: %d\n", chunksize);
|
|
core->in_search = false;
|
|
for (i=0; i<len; i += chunksize) {
|
|
chunksize = ochunksize;
|
|
again:
|
|
r_hex_bin2str ((ut8*)str+i, R_MIN (chunksize, len-i), buf);
|
|
eprintf ("/x %s\n", buf);
|
|
r_core_cmdf (core, "/x %s", buf);
|
|
if (core->num->value == 0) {
|
|
chunksize--;
|
|
if (chunksize<1) {
|
|
eprintf ("Oops\n");
|
|
free (buf);
|
|
free (str);
|
|
goto beach;
|
|
}
|
|
eprintf ("Repeat with chunk size %d\n", chunksize);
|
|
goto again;
|
|
}
|
|
}
|
|
free (str);
|
|
free (buf);
|
|
} else {
|
|
eprintf ("Usage: /+ [string]\n");
|
|
}
|
|
break;
|
|
case 'z': /* search asm */
|
|
{
|
|
char *p;
|
|
ut32 min, max;
|
|
if (!input[1]) {
|
|
eprintf ("Usage: /z min max\n");
|
|
break;
|
|
}
|
|
if ((p = strchr (input+2, ' '))) {
|
|
*p = 0;
|
|
max = r_num_math (core->num, p+1);
|
|
} else {
|
|
eprintf ("Usage: /z min max\n");
|
|
break;
|
|
}
|
|
min = r_num_math (core->num, input+2);
|
|
if (!r_search_set_string_limits (core->search, min, max)) {
|
|
eprintf ("Error: min must be lower than max\n");
|
|
break;
|
|
}
|
|
r_search_reset (core->search, R_SEARCH_STRING);
|
|
r_search_set_distance (core->search, (int)
|
|
r_config_get_i (core->config, "search.distance"));
|
|
r_search_kw_add (core->search,
|
|
r_search_keyword_new_hexmask ("00", NULL)); //XXX
|
|
r_search_begin (core->search);
|
|
dosearch = true;
|
|
}
|
|
break;
|
|
case '?':{
|
|
const char* help_msg[] = {
|
|
"Usage:", "/[amx/] [arg]", "Search stuff (see 'e??search' for options)",
|
|
"/"," foo\\x00", "search for string 'foo\\0'",
|
|
"/j"," foo\\x00", "search for string 'foo\\0' (json output)",
|
|
"/!", " ff", "search for first occurrence not matching",
|
|
"/+", " /bin/sh", "construct the string with chunks",
|
|
"/!x", " 00", "inverse hexa search (find first byte != 0x00)",
|
|
"//", "", "repeat last search",
|
|
"/#", "[t] [hash] [len]", "find block matching this hash. See /#?",
|
|
"/a", " jmp eax", "assemble opcode and search its bytes",
|
|
"/A", " jmp", "find analyzed instructions of this type (/A? for help)",
|
|
"/b", "", "search backwards",
|
|
"/B", "", "search recognized RBin headers",
|
|
"/c", " jmp [esp]", "search for asm code",
|
|
"/C", "[ar]", "search for crypto materials",
|
|
"/d", " 101112", "search for a deltified sequence of bytes",
|
|
"/e", " /E.F/i", "match regular expression",
|
|
"/E", " esil-expr", "offset matching given esil expressions %%= here ",
|
|
"/i", " foo", "search for string 'foo' ignoring case",
|
|
"/m", " magicfile", "search for matching magic file (use blocksize)",
|
|
"/p", " patternsize", "search for pattern of given size",
|
|
"/P", "", "show offset of previous instruction",
|
|
"/r", " sym.printf", "analyze opcode reference an offset",
|
|
"/R", " [grepopcode]", "search for matching ROP gadgets, semicolon-separated",
|
|
"/v", "[1248] value", "look for an `asm.bigendian` 32bit value",
|
|
"/V", "[1248] min max", "look for an `asm.bigendian` 32bit value in range",
|
|
"/w", " foo", "search for wide string 'f\\0o\\0o\\0'",
|
|
"/wi", " foo", "search for wide string ignoring case 'f\\0o\\0o\\0'",
|
|
"/x"," ff..33", "search for hex string ignoring some nibbles",
|
|
"/x"," ff0033", "search for hex string",
|
|
"/x"," ff43 ffd0", "search for hexpair with mask",
|
|
"/z"," min max", "search for strings of given size",
|
|
#if 0
|
|
"\nConfiguration:", "", " (type `e??search.` for a complete list)",
|
|
"e", " cmd.hit = x", "command to execute on every search hit",
|
|
"e", " search.in = ?", "specify where to search stuff (depends on .from/.to)",
|
|
"e", " search.align = 4", "only catch aligned search hits",
|
|
"e", " search.from = 0", "start address",
|
|
"e", " search.to = 0", "end address",
|
|
"e", " search.flags = true", "if enabled store flags on keyword hits",
|
|
#endif
|
|
NULL};
|
|
r_core_cmd_help (core, help_msg);
|
|
}
|
|
break;
|
|
default:
|
|
eprintf ("See /? for help.\n");
|
|
break;
|
|
}
|
|
searchhits = 0;
|
|
r_config_set_i (core->config, "search.kwidx", core->search->n_kws);
|
|
if (dosearch)
|
|
do_string_search (core, ¶m);
|
|
beach:
|
|
core->num->value = searchhits;
|
|
core->in_search = false;
|
|
r_flag_space_pop (core->flags);
|
|
if (json) {
|
|
r_cons_newline ();
|
|
}
|
|
return ret;
|
|
}
|