717 lines
19 KiB
C
717 lines
19 KiB
C
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_analysis.h>
|
|
#include <rz_util.h>
|
|
#include <rz_list.h>
|
|
#include <rz_io.h>
|
|
#include <config.h>
|
|
|
|
RZ_LIB_VERSION(rz_analysis);
|
|
|
|
static RzAnalysisPlugin *analysis_static_plugins[] = {
|
|
RZ_ANALYSIS_STATIC_PLUGINS
|
|
};
|
|
|
|
RZ_API void rz_analysis_set_limits(RzAnalysis *analysis, ut64 from, ut64 to) {
|
|
free (analysis->limit);
|
|
analysis->limit = RZ_NEW0 (RzAnalysisRange);
|
|
if (analysis->limit) {
|
|
analysis->limit->from = from;
|
|
analysis->limit->to = to;
|
|
}
|
|
}
|
|
|
|
RZ_API void rz_analysis_unset_limits(RzAnalysis *analysis) {
|
|
RZ_FREE (analysis->limit);
|
|
}
|
|
|
|
static void meta_unset_for(RzEvent *ev, int type, void *user, void *data) {
|
|
RzSpaces *s = (RzSpaces *)ev->user;
|
|
RzAnalysis *analysis = container_of (s, RzAnalysis, meta_spaces);
|
|
RzSpaceEvent *se = (RzSpaceEvent *)data;
|
|
rz_meta_space_unset_for (analysis, se->data.unset.space);
|
|
}
|
|
|
|
static void meta_count_for(RzEvent *ev, int type, void *user, void *data) {
|
|
RzSpaces *s = (RzSpaces *)ev->user;
|
|
RzAnalysis *analysis = container_of (s, RzAnalysis, meta_spaces);
|
|
RzSpaceEvent *se = (RzSpaceEvent *)data;
|
|
se->res = rz_meta_space_count_for (analysis, se->data.count.space);
|
|
}
|
|
|
|
static void zign_unset_for(RzEvent *ev, int type, void *user, void *data) {
|
|
RzSpaces *s = (RzSpaces *)ev->user;
|
|
RzAnalysis *analysis = container_of (s, RzAnalysis, zign_spaces);
|
|
RzSpaceEvent *se = (RzSpaceEvent *)data;
|
|
rz_sign_space_unset_for (analysis, se->data.unset.space);
|
|
}
|
|
|
|
static void zign_count_for(RzEvent *ev, int type, void *user, void *data) {
|
|
RzSpaces *s = (RzSpaces *)ev->user;
|
|
RzAnalysis *analysis = container_of (s, RzAnalysis, zign_spaces);
|
|
RzSpaceEvent *se = (RzSpaceEvent *)data;
|
|
se->res = rz_sign_space_count_for (analysis, se->data.count.space);
|
|
}
|
|
|
|
static void zign_rename_for(RzEvent *ev, int type, void *user, void *data) {
|
|
RzSpaces *s = (RzSpaces *)ev->user;
|
|
RzAnalysis *analysis = container_of (s, RzAnalysis, zign_spaces);
|
|
RzSpaceEvent *se = (RzSpaceEvent *)data;
|
|
rz_sign_space_rename_for (analysis, se->data.rename.space,
|
|
se->data.rename.oldname, se->data.rename.newname);
|
|
}
|
|
|
|
void rz_analysis_hint_storage_init(RzAnalysis *a);
|
|
void rz_analysis_hint_storage_fini(RzAnalysis *a);
|
|
|
|
static void rz_meta_item_fini(RzAnalysisMetaItem *item) {
|
|
free (item->str);
|
|
}
|
|
|
|
static void rz_meta_item_free(void *_item) {
|
|
if (_item) {
|
|
RzAnalysisMetaItem *item = _item;
|
|
rz_meta_item_fini (item);
|
|
free (item);
|
|
}
|
|
}
|
|
|
|
RZ_API RzAnalysis *rz_analysis_new(void) {
|
|
int i;
|
|
RzAnalysis *analysis = RZ_NEW0 (RzAnalysis);
|
|
if (!analysis) {
|
|
return NULL;
|
|
}
|
|
if (!rz_str_constpool_init (&analysis->constpool)) {
|
|
free (analysis);
|
|
return NULL;
|
|
}
|
|
analysis->bb_tree = NULL;
|
|
analysis->ht_addr_fun = ht_up_new0 ();
|
|
analysis->ht_name_fun = ht_pp_new0 ();
|
|
analysis->os = strdup (RZ_SYS_OS);
|
|
analysis->esil_goto_limit = RZ_ANALYSIS_ESIL_GOTO_LIMIT;
|
|
analysis->opt.nopskip = true; // skip nops in code analysis
|
|
analysis->opt.hpskip = false; // skip `mov reg,reg` and `lea reg,[reg]`
|
|
analysis->gp = 0LL;
|
|
analysis->sdb = sdb_new0 ();
|
|
analysis->cpp_abi = RZ_ANALYSIS_CPP_ABI_ITANIUM;
|
|
analysis->opt.depth = 32;
|
|
analysis->opt.noncode = false; // do not analyze data by default
|
|
rz_spaces_init (&analysis->meta_spaces, "CS");
|
|
rz_event_hook (analysis->meta_spaces.event, RZ_SPACE_EVENT_UNSET, meta_unset_for, NULL);
|
|
rz_event_hook (analysis->meta_spaces.event, RZ_SPACE_EVENT_COUNT, meta_count_for, NULL);
|
|
|
|
rz_spaces_init (&analysis->zign_spaces, "zs");
|
|
rz_event_hook (analysis->zign_spaces.event, RZ_SPACE_EVENT_UNSET, zign_unset_for, NULL);
|
|
rz_event_hook (analysis->zign_spaces.event, RZ_SPACE_EVENT_COUNT, zign_count_for, NULL);
|
|
rz_event_hook (analysis->zign_spaces.event, RZ_SPACE_EVENT_RENAME, zign_rename_for, NULL);
|
|
rz_analysis_hint_storage_init (analysis);
|
|
rz_interval_tree_init (&analysis->meta, rz_meta_item_free);
|
|
analysis->sdb_types = sdb_ns (analysis->sdb, "types", 1);
|
|
analysis->sdb_fmts = sdb_ns (analysis->sdb, "spec", 1);
|
|
analysis->sdb_cc = sdb_ns (analysis->sdb, "cc", 1);
|
|
analysis->sdb_zigns = sdb_ns (analysis->sdb, "zigns", 1);
|
|
analysis->sdb_classes = sdb_ns (analysis->sdb, "classes", 1);
|
|
analysis->sdb_classes_attrs = sdb_ns (analysis->sdb_classes, "attrs", 1);
|
|
analysis->zign_path = strdup ("");
|
|
analysis->cb_printf = (PrintfCallback) printf;
|
|
(void)rz_analysis_pin_init (analysis);
|
|
(void)rz_analysis_xrefs_init (analysis);
|
|
analysis->diff_thbb = RZ_ANALYSIS_THRESHOLDBB;
|
|
analysis->diff_thfcn = RZ_ANALYSIS_THRESHOLDFCN;
|
|
analysis->syscall = rz_syscall_new ();
|
|
rz_io_bind_init (analysis->iob);
|
|
rz_flag_bind_init (analysis->flb);
|
|
analysis->reg = rz_reg_new ();
|
|
analysis->last_disasm_reg = NULL;
|
|
analysis->stackptr = 0;
|
|
analysis->lineswidth = 0;
|
|
analysis->fcns = rz_list_newf (rz_analysis_function_free);
|
|
analysis->leaddrs = NULL;
|
|
analysis->imports = rz_list_newf (free);
|
|
rz_analysis_set_bits (analysis, 32);
|
|
analysis->plugins = rz_list_newf ((RzListFree) rz_analysis_plugin_free);
|
|
if (analysis->plugins) {
|
|
for (i = 0; analysis_static_plugins[i]; i++) {
|
|
rz_analysis_add (analysis, analysis_static_plugins[i]);
|
|
}
|
|
}
|
|
return analysis;
|
|
}
|
|
|
|
RZ_API void rz_analysis_plugin_free (RzAnalysisPlugin *p) {
|
|
if (p && p->fini) {
|
|
p->fini (NULL);
|
|
}
|
|
}
|
|
|
|
void __block_free_rb(RBNode *node, void *user);
|
|
|
|
RZ_API RzAnalysis *rz_analysis_free(RzAnalysis *a) {
|
|
if (!a) {
|
|
return NULL;
|
|
}
|
|
/* TODO: Free anals here */
|
|
rz_list_free (a->fcns);
|
|
ht_up_free (a->ht_addr_fun);
|
|
ht_pp_free (a->ht_name_fun);
|
|
set_u_free (a->visited);
|
|
rz_analysis_hint_storage_fini (a);
|
|
rz_interval_tree_fini (&a->meta);
|
|
free (a->cpu);
|
|
free (a->os);
|
|
free (a->zign_path);
|
|
rz_list_free (a->plugins);
|
|
rz_rbtree_free (a->bb_tree, __block_free_rb, NULL);
|
|
rz_spaces_fini (&a->meta_spaces);
|
|
rz_spaces_fini (&a->zign_spaces);
|
|
rz_analysis_pin_fini (a);
|
|
rz_syscall_free (a->syscall);
|
|
rz_reg_free (a->reg);
|
|
ht_up_free (a->dict_refs);
|
|
ht_up_free (a->dict_xrefs);
|
|
rz_list_free (a->leaddrs);
|
|
sdb_free (a->sdb);
|
|
if (a->esil) {
|
|
rz_analysis_esil_free (a->esil);
|
|
a->esil = NULL;
|
|
}
|
|
free (a->last_disasm_reg);
|
|
rz_list_free (a->imports);
|
|
rz_str_constpool_fini (&a->constpool);
|
|
free (a);
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API void rz_analysis_set_user_ptr(RzAnalysis *analysis, void *user) {
|
|
analysis->user = user;
|
|
}
|
|
|
|
RZ_API int rz_analysis_add(RzAnalysis *analysis, RzAnalysisPlugin *foo) {
|
|
if (foo->init) {
|
|
foo->init (analysis->user);
|
|
}
|
|
rz_list_append (analysis->plugins, foo);
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_analysis_use(RzAnalysis *analysis, const char *name) {
|
|
RzListIter *it;
|
|
RzAnalysisPlugin *h;
|
|
|
|
if (analysis) {
|
|
rz_list_foreach (analysis->plugins, it, h) {
|
|
if (!h->name || strcmp (h->name, name)) {
|
|
continue;
|
|
}
|
|
#if 0
|
|
// regression happening here for asm.emu
|
|
if (analysis->cur && analysis->cur == h) {
|
|
return true;
|
|
}
|
|
#endif
|
|
analysis->cur = h;
|
|
rz_analysis_set_reg_profile (analysis);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API char *rz_analysis_get_reg_profile(RzAnalysis *analysis) {
|
|
return (analysis && analysis->cur && analysis->cur->get_reg_profile)
|
|
? analysis->cur->get_reg_profile (analysis) : NULL;
|
|
}
|
|
|
|
// deprecate.. or at least reuse get_reg_profile...
|
|
RZ_API bool rz_analysis_set_reg_profile(RzAnalysis *analysis) {
|
|
bool ret = false;
|
|
if (analysis && analysis->cur && analysis->cur->set_reg_profile) {
|
|
ret = analysis->cur->set_reg_profile (analysis);
|
|
} else {
|
|
char *p = rz_analysis_get_reg_profile (analysis);
|
|
if (p && *p) {
|
|
rz_reg_set_profile_string (analysis->reg, p);
|
|
ret = true;
|
|
}
|
|
free (p);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
RZ_API bool rz_analysis_set_triplet(RzAnalysis *analysis, const char *os, const char *arch, int bits) {
|
|
rz_return_val_if_fail (analysis, false);
|
|
if (!os || !*os) {
|
|
os = RZ_SYS_OS;
|
|
}
|
|
if (!arch || !*arch) {
|
|
arch = analysis->cur? analysis->cur->arch: RZ_SYS_ARCH;
|
|
}
|
|
if (bits < 1) {
|
|
bits = analysis->bits;
|
|
}
|
|
free (analysis->os);
|
|
analysis->os = strdup (os);
|
|
rz_analysis_set_bits (analysis, bits);
|
|
return rz_analysis_use (analysis, arch);
|
|
}
|
|
|
|
// copypasta from core/cbin.c
|
|
static void sdb_concat_by_path(Sdb *s, const char *path) {
|
|
Sdb *db = sdb_new (0, path, 0);
|
|
sdb_merge (s, db);
|
|
sdb_close (db);
|
|
sdb_free (db);
|
|
}
|
|
|
|
RZ_API bool rz_analysis_set_os(RzAnalysis *analysis, const char *os) {
|
|
Sdb *types = analysis->sdb_types;
|
|
const char *dir_prefix = rz_sys_prefix (NULL);
|
|
const char *dbpath = sdb_fmt (RZ_JOIN_3_PATHS ("%s", RZ_SDB_FCNSIGN, "types-%s.sdb"),
|
|
dir_prefix, os);
|
|
if (rz_file_exists (dbpath)) {
|
|
sdb_concat_by_path (types, dbpath);
|
|
}
|
|
return rz_analysis_set_triplet (analysis, os, NULL, -1);
|
|
}
|
|
|
|
RZ_API bool rz_analysis_set_bits(RzAnalysis *analysis, int bits) {
|
|
switch (bits) {
|
|
case 8:
|
|
case 16:
|
|
case 27:
|
|
case 32:
|
|
case 64:
|
|
if (analysis->bits != bits) {
|
|
analysis->bits = bits;
|
|
rz_analysis_set_reg_profile (analysis);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API void rz_analysis_set_cpu(RzAnalysis *analysis, const char *cpu) {
|
|
free (analysis->cpu);
|
|
analysis->cpu = cpu ? strdup (cpu) : NULL;
|
|
int v = rz_analysis_archinfo (analysis, RZ_ANALYSIS_ARCHINFO_ALIGN);
|
|
if (v != -1) {
|
|
analysis->pcalign = v;
|
|
}
|
|
}
|
|
|
|
RZ_API int rz_analysis_set_big_endian(RzAnalysis *analysis, int bigend) {
|
|
analysis->big_endian = bigend;
|
|
analysis->reg->big_endian = bigend;
|
|
return true;
|
|
}
|
|
|
|
RZ_API ut8 *rz_analysis_mask(RzAnalysis *analysis, int size, const ut8 *data, ut64 at) {
|
|
RzAnalysisOp *op = NULL;
|
|
ut8 *ret = NULL;
|
|
int oplen, idx = 0;
|
|
|
|
if (!data) {
|
|
return NULL;
|
|
}
|
|
|
|
if (analysis->cur && analysis->cur->analysis_mask) {
|
|
return analysis->cur->analysis_mask (analysis, size, data, at);
|
|
}
|
|
|
|
if (!(op = rz_analysis_op_new ())) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!(ret = malloc (size))) {
|
|
rz_analysis_op_free (op);
|
|
return NULL;
|
|
}
|
|
|
|
memset (ret, 0xff, size);
|
|
|
|
while (idx < size) {
|
|
if ((oplen = rz_analysis_op (analysis, op, at, data + idx, size - idx, RZ_ANALYSIS_OP_MASK_BASIC)) < 1) {
|
|
break;
|
|
}
|
|
if ((op->ptr != UT64_MAX || op->jump != UT64_MAX) && op->nopcode != 0) {
|
|
memset (ret + idx + op->nopcode, 0, oplen - op->nopcode);
|
|
}
|
|
idx += oplen;
|
|
at += oplen;
|
|
}
|
|
|
|
rz_analysis_op_free (op);
|
|
|
|
return ret;
|
|
}
|
|
|
|
RZ_API void rz_analysis_trace_bb(RzAnalysis *analysis, ut64 addr) {
|
|
RzAnalysisBlock *bbi;
|
|
RzAnalysisFunction *fcni;
|
|
RzListIter *iter2;
|
|
fcni = rz_analysis_get_fcn_in (analysis, addr, 0);
|
|
if (fcni) {
|
|
rz_list_foreach (fcni->bbs, iter2, bbi) {
|
|
if (addr >= bbi->addr && addr < (bbi->addr + bbi->size)) {
|
|
bbi->traced = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_API RzList* rz_analysis_get_fcns (RzAnalysis *analysis) {
|
|
// avoid received to free this thing
|
|
analysis->fcns->free = NULL;
|
|
return analysis->fcns;
|
|
}
|
|
|
|
RZ_API RzAnalysisOp *rz_analysis_op_hexstr(RzAnalysis *analysis, ut64 addr, const char *str) {
|
|
RzAnalysisOp *op = RZ_NEW0 (RzAnalysisOp);
|
|
if (!op) {
|
|
return NULL;
|
|
}
|
|
ut8 *buf = calloc (1, strlen (str) + 1);
|
|
if (!buf) {
|
|
free (op);
|
|
return NULL;
|
|
}
|
|
int len = rz_hex_str2bin (str, buf);
|
|
rz_analysis_op (analysis, op, addr, buf, len, RZ_ANALYSIS_OP_MASK_BASIC);
|
|
free (buf);
|
|
return op;
|
|
}
|
|
|
|
RZ_API bool rz_analysis_op_is_eob(RzAnalysisOp *op) {
|
|
if (op->eob) {
|
|
return true;
|
|
}
|
|
switch (op->type) {
|
|
case RZ_ANALYSIS_OP_TYPE_JMP:
|
|
case RZ_ANALYSIS_OP_TYPE_UJMP:
|
|
case RZ_ANALYSIS_OP_TYPE_RJMP:
|
|
case RZ_ANALYSIS_OP_TYPE_IJMP:
|
|
case RZ_ANALYSIS_OP_TYPE_IRJMP:
|
|
case RZ_ANALYSIS_OP_TYPE_CJMP:
|
|
case RZ_ANALYSIS_OP_TYPE_RET:
|
|
case RZ_ANALYSIS_OP_TYPE_TRAP:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
RZ_API void rz_analysis_purge(RzAnalysis *analysis) {
|
|
rz_analysis_hint_clear (analysis);
|
|
rz_interval_tree_fini (&analysis->meta);
|
|
rz_interval_tree_init (&analysis->meta, rz_meta_item_free);
|
|
sdb_reset (analysis->sdb_types);
|
|
sdb_reset (analysis->sdb_zigns);
|
|
sdb_reset (analysis->sdb_classes);
|
|
sdb_reset (analysis->sdb_classes_attrs);
|
|
rz_analysis_pin_fini (analysis);
|
|
rz_analysis_pin_init (analysis);
|
|
sdb_reset (analysis->sdb_cc);
|
|
rz_list_free (analysis->fcns);
|
|
analysis->fcns = rz_list_newf (rz_analysis_function_free);
|
|
rz_analysis_purge_imports (analysis);
|
|
}
|
|
|
|
RZ_API int rz_analysis_archinfo(RzAnalysis *analysis, int query) {
|
|
rz_return_val_if_fail (analysis, -1);
|
|
switch (query) {
|
|
case RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE:
|
|
case RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE:
|
|
case RZ_ANALYSIS_ARCHINFO_ALIGN:
|
|
if (analysis->cur && analysis->cur->archinfo) {
|
|
return analysis->cur->archinfo (analysis, query);
|
|
}
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static bool __nonreturn_print_commands(void *p, const char *k, const char *v) {
|
|
RzAnalysis *analysis = (RzAnalysis *)p;
|
|
if (!strncmp (v, "func", strlen ("func") + 1)) {
|
|
char *query = sdb_fmt ("func.%s.noreturn", k);
|
|
if (sdb_bool_get (analysis->sdb_types, query, NULL)) {
|
|
analysis->cb_printf ("tnn %s\n", k);
|
|
}
|
|
}
|
|
if (!strncmp (k, "addr.", 5)) {
|
|
analysis->cb_printf ("tna 0x%s %s\n", k + 5, v);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool __nonreturn_print(void *p, const char *k, const char *v) {
|
|
RzAnalysis *analysis = (RzAnalysis *)p;
|
|
if (!strncmp (k, "func.", 5) && strstr (k, ".noreturn")) {
|
|
char *s = strdup (k + 5);
|
|
char *d = strchr (s, '.');
|
|
if (d) {
|
|
*d = 0;
|
|
}
|
|
analysis->cb_printf ("%s\n", s);
|
|
free (s);
|
|
}
|
|
if (!strncmp (k, "addr.", 5)) {
|
|
char *off;
|
|
if (!(off = strdup (k + 5))) {
|
|
return 1;
|
|
}
|
|
char *ptr = strstr (off, ".noreturn");
|
|
if (ptr) {
|
|
*ptr = 0;
|
|
analysis->cb_printf ("0x%s\n", off);
|
|
}
|
|
free (off);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RZ_API void rz_analysis_noreturn_list(RzAnalysis *analysis, int mode) {
|
|
switch (mode) {
|
|
case 1:
|
|
case '*':
|
|
case 'r':
|
|
sdb_foreach (analysis->sdb_types, __nonreturn_print_commands, analysis);
|
|
break;
|
|
default:
|
|
sdb_foreach (analysis->sdb_types, __nonreturn_print, analysis);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define K_NORET_ADDR(x) sdb_fmt ("addr.%"PFMT64x".noreturn", x)
|
|
#define K_NORET_FUNC(x) sdb_fmt ("func.%s.noreturn", x)
|
|
|
|
RZ_API bool rz_analysis_noreturn_add(RzAnalysis *analysis, const char *name, ut64 addr) {
|
|
const char *tmp_name = NULL;
|
|
Sdb *TDB = analysis->sdb_types;
|
|
char *fnl_name = NULL;
|
|
if (addr != UT64_MAX) {
|
|
if (sdb_bool_set (TDB, K_NORET_ADDR (addr), true, 0)) {
|
|
RzAnalysisFunction *fcn = rz_analysis_get_function_at (analysis, addr);
|
|
if (fcn) {
|
|
fcn->is_noreturn = true;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
if (name && *name) {
|
|
tmp_name = name;
|
|
} else {
|
|
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in (analysis, addr, -1);
|
|
RzFlagItem *fi = analysis->flb.get_at (analysis->flb.f, addr, false);
|
|
if (!fcn && !fi) {
|
|
eprintf ("Can't find Function at given address\n");
|
|
return false;
|
|
}
|
|
tmp_name = fcn ? fcn->name: fi->name;
|
|
if (fcn) {
|
|
fcn->is_noreturn = true;
|
|
}
|
|
}
|
|
if (rz_type_func_exist (TDB, tmp_name)) {
|
|
fnl_name = strdup (tmp_name);
|
|
} else if (!(fnl_name = rz_type_func_guess (TDB, (char *)tmp_name))) {
|
|
if (addr == UT64_MAX) {
|
|
if (name) {
|
|
sdb_bool_set (TDB, K_NORET_FUNC (name), true, 0);
|
|
} else {
|
|
eprintf ("Can't find prototype for: %s\n", tmp_name);
|
|
}
|
|
} else {
|
|
eprintf ("Can't find prototype for: %s\n", tmp_name);
|
|
}
|
|
//return false;
|
|
}
|
|
if (fnl_name) {
|
|
sdb_bool_set (TDB, K_NORET_FUNC (fnl_name), true, 0);
|
|
free (fnl_name);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
RZ_API bool rz_analysis_noreturn_drop(RzAnalysis *analysis, const char *expr) {
|
|
Sdb *TDB = analysis->sdb_types;
|
|
expr = rz_str_trim_head_ro (expr);
|
|
const char *fcnname = NULL;
|
|
if (!strncmp (expr, "0x", 2)) {
|
|
ut64 n = rz_num_math (NULL, expr);
|
|
sdb_unset (TDB, K_NORET_ADDR (n), 0);
|
|
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in (analysis, n, -1);
|
|
if (!fcn) {
|
|
// eprintf ("can't find function at 0x%"PFMT64x"\n", n);
|
|
return false;
|
|
}
|
|
fcnname = fcn->name;
|
|
} else {
|
|
fcnname = expr;
|
|
}
|
|
sdb_unset (TDB, K_NORET_FUNC (fcnname), 0);
|
|
#if 0
|
|
char *tmp;
|
|
// unnsecessary checks, imho the noreturn db should be pretty simple to allow forward and custom declarations without having to define the function prototype before
|
|
if (rz_type_func_exist (TDB, fcnname)) {
|
|
sdb_unset (TDB, K_NORET_FUNC (fcnname), 0);
|
|
return true;
|
|
} else if ((tmp = rz_type_func_guess (TDB, (char *)fcnname))) {
|
|
sdb_unset (TDB, K_NORET_FUNC (fcnname), 0);
|
|
free (tmp);
|
|
return true;
|
|
}
|
|
eprintf ("Can't find prototype for %s in types database", fcnname);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
static bool rz_analysis_noreturn_at_name(RzAnalysis *analysis, const char *name) {
|
|
if (sdb_bool_get (analysis->sdb_types, K_NORET_FUNC(name), NULL)) {
|
|
return true;
|
|
}
|
|
char *tmp = rz_type_func_guess (analysis->sdb_types, (char *)name);
|
|
if (tmp) {
|
|
if (sdb_bool_get (analysis->sdb_types, K_NORET_FUNC (tmp), NULL)) {
|
|
free (tmp);
|
|
return true;
|
|
}
|
|
free (tmp);
|
|
}
|
|
if (rz_str_startswith (name, "reloc.")) {
|
|
return rz_analysis_noreturn_at_name (analysis, name + 6);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API bool rz_analysis_noreturn_at_addr(RzAnalysis *analysis, ut64 addr) {
|
|
return sdb_bool_get (analysis->sdb_types, K_NORET_ADDR (addr), NULL);
|
|
}
|
|
|
|
static bool noreturn_recurse(RzAnalysis *analysis, ut64 addr) {
|
|
RzAnalysisOp op = {0};
|
|
ut8 bbuf[0x10] = {0};
|
|
ut64 recurse_addr = UT64_MAX;
|
|
if (!analysis->iob.read_at (analysis->iob.io, addr, bbuf, sizeof (bbuf))) {
|
|
eprintf ("Couldn't read buffer\n");
|
|
return false;
|
|
}
|
|
if (rz_analysis_op (analysis, &op, addr, bbuf, sizeof (bbuf), RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_VAL) < 1) {
|
|
return false;
|
|
}
|
|
switch (op.type & RZ_ANALYSIS_OP_TYPE_MASK) {
|
|
case RZ_ANALYSIS_OP_TYPE_JMP:
|
|
if (op.jump == UT64_MAX) {
|
|
recurse_addr = op.ptr;
|
|
} else {
|
|
recurse_addr = op.jump;
|
|
}
|
|
break;
|
|
case RZ_ANALYSIS_OP_TYPE_UCALL:
|
|
case RZ_ANALYSIS_OP_TYPE_RCALL:
|
|
case RZ_ANALYSIS_OP_TYPE_ICALL:
|
|
case RZ_ANALYSIS_OP_TYPE_IRCALL:
|
|
recurse_addr = op.ptr;
|
|
break;
|
|
case RZ_ANALYSIS_OP_TYPE_CCALL:
|
|
case RZ_ANALYSIS_OP_TYPE_CALL:
|
|
recurse_addr = op.jump;
|
|
break;
|
|
}
|
|
if (recurse_addr == UT64_MAX || recurse_addr == addr) {
|
|
return false;
|
|
}
|
|
return rz_analysis_noreturn_at (analysis, recurse_addr);
|
|
}
|
|
|
|
RZ_API bool rz_analysis_noreturn_at(RzAnalysis *analysis, ut64 addr) {
|
|
if (!addr || addr == UT64_MAX) {
|
|
return false;
|
|
}
|
|
if (rz_analysis_noreturn_at_addr (analysis, addr)) {
|
|
return true;
|
|
}
|
|
/* XXX this is very slow */
|
|
RzAnalysisFunction *f = rz_analysis_get_function_at (analysis, addr);
|
|
if (f) {
|
|
if (rz_analysis_noreturn_at_name (analysis, f->name)) {
|
|
return true;
|
|
}
|
|
}
|
|
RzFlagItem *fi = analysis->flag_get (analysis->flb.f, addr);
|
|
if (fi) {
|
|
if (rz_analysis_noreturn_at_name (analysis, fi->realname ? fi->realname : fi->name)) {
|
|
return true;
|
|
}
|
|
}
|
|
if (analysis->recursive_noreturn) {
|
|
return noreturn_recurse (analysis, addr);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API void rz_analysis_bind(RzAnalysis *analysis, RzAnalysisBind *b) {
|
|
if (b) {
|
|
b->analysis = analysis;
|
|
b->get_fcn_in = rz_analysis_get_fcn_in;
|
|
b->get_hint = rz_analysis_hint_get;
|
|
}
|
|
}
|
|
|
|
RZ_API RzList *rz_analysis_preludes(RzAnalysis *analysis) {
|
|
if (analysis->cur && analysis->cur->preludes ) {
|
|
return analysis->cur->preludes (analysis);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API bool rz_analysis_is_prelude(RzAnalysis *analysis, const ut8 *data, int len) {
|
|
RzList *l = rz_analysis_preludes (analysis);
|
|
if (l) {
|
|
RzSearchKeyword *kw;
|
|
RzListIter *iter;
|
|
rz_list_foreach (l, iter, kw) {
|
|
int ks = kw->keyword_length;
|
|
if (len >= ks && !memcmp (data, kw->bin_keyword, ks)) {
|
|
rz_list_free (l);
|
|
return true;
|
|
}
|
|
}
|
|
rz_list_free (l);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
RZ_API void rz_analysis_add_import(RzAnalysis *analysis, const char *imp) {
|
|
RzListIter *it;
|
|
const char *eimp;
|
|
rz_list_foreach (analysis->imports, it, eimp) {
|
|
if (!strcmp (eimp, imp)) {
|
|
return;
|
|
}
|
|
}
|
|
char *cimp = strdup (imp);
|
|
if (!cimp) {
|
|
return;
|
|
}
|
|
rz_list_push (analysis->imports, cimp);
|
|
}
|
|
|
|
RZ_API void rz_analysis_remove_import(RzAnalysis *analysis, const char *imp) {
|
|
RzListIter *it;
|
|
const char *eimp;
|
|
rz_list_foreach (analysis->imports, it, eimp) {
|
|
if (!strcmp (eimp, imp)) {
|
|
rz_list_delete (analysis->imports, it);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_API void rz_analysis_purge_imports(RzAnalysis *analysis) {
|
|
rz_list_purge (analysis->imports);
|
|
}
|