rizin/librz/core/canalysis.c
NOT XVilka f91ef729bf
Improve ARM/AArch64 DATA xrefs analysis (#6506)
Co-authored-by: Anton Kochkov <anton.kochkov@gmail.com>
2026-06-14 21:10:30 +08:00

6381 lines
186 KiB
C

// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2009-2020 nibble <nibble.ds@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <string.h>
#include <rz_types.h>
#include <rz_list.h>
#include <rz_flag.h>
#include <rz_core.h>
#include <rz_bin.h>
#include <rz_util/ht_uu.h>
#include <rz_util/rz_graph_drawable.h>
#include <rz_util/rz_path.h>
#include "core_private.h"
#include "rz_util/rz_iterator.h"
HEAPTYPE(ut64);
// used to speedup strcmp with rconfig.get in loops
enum {
RZ_ARCH_THUMB,
RZ_ARCH_ARM32,
RZ_ARCH_ARM64,
RZ_ARCH_MIPS
};
// 128M
#define MAX_SCAN_SIZE 0x7ffffff
static void loganalysis(ut64 from, ut64 to, int depth) {
rz_cons_clear_line(stderr);
eprintf("0x%08" PFMT64x " > 0x%08" PFMT64x " %d\r", from, to, depth);
}
RZ_IPI int bb_cmpaddr(const void *_a, const void *_b, void *user) {
const RzAnalysisBlock *a = _a, *b = _b;
return (a->addr > b->addr) - (a->addr < b->addr);
}
RZ_IPI int fcn_cmpaddr(const void *_a, const void *_b, void *user) {
const RzAnalysisFunction *a = _a, *b = _b;
return (a->addr > b->addr) - (a->addr < b->addr);
}
static char *getFunctionName(RzCore *core, ut64 addr) {
RzBinFile *bf = rz_bin_cur(core->bin);
RzBinSymbol *sym = bf && bf->o ? rz_bin_object_find_method_by_vaddr(bf->o, addr) : NULL;
if (sym && sym->classname && sym->name) {
return rz_str_newf("method.%s.%s", sym->classname, sym->name);
}
RzFlagItem *flag = rz_core_flag_get_by_spaces(core->flags, addr);
return (flag && flag->name) ? rz_str_dup(flag->name) : NULL;
}
static char *getFunctionNamePrefix(RzCore *core, ut64 off, const char *name) {
RzReg *rreg = rz_analysis_get_reg(core->analysis);
if (rz_reg_get(rreg, name, -1)) {
return rz_str_newf("%s.%08" PFMT64x, "fcn", off);
}
return rz_str_dup(name);
}
static bool find_string_at(RzCore *core, RzBinObject *bobj, ut64 pointer, char **string, size_t *length, RzStrEnc *encoding) {
RzBin *bin = core->bin;
ut8 buffer[512] = { 0 };
bool ret = false;
RzDetectedString *detected = NULL;
bool big_endian = rz_asm_is_big_endian_set(core->rasm);
RzList *strings = rz_list_newf((RzListFree)rz_detected_string_free);
if (!strings) {
return false;
}
RzStrEnc strenc = bin->str_search_cfg.string_encoding;
RzUtilStrScanOptions scan_opt = {
.max_str_length = sizeof(buffer),
.min_str_length = bin->str_search_cfg.min_length,
.prefer_big_endian = big_endian,
.check_ascii_freq = bin->str_search_cfg.check_ascii_freq,
.user_unprintable = bin->str_search_cfg.user_unprintable,
};
rz_io_pread_at(core->io, pointer, buffer, sizeof(buffer));
if (rz_scan_strings_raw(buffer, strings, &scan_opt, 0, sizeof(buffer), strenc) < 1 ||
!(detected = rz_list_first_val(strings)) ||
// ignore any address that is not address 0
// because we only want strings starting at 0
detected->addr) {
goto end;
}
if (string) {
*string = detected->string;
detected->string = NULL;
}
if (length) {
*length = detected->size;
}
if (encoding) {
*encoding = detected->encoding;
}
ret = true;
end:
rz_list_free(strings);
return ret;
}
RZ_IPI bool rz_core_get_string_at(RzCore *core, ut64 address, char **string, size_t *length, RzStrEnc *encoding, bool can_search) {
ut8 tmp64[32] = { 0 };
ut64 pointer = UT64_MAX, paddress = 0;
RzIOMap *map = NULL;
RzBinString *bstr = NULL;
bool big_endian = rz_analysis_is_big_endian_set(core->analysis);
int bits = rz_analysis_get_bits(core->analysis);
RzBinObject *bobj = rz_bin_cur_object(core->bin);
if (!bobj) {
return false;
}
map = rz_io_map_get(core->io, address);
if (map && (map->perm & RZ_PERM_RX) != RZ_PERM_RX && (map->perm & RZ_PERM_X)) {
return false;
}
if (core->io->va && (paddress = rz_io_v2p(core->io, address)) != UT64_MAX) {
address = paddress;
}
if (rz_io_read_at_mapped(core->io, address, tmp64, sizeof(tmp64))) {
// checks if is a pointer to a string structure
pointer = rz_read_ble(tmp64, big_endian, bits);
}
bstr = rz_bin_object_get_string_at(bobj, address, false);
if (!bstr) {
if (!pointer) {
// maybe is a cstring
// usually a cstring has a header set to 0, then the length and then the actual pointer to the string.
ut32 n_bytes = bits / 8;
ut64 clength = rz_read_ble(&tmp64[n_bytes], big_endian, bits);
if (clength < 1000) {
pointer = rz_read_ble(tmp64, big_endian, bits);
}
}
if (core->io->va && (paddress = rz_io_v2p(core->io, pointer)) != UT64_MAX) {
pointer = paddress;
}
bstr = rz_bin_object_get_string_at(bobj, pointer, false);
if (!bstr) {
return can_search && (find_string_at(core, bobj, address, string, length, encoding) || find_string_at(core, bobj, pointer, string, length, encoding));
}
}
if (string) {
*string = rz_str_ndup(bstr->string, bstr->length);
}
if (length) {
*length = bstr->size;
}
if (encoding) {
*encoding = bstr->type;
}
return true;
}
/* returns the RZ_ANALYSIS_ADDR_TYPE_* of the address 'addr' */
RZ_API ut64 rz_core_analysis_address(RzCore *core, ut64 addr) {
ut64 types = 0;
RzRegSet *rs = NULL;
if (!core) {
return 0;
}
RzReg *rreg = rz_analysis_get_reg(core->analysis);
rs = rz_reg_regset_get(rreg, RZ_REG_TYPE_GPR);
if (rs) {
RzRegItem *r;
RzListIter *iter;
rz_list_foreach (rs->regs, iter, r) {
if (r->type == RZ_REG_TYPE_GPR) {
ut64 val = rz_reg_getv(rreg, r->name);
if (addr == val) {
types |= RZ_ANALYSIS_ADDR_TYPE_REG;
break;
}
}
}
}
if (rz_flag_get_i(core->flags, addr)) {
types |= RZ_ANALYSIS_ADDR_TYPE_FLAG;
}
if (rz_analysis_get_fcn_in(core->analysis, addr, 0)) {
types |= RZ_ANALYSIS_ADDR_TYPE_FUNC;
}
// check registers
if (rz_core_is_debug(core)) {
RzDebugMap *map;
RzListIter *iter;
// use 'dm'
// XXX: this line makes rz debugging MUCH slower
// rz_debug_map_sync (core->dbg);
rz_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 |= RZ_ANALYSIS_ADDR_TYPE_PROGRAM;
} else {
types |= RZ_ANALYSIS_ADDR_TYPE_LIBRARY;
}
}
if (map->perm & RZ_PERM_X) {
types |= RZ_ANALYSIS_ADDR_TYPE_EXEC;
}
if (map->perm & RZ_PERM_R) {
types |= RZ_ANALYSIS_ADDR_TYPE_READ;
}
if (map->perm & RZ_PERM_W) {
types |= RZ_ANALYSIS_ADDR_TYPE_WRITE;
}
// find function
if (map->name && strstr(map->name, "heap")) {
types |= RZ_ANALYSIS_ADDR_TYPE_HEAP;
}
if (map->name && strstr(map->name, "stack")) {
types |= RZ_ANALYSIS_ADDR_TYPE_STACK;
}
break;
}
}
} else {
int _perm = -1;
if (core->io) {
// sections
void **it;
RzPVector *maps = rz_io_maps(core->io);
rz_pvector_foreach (maps, it) {
RzIOMap *s = *it;
if (addr >= s->itv.addr && addr < (s->itv.addr + s->itv.size)) {
// sections overlap, so we want to get the one with lower perms
_perm = (_perm != -1) ? RZ_MIN(_perm, s->perm) : s->perm;
// TODO: we should identify which maps come from the program or other
// types |= RZ_ANALYSIS_ADDR_TYPE_PROGRAM;
// find function those sections should be created by hand or esil init
if (s->name && strstr(s->name, "heap")) {
types |= RZ_ANALYSIS_ADDR_TYPE_HEAP;
}
if (s->name && strstr(s->name, "stack")) {
types |= RZ_ANALYSIS_ADDR_TYPE_STACK;
}
}
}
}
if (_perm != -1) {
if (_perm & RZ_PERM_X) {
types |= RZ_ANALYSIS_ADDR_TYPE_EXEC;
}
if (_perm & RZ_PERM_R) {
types |= RZ_ANALYSIS_ADDR_TYPE_READ;
}
if (_perm & RZ_PERM_W) {
types |= RZ_ANALYSIS_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 |= RZ_ANALYSIS_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 |= RZ_ANALYSIS_ADDR_TYPE_SEQUENCE;
}
}
return types;
}
RZ_IPI void rz_core_analysis_bbs_asciiart(RzCore *core, RzAnalysisFunction *fcn) {
RzList *flist = rz_list_newf((RzListFree)rz_debug_listinfo_free);
if (!flist) {
return;
}
RzAnalysisBlock *b;
void **iter;
rz_pvector_foreach (fcn->bbs, iter) {
b = (RzAnalysisBlock *)*iter;
RzInterval inter = (RzInterval){ b->addr, b->size };
RzDbgListInfo *info = rz_debug_listinfo_new(NULL, inter, inter, -1, NULL);
if (!info) {
break;
}
rz_list_append(flist, info);
}
RzTable *table = rz_core_table(core);
rz_core_debug_listinfo_to_table(table, flist, core->offset, core->blocksize,
rz_cons_get_size(NULL), rz_config_get_i(core->config, "scr.color"));
rz_cons_printf("\n%s\n", rz_table_tostring(table));
rz_table_free(table);
rz_list_free(flist);
}
RZ_IPI void rz_core_analysis_fcn_returns(RzCore *core, RzAnalysisFunction *fcn) {
RzAnalysisBlock *b;
void **iter;
rz_pvector_foreach (fcn->bbs, iter) {
b = (RzAnalysisBlock *)*iter;
if (b->jump == UT64_MAX) {
ut64 retaddr = rz_analysis_block_get_op_addr(b, b->ninstr - 1);
if (retaddr == UT64_MAX) {
break;
}
rz_cons_printf("0x%08" PFMT64x "\n", retaddr);
}
}
}
static int casecmp(const void *_a, const void *_b, void *user) {
const RzAnalysisCaseOp *a = _a;
const RzAnalysisCaseOp *b = _b;
return a->addr != b->addr;
}
static ut64 __opaddr(RzAnalysisBlock *b, ut64 addr) {
int i;
if (addr >= b->addr && addr < (b->addr + b->size)) {
for (i = 0; i < b->ninstr; i++) {
ut64 aa = rz_analysis_block_get_op_addr(b, i);
ut64 ab = rz_analysis_block_get_op_addr(b, i + 1);
if (addr >= aa && addr < ab) {
return aa;
}
}
}
return UT64_MAX;
}
static void bb_info_print(RzCore *core, RzAnalysisFunction *fcn, RzAnalysisBlock *bb,
ut64 addr, RzOutputMode mode, PJ *pj, RzTable *t) {
RzDebugTracepoint *tp = NULL;
int outputs = (bb->jump != UT64_MAX) + (bb->fail != UT64_MAX);
int inputs = 0;
void **iter;
RzAnalysisBlock *bb2;
rz_pvector_foreach (fcn->bbs, iter) {
bb2 = (RzAnalysisBlock *)*iter;
inputs += (bb2->jump == bb->addr) + (bb2->fail == bb->addr);
}
if (bb->switch_op) {
RzList *unique_cases = rz_list_uniq(bb->switch_op->cases, casecmp, NULL);
outputs += rz_list_length(unique_cases);
rz_list_free(unique_cases);
}
ut64 opaddr = __opaddr(bb, addr);
switch (mode) {
case RZ_OUTPUT_MODE_STANDARD:
tp = rz_debug_trace_get(core->dbg, bb->addr);
rz_cons_printf("0x%08" PFMT64x " 0x%08" PFMT64x " %02X:%04X %" PFMT64d,
bb->addr, bb->addr + bb->size,
tp ? tp->times : 0, tp ? tp->count : 0,
bb->size);
if (bb->jump != UT64_MAX) {
rz_cons_printf(" j 0x%08" PFMT64x, bb->jump);
}
if (bb->fail != UT64_MAX) {
rz_cons_printf(" f 0x%08" PFMT64x, bb->fail);
}
if (bb->switch_op) {
RzAnalysisCaseOp *cop;
RzListIter *iter;
RzList *unique_cases = rz_list_uniq(bb->switch_op->cases, casecmp, NULL);
rz_list_foreach (unique_cases, iter, cop) {
rz_cons_printf(" s 0x%08" PFMT64x, cop->addr);
}
rz_list_free(unique_cases);
}
rz_cons_newline();
break;
case RZ_OUTPUT_MODE_JSON: {
pj_o(pj);
if (bb->jump != UT64_MAX) {
pj_kn(pj, "jump", bb->jump);
}
if (bb->fail != UT64_MAX) {
pj_kn(pj, "fail", bb->fail);
}
if (bb->switch_op) {
pj_k(pj, "switch_op");
pj_o(pj);
pj_kn(pj, "addr", bb->switch_op->addr);
pj_kn(pj, "min_val", bb->switch_op->min_val);
pj_kn(pj, "def_val", bb->switch_op->def_val);
pj_kn(pj, "max_val", bb->switch_op->max_val);
pj_k(pj, "cases");
pj_a(pj);
{
RzListIter *case_op_iter;
RzAnalysisCaseOp *case_op;
rz_list_foreach (bb->switch_op->cases, case_op_iter, case_op) {
pj_o(pj);
pj_kn(pj, "addr", case_op->addr);
pj_kn(pj, "jump", case_op->jump);
pj_kn(pj, "value", case_op->value);
pj_end(pj);
}
}
pj_end(pj);
pj_end(pj);
}
pj_kn(pj, "opaddr", opaddr);
pj_kn(pj, "addr", bb->addr);
pj_ki(pj, "size", bb->size);
pj_ki(pj, "inputs", inputs);
pj_ki(pj, "outputs", outputs);
pj_ki(pj, "ninstr", bb->ninstr);
pj_kb(pj, "traced", bb->traced);
pj_end(pj);
break;
}
case RZ_OUTPUT_MODE_TABLE:
rz_table_add_rowf(t, "xdxx", bb->addr, bb->size, bb->jump, bb->fail);
break;
case RZ_OUTPUT_MODE_QUIET:
rz_cons_printf("0x%08" PFMT64x "\n", bb->addr);
break;
case RZ_OUTPUT_MODE_LONG: {
if (bb->jump != UT64_MAX) {
rz_cons_printf("jump: 0x%08" PFMT64x "\n", bb->jump);
}
if (bb->fail != UT64_MAX) {
rz_cons_printf("fail: 0x%08" PFMT64x "\n", bb->fail);
}
rz_cons_printf("opaddr: 0x%08" PFMT64x "\n", opaddr);
rz_cons_printf("addr: 0x%08" PFMT64x "\nsize: %" PFMT64d "\ninputs: %d\noutputs: %d\nninstr: %d\ntraced: %s\n",
bb->addr, bb->size, inputs, outputs, bb->ninstr, rz_str_bool(bb->traced));
break;
}
default:
rz_warn_if_reached();
break;
}
}
static int bb_cmp(const void *a, const void *b, void *user) {
const RzAnalysisBlock *ba = a;
const RzAnalysisBlock *bb = b;
return ba->addr - bb->addr;
}
RZ_IPI void rz_core_analysis_bbs_info_print(RzCore *core, RzAnalysisFunction *fcn, RzCmdStateOutput *state) {
rz_return_if_fail(core && fcn && state);
void **iter;
RzAnalysisBlock *bb;
rz_cmd_state_output_array_start(state);
rz_cmd_state_output_set_columnsf(state, "xdxx", "addr", "size", "jump", "fail");
rz_pvector_sort(fcn->bbs, bb_cmp, NULL);
rz_pvector_foreach (fcn->bbs, iter) {
bb = (RzAnalysisBlock *)*iter;
bb_info_print(core, fcn, bb, bb->addr, state->mode, state->d.pj, state->d.t);
}
rz_cmd_state_output_array_end(state);
}
RZ_IPI void rz_core_analysis_bb_info_print(RzCore *core, RzAnalysisBlock *bb, ut64 addr, RzCmdStateOutput *state) {
rz_return_if_fail(core && bb && state);
rz_cmd_state_output_set_columnsf(state, "xdxx", "addr", "size", "jump", "fail");
RzAnalysisFunction *fcn = rz_list_first_val(bb->fcns);
bb_info_print(core, fcn, bb, addr, state->mode, state->d.pj, state->d.t);
}
/*this only autoname those function that start with fcn.* or sym.func.* */
RZ_API void rz_core_analysis_autoname_all_fcns(RzCore *core) {
RzListIter *it;
RzAnalysisFunction *fcn;
RzList *fcns = rz_analysis_function_list(core->analysis);
rz_list_foreach (fcns, it, fcn) {
if (!strncmp(fcn->name, "fcn.", 4) || !strncmp(fcn->name, "sym.func.", 9)) {
RzFlagItem *item = rz_flag_get(core->flags, fcn->name);
if (item) {
char *name = rz_core_analysis_function_autoname(core, fcn);
if (name) {
rz_flag_rename(core->flags, item, name);
free(fcn->name);
fcn->name = name;
}
} else {
// there should always be a flag for a function
rz_warn_if_reached();
}
}
}
}
static bool blacklisted_word(const char *name) {
const char *list[] = {
"__stack_chk_guard",
"__stderrp",
"__stdinp",
"__stdoutp",
"_DefaultRuneLocale"
};
for (int i = 0; i < RZ_ARRAY_SIZE(list); i++) {
if (strstr(name, list[i])) {
return true;
}
}
return false;
}
/**
* \brief Suggest a name for the function
*/
RZ_API RZ_OWN char *rz_core_analysis_function_autoname(RZ_NONNULL RzCore *core, RZ_NONNULL RzAnalysisFunction *fcn) {
rz_return_val_if_fail(core && fcn, NULL);
RzAnalysisXRef *xref;
RzListIter *iter;
bool use_getopt = false;
bool use_isatty = false;
char *do_call = NULL;
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
rz_list_foreach (xrefs, iter, xref) {
RzFlagItem *f = rz_flag_get_i(core->flags, xref->to);
if (f && !blacklisted_word(f->name)) {
if (strstr(f->name, ".isatty")) {
use_isatty = 1;
}
if (strstr(f->name, ".getopt")) {
use_getopt = 1;
}
if (!strncmp(f->name, "method.", 7)) {
free(do_call);
do_call = rz_str_dup(f->name + 7);
break;
}
if (!strncmp(f->name, "str.", 4)) {
free(do_call);
do_call = rz_str_dup(f->name + 4);
break;
}
if (!strncmp(f->name, "sym.imp.", 8)) {
free(do_call);
do_call = rz_str_dup(f->name + 8);
break;
}
if (!strncmp(f->name, "reloc.", 6)) {
free(do_call);
do_call = rz_str_dup(f->name + 6);
break;
}
}
}
rz_list_free(xrefs);
// TODO: append counter if name already exists
if (use_getopt) {
RzFlagItem *item = rz_flag_get(core->flags, "main");
free(do_call);
// if referenced from entrypoint. this should be main
if (item && item->offset == fcn->addr) {
return rz_str_dup("main"); // main?
}
return rz_str_dup("parse_args"); // main?
}
if (use_isatty) {
char *ret = rz_str_newf("sub.setup_tty_%s_%" PFMT64x, do_call, fcn->addr);
free(do_call);
return ret;
}
if (do_call) {
char *ret = rz_str_newf("sub.%s_%" PFMT64x, do_call, fcn->addr);
free(do_call);
return ret;
}
return NULL;
}
/**
* \brief Print all string flags referenced by the function
*/
RZ_API void rz_core_analysis_function_strings_print(RZ_NONNULL RzCore *core, RZ_NONNULL RzAnalysisFunction *fcn, RZ_NULLABLE PJ *pj) {
rz_return_if_fail(core && fcn);
RzAnalysisXRef *xref;
RzListIter *iter;
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
rz_list_foreach (xrefs, iter, xref) {
RzFlagItem *f = rz_flag_get_by_spaces(core->flags, xref->to, RZ_FLAGS_FS_STRINGS, NULL);
if (!f || !f->space || strcmp(f->space->name, RZ_FLAGS_FS_STRINGS)) {
continue;
}
if (pj) {
pj_o(pj);
pj_kn(pj, "addr", xref->from);
pj_kn(pj, "ref", xref->to);
pj_ks(pj, "flag", f->name);
pj_end(pj);
} else {
rz_cons_printf("0x%08" PFMT64x " 0x%08" PFMT64x " %s\n", xref->from, xref->to, f->name);
}
}
rz_list_free(xrefs);
}
static void rz_analysis_set_stringrefs(RzCore *core, RzAnalysisFunction *fcn) {
bool is_va = core->io->va;
RzBinObject *bobj = rz_bin_cur_object(core->bin);
if (!bobj) {
return;
}
RzListIter *iter;
RzAnalysisXRef *xref;
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
rz_list_foreach (xrefs, iter, xref) {
if (xref->type == RZ_ANALYSIS_XREF_TYPE_DATA &&
rz_bin_object_get_string_at(bobj, xref->to, is_va)) {
rz_analysis_xrefs_set(core->analysis, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_STRING);
}
}
rz_list_free(xrefs);
}
static bool rz_analysis_try_get_fcn(RzCore *core, RzAnalysisXRef *xref, int fcndepth, int refdepth) {
if (!refdepth) {
return false;
}
RzIOMap *map = rz_io_map_get(core->io, xref->to);
if (!map) {
return false;
}
if (map->perm & RZ_PERM_X) {
ut8 buf[64];
rz_io_read_at_mapped(core->io, xref->to, buf, sizeof(buf));
bool looksLikeAFunction = rz_analysis_check_fcn(core->analysis, buf, sizeof(buf), xref->to, map->itv.addr,
map->itv.addr + map->itv.size);
if (looksLikeAFunction) {
if (rz_analysis_is_beyond_limits(core->analysis, xref->to)) {
return 1;
}
rz_core_analysis_fcn(core, xref->to, xref->from, xref->type, fcndepth - 1);
}
} else {
ut64 offs = 0;
ut64 sz = rz_analysis_get_bits(core->analysis) >> 3;
RzAnalysisXRef xref1;
xref1.type = RZ_ANALYSIS_XREF_TYPE_DATA;
xref1.from = xref->to;
xref1.to = 0;
ut32 i32;
ut16 i16;
ut8 i8;
ut64 offe = offs + 1024;
for (offs = 0; offs < offe; offs += sz, xref1.from += sz) {
ut8 bo[8];
rz_io_read_at_mapped(core->io, xref->to + offs, bo, RZ_MIN(sizeof(bo), sz));
bool be = rz_analysis_is_big_endian_set(core->analysis);
switch (sz) {
case 1:
i8 = rz_read_ble8(bo);
xref1.to = (ut64)i8;
break;
case 2:
i16 = rz_read_ble16(bo, be);
xref1.to = (ut64)i16;
break;
case 4:
i32 = rz_read_ble32(bo, be);
xref1.to = (ut64)i32;
break;
case 8:
xref1.to = rz_read_ble64(bo, be);
break;
}
rz_analysis_try_get_fcn(core, &xref1, fcndepth, refdepth - 1);
}
}
return 1;
}
static int rz_analysis_analyze_fcn_refs(RzCore *core, RzAnalysisFunction *fcn, int depth) {
RzListIter *iter;
RzAnalysisXRef *xref;
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
RzAnalysisOptions *aopt = rz_analysis_get_options(core->analysis);
rz_list_foreach (xrefs, iter, xref) {
if (xref->to == UT64_MAX) {
continue;
}
switch (xref->type) {
case RZ_ANALYSIS_XREF_TYPE_DATA:
if (aopt->followdatarefs) {
rz_analysis_try_get_fcn(core, xref, depth, 2);
}
break;
case RZ_ANALYSIS_XREF_TYPE_CODE:
case RZ_ANALYSIS_XREF_TYPE_CALL:
rz_core_analysis_fcn(core, xref->to, xref->from, xref->type, depth - 1);
break;
default:
break;
}
// TODO: fix memleak here, fcn not freed even though it is
// added in core->analysis->fcns which is freed in rz_analysis_free()
}
rz_list_free(xrefs);
return 1;
}
static void function_rename(RzFlag *flags, RzAnalysisFunction *fcn) {
const char *locname = "loc.";
const size_t locsize = strlen(locname);
char *fcnname = fcn->name;
if (strncmp(fcn->name, locname, locsize) == 0) {
const char *fcnpfx, *restofname;
RzFlagItem *f;
fcn->type = RZ_ANALYSIS_FCN_TYPE_FCN;
fcnpfx = rz_analysis_fcntype_tostring(fcn->type);
restofname = fcn->name + locsize;
fcn->name = rz_str_newf("%s.%s", fcnpfx, restofname);
f = rz_flag_get_i(flags, fcn->addr);
rz_flag_rename(flags, f, fcn->name);
free(fcnname);
}
}
static void autoname_imp_trampoline(RzCore *core, RzAnalysisFunction *fcn) {
if (rz_pvector_len(fcn->bbs) == 1 && ((RzAnalysisBlock *)rz_pvector_head(fcn->bbs))->ninstr == 1) {
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
if (xrefs && rz_list_length(xrefs) == 1) {
RzAnalysisXRef *xref = rz_list_first_val(xrefs);
if (xref->type != RZ_ANALYSIS_XREF_TYPE_CALL) { /* Some fcns don't return */
RzFlagItem *flg = rz_flag_get_i(core->flags, xref->to);
if (flg && rz_str_startswith(flg->name, "sym.imp.")) {
RZ_FREE(fcn->name);
fcn->name = rz_str_newf("sub.%s", flg->name + 8);
}
}
}
rz_list_free(xrefs);
}
}
static void set_fcn_name_from_flag(RzAnalysisFunction *fcn, RzFlagItem *f, const char *fcnpfx) {
char tmpbuf[128];
bool nameChanged = false;
if (f && f->name) {
if (!strncmp(fcn->name, "loc.", 4) || !strncmp(fcn->name, "fcn.", 4)) {
rz_analysis_function_rename(fcn, f->name);
nameChanged = true;
} else if (strncmp(f->name, "sect", 4)) {
rz_analysis_function_rename(fcn, f->name);
nameChanged = true;
}
}
if (!nameChanged) {
rz_analysis_function_rename(fcn, rz_strf(tmpbuf, "%s.%08" PFMT64x, fcnpfx, fcn->addr));
}
}
static bool is_entry_flag(RzFlagItem *f) {
return f->space && !strcmp(f->space->name, RZ_FLAGS_FS_SYMBOLS) && rz_str_startswith(f->name, "entry.");
}
static int core_analysis_fcn(RzCore *core, ut64 at, ut64 from, int reftype, int depth) {
if (depth < 0) {
return false;
}
Sdb *cc = rz_analysis_get_sdb_cc(core->analysis);
RzStrConstPool *cpool = rz_analysis_get_const_pool(core->analysis);
RzAnalysisOptions *aopt = rz_analysis_get_options(core->analysis);
int has_next = rz_config_get_i(core->config, "analysis.hasnext");
RzAnalysisHint *hint = NULL;
ut64 next = UT64_MAX;
int fcnlen;
RzAnalysisFunction *fcn = rz_analysis_function_new(core->analysis);
const char *fcnpfx = rz_config_get(core->config, "analysis.fcnprefix");
if (!fcnpfx) {
fcnpfx = "fcn";
}
if (!fcn) {
RZ_LOG_ERROR("core: cannot allocate RzAnalysisFunction struct.\n");
return false;
}
fcn->cc = rz_str_constpool_get(cpool, rz_analysis_cc_default(core->analysis));
rz_warn_if_fail(!cc->path || fcn->cc);
hint = rz_analysis_hint_get(core->analysis, at);
if (hint && hint->bits == 16) {
// expand 16bit for function
fcn->bits = 16;
} else {
fcn->bits = rz_analysis_get_bits(core->analysis);
}
fcn->addr = at;
fcn->name = getFunctionName(core, at);
if (!fcn->name) {
fcn->name = rz_str_newf("%s.%08" PFMT64x, fcnpfx, at);
}
do {
RzFlagItem *f;
ut64 delta = rz_analysis_function_linear_size(fcn);
if (!rz_io_is_valid_offset(core->io, at + delta, !aopt->noncode)) {
goto error;
}
if (rz_cons_is_breaked()) {
break;
}
fcnlen = rz_analysis_fcn(core->analysis, fcn, at + delta, aopt->bb_max_size, reftype);
at = fcn->addr; // potentially shifted by nopskip
if (aopt->searchstringrefs) {
rz_analysis_set_stringrefs(core, fcn);
}
if (fcnlen == 0) {
RZ_LOG_DEBUG("Analyzed function has size of 0 at 0x%08" PFMT64x "\n", fcn->addr);
goto error;
}
if (fcnlen < 0) {
switch (fcnlen) {
case RZ_ANALYSIS_RET_ERROR:
case RZ_ANALYSIS_RET_END:
break;
case RZ_ANALYSIS_RET_COND:
case RZ_ANALYSIS_RET_BRANCH:
continue;
default:
RZ_LOG_ERROR("Found negative function size at 0x%08" PFMT64x " (%d)\n", at, fcnlen);
continue;
}
}
f = rz_core_flag_get_by_spaces(core->flags, fcn->addr);
set_fcn_name_from_flag(fcn, f, fcnpfx);
if (fcnlen == RZ_ANALYSIS_RET_ERROR ||
(fcnlen == RZ_ANALYSIS_RET_END && !rz_analysis_function_realsize(fcn))) { /* Error analyzing function */
if (aopt->followbrokenfcnsrefs) {
rz_analysis_analyze_fcn_refs(core, fcn, depth);
}
goto error;
} else if (fcnlen == RZ_ANALYSIS_RET_END) { /* Function analysis complete */
f = rz_core_flag_get_by_spaces(core->flags, fcn->addr);
if (f && f->name && strncmp(f->name, "sect", 4)) { /* Check if it's already flagged */
char *new_name = rz_str_dup(f->name);
if (is_entry_flag(f)) {
RzBinSymbol *sym;
RzBinObject *o = rz_bin_cur_object(core->bin);
const RzPVector *syms = o ? rz_bin_object_get_symbols(o) : NULL;
ut64 baddr = rz_config_get_i(core->config, "bin.baddr");
void **it;
rz_pvector_foreach (syms, it) {
sym = *it;
if ((sym->paddr + baddr) == fcn->addr && !strcmp(sym->type, RZ_BIN_TYPE_FUNC_STR)) {
free(new_name);
new_name = rz_str_newf("sym.%s", sym->name);
break;
}
}
}
free(fcn->name);
fcn->name = new_name;
} else {
RZ_FREE(fcn->name);
const char *fcnpfx = rz_analysis_fcntype_tostring(fcn->type);
if (!fcnpfx || !*fcnpfx || !strcmp(fcnpfx, "fcn")) {
fcnpfx = rz_config_get(core->config, "analysis.fcnprefix");
}
fcn->name = rz_str_newf("%s.%08" PFMT64x, fcnpfx, fcn->addr);
autoname_imp_trampoline(core, fcn);
/* Add flag */
rz_flag_space_push(core->flags, RZ_FLAGS_FS_FUNCTIONS);
rz_flag_set(core->flags, fcn->name, fcn->addr, rz_analysis_function_linear_size(fcn));
rz_flag_space_pop(core->flags);
}
/* New function: Add initial xref */
if (from != UT64_MAX) {
rz_analysis_xrefs_set(core->analysis, from, fcn->addr, reftype);
}
// XXX: this is wrong. See CID 1134565
rz_analysis_add_function(core->analysis, fcn);
if (has_next) {
ut64 addr = rz_analysis_function_max_addr(fcn);
RzIOMap *map = rz_io_map_get(core->io, addr);
// only get next if found on an executable section
if (!map || (map && map->perm & RZ_PERM_X)) {
ut64 at = rz_analysis_function_max_addr(fcn);
while (true) {
ut64 size;
RzAnalysisMetaItem *mi = rz_meta_get_at(core->analysis, at, RZ_META_TYPE_ANY, &size);
if (!mi) {
break;
}
at += size;
}
// TODO: ensure next address is function after padding (nop or trap or wat)
// nopskip already does that in run_basic_block_analysis, but it might make sense
// to move it here
// XXX noisy for test cases because we want to clear the stderr
rz_cons_clear_line(stderr);
loganalysis(fcn->addr, at, 10000 - depth);
next = at;
}
}
if (!rz_analysis_analyze_fcn_refs(core, fcn, depth)) {
goto error;
}
}
} while (fcnlen != RZ_ANALYSIS_RET_END);
rz_analysis_le_addr_pair_reset(core->analysis);
if (has_next && next != UT64_MAX && !rz_analysis_get_fcn_in(core->analysis, next, 0)) {
rz_core_analysis_fcn(core, next, from, 0, depth - 1);
}
if (rz_analysis_plugin_is_arch(core->analysis, "x86")) {
rz_analysis_function_check_bp_use(fcn);
}
rz_analysis_hint_free(hint);
return true;
error:
rz_analysis_le_addr_pair_reset(core->analysis);
// ugly hack to free fcn
if (fcn) {
if (!rz_analysis_function_realsize(fcn) || fcn->addr == UT64_MAX) {
rz_analysis_function_free(fcn);
fcn = NULL;
} else {
// TODO: mark this function as not properly analyzed
if (!fcn->name) {
// XXX dupped code.
fcn->name = rz_str_newf(
"%s.%08" PFMT64x,
rz_analysis_fcntype_tostring(fcn->type),
at);
/* Add flag */
rz_flag_space_push(core->flags, RZ_FLAGS_FS_FUNCTIONS);
rz_flag_set(core->flags, fcn->name, at, rz_analysis_function_linear_size(fcn));
rz_flag_space_pop(core->flags);
}
rz_analysis_add_function(core->analysis, fcn);
if (has_next) {
ut64 newaddr = rz_analysis_function_max_addr(fcn);
RzIOMap *map = rz_io_map_get(core->io, newaddr);
if (!map || (map && (map->perm & RZ_PERM_X))) {
next = newaddr;
rz_core_analysis_fcn(core, next, next, 0, depth - 1);
}
}
if (rz_analysis_plugin_is_arch(core->analysis, "x86")) {
rz_analysis_function_check_bp_use(fcn);
}
}
}
rz_analysis_hint_free(hint);
return false;
}
/* decode and return the RzAnalysisOp at the address addr */
RZ_API RzAnalysisOp *rz_core_analysis_op(RzCore *core, ut64 addr, int mask) {
int len;
ut8 buf[32];
ut8 *ptr;
rz_return_val_if_fail(core, NULL);
if (addr == UT64_MAX) {
return NULL;
}
RzAnalysisOp *op = rz_analysis_op_new();
if (!op) {
return NULL;
}
int delta = (addr - core->offset);
int minopsz = 8;
if (delta > 0 && delta + minopsz < core->blocksize && addr >= core->offset && addr + 16 < core->offset + core->blocksize) {
ptr = core->block + delta;
len = core->blocksize - delta;
if (len < 1) {
goto err_op;
}
} else {
if (!rz_io_read_at_mapped(core->io, addr, buf, sizeof(buf))) {
goto err_op;
}
ptr = buf;
len = sizeof(buf);
}
if (rz_analysis_op(core->analysis, op, addr, ptr, len, mask) < 1) {
goto err_op;
}
// TODO This code block must be deleted when all the analysis plugins support disasm
if (!op->mnemonic && mask & RZ_ANALYSIS_OP_MASK_DISASM) {
RzAsmOp asmop = { 0 };
RZ_LOG_DEBUG("Unimplemented RZ_ANALYSIS_OP_MASK_DISASM for current analysis.arch. Using the RzAsmOp as fallback for now.\n");
rz_asm_set_pc(core->rasm, addr);
rz_asm_op_init(&asmop);
if (rz_asm_disassemble(core->rasm, &asmop, ptr, len) > 0) {
op->mnemonic = rz_str_dup(rz_strbuf_get(&asmop.buf_asm));
}
rz_asm_op_fini(&asmop);
}
return op;
err_op:
rz_analysis_op_free(op);
return NULL;
}
// Node for tree-sorting analysis hints or collecting hint records at a single addr
typedef struct {
RBNode rb;
ut64 addr;
enum {
HINT_NODE_ADDR,
HINT_NODE_ARCH,
HINT_NODE_BITS
} type;
union {
const RzVector /*<const RzAnalysisAddrHintRecord>*/ *addr_hints;
const char *arch;
int bits;
};
} HintNode;
static void print_hint_h_format(HintNode *node) {
switch (node->type) {
case HINT_NODE_ADDR: {
const RzAnalysisAddrHintRecord *record;
rz_vector_foreach (node->addr_hints, record) {
switch (record->type) {
case RZ_ANALYSIS_ADDR_HINT_TYPE_IMMBASE:
rz_cons_printf(" immbase=%d", record->immbase);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_JUMP:
rz_cons_printf(" jump=0x%08" PFMT64x, record->jump);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_FAIL:
rz_cons_printf(" fail=0x%08" PFMT64x, record->fail);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_STACKFRAME:
rz_cons_printf(" stackframe=0x%" PFMT64x, record->stackframe);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_PTR:
rz_cons_printf(" ptr=0x%" PFMT64x, record->ptr);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_NWORD:
rz_cons_printf(" nword=%d", record->nword);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_RET:
rz_cons_printf(" ret=0x%08" PFMT64x, record->retval);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_NEW_BITS:
rz_cons_printf(" newbits=%d", record->newbits);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_SIZE:
rz_cons_printf(" size=%" PFMT64u, record->size);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_SYNTAX:
rz_cons_printf(" syntax='%s'", record->syntax);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_OPTYPE: {
const char *type = rz_analysis_optype_to_string(record->optype);
if (type) {
rz_cons_printf(" type='%s'", type);
}
break;
}
case RZ_ANALYSIS_ADDR_HINT_TYPE_OPCODE:
rz_cons_printf(" opcode='%s'", record->opcode);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_TYPE_OFFSET:
rz_cons_printf(" offset='%s'", record->type_offset);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_ESIL:
rz_cons_printf(" esil='%s'", record->esil);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_HIGH:
rz_cons_printf(" high=true");
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_VAL:
rz_cons_printf(" val=0x%08" PFMT64x, record->val);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_ENUM:
rz_cons_printf(" enum='%s'", rz_str_get(record->enum_name));
break;
}
}
break;
}
case HINT_NODE_ARCH:
if (node->arch) {
rz_cons_printf(" arch='%s'", node->arch);
} else {
rz_cons_print(" arch=RESET");
}
break;
case HINT_NODE_BITS:
if (node->bits) {
rz_cons_printf(" bits=%d", node->bits);
} else {
rz_cons_print(" bits=RESET");
}
break;
}
}
static void hint_node_print(HintNode *node, RzOutputMode mode, PJ *pj) {
switch (mode) {
case RZ_OUTPUT_MODE_JSON:
switch (node->type) {
case HINT_NODE_ADDR: {
const RzAnalysisAddrHintRecord *record;
rz_vector_foreach (node->addr_hints, record) {
switch (record->type) {
case RZ_ANALYSIS_ADDR_HINT_TYPE_IMMBASE:
pj_ki(pj, "immbase", record->immbase);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_JUMP:
pj_kn(pj, "jump", record->jump);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_FAIL:
pj_kn(pj, "fail", record->fail);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_STACKFRAME:
pj_kn(pj, "stackframe", record->stackframe);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_PTR:
pj_kn(pj, "ptr", record->ptr);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_NWORD:
pj_ki(pj, "nword", record->nword);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_RET:
pj_kn(pj, "ret", record->retval);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_NEW_BITS:
pj_ki(pj, "newbits", record->newbits);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_SIZE:
pj_kn(pj, "size", record->size);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_SYNTAX:
pj_ks(pj, "syntax", record->syntax);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_OPTYPE: {
const char *type = rz_analysis_optype_to_string(record->optype);
if (type) {
pj_ks(pj, "type", type);
}
break;
}
case RZ_ANALYSIS_ADDR_HINT_TYPE_OPCODE:
pj_ks(pj, "opcode", record->opcode);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_TYPE_OFFSET:
pj_ks(pj, "offset", record->type_offset);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_ESIL:
pj_ks(pj, "esil", record->esil);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_HIGH:
pj_kb(pj, "high", true);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_VAL:
pj_kn(pj, "val", record->val);
break;
case RZ_ANALYSIS_ADDR_HINT_TYPE_ENUM:
pj_ks(pj, "enum", rz_str_get(record->enum_name));
break;
}
}
break;
}
case HINT_NODE_ARCH:
if (node->arch) {
pj_ks(pj, "arch", node->arch);
} else {
pj_knull(pj, "arch");
}
break;
case HINT_NODE_BITS:
pj_ki(pj, "bits", node->bits);
break;
}
break;
default:
print_hint_h_format(node);
break;
}
}
void hint_node_free(RBNode *node, void *user) {
free(container_of(node, HintNode, rb));
}
int hint_node_cmp(const void *incoming, const RBNode *in_tree, void *user) {
ut64 ia = *(ut64 *)incoming;
ut64 ta = container_of(in_tree, const HintNode, rb)->addr;
if (ia < ta) {
return -1;
} else if (ia > ta) {
return 1;
}
return 0;
}
bool print_addr_hint_cb(ut64 addr, const RzVector /*<const RzAnalysisAddrHintRecord>*/ *records, void *user) {
HintNode *node = RZ_NEW0(HintNode);
if (!node) {
return false;
}
node->addr = addr;
node->type = HINT_NODE_ADDR;
node->addr_hints = records;
rz_rbtree_insert(user, &addr, &node->rb, hint_node_cmp, NULL);
return true;
}
bool print_arch_hint_cb(ut64 addr, RZ_NULLABLE const char *arch, void *user) {
HintNode *node = RZ_NEW0(HintNode);
if (!node) {
return false;
}
node->addr = addr;
node->type = HINT_NODE_ARCH;
node->arch = arch;
rz_rbtree_insert(user, &addr, &node->rb, hint_node_cmp, NULL);
return true;
}
bool print_bits_hint_cb(ut64 addr, int bits, void *user) {
HintNode *node = RZ_NEW0(HintNode);
if (!node) {
return false;
}
node->addr = addr;
node->type = HINT_NODE_BITS;
node->bits = bits;
rz_rbtree_insert(user, &addr, &node->rb, hint_node_cmp, NULL);
return true;
}
static void print_hint_tree(RBTree tree, RzCmdStateOutput *state) {
PJ *pj = state->mode == RZ_OUTPUT_MODE_JSON ? state->d.pj : NULL;
if (state->mode == RZ_OUTPUT_MODE_JSON) {
pj_a(pj);
}
#define END_ADDR \
if (pj) { \
pj_end(pj); \
} else if (state->mode == RZ_OUTPUT_MODE_STANDARD) { \
rz_cons_newline(); \
}
RBIter it;
HintNode *node;
ut64 last_addr = 0;
bool in_addr = false;
rz_rbtree_foreach (tree, it, node, HintNode, rb) {
if (!in_addr || last_addr != node->addr) {
if (in_addr) {
END_ADDR
}
in_addr = true;
last_addr = node->addr;
if (pj) {
pj_o(pj);
pj_kn(pj, "addr", node->addr);
} else if (state->mode == RZ_OUTPUT_MODE_STANDARD) {
rz_cons_printf(" 0x%08" PFMT64x " =>", node->addr);
}
}
hint_node_print(node, state->mode, pj);
}
if (in_addr) {
END_ADDR
}
#undef BEGIN_ADDR
#undef END_ADDR
if (pj) {
pj_end(pj);
}
}
RZ_API void rz_core_analysis_hint_list_print(RzAnalysis *a, RzCmdStateOutput *state) {
rz_return_if_fail(a && state);
RBTree tree = NULL;
// Collect all hints in the tree to sort them
rz_analysis_arch_hints_foreach(a, print_arch_hint_cb, &tree);
rz_analysis_bits_hints_foreach(a, print_bits_hint_cb, &tree);
rz_analysis_addr_hints_foreach(a, print_addr_hint_cb, &tree);
print_hint_tree(tree, state);
rz_rbtree_free(tree, hint_node_free, NULL);
}
RZ_API void rz_core_analysis_hint_print(RzAnalysis *a, ut64 addr, RzCmdStateOutput *state) {
rz_return_if_fail(a && state);
RBTree tree = NULL;
ut64 hint_addr = UT64_MAX;
const char *arch = rz_analysis_hint_arch_at(a, addr, &hint_addr);
if (hint_addr != UT64_MAX) {
print_arch_hint_cb(hint_addr, arch, &tree);
}
int bits = rz_analysis_hint_bits_at(a, addr, &hint_addr);
if (hint_addr != UT64_MAX) {
print_bits_hint_cb(hint_addr, bits, &tree);
}
const RzVector *addr_hints = rz_analysis_addr_hints_at(a, addr);
if (addr_hints) {
print_addr_hint_cb(addr, addr_hints, &tree);
}
print_hint_tree(tree, state);
rz_rbtree_free(tree, hint_node_free, NULL);
}
/* seek basic block that contains address addr or do nothing if there is no block. */
RZ_API bool rz_core_analysis_bb_seek(RzCore *core, ut64 addr) {
RzAnalysisBlock *block = rz_analysis_find_most_relevant_block_in(core->analysis, addr);
if (block) {
rz_core_seek_and_save(core, block->addr, false);
return true;
}
return false;
}
RZ_API int rz_core_analysis_esil_fcn(RzCore *core, ut64 at, ut64 from, int reftype, int depth) {
const char *esil;
eprintf("TODO\n");
while (1) {
// TODO: Implement the proper logic for doing esil analysis
RzAnalysisOp *op = rz_core_analysis_op(core, at, RZ_ANALYSIS_OP_MASK_ESIL);
if (!op) {
break;
}
esil = RZ_STRBUF_SAFEGET(&op->esil);
eprintf("0x%08" PFMT64x " %d %s\n", at, op->size, esil);
// at += op->size;
// esilIsRet()
// esilIsCall()
// esilIsJmp()
rz_analysis_op_free(op);
break;
}
return 0;
}
static int find_sym_flag(const void *a1, const void *a2, void *user) {
const RzFlagItem *f = (const RzFlagItem *)a2;
return f->space && !strcmp(f->space->name, RZ_FLAGS_FS_SYMBOLS) ? 0 : 1;
}
static bool is_skippable_addr(RzCore *core, ut64 addr) {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
if (!fcn) {
return false;
}
if (fcn->addr == addr) {
return true;
}
const RzList *flags = rz_flag_get_list(core->flags, addr);
return !(flags && rz_list_find(flags, fcn, find_sym_flag, NULL));
}
static bool should_perform_split(RzCore *core, RzAnalysisFunction *fcn, int reftype, ut64 at, ut64 from) {
if (reftype != RZ_ANALYSIS_XREF_TYPE_CALL || from == UT64_MAX) {
return false;
}
ut64 fcn_end = fcn->addr + rz_analysis_function_linear_size(fcn);
if (at >= fcn->addr && at <= fcn_end) {
return false;
}
RzFlagItem *item = rz_flag_get_at(core->flags, at, true);
bool is_valid_sym = (item && item->name && !strncmp(item->name, "sym.", 4));
if (is_valid_sym) {
return true;
}
return false;
}
// XXX: This function takes sometimes forever
/* analyze a RzAnalysisFunction at the address 'at'.
* If the function has been already analyzed, it adds a
* reference to that fcn */
RZ_API int rz_core_analysis_fcn(RzCore *core, ut64 at, ut64 from, int reftype, int depth) {
if (from == UT64_MAX && is_skippable_addr(core, at)) {
RZ_LOG_DEBUG("invalid address for function 0x%08" PFMT64x "\n", at);
return 0;
}
const bool use_esil = rz_config_get_i(core->config, "analysis.esil");
RzAnalysisOptions *aopt = rz_analysis_get_options(core->analysis);
RzAnalysisFunction *fcn;
// update bits based on the core->offset otherwise we could have the
// last value set and blow everything up
rz_core_seek_arch_bits(core, at);
if (core->io->va) {
if (!rz_io_is_valid_offset(core->io, at, !aopt->noncode)) {
RZ_LOG_VERBOSE("Address not mapped or not executable at 0x%" PFMT64x ".\n", at);
return false;
}
}
if (use_esil) {
return rz_core_analysis_esil_fcn(core, at, from, reftype, depth);
}
if ((from != UT64_MAX && !at) || at == UT64_MAX) {
RZ_LOG_WARN("invalid address from 0x%08" PFMT64x "\n", from);
return false;
}
if (depth < 0) {
RZ_LOG_DEBUG("analysis depth reached\n");
return false;
}
if (rz_cons_is_breaked()) {
return false;
}
fcn = rz_analysis_get_fcn_in(core->analysis, at, 0);
if (fcn) {
if (fcn->addr == at) {
// if the function was already analyzed as a "loc.",
// convert it to function and rename it to "fcn.",
// because we found a call to this address
if (reftype == RZ_ANALYSIS_XREF_TYPE_CALL && fcn->type == RZ_ANALYSIS_FCN_TYPE_LOC) {
function_rename(core->flags, fcn);
}
return 0; // already analyzed function
}
if (rz_analysis_function_contains(fcn, from)) { // inner function
if (should_perform_split(core, fcn, reftype, at, from)) {
goto perform_split;
}
RzList *l = rz_analysis_xrefs_get_to(core->analysis, from);
if (l && !rz_list_empty(l)) {
rz_list_free(l);
return true;
}
rz_list_free(l);
// we should analyze and add code ref otherwise aaa != aac
if (from != UT64_MAX) {
rz_analysis_xrefs_set(core->analysis, from, at, reftype);
}
return true;
}
}
perform_split:
if (core_analysis_fcn(core, at, from, reftype, depth - 1)) {
// split function if overlaps
if (fcn) {
rz_analysis_function_resize(fcn, at - fcn->addr);
}
return true;
}
return false;
}
/* if addr is 0, remove all functions
* otherwise remove the function addr falls into */
RZ_API int rz_core_analysis_fcn_clean(RzCore *core, ut64 addr) {
if (!addr) {
rz_analysis_function_delete_all(core->analysis);
} else {
rz_analysis_function_delete_address(core->analysis, addr);
}
return true;
}
/**
* \brief for a given function returns an RzList of all functions that were called in it
*/
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_core_analysis_fcn_get_calls(RzCore *core, RzAnalysisFunction *fcn) {
RzAnalysisXRef *xrefi;
RzListIter *iter, *iter2;
// get all references from this function
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
// sanity check
if (!rz_list_empty(xrefs)) {
// iterate over all the references and remove these which aren't of type call
rz_list_foreach_safe (xrefs, iter, iter2, xrefi) {
if (xrefi->type != RZ_ANALYSIS_XREF_TYPE_CALL) {
rz_list_delete(xrefs, iter);
}
}
}
return xrefs;
}
#define REG_SET_SIZE (RZ_ANALYSIS_CC_MAXARG + 2)
typedef struct {
int count;
RzPVector /*<int *>*/ reg_set;
bool argonly;
RzAnalysisFunction *fcn;
RzCore *core;
} BlockRecurseCtx;
static bool analysis_block_on_exit(RzAnalysisBlock *bb, BlockRecurseCtx *ctx) {
int *cur_regset = rz_pvector_pop(&ctx->reg_set);
int *prev_regset = rz_pvector_at(&ctx->reg_set, rz_pvector_len(&ctx->reg_set) - 1);
size_t i;
for (i = 0; i < REG_SET_SIZE; i++) {
if (!prev_regset[i] && cur_regset[i] == 1) {
prev_regset[i] = 1;
}
}
free(cur_regset);
return true;
}
static bool analysis_block_cb(RzAnalysisBlock *bb, BlockRecurseCtx *ctx) {
if (rz_cons_is_breaked()) {
return false;
}
if (bb->size < 1) {
return true;
}
RzAnalysisOptions *aopt = rz_analysis_get_options(ctx->core->analysis);
if (bb->size > aopt->bb_max_size) {
return true;
}
int *parent_reg_set = rz_pvector_at(&ctx->reg_set, rz_pvector_len(&ctx->reg_set) - 1);
int *reg_set = RZ_NEWS(int, REG_SET_SIZE);
memcpy(reg_set, parent_reg_set, REG_SET_SIZE * sizeof(int));
rz_pvector_push(&ctx->reg_set, reg_set);
RzCore *core = ctx->core;
RzAnalysisFunction *fcn = ctx->fcn;
fcn->stack = bb->sp_entry;
ut64 pos = bb->addr;
while (pos < bb->addr + bb->size) {
if (rz_cons_is_breaked()) {
break;
}
RzAnalysisOp *op = rz_core_analysis_op(core, pos, RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_VAL | RZ_ANALYSIS_OP_MASK_HINT);
if (!op) {
break;
}
rz_analysis_extract_rarg(core->analysis, op, fcn, reg_set, &ctx->count);
if (!ctx->argonly) {
if (op->stackop == RZ_ANALYSIS_STACK_INC) {
fcn->stack += op->stackptr;
} else if (op->stackop == RZ_ANALYSIS_STACK_RESET) {
fcn->stack = 0;
}
rz_analysis_extract_vars(core->analysis, fcn, op, -fcn->stack);
}
int opsize = op->size;
int optype = op->type;
rz_analysis_op_free(op);
if (opsize < 1) {
break;
}
if (optype == RZ_ANALYSIS_OP_TYPE_CALL) {
size_t i;
int max_count = fcn->cc ? rz_analysis_cc_max_arg(core->analysis, fcn->cc) : 0;
for (i = 0; i < max_count; i++) {
reg_set[i] = 2;
}
}
pos += opsize;
}
return true;
}
// TODO: move this logic into the main analysis loop
RZ_API void rz_core_recover_vars(RzCore *core, RzAnalysisFunction *fcn, bool argonly) {
rz_return_if_fail(core && core->analysis && fcn);
RzAnalysisOptions *aopt = rz_analysis_get_options(core->analysis);
if (aopt->bb_max_size < 1) {
return;
}
BlockRecurseCtx ctx = { 0, { { 0 } }, argonly, fcn, core };
rz_pvector_init(&ctx.reg_set, free);
int *reg_set = RZ_NEWS0(int, REG_SET_SIZE);
rz_pvector_push(&ctx.reg_set, reg_set);
int saved_stack = fcn->stack;
RzAnalysisBlock *first_bb = rz_analysis_get_block_at(fcn->analysis, fcn->addr);
if (first_bb) {
rz_analysis_block_recurse_depth_first(first_bb, (RzAnalysisBlockCb)analysis_block_cb, (RzAnalysisBlockCb)analysis_block_on_exit, &ctx);
}
rz_pvector_fini(&ctx.reg_set);
fcn->stack = saved_stack;
}
static bool analysis_path_exists(RzCore *core, ut64 from, ut64 to, RzList /*<RzAnalysisBlock *>*/ *bbs, int depth, HtUP *state, HtUP *avoid) {
rz_return_val_if_fail(bbs, false);
RzAnalysisBlock *bb = rz_analysis_find_most_relevant_block_in(core->analysis, from);
RzListIter *iter = NULL;
RzAnalysisXRef *xrefi;
if (depth < 1) {
RZ_LOG_ERROR("core: maximum recursive depth reached (%d)\n", depth);
return false;
}
if (!bb) {
return false;
}
ht_up_update(state, from, bb);
// try to find the target in the current function
if (rz_analysis_block_contains(bb, to) ||
((!ht_up_find(avoid, bb->jump, NULL) &&
!ht_up_find(state, bb->jump, NULL) &&
analysis_path_exists(core, bb->jump, to, bbs, depth - 1, state, avoid))) ||
((!ht_up_find(avoid, bb->fail, NULL) &&
!ht_up_find(state, bb->fail, NULL) &&
analysis_path_exists(core, bb->fail, to, bbs, depth - 1, state, avoid)))) {
rz_list_prepend(bbs, bb);
return true;
}
// find our current function
RzAnalysisFunction *cur_fcn = rz_analysis_get_fcn_in(core->analysis, from, 0);
// get call refs from current basic block and find a path from them
if (cur_fcn) {
RzList *xrefs = rz_analysis_function_get_xrefs_from(cur_fcn);
if (xrefs) {
rz_list_foreach (xrefs, iter, xrefi) {
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
if (rz_analysis_block_contains(bb, xrefi->from)) {
if ((xrefi->from != xrefi->to) && !ht_up_find(state, xrefi->to, NULL) && analysis_path_exists(core, xrefi->to, to, bbs, depth - 1, state, avoid)) {
rz_list_prepend(bbs, bb);
rz_list_free(xrefs);
return true;
}
}
}
}
}
rz_list_free(xrefs);
}
return false;
}
static RzList /*<RzAnalysisBlock *>*/ *analysis_graph_to(RzCore *core, ut64 addr, int depth, HtUP *avoid) {
RzAnalysisFunction *cur_fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
RzList *list = rz_list_new();
HtUP *state = ht_up_new(NULL, NULL);
if (!list || !state || !cur_fcn) {
rz_list_free(list);
ht_up_free(state);
return NULL;
}
// forward search
if (analysis_path_exists(core, core->offset, addr, list, depth - 1, state, avoid)) {
ht_up_free(state);
return list;
}
// backward search
RzList *xrefs = rz_analysis_xrefs_get_to(core->analysis, cur_fcn->addr);
if (xrefs) {
RzListIter *iter;
RzAnalysisXRef *xref = NULL;
rz_list_foreach (xrefs, iter, xref) {
if (xref->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
ut64 offset = core->offset;
core->offset = xref->from;
rz_list_free(list);
list = analysis_graph_to(core, addr, depth - 1, avoid);
core->offset = offset;
if (list && rz_list_length(list)) {
rz_list_free(xrefs);
ht_up_free(state);
return list;
}
}
}
}
rz_list_free(xrefs);
ht_up_free(state);
rz_list_free(list);
return NULL;
}
RZ_API RzList /*<RzAnalysisBlock *>*/ *rz_core_analysis_graph_to(RzCore *core, ut64 addr, int n) {
int depth = rz_config_get_i(core->config, "analysis.graph_depth");
RzList *path, *paths = rz_list_new();
HtUP *avoid = ht_up_new(NULL, NULL);
while (n) {
path = analysis_graph_to(core, addr, depth, avoid);
if (path) {
rz_list_append(paths, path);
if (rz_list_length(path) >= 2) {
RzAnalysisBlock *last = rz_list_get_n(path, rz_list_length(path) - 2);
ht_up_update(avoid, last->addr, last);
n--;
continue;
}
}
// no more path found
break;
}
ht_up_free(avoid);
return paths;
}
static int core_analysis_followptr(RzCore *core, int type, ut64 at, ut64 ptr, ut64 ref, int code, int depth) {
// SLOW Operation try to reduce as much as possible
if (!ptr) {
return false;
}
if (ref == UT64_MAX || ptr == ref) {
const RzAnalysisXRefType t = code ? type ? type : RZ_ANALYSIS_XREF_TYPE_CODE : RZ_ANALYSIS_XREF_TYPE_DATA;
rz_analysis_xrefs_set(core->analysis, at, ptr, t);
return true;
}
if (depth < 1) {
return false;
}
int wordsize = (int)(rz_analysis_get_bits(core->analysis) / 8);
ut64 dataptr;
if (!rz_io_read_i(core->io, ptr, &dataptr, wordsize, false)) {
// RZ_LOG_ERROR("core_analysis_followptr: Cannot read word at destination\n");
return false;
}
return core_analysis_followptr(core, type, at, dataptr, ref, code, depth - 1);
}
static bool opiscall(RzCore *core, RzAnalysisOp *aop, ut64 addr, const ut8 *buf, int len, int arch) {
if (rz_analysis_op(core->analysis, aop, addr, buf, len, RZ_ANALYSIS_OP_MASK_BASIC) > 0) {
switch (aop->type & RZ_ANALYSIS_OP_TYPE_MASK) {
case RZ_ANALYSIS_OP_TYPE_CALL:
case RZ_ANALYSIS_OP_TYPE_CCALL:
return true;
}
}
return false;
}
#define OPSZ 8
RZ_API int rz_core_analysis_search(RzCore *core, ut64 from, ut64 to, ut64 ref, int mode) {
ut8 *buf = (ut8 *)malloc(core->blocksize);
if (!buf) {
return -1;
}
int ptrdepth = rz_config_get_i(core->config, "analysis.ptrdepth");
int i, count = 0;
RzAnalysisOp op = { 0 };
ut64 at;
int arch = -1;
if (rz_asm_is_arch(core->rasm, "arm") && rz_asm_is_bits(core->rasm, 64)) {
// speedup search
arch = RZ_ARCH_ARM64;
}
if (core->file) {
rz_io_use_fd(core->io, core->file->fd);
}
if (!ref) {
RZ_LOG_ERROR("core: null reference search is not supported\n");
free(buf);
return -1;
}
rz_cons_break_push(NULL, NULL);
if (core->blocksize > OPSZ) {
at = from;
while (at < to) {
eprintf("\r[0x%08" PFMT64x "-0x%08" PFMT64x "] ", at, to);
if (rz_cons_is_breaked()) {
break;
}
// TODO: this can be probably enhanced
if (!rz_io_read_at_mapped(core->io, at, buf, core->blocksize)) {
RZ_LOG_ERROR("core: failed to read at 0x%08" PFMT64x "\n", at);
break;
}
for (i = 0; (i < core->blocksize - OPSZ); i++) {
// TODO: honor analysis.align
if (rz_cons_is_breaked()) {
break;
}
switch (mode) {
case 'c':
(void)opiscall(core, &op, at + i, buf + i, core->blocksize - i, arch);
if (op.size < 1) {
op.size = 1;
}
break;
case 'r':
case 'w':
case 'x': {
rz_analysis_op_init(&op);
rz_analysis_op(core->analysis, &op, at + i, buf + i, core->blocksize - i, RZ_ANALYSIS_OP_MASK_BASIC);
int mask = mode == 'r' ? 1 : mode == 'w' ? 2
: mode == 'x' ? 4
: 0;
if (op.direction == mask) {
i += op.size;
}
rz_analysis_op_fini(&op);
continue;
} break;
default:
rz_analysis_op_init(&op);
if (rz_analysis_op(core->analysis, &op, at + i, buf + i, core->blocksize - i, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&op);
continue;
}
}
switch (op.type) {
case RZ_ANALYSIS_OP_TYPE_JMP:
case RZ_ANALYSIS_OP_TYPE_CJMP:
case RZ_ANALYSIS_OP_TYPE_CALL:
case RZ_ANALYSIS_OP_TYPE_CCALL:
if (op.jump != UT64_MAX &&
core_analysis_followptr(core, RZ_ANALYSIS_XREF_TYPE_CALL, at + i, op.jump, ref, true, 0)) {
count++;
}
break;
case RZ_ANALYSIS_OP_TYPE_UCJMP:
case RZ_ANALYSIS_OP_TYPE_UJMP:
case RZ_ANALYSIS_OP_TYPE_IJMP:
case RZ_ANALYSIS_OP_TYPE_RJMP:
case RZ_ANALYSIS_OP_TYPE_IRJMP:
case RZ_ANALYSIS_OP_TYPE_MJMP:
if (op.ptr != UT64_MAX &&
core_analysis_followptr(core, RZ_ANALYSIS_XREF_TYPE_CODE, at + i, op.ptr, ref, true, 1)) {
count++;
}
break;
case RZ_ANALYSIS_OP_TYPE_UCALL:
case RZ_ANALYSIS_OP_TYPE_ICALL:
case RZ_ANALYSIS_OP_TYPE_RCALL:
case RZ_ANALYSIS_OP_TYPE_IRCALL:
case RZ_ANALYSIS_OP_TYPE_UCCALL:
if (op.ptr != UT64_MAX &&
core_analysis_followptr(core, RZ_ANALYSIS_XREF_TYPE_CALL, at + i, op.ptr, ref, true, 1)) {
count++;
}
break;
default: {
rz_analysis_op_init(&op);
if (rz_analysis_op(core->analysis, &op, at + i, buf + i, core->blocksize - i, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&op);
continue;
}
}
if (op.ptr != UT64_MAX &&
core_analysis_followptr(core, RZ_ANALYSIS_XREF_TYPE_DATA, at + i, op.ptr, ref, false, ptrdepth)) {
count++;
}
break;
}
if (op.size < 1) {
op.size = 1;
}
i += op.size - 1;
rz_analysis_op_fini(&op);
}
at += core->blocksize - OPSZ;
}
} else {
RZ_LOG_ERROR("core: block size too small\n");
}
rz_cons_break_pop();
free(buf);
rz_analysis_op_fini(&op);
return count;
}
static bool core_search_for_xrefs_in_boundaries(RzCore *core, ut64 from, ut64 to) {
if ((from == UT64_MAX && to == UT64_MAX) ||
(!from && !to) ||
(to - from > rz_io_size(core->io))) {
return false;
}
return rz_core_analysis_search_xrefs(core, from, to) > 0;
}
/**
* \brief Resolves any unresolved jump
*
* \param[in] core The RzCore to use
*/
RZ_API void rz_core_analysis_resolve_jumps(RZ_NONNULL RzCore *core) {
RzListIter *iter;
RzAnalysisXRef *xref;
RzList *xrefs = rz_analysis_xrefs_list(core->analysis);
bool analyze_recursively = rz_config_get_b(core->config, "analysis.calls");
rz_list_foreach (xrefs, iter, xref) {
if (xref->type != RZ_ANALYSIS_XREF_TYPE_CALL) {
continue;
}
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, xref->from, -1);
if (fcn) {
continue;
}
rz_core_analysis_function_add(core, NULL, xref->from, analyze_recursively);
}
rz_list_free(xrefs);
}
/**
* \brief Analyze xrefs and prints the result.
*
* \param[in] core The RzCore to use
* \param[in] nbytes Sets a custom boundary from current offset for N bytes (set it to 0 to use the maps)
*
* \return False on failure, otherwise true
*/
RZ_API bool rz_core_analysis_refs(RZ_NONNULL RzCore *core, size_t nbytes) {
rz_return_val_if_fail(core, false);
bool cfg_debug = rz_config_get_b(core->config, "cfg.debug");
ut64 from = 0, to = 0;
if (nbytes) {
from = core->offset;
to = core->offset + nbytes;
return core_search_for_xrefs_in_boundaries(core, from, to);
} else if (cfg_debug) {
// get boundaries of current memory map, section or io map
RzDebugMap *map = rz_debug_map_get(core->dbg, core->offset);
if (!map) {
RZ_LOG_ERROR("Cannot find debug map boundaries at current offset\n");
return false;
}
from = map->addr;
to = map->addr_end;
return core_search_for_xrefs_in_boundaries(core, from, to);
}
RzList *list = rz_core_get_boundaries_select(core, "analysis.from", "analysis.to", "analysis.in");
RzListIter *iter;
RzIOMap *map;
if (!list) {
RZ_LOG_ERROR("Cannot get xrefs boundaries when analysis.in=%s.\n", rz_config_get(core->config, "analysis.in"));
return false;
}
rz_list_foreach (list, iter, map) {
from = map->itv.addr;
to = rz_itv_end(map->itv);
if (rz_cons_is_breaked()) {
break;
}
core_search_for_xrefs_in_boundaries(core, from, to);
}
rz_list_free(list);
return true;
}
/**
* \brief Validates a xref. Mainly checks if it points out of the memory map.
*
* \param core The rizin core.
* \param xref_to The target address of the xref.
* \param type The xref type.
* \param cfg_debug Flag if debugging configured.
* \return true xref is valid.
* \return false xref is not valid.
*/
static bool is_valid_xref(RzCore *core, ut64 xref_to, RzAnalysisXRefType type, int cfg_debug) {
if (type == RZ_ANALYSIS_XREF_TYPE_NULL) {
return false;
}
if (cfg_debug) {
if (!rz_debug_map_get(core->dbg, xref_to)) {
return false;
}
} else if (core->io->va) {
if (!rz_io_is_valid_offset(core->io, xref_to, 0)) {
return false;
}
}
return true;
}
/**
* \brief Sets a new xref according to the given to and from addresses.
*
* \param core The rizin core.
* \param xref_from The address where the xref is located.
* \param xref_to The target address of the xref.
* \param type The xref type.
* \param can_search When true, search and set the new string.
*/
static void set_new_xref(RzCore *core, ut64 xref_from, ut64 xref_to, RzAnalysisXRefType type, bool can_search) {
size_t length = 0;
char *string = NULL;
RzStrEnc encoding = 0;
if (type == RZ_ANALYSIS_XREF_TYPE_DATA && rz_core_get_string_at(core, xref_to, &string, &length, &encoding, can_search)) {
rz_meta_set_with_subtype(core->analysis, RZ_META_TYPE_STRING, encoding, xref_to, length, string);
rz_name_filter(string, -1, true);
char *flagname = rz_str_newf("str.%s", string);
rz_flag_space_push(core->flags, RZ_FLAGS_FS_STRINGS);
(void)rz_flag_set(core->flags, flagname, xref_to, length);
rz_flag_space_pop(core->flags);
free(flagname);
free(string);
}
// Add to SDB
if (xref_to) {
rz_analysis_xrefs_set(core->analysis, xref_from, xref_to, type);
}
}
/**
* \brief Search for xrefs in the form [reg*mul + disp]
*
* Assumes that disp is absolute address and reg is either array index or
* some kind of base offset.
*
* Potential false positives (might be filtered elsewhere):
* - RIP relative addressing
* - stack relative addressing - mitigated by disp > 512 check
* - field access within struct - mitigated by disp > 512 check
*/
static bool maybe_disp_xref(RzAnalysisOp *op) {
return op->disp && op->disp != UT64_MAX &&
op->disp > 512 && /* small offset could likely be a stack or struct relative access */
op->addr > 512;
}
/**
* \brief Searches for xrefs in the range of the paramters \p 'from' and \p 'to'.
*
* \param core The Rizin core.
* \param from Start of search interval.
* \param to End of search interval.
* \return int Number of found xrefs. -1 in case of failure.
*/
RZ_API int rz_core_analysis_search_xrefs(RZ_NONNULL RzCore *core, ut64 from, ut64 to) {
rz_return_val_if_fail(core, -1);
bool cfg_debug = rz_config_get_b(core->config, "cfg.debug");
bool can_search_string = rz_config_get_b(core->config, "analysis.strings");
ut64 at;
int count = 0;
const int bsz = 8096;
RzAnalysisOp op = { 0 };
if (from == to) {
return -1;
} else if (from > to) {
RZ_LOG_ERROR("Invalid range (0x%" PFMT64x " >= 0x%" PFMT64x ")\n", from, to);
return -1;
} else if (core->blocksize <= OPSZ) {
RZ_LOG_ERROR("block size is too small (blocksize <= %u)\n", OPSZ);
return -1;
}
ut8 *buf = malloc(bsz);
if (!buf) {
RZ_LOG_ERROR("cannot allocate a block\n");
return -1;
}
ut8 *block = malloc(bsz);
if (!block) {
RZ_LOG_ERROR("cannot allocate a temp block\n");
free(buf);
return -1;
}
rz_cons_break_push(NULL, NULL);
at = from;
st64 asm_sub_varmin = rz_config_get_i(core->config, "asm.sub.varmin");
while (at < to && !rz_cons_is_breaked()) {
int i = 0, ret = bsz;
if (!rz_io_is_valid_offset(core->io, at, RZ_PERM_X)) {
break;
}
(void)rz_io_read_at_mapped(core->io, at, buf, bsz);
memset(block, -1, bsz);
if (!memcmp(buf, block, bsz)) {
at += ret;
continue;
}
memset(block, 0, bsz);
if (!memcmp(buf, block, bsz)) {
at += ret;
continue;
}
while (i < bsz && !rz_cons_is_breaked()) {
int count_before = count;
rz_analysis_op_init(&op);
ret = rz_analysis_op(core->analysis, &op, at + i, buf + i, bsz - i, RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_HINT);
ret = ret > 0 ? ret : 1;
i += ret;
if (ret <= 0 || i > bsz) {
break;
}
// find references
if ((st64)op.val > asm_sub_varmin && op.val != UT64_MAX && op.val != UT32_MAX) {
if (is_valid_xref(core, op.val, RZ_ANALYSIS_XREF_TYPE_DATA, cfg_debug)) {
set_new_xref(core, op.addr, op.val, RZ_ANALYSIS_XREF_TYPE_DATA, can_search_string);
count++;
}
}
for (ut8 i = 0; i < 6; ++i) {
st64 aval = op.analysis_vals[i].imm;
if (aval > asm_sub_varmin && aval != UT64_MAX && aval != UT32_MAX) {
if (is_valid_xref(core, aval, RZ_ANALYSIS_XREF_TYPE_DATA, cfg_debug)) {
set_new_xref(core, op.addr, aval, RZ_ANALYSIS_XREF_TYPE_DATA, can_search_string);
count++;
}
}
}
// find references
if (op.ptr && op.ptr != UT64_MAX && op.ptr != UT32_MAX) {
if (is_valid_xref(core, op.ptr, RZ_ANALYSIS_XREF_TYPE_DATA, cfg_debug)) {
set_new_xref(core, op.addr, op.ptr, RZ_ANALYSIS_XREF_TYPE_DATA, can_search_string);
count++;
}
}
switch (op.type) {
case RZ_ANALYSIS_OP_TYPE_JMP:
if (is_valid_xref(core, op.jump, RZ_ANALYSIS_XREF_TYPE_CODE, cfg_debug)) {
set_new_xref(core, op.addr, op.jump, RZ_ANALYSIS_XREF_TYPE_CODE, can_search_string);
count++;
}
break;
case RZ_ANALYSIS_OP_TYPE_CJMP:
if (rz_config_get_b(core->config, "analysis.jmp.cref") &&
is_valid_xref(core, op.jump, RZ_ANALYSIS_XREF_TYPE_CODE, cfg_debug)) {
set_new_xref(core, op.addr, op.jump, RZ_ANALYSIS_XREF_TYPE_CODE, can_search_string);
count++;
}
break;
case RZ_ANALYSIS_OP_TYPE_CALL:
case RZ_ANALYSIS_OP_TYPE_CCALL:
if (is_valid_xref(core, op.jump, RZ_ANALYSIS_XREF_TYPE_CALL, cfg_debug)) {
set_new_xref(core, op.addr, op.jump, RZ_ANALYSIS_XREF_TYPE_CALL, can_search_string);
count++;
}
break;
case RZ_ANALYSIS_OP_TYPE_UJMP:
case RZ_ANALYSIS_OP_TYPE_IJMP:
case RZ_ANALYSIS_OP_TYPE_RJMP:
case RZ_ANALYSIS_OP_TYPE_IRJMP:
case RZ_ANALYSIS_OP_TYPE_MJMP:
case RZ_ANALYSIS_OP_TYPE_UCJMP:
count++;
if (is_valid_xref(core, op.ptr, RZ_ANALYSIS_XREF_TYPE_CODE, cfg_debug)) {
set_new_xref(core, op.addr, op.ptr, RZ_ANALYSIS_XREF_TYPE_CODE, can_search_string);
count++;
}
break;
case RZ_ANALYSIS_OP_TYPE_UCALL:
case RZ_ANALYSIS_OP_TYPE_ICALL:
case RZ_ANALYSIS_OP_TYPE_RCALL:
case RZ_ANALYSIS_OP_TYPE_IRCALL:
case RZ_ANALYSIS_OP_TYPE_UCCALL:
if (is_valid_xref(core, op.ptr, RZ_ANALYSIS_XREF_TYPE_CALL, cfg_debug)) {
set_new_xref(core, op.addr, op.ptr, RZ_ANALYSIS_XREF_TYPE_CALL, can_search_string);
count++;
}
break;
default:
break;
}
// find references
if (count - count_before == 0 && maybe_disp_xref(&op)) {
// This can easily produce false positives, skip when any other method has already detetcted
// probably more accurate xref.
if (is_valid_xref(core, op.disp, RZ_ANALYSIS_XREF_TYPE_DATA, cfg_debug)) {
set_new_xref(core, op.addr, op.disp, RZ_ANALYSIS_XREF_TYPE_DATA, can_search_string);
count++;
}
}
rz_analysis_op_fini(&op);
}
at += bsz;
rz_analysis_op_fini(&op);
}
rz_cons_break_pop();
free(buf);
free(block);
return count;
}
static bool isValidSymbol(RzBinSymbol *symbol) {
if (symbol && symbol->type) {
const char *type = symbol->type;
return (symbol->paddr != UT64_MAX) && (!strcmp(type, RZ_BIN_TYPE_FUNC_STR) || !strcmp(type, RZ_BIN_TYPE_HIOS_STR) || !strcmp(type, RZ_BIN_TYPE_LOOS_STR) || !strcmp(type, RZ_BIN_TYPE_METH_STR) || !strcmp(type, RZ_BIN_TYPE_STATIC_STR));
}
return false;
}
static bool isSkippable(RzBinSymbol *s) {
if (s && s->name && s->bind) {
if (rz_str_startswith(s->name, "radr://")) {
return true;
}
if (!strcmp(s->name, "__mh_execute_header")) {
return true;
}
if (!strcmp(s->bind, "NONE")) {
if (s->is_imported && s->libname && strstr(s->libname, ".dll")) {
return true;
}
}
}
return false;
}
static bool arch_is(RzCore *core, const char *x) {
if (!core || !core->rasm || rz_asm_get_bits(core->rasm) > 32) {
return false;
}
return rz_asm_is_arch(core->rasm, x);
}
static bool archIsThumbable(RzCore *core) {
return arch_is(core, "arm");
}
static int compare_symbol_names(const char *s1, RzBinSymbol *sym, RZ_UNUSED void *user) {
return strcmp(s1, sym->name);
}
RZ_API int rz_core_analysis_all(RzCore *core) {
RzPVector *vector;
RzListIter *iter;
RzFlagItem *item;
RzAnalysisFunction *fcni;
const RzBinAddr *binmain;
RzBinAddr *entry;
RzBinSymbol *symbol;
RzAnalysisOptions *aopt = rz_analysis_get_options(core->analysis);
int depth = aopt->depth;
bool analysis_vars = rz_config_get_i(core->config, "analysis.vars");
/* Analyze Functions */
/* Entries */
item = rz_flag_get(core->flags, "entry0");
if (item) {
rz_core_analysis_fcn(core, item->offset, -1, RZ_ANALYSIS_XREF_TYPE_NULL, depth - 1);
rz_core_analysis_function_rename(core, item->offset, "entry0");
} else {
rz_core_analysis_function_add(core, NULL, core->offset, false);
}
rz_core_task_yield(&core->tasks);
rz_cons_break_push(NULL, NULL);
RzBinFile *bf = core->bin->cur;
RzBinObject *o = bf ? bf->o : NULL;
/* Symbols (Imports are already analyzed by rz_bin on init) */
void **it;
if (o && (vector = o->symbols) != NULL) {
// Find address of `__gnu_thumb1_case_uqi` GCC helper function on ARM (Thumb-1 mode)
if (archIsThumbable(core) && (it = rz_pvector_find(vector, "__gnu_thumb1_case_uqi", (RzPVectorComparator)compare_symbol_names, NULL))) {
RzBinSymbol *symbol = *it;
ut64 gnu_thumb1_case_uqi_addr = 0;
if (isValidSymbol(symbol)) {
gnu_thumb1_case_uqi_addr = rz_bin_object_get_vaddr(o, symbol->paddr, symbol->vaddr);
}
rz_analysis_set_gnu_thumb1_case_uqi_addr(core->analysis, gnu_thumb1_case_uqi_addr);
}
rz_pvector_foreach (vector, it) {
symbol = *it;
if (rz_cons_is_breaked()) {
break;
}
// Stop analyzing PE imports further
if (isSkippable(symbol)) {
continue;
}
if (isValidSymbol(symbol)) {
ut64 addr = rz_bin_object_get_vaddr(o, symbol->paddr, symbol->vaddr);
rz_core_analysis_fcn(core, addr, -1, RZ_ANALYSIS_XREF_TYPE_NULL, depth - 1);
}
}
}
rz_core_task_yield(&core->tasks);
/* Main */
if (o && (binmain = rz_bin_object_get_special_symbol(o, RZ_BIN_SPECIAL_SYMBOL_MAIN))) {
if (binmain->paddr != UT64_MAX) {
ut64 addr = rz_bin_object_get_vaddr(o, binmain->paddr, binmain->vaddr);
rz_core_analysis_fcn(core, addr, -1, RZ_ANALYSIS_XREF_TYPE_NULL, depth - 1);
}
}
rz_core_task_yield(&core->tasks);
RzBinObject *bin = rz_bin_cur_object(core->bin);
vector = bin ? (RzPVector *)rz_bin_object_get_entries(bin) : NULL;
if (vector) {
rz_pvector_foreach (vector, it) {
entry = *it;
if (entry->paddr == UT64_MAX) {
continue;
}
ut64 addr = rz_bin_object_get_vaddr(o, entry->paddr, entry->vaddr);
rz_core_analysis_fcn(core, addr, -1, RZ_ANALYSIS_XREF_TYPE_NULL, depth - 1);
}
}
rz_core_task_yield(&core->tasks);
if (analysis_vars) {
/* Set fcn type to RZ_ANALYSIS_FCN_TYPE_SYM for symbols */
RzList *fcns = rz_analysis_function_list(core->analysis);
rz_list_foreach_prev(fcns, iter, fcni) {
if (rz_cons_is_breaked()) {
break;
}
rz_core_recover_vars(core, fcni, true);
if (!strncmp(fcni->name, "sym.", 4) || !strncmp(fcni->name, "main", 4)) {
fcni->type = RZ_ANALYSIS_FCN_TYPE_SYM;
}
}
}
rz_core_task_yield(&core->tasks);
RzPlatformTarget *arch_target = rz_analysis_get_arch_target(core->analysis);
rz_platform_profile_add_flag_every_io(arch_target->profile, core->flags);
rz_platform_index_add_flags_comments(core);
rz_cons_break_pop();
return true;
}
/**
* \brief Tries to detect the type of data at a given address and prints its contents.
*
* \param core The RzCore structure to use
* \param addr The address to analyze
* \param count The number of bytes to analyze
* \param depth The max depth for analyzing pointers
* \param wordsize The word size (when 0, the word size will be set to arch bits/8)
*/
RZ_API void rz_core_analysis_data(RZ_NONNULL RzCore *core, ut64 addr, ut32 count, ut32 depth, ut32 wordsize) {
rz_return_if_fail(core);
RzAnalysisData *d = NULL;
ut8 *buf = core->block;
ut32 old_len = core->blocksize;
ut64 old_offset = core->offset;
rz_core_seek_arch_bits(core, addr);
int bits = rz_asm_get_bits(core->rasm);
int word = wordsize ? wordsize : bits / 8;
char *str = NULL;
RzConsPrintablePalette *pal = rz_config_get_i(core->config, "scr.color") ? &rz_cons_singleton()->context->pal : NULL;
if (count > old_len) {
rz_core_block_size(core, count);
}
rz_core_seek(core, addr, true);
for (ut32 i = 0, j = 0; j < count; j++) {
d = rz_analysis_data(core->analysis, addr + i, buf + i, count - i, wordsize);
if (!d) {
i += word;
continue;
}
str = rz_analysis_data_to_string(d, pal);
if (RZ_STR_ISNOTEMPTY(str)) {
rz_cons_println(str);
}
switch (d->type) {
case RZ_ANALYSIS_DATA_INFO_TYPE_POINTER:
rz_cons_printf("`- ");
if (depth > 0) {
ut64 pointer = rz_mem_get_num(buf + i, word);
rz_core_analysis_data(core, pointer, 1, depth - 1, wordsize);
}
i += word;
break;
case RZ_ANALYSIS_DATA_INFO_TYPE_STRING:
i += d->len;
break;
default:
i += (d->len > 3) ? d->len : word;
break;
}
free(str);
rz_analysis_data_free(d);
}
if (count > old_len) {
rz_core_block_size(core, old_len);
}
rz_core_seek(core, old_offset, true);
}
struct block_flags_stat_t {
ut64 step;
ut64 from;
RzCoreAnalysisStatsItem *blocks;
};
static bool block_flags_stat(RzFlagItem *fi, void *user) {
struct block_flags_stat_t *u = (struct block_flags_stat_t *)user;
size_t piece = (fi->offset - u->from) / u->step;
if (fi->space && !strcmp(fi->space->name, "flirt")) {
u->blocks[piece].signatures++;
}
u->blocks[piece].flags++;
return true;
}
/**
* Generate statistics for a range of memory, e.g. for a colorful overview bar.
*
* Let `fullsz = to + 1 - from`.
* If `fullsz % step = 0`, then the result will be `fullsz / step` blocks of size `step`.
* Otherwise, it will be `fullsz / step` blocks of size `step` and one additional block
* covering the rest.
*
* \param lowest address to consider
* \param highest address to consider, inclusive. Must be greater than or equal to from.
* \param size of a single block in the output
*/
RZ_API RZ_OWN RzCoreAnalysisStats *rz_core_analysis_get_stats(RZ_NONNULL RzCore *core, ut64 from, ut64 to, ut64 step) {
rz_return_val_if_fail(core && to >= from && step, NULL);
RzList *fcns = rz_analysis_function_list(core->analysis);
RzAnalysisFunction *F;
RzAnalysisBlock *B;
RzBinSymbol *S;
RzListIter *iter;
void **it;
ut64 at;
RzCoreAnalysisStats *as = RZ_NEW0(RzCoreAnalysisStats);
if (!as) {
return NULL;
}
as->from = from;
as->to = to;
as->step = step;
rz_vector_init(&as->blocks, sizeof(RzCoreAnalysisStatsItem), NULL, NULL);
size_t count = (to == UT64_MAX && from == 0) ? rz_num_2_pow_64_div(step) : (to + 1 - from) / step;
if (from + count * step != to + 1) {
count++;
}
if (!count || SZT_MUL_OVFCHK(count, sizeof(RzCoreAnalysisStatsItem))) {
rz_core_analysis_stats_free(as);
return NULL;
}
RzCoreAnalysisStatsItem *blocks = rz_vector_insert_range(&as->blocks, 0, NULL, count);
if (!blocks) {
rz_core_analysis_stats_free(as);
return NULL;
}
memset(blocks, 0, count * sizeof(RzCoreAnalysisStatsItem));
for (at = from; at < to;) {
RzIOMap *map = rz_io_map_get(core->io, at);
size_t piece = (at - from) / step;
blocks[piece].perm = map ? map->perm : (core->io->desc ? core->io->desc->perm : 0);
ut64 prev = at;
at += step;
if (at < prev) {
break;
}
}
// iter all flags
struct block_flags_stat_t u = { .step = step, .from = from, .blocks = blocks };
rz_flag_foreach_range(core->flags, from, to, block_flags_stat, &u);
// iter all functions
rz_list_foreach (fcns, iter, F) {
if (F->addr < from || F->addr > to) {
continue;
}
size_t piece = (F->addr - from) / step;
blocks[piece].functions++;
ut64 last_piece = RZ_MIN((F->addr + rz_analysis_function_linear_size(F) - 1) / step, count - 1);
for (; piece <= last_piece; piece++) {
blocks[piece].in_functions++;
}
// iter all basic blocks
rz_pvector_foreach (F->bbs, it) {
B = (RzAnalysisBlock *)*it;
if (B->addr < from || B->addr > to) {
continue;
}
piece = (B->addr - from) / step;
blocks[piece].blocks++;
}
}
// iter all symbols
RzBinObject *o = rz_bin_cur_object(core->bin);
RzPVector *symbols = o ? (RzPVector *)rz_bin_object_get_symbols(o) : NULL;
rz_pvector_foreach (symbols, it) {
S = *it;
if (S->vaddr < from || S->vaddr > to) {
continue;
}
size_t piece = (S->vaddr - from) / step;
if (S->is_imported) {
blocks[piece].imports++;
}
blocks[piece].symbols++;
}
RzPVector *metas = to > from ? rz_meta_get_all_intersect(core->analysis, from, to - from, RZ_META_TYPE_ANY) : NULL;
if (metas) {
void **it;
rz_pvector_foreach (metas, it) {
RzIntervalNode *node = *it;
RzAnalysisMetaItem *mi = node->data;
if (node->start < from || node->end > to) {
continue;
}
size_t piece = (node->start - from) / step;
switch (mi->type) {
case RZ_META_TYPE_STRING:
blocks[piece].strings++;
break;
case RZ_META_TYPE_COMMENT:
blocks[piece].comments++;
break;
default:
break;
}
}
rz_pvector_free(metas);
}
return as;
}
RZ_API void rz_core_analysis_stats_free(RzCoreAnalysisStats *s) {
if (!s) {
return;
}
rz_vector_fini(&s->blocks);
free(s);
}
/**
* Get the lowest address that the i-th block in s covers (inclusive)
*/
RZ_API ut64 rz_core_analysis_stats_get_block_from(RZ_NONNULL const RzCoreAnalysisStats *s, size_t i) {
rz_return_val_if_fail(s, 0);
return s->from + s->step * i;
}
/**
* Get the highest address that the i-th block in s covers (inclusive)
*/
RZ_API ut64 rz_core_analysis_stats_get_block_to(RZ_NONNULL const RzCoreAnalysisStats *s, size_t i) {
rz_return_val_if_fail(s, 0);
size_t count = rz_vector_len(&s->blocks);
rz_return_val_if_fail(i < count, 0);
if (i + 1 == count) {
return s->to;
}
return rz_core_analysis_stats_get_block_from(s, i + 1) - 1;
}
RZ_API RzList /*<RzAnalysisCycleHook *>*/ *rz_core_analysis_cycles(RzCore *core, int ccl) {
ut64 addr = core->offset;
int depth = 0;
RzAnalysisOp *op = NULL;
RzAnalysisCycleFrame *prev = NULL, *cf = NULL;
RzAnalysisCycleHook *ch;
RzList *hooks = rz_list_new();
if (!hooks) {
return NULL;
}
cf = rz_analysis_cycle_frame_new();
rz_cons_break_push(NULL, NULL);
while (cf && !rz_cons_is_breaked()) {
if ((op = rz_core_analysis_op(core, addr, RZ_ANALYSIS_OP_MASK_BASIC)) && (op->cycles) && (ccl > 0)) {
rz_cons_clear_line(stderr);
eprintf("%i -- ", ccl);
addr += op->size;
switch (op->type) {
case RZ_ANALYSIS_OP_TYPE_JMP:
addr = op->jump;
ccl -= op->cycles;
loganalysis(op->addr, addr, depth);
break;
case RZ_ANALYSIS_OP_TYPE_UJMP:
case RZ_ANALYSIS_OP_TYPE_MJMP:
case RZ_ANALYSIS_OP_TYPE_UCALL:
case RZ_ANALYSIS_OP_TYPE_ICALL:
case RZ_ANALYSIS_OP_TYPE_RCALL:
case RZ_ANALYSIS_OP_TYPE_IRCALL:
ch = RZ_NEW0(RzAnalysisCycleHook);
ch->addr = op->addr;
eprintf("0x%08" PFMT64x " > ?\r", op->addr);
ch->cycles = ccl;
rz_list_append(hooks, ch);
ch = NULL;
while (!ch && cf) {
ch = rz_list_pop(cf->hooks);
if (ch) {
addr = ch->addr;
ccl = ch->cycles;
free(ch);
} else {
rz_analysis_cycle_frame_free(cf);
cf = prev;
if (cf) {
prev = cf->prev;
}
}
}
break;
case RZ_ANALYSIS_OP_TYPE_CJMP:
ch = RZ_NEW0(RzAnalysisCycleHook);
ch->addr = addr;
ch->cycles = ccl - op->failcycles;
rz_list_push(cf->hooks, ch);
ch = NULL;
addr = op->jump;
loganalysis(op->addr, addr, depth);
break;
case RZ_ANALYSIS_OP_TYPE_UCJMP:
case RZ_ANALYSIS_OP_TYPE_UCCALL:
ch = RZ_NEW0(RzAnalysisCycleHook);
ch->addr = op->addr;
ch->cycles = ccl;
rz_list_append(hooks, ch);
ch = NULL;
ccl -= op->failcycles;
eprintf("0x%08" PFMT64x " > ?\r", op->addr);
break;
case RZ_ANALYSIS_OP_TYPE_CCALL:
ch = RZ_NEW0(RzAnalysisCycleHook);
ch->addr = addr;
ch->cycles = ccl - op->failcycles;
rz_list_push(cf->hooks, ch);
ch = NULL;
// fallthrough
case RZ_ANALYSIS_OP_TYPE_CALL:
if (op->addr != op->jump) { // no selfies
cf->naddr = addr;
prev = cf;
cf = rz_analysis_cycle_frame_new();
cf->prev = prev;
}
ccl -= op->cycles;
addr = op->jump;
loganalysis(op->addr, addr, depth - 1);
break;
case RZ_ANALYSIS_OP_TYPE_RET:
ch = RZ_NEW0(RzAnalysisCycleHook);
if (prev) {
ch->addr = prev->naddr;
ccl -= op->cycles;
ch->cycles = ccl;
rz_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;
rz_list_append(hooks, ch);
eprintf("? < 0x%08" PFMT64x "\r", op->addr);
}
ch = NULL;
while (!ch && cf) {
ch = rz_list_pop(cf->hooks);
if (ch) {
addr = ch->addr;
ccl = ch->cycles;
free(ch);
} else {
rz_analysis_cycle_frame_free(cf);
cf = prev;
if (cf) {
prev = cf->prev;
}
}
}
break;
case RZ_ANALYSIS_OP_TYPE_CRET:
ch = RZ_NEW0(RzAnalysisCycleHook);
if (prev) {
ch->addr = prev->naddr;
ch->cycles = ccl - op->cycles;
rz_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;
rz_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 = RZ_NEW0(RzAnalysisCycleHook);
if (!ch) {
rz_analysis_cycle_frame_free(cf);
rz_list_free(hooks);
rz_cons_break_pop();
return NULL;
}
ch->addr = addr;
ch->cycles = ccl;
rz_list_append(hooks, ch);
ch = NULL;
while (!ch && cf) {
ch = rz_list_pop(cf->hooks);
if (ch) {
addr = ch->addr;
ccl = ch->cycles;
free(ch);
} else {
rz_analysis_cycle_frame_free(cf);
cf = prev;
if (cf) {
prev = cf->prev;
}
}
}
}
rz_analysis_op_free(op);
}
if (rz_cons_is_breaked()) {
while (cf) {
ch = rz_list_pop(cf->hooks);
while (ch) {
free(ch);
ch = rz_list_pop(cf->hooks);
}
prev = cf->prev;
rz_analysis_cycle_frame_free(cf);
cf = prev;
}
}
rz_cons_break_pop();
return hooks;
}
RZ_API void rz_core_analysis_undefine(RzCore *core, ut64 off) {
RzAnalysisFunction *f = rz_analysis_get_fcn_in(core->analysis, off, -1);
if (f) {
if (!strncmp(f->name, "fcn.", 4)) {
rz_flag_unset_name(core->flags, f->name);
}
rz_meta_del(core->analysis, RZ_META_TYPE_ANY, rz_analysis_function_min_addr(f), rz_analysis_function_linear_size(f));
}
rz_analysis_fcn_del_locs(core->analysis, off);
rz_analysis_fcn_del(core->analysis, off);
}
/* Join function at addr2 into function at addr */
// addr use to be core->offset
RZ_API void rz_core_analysis_fcn_merge(RzCore *core, ut64 addr, ut64 addr2) {
void **iter;
ut64 min = 0;
ut64 max = 0;
int first = 1;
RzAnalysisBlock *bb;
RzAnalysisFunction *f1 = rz_analysis_get_function_at(core->analysis, addr);
RzAnalysisFunction *f2 = rz_analysis_get_function_at(core->analysis, addr2);
if (!f1 || !f2) {
RZ_LOG_ERROR("core: cannot find function\n");
return;
} else if (f1 == f2) {
RZ_LOG_ERROR("core: cannot merge the same function\n");
return;
}
// join all basic blocks from f1 into f2 if they are not
// delete f2
RZ_LOG_WARN("core: merging 0x%08" PFMT64x " into 0x%08" PFMT64x "\n", addr, addr2);
rz_pvector_foreach (f1->bbs, iter) {
bb = (RzAnalysisBlock *)*iter;
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;
}
}
}
rz_pvector_foreach (f2->bbs, iter) {
bb = (RzAnalysisBlock *)*iter;
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;
}
}
rz_analysis_function_add_block(f1, bb);
}
// TODO: import data/code/refs
rz_analysis_function_delete(f2);
// update size
rz_analysis_function_relocate(f2, RZ_MIN(addr, addr2));
}
static bool isValidAddress(RzCore *core, ut64 addr) {
// check if address is mapped
RzIOMap *map = rz_io_map_get(core->io, addr);
if (!map) {
return false;
}
st64 fdsz = (st64)rz_io_fd_size(core->io, map->fd);
if (fdsz > 0 && map->delta > fdsz) {
return false;
}
// check if associated file is opened
RzIODesc *desc = rz_io_desc_get(core->io, map->fd);
if (!desc) {
return false;
}
// check if current map->fd is null://
if (!strncmp(desc->name, "null://", 7)) {
return false;
}
return true;
}
RZ_IPI void rz_core_add_string_ref(RzCore *core, ut64 xref_from, ut64 xref_to) {
if (xref_to == UT64_MAX || !xref_to) {
return;
}
RzAnalysisEsil *esil = rz_analysis_get_esil(core->analysis);
if (!xref_from || xref_from == UT64_MAX) {
xref_from = esil->address;
}
size_t length = 0;
char *string = NULL;
RzStrEnc encoding = 0;
if (rz_core_get_string_at(core, xref_to, &string, &length, &encoding, true)) {
rz_analysis_xrefs_set(core->analysis, xref_from, xref_to, RZ_ANALYSIS_XREF_TYPE_DATA);
rz_name_filter(string, -1, true);
char *flagname = rz_str_newf("str.%s", string);
rz_flag_space_push(core->flags, RZ_FLAGS_FS_STRINGS);
rz_flag_set(core->flags, flagname, xref_to, length);
rz_flag_space_pop(core->flags);
rz_meta_set_with_subtype(core->analysis, RZ_META_TYPE_STRING, encoding, xref_to, length, string);
free(string);
free(flagname);
}
}
static inline bool aligns(ut64 addr, size_t align) {
return align > 0 && addr % align == 0;
}
static void cb_in_range_aav(RzCore *core, ut64 from, ut64 to, int vsize, void *user);
RZ_API int rz_core_search_value_in_range(RzCore *core, RzInterval search_itv, ut64 vmin,
ut64 vmax, int vsize, inRangeCb cb, void *cb_user) {
int align = core->search->align, hitctr = 0;
bool vinfun = rz_config_get_b(core->config, "analysis.vinfun");
bool vinfunr = rz_config_get_b(core->config, "analysis.vinfunrange");
bool analyze_strings = rz_config_get_b(core->config, "analysis.strings");
bool big_endian = rz_config_get_b(core->config, "cfg.bigendian");
ut8 buf[4096];
ut64 v64, value = 0, size;
ut64 from = rz_itv_begin(search_itv), to = rz_itv_end(search_itv);
ut32 v32;
ut16 v16;
if (from >= to) {
RZ_LOG_ERROR("core: `from` must be lower than `to`\n");
return -1;
} else if (vmin >= vmax) {
RZ_LOG_ERROR("core: `vmin` must be lower than `vmax`\n");
return -1;
} else if (to == UT64_MAX) {
RZ_LOG_ERROR("core: invalid destination boundary\n");
return -1;
}
bool maybeThumb = align > 1 &&
rz_analysis_plugin_is_arch(core->analysis, "arm") &&
rz_analysis_get_bits(core->analysis) != 64;
rz_cons_break_push(NULL, NULL);
if (!rz_io_is_valid_offset(core->io, from, 0)) {
hitctr = -1;
goto beach;
}
while (from < to) {
size = RZ_MIN(to - from, sizeof(buf));
memset(buf, 0xff, sizeof(buf)); // probably unnecessary
if (rz_cons_is_breaked()) {
goto beach;
}
bool res = rz_io_read_at_mapped(core->io, from, buf, size);
if (!res || !memcmp(buf, "\xff\xff\xff\xff", 4) || !memcmp(buf, "\x00\x00\x00\x00", 4)) {
if (!isValidAddress(core, from)) {
ut64 next = rz_io_map_next_address(core->io, from);
if (next == UT64_MAX) {
from += sizeof(buf);
} else {
from += (next - from);
}
continue;
}
}
if (size <= vsize) {
break;
}
RzAnalysisOp *op = rz_analysis_op_new();
for (ut64 i = 0; i <= (size - vsize); i++) {
void *v = (buf + i);
ut64 addr = from + i;
if (rz_cons_is_breaked()) {
rz_analysis_op_free(op);
goto beach;
}
if (!aligns(addr, align)) {
continue;
}
int match = false;
int left = size - i;
if (vsize > left) {
break;
}
switch (vsize) {
case 1:
value = rz_read_ble8(v);
match = (buf[i] >= vmin && buf[i] <= vmax);
break;
case 2:
v16 = rz_read_ble16(v, big_endian);
match = (v16 >= vmin && v16 <= vmax);
value = v16;
break;
case 4:
v32 = rz_read_ble32(v, big_endian);
match = (v32 >= vmin && v32 <= vmax);
value = v32;
break;
case 8:
v64 = rz_read_ble64(v, big_endian);
match = (v64 >= vmin && v64 <= vmax);
value = v64;
break;
default:
RZ_LOG_ERROR("core: unknown vsize %d (supported only 1,2,4,8)\n", vsize);
hitctr = -1;
rz_analysis_op_free(op);
goto beach;
}
if (match && !vinfun) {
if (vinfunr) {
if (rz_analysis_get_fcn_in_bounds(core->analysis, addr, RZ_ANALYSIS_FCN_TYPE_NULL)) {
match = false;
}
} else {
if (rz_analysis_get_fcn_in(core->analysis, addr, RZ_ANALYSIS_FCN_TYPE_NULL)) {
match = false;
}
}
}
if (match && value) {
bool isValidMatch = true;
if (!aligns(value, align)) {
// ignored .. unless we are analyzing arm/thumb and lower bit is 1
isValidMatch = false;
if (maybeThumb && (value & 1)) {
isValidMatch = true;
}
}
if (isValidMatch) {
cb(core, addr, value, vsize, cb_user);
if (analyze_strings) {
rz_core_add_string_ref(core, addr, value);
}
hitctr++;
}
}
if (!match && core->analysis && op && cb == cb_in_range_aav) {
rz_analysis_op_init(op);
int oplen = rz_analysis_op(core->analysis, op, from + i, buf + i, vsize, RZ_ANALYSIS_OP_MASK_BASIC);
if (oplen > 0) {
i += oplen - 1;
}
rz_analysis_op_fini(op);
}
}
rz_analysis_op_free(op);
if (size == to - from) {
break;
}
from += size - vsize + 1;
}
beach:
rz_cons_break_pop();
return hitctr;
}
typedef struct {
HtUU *visited;
RzList /*<RzAnalysisBlock *>*/ *path;
RzCore *core;
ut64 from;
RzAnalysisBlock *fromBB;
ut64 to;
RzAnalysisBlock *toBB;
RzAnalysisBlock *cur;
bool followCalls;
int followDepth;
int count; // max number of results
} RzCoreAnalPaths;
static bool printAnalPaths(RzCoreAnalPaths *p, PJ *pj) {
RzListIter *iter;
RzAnalysisBlock *path;
if (pj) {
pj_a(pj);
} else {
rz_cons_printf("pdb @@= ");
}
rz_list_foreach (p->path, iter, path) {
if (pj) {
pj_n(pj, path->addr);
} else {
rz_cons_printf("0x%08" PFMT64x " ", path->addr);
}
}
if (pj) {
pj_end(pj);
} else {
rz_cons_printf("\n");
}
return (p->count < 1 || --p->count > 0);
}
static void analPaths(RzCoreAnalPaths *p, PJ *pj);
static void analPathFollow(RzCoreAnalPaths *p, ut64 addr, PJ *pj) {
if (addr == UT64_MAX) {
return;
}
bool found;
ht_uu_find(p->visited, addr, &found);
if (!found) {
p->cur = rz_analysis_find_most_relevant_block_in(p->core->analysis, addr);
analPaths(p, pj);
}
}
static void analPaths(RzCoreAnalPaths *p, PJ *pj) {
RzAnalysisBlock *cur = p->cur;
if (!cur) {
// eprintf ("eof\n");
return;
}
/* handle ^C */
if (rz_cons_is_breaked()) {
return;
}
ht_uu_insert(p->visited, cur->addr, 1);
rz_list_append(p->path, cur);
if (p->followDepth && --p->followDepth == 0) {
return;
}
if (p->toBB && cur->addr == p->toBB->addr) {
if (!printAnalPaths(p, pj)) {
return;
}
} else {
RzAnalysisBlock *c = cur;
ut64 j = cur->jump;
ut64 f = cur->fail;
analPathFollow(p, j, pj);
cur = c;
analPathFollow(p, f, pj);
if (p->followCalls) {
int i;
for (i = 0; i < cur->op_pos_size; i++) {
ut64 addr = cur->addr + cur->op_pos[i];
RzAnalysisOp *op = rz_core_analysis_op(p->core, addr, RZ_ANALYSIS_OP_MASK_BASIC);
if (op && op->type == RZ_ANALYSIS_OP_TYPE_CALL) {
analPathFollow(p, op->jump, pj);
}
cur = c;
rz_analysis_op_free(op);
}
}
}
p->cur = rz_list_pop(p->path);
ht_uu_delete(p->visited, cur->addr);
if (p->followDepth) {
p->followDepth++;
}
}
RZ_API void rz_core_analysis_paths(RzCore *core, ut64 from, ut64 to, bool followCalls, int followDepth, bool is_json) {
RzAnalysisBlock *b0 = rz_analysis_find_most_relevant_block_in(core->analysis, from);
RzAnalysisBlock *b1 = rz_analysis_find_most_relevant_block_in(core->analysis, to);
PJ *pj = NULL;
if (!b0) {
RZ_LOG_ERROR("core: cannot find basic block for 0x%08" PFMT64x "\n", from);
return;
}
if (!b1) {
RZ_LOG_ERROR("core: cannot find basic block for 0x%08" PFMT64x "\n", to);
return;
}
RzCoreAnalPaths rcap = { 0 };
rcap.visited = ht_uu_new();
rcap.path = rz_list_new();
rcap.core = core;
rcap.from = from;
rcap.fromBB = b0;
rcap.to = to;
rcap.toBB = b1;
rcap.cur = b0;
rcap.count = rz_config_get_i(core->config, "search.maxhits");
rcap.followCalls = followCalls;
rcap.followDepth = followDepth;
// Initialize a PJ object for json mode
if (is_json) {
pj = pj_new();
pj_a(pj);
}
analPaths(&rcap, pj);
if (is_json) {
pj_end(pj);
rz_cons_printf("%s", pj_string(pj));
}
if (pj) {
pj_free(pj);
}
ht_uu_free(rcap.visited);
rz_list_free(rcap.path);
}
static bool analyze_noreturn_function(RzCore *core, RzAnalysisFunction *f) {
void **iter;
RzAnalysisBlock *bb;
rz_pvector_foreach (f->bbs, iter) {
bb = (RzAnalysisBlock *)*iter;
ut64 opaddr = rz_analysis_block_get_op_addr(bb, bb->ninstr - 1);
if (opaddr == UT64_MAX) {
return false;
}
// get last opcode
RzAnalysisOp *op = rz_core_op_analysis(core, opaddr, RZ_ANALYSIS_OP_MASK_HINT);
if (!op) {
RZ_LOG_ERROR("core: cannot analyze opcode at 0x%08" PFMT64x "\n", opaddr);
return false;
}
switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) {
case RZ_ANALYSIS_OP_TYPE_ILL:
case RZ_ANALYSIS_OP_TYPE_RET:
rz_analysis_op_free(op);
return false;
case RZ_ANALYSIS_OP_TYPE_JMP:
if (!rz_analysis_function_contains(f, op->jump)) {
rz_analysis_op_free(op);
return false;
}
break;
}
rz_analysis_op_free(op);
}
return true;
}
/* set flags for every function */
RZ_API void rz_core_analysis_flag_every_function(RzCore *core) {
RzList *fcns = rz_analysis_function_list(core->analysis);
RzListIter *iter;
RzAnalysisFunction *fcn;
rz_flag_space_push(core->flags, RZ_FLAGS_FS_FUNCTIONS);
rz_list_foreach (fcns, iter, fcn) {
rz_flag_set(core->flags, fcn->name,
fcn->addr, rz_analysis_function_size_from_entry(fcn));
}
rz_flag_space_pop(core->flags);
}
static bool add_mmio_flag_cb(void *user, const ut64 addr, const void *v) {
const char *name = v;
RzFlag *flags = (RzFlag *)user;
rz_flag_space_push(flags, RZ_FLAGS_FS_MMIO_REGISTERS);
rz_flag_set(flags, name, addr, 1);
rz_flag_space_pop(flags);
return true;
}
static bool add_mmio_extended_flag_cb(void *user, const ut64 addr, const void *v) {
const char *name = v;
RzFlag *flags = (RzFlag *)user;
rz_flag_space_push(flags, RZ_FLAGS_FS_MMIO_REGISTERS_EXTENDED);
rz_flag_set(flags, name, addr, 1);
rz_flag_space_pop(flags);
return true;
}
/**
* \brief Adds the IO and extended IO registers from the CPU profiles as flags
* \param profile reference to RzPlatformProfile
* \param flags reference to RzFlag
*/
RZ_API void rz_platform_profile_add_flag_every_io(RZ_NONNULL RzPlatformProfile *profile, RZ_NONNULL RzFlag *flags) {
rz_return_if_fail(profile && flags);
rz_flag_unset_all_in_space(flags, RZ_FLAGS_FS_MMIO_REGISTERS);
rz_flag_unset_all_in_space(flags, RZ_FLAGS_FS_MMIO_REGISTERS_EXTENDED);
ht_up_foreach(profile->registers_mmio, add_mmio_flag_cb, flags);
ht_up_foreach(profile->registers_extended, add_mmio_extended_flag_cb, flags);
}
static bool add_arch_platform_flag_comment_cb(void *user, const ut64 addr, const void *v) {
if (!v) {
return false;
}
RzPlatformItem *item = (RzPlatformItem *)v;
RzCore *core = (RzCore *)user;
rz_flag_space_push(core->flags, RZ_FLAGS_FS_PLATFORM_PORTS);
rz_flag_set(core->flags, item->name, addr, 1);
rz_flag_space_pop(core->flags);
if (item->comment) {
rz_core_meta_comment_add(core, item->comment, addr);
}
return true;
}
/**
* \brief Adds the information from the Platform Profiles as flags and comments
*
* \param core reference to RzCore
*/
RZ_API bool rz_platform_index_add_flags_comments(RzCore *core) {
rz_flag_unset_all_in_space(core->flags, RZ_FLAGS_FS_PLATFORM_PORTS);
RzPlatformTargetIndex *platform_target = rz_analysis_get_platform_target(core->analysis);
ht_up_foreach(platform_target->platforms, add_arch_platform_flag_comment_cb, core);
return true;
}
/* TODO: move into rz_analysis_function_rename (); */
RZ_API bool rz_core_analysis_function_rename(RzCore *core, ut64 addr, const char *_name) {
rz_return_val_if_fail(core && _name, false);
_name = rz_str_trim_head_ro(_name);
RzAnalysisCallbacks *acb = rz_analysis_get_callbacks(core->analysis);
char *name = getFunctionNamePrefix(core, addr, _name);
// RzAnalysisFunction *fcn = rz_analysis_get_fcn_in (core->analysis, addr, RZ_ANALYSIS_FCN_TYPE_ANY);
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, addr);
if (fcn) {
RzFlagItem *flag = rz_flag_get(core->flags, fcn->name);
if (flag && flag->space && strcmp(flag->space->name, RZ_FLAGS_FS_FUNCTIONS) == 0) {
// Only flags in the functions fs should be renamed, e.g. we don't want to rename symbol flags.
if (!rz_flag_rename(core->flags, flag, name)) {
// If the rename failed, it may be because there is already a flag with the target name
if (rz_flag_get(core->flags, name)) {
// If that is the case, just unset the old one to not leak it (e.g. leaving behind fcn.<offset>)
rz_flag_unset(core->flags, flag);
}
}
} else {
// No flag or not specific to the function, create a new one.
rz_flag_space_push(core->flags, RZ_FLAGS_FS_FUNCTIONS);
rz_flag_set(core->flags, name, fcn->addr, rz_analysis_function_size_from_entry(fcn));
rz_flag_space_pop(core->flags);
}
rz_analysis_function_rename(fcn, name);
if (acb->on_fcn_rename) {
acb->on_fcn_rename(core->analysis, core, fcn, name);
}
free(name);
return true;
}
free(name);
return false;
}
RZ_API bool rz_core_analysis_function_add(RzCore *core, const char *name, ut64 addr, bool analyze_recursively) {
int depth = rz_config_get_i(core->config, "analysis.depth");
RzAnalysisOptions *aopt = rz_analysis_get_options(core->analysis);
RzAnalysisFunction *fcn = NULL;
// rz_core_analysis_undefine (core, core->offset);
rz_core_analysis_fcn(core, addr, UT64_MAX, RZ_ANALYSIS_XREF_TYPE_NULL, depth);
fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
if (fcn) {
/* ensure we use a proper name */
rz_core_analysis_function_rename(core, addr, fcn->name);
if (aopt->vars) {
rz_core_recover_vars(core, fcn, true);
}
rz_analysis_fcn_vars_add_types(core->analysis, fcn);
} else {
RZ_LOG_DEBUG("Unable to analyze function at 0x%08" PFMT64x "\n", addr);
}
if (analyze_recursively) {
fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0); /// XXX wrong in case of nopskip
if (fcn) {
RzAnalysisXRef *xref;
RzListIter *iter;
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
rz_list_foreach (xrefs, iter, xref) {
if (xref->to == UT64_MAX) {
// RZ_LOG_WARN("core: ignore 0x%08"PFMT64x" call 0x%08"PFMT64x"\n", ref->at, ref->addr);
continue;
}
if (xref->type != RZ_ANALYSIS_XREF_TYPE_CODE && xref->type != RZ_ANALYSIS_XREF_TYPE_CALL) {
/* only follow code/call references */
continue;
}
if (!rz_io_is_valid_offset(core->io, xref->to, !aopt->noncode)) {
continue;
}
rz_core_analysis_fcn(core, xref->to, fcn->addr, RZ_ANALYSIS_XREF_TYPE_CALL, depth);
/* use recursivity here */
RzAnalysisFunction *f = rz_analysis_get_function_at(core->analysis, xref->to);
if (f) {
RzListIter *iter;
RzAnalysisXRef *xref1;
RzList *xrefs1 = rz_analysis_function_get_xrefs_from(f);
rz_list_foreach (xrefs1, iter, xref1) {
if (!rz_io_is_valid_offset(core->io, xref1->to, !aopt->noncode)) {
continue;
}
if (xref1->type != RZ_ANALYSIS_XREF_TYPE_CODE && xref1->type != RZ_ANALYSIS_XREF_TYPE_CALL) {
continue;
}
rz_core_analysis_fcn(core, xref1->to, f->addr, RZ_ANALYSIS_XREF_TYPE_CALL, depth);
// recursively follow fcn->refs again and again
}
rz_list_free(xrefs1);
} else {
f = rz_analysis_get_fcn_in(core->analysis, fcn->addr, 0);
if (f) {
/* cut function */
rz_analysis_function_resize(f, addr - fcn->addr);
rz_core_analysis_fcn(core, xref->to, fcn->addr,
RZ_ANALYSIS_XREF_TYPE_CALL, depth);
f = rz_analysis_get_function_at(core->analysis, fcn->addr);
}
if (!f) {
RZ_LOG_ERROR("core: cannot find function at 0x%08" PFMT64x "\n", fcn->addr);
rz_list_free(xrefs);
return false;
}
}
}
rz_list_free(xrefs);
if (aopt->vars) {
rz_core_recover_vars(core, fcn, true);
}
}
}
if (RZ_STR_ISNOTEMPTY(name) && !rz_core_analysis_function_rename(core, addr, name)) {
RZ_LOG_ERROR("core: cannot find function at 0x%08" PFMT64x "\n", addr);
return false;
}
rz_core_analysis_propagate_noreturn(core, addr);
rz_core_analysis_flag_every_function(core);
return true;
}
RZ_IPI char *rz_core_analysis_function_signature(RzCore *core, RzOutputMode mode, char *fcn_name) {
RzListIter *iter;
RzAnalysisFuncArg *arg;
RzAnalysisFunction *fcn;
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
if (fcn_name) {
fcn = rz_analysis_get_function_byname(core->analysis, fcn_name);
} else {
fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
if (fcn) {
fcn_name = fcn->name;
}
}
if (!fcn) {
return NULL;
}
char *signature = NULL;
if (mode == RZ_OUTPUT_MODE_JSON) {
PJ *j = pj_new();
if (!j) {
return NULL;
}
pj_a(j);
char *key = NULL;
if (fcn_name) {
key = rz_analysis_function_name_resolve(core->analysis, fcn_name);
}
if (key) {
RzType *ret_type = rz_type_func_ret(typedb, key);
char *ret_type_str = NULL;
if (ret_type) {
ret_type_str = rz_type_as_string(typedb, ret_type);
}
int nargs = rz_type_func_args_count(typedb, key);
pj_o(j);
pj_ks(j, "name", rz_str_get_null(key));
if (ret_type_str) {
pj_ks(j, "return", ret_type_str);
}
pj_k(j, "args");
pj_a(j);
if (nargs) {
RzList *list = rz_core_get_func_args(core, fcn_name);
rz_list_foreach (list, iter, arg) {
char *type = rz_type_as_string(typedb, arg->orig_c_type);
pj_o(j);
pj_ks(j, "name", arg->name);
pj_ks(j, "type", type);
pj_end(j);
free(type);
}
rz_list_free(list);
}
pj_end(j);
pj_ki(j, "count", nargs);
pj_end(j);
free(ret_type_str);
free(key);
} else {
pj_o(j);
pj_ks(j, "name", rz_str_get_null(fcn_name));
pj_k(j, "args");
pj_a(j);
RzAnalysisFcnVarsCache cache;
rz_analysis_fcn_vars_cache_init(core->analysis, &cache, fcn);
int nargs = rz_list_length(cache.arg_vars);
RzAnalysisVar *var;
rz_list_foreach (cache.sorted_vars, iter, var) {
pj_o(j);
pj_ks(j, "name", var->name);
char *vartype = rz_type_as_string(typedb, var->type);
pj_ks(j, "type", vartype);
pj_end(j);
free(vartype);
}
rz_analysis_fcn_vars_cache_fini(&cache);
pj_end(j);
pj_ki(j, "count", nargs);
pj_end(j);
}
pj_end(j);
signature = rz_str_dup(pj_string(j));
pj_free(j);
} else {
signature = rz_analysis_fcn_format_sig(core->analysis, fcn, fcn_name, NULL, NULL, NULL);
}
return signature;
}
static RzAnalysisBlock *find_block_at_xref_addr(RzCore *core, ut64 addr) {
RzList *blocks = rz_analysis_get_blocks_in(core->analysis, addr);
if (!blocks) {
return NULL;
}
RzAnalysisBlock *block = NULL;
RzListIter *bit;
RzAnalysisBlock *block_cur;
rz_list_foreach (blocks, bit, block_cur) {
if (rz_analysis_block_op_starts_at(block_cur, addr)) {
block = block_cur;
break;
}
}
if (block) {
rz_analysis_block_ref(block);
}
rz_list_free(blocks);
return block;
}
static void relocation_function_process_noreturn(RzCore *core, RzAnalysisBlock *b, RzSetU *todo, ut64 opsize, ut64 reladdr, ut64 addr) {
rz_analysis_noreturn_add(core->analysis, NULL, reladdr);
// Add all functions that might have become noreturn by this to the todo list to reanalyze them later.
// This must be done before chopping because b might get freed.
RzListIter *it;
RzAnalysisFunction *fcn;
rz_list_foreach (b->fcns, it, fcn) {
rz_set_u_add(todo, (ut64)(size_t)fcn);
}
// Chop the block
rz_analysis_block_chop_noreturn(b, addr + opsize);
}
static void relocation_noreturn_process(RzCore *core, RzList /*<char *>*/ *noretl, RzSetU *todo, RzAnalysisBlock *b, RzBinReloc *rel, ut64 opsize, ut64 addr) {
RzListIter *iter3;
char *noret;
if (rel->import) {
rz_list_foreach (noretl, iter3, noret) {
if (!strcmp(rel->import->name, noret)) {
relocation_function_process_noreturn(core, b, todo, opsize, rel->vaddr, addr);
}
}
} else if (rel->symbol) {
rz_list_foreach (noretl, iter3, noret) {
if (!strcmp(rel->symbol->name, noret)) {
relocation_function_process_noreturn(core, b, todo, opsize, rel->symbol->vaddr, addr);
}
}
}
}
#define CALL_BUF_SIZE 32
struct core_noretl {
RzCore *core;
RzList /*<char *>*/ *noretl;
RzSetU *todo;
};
static bool process_reference_noreturn_cb(void *u, const ut64 addr, const void *v) {
RzCore *core = ((struct core_noretl *)u)->core;
RzList *noretl = ((struct core_noretl *)u)->noretl;
RzSetU *todo = ((struct core_noretl *)u)->todo;
RzAnalysisXRef *xref = (RzAnalysisXRef *)v;
if (xref->type != RZ_ANALYSIS_XREF_TYPE_CALL && xref->type != RZ_ANALYSIS_XREF_TYPE_CODE) {
return true;
}
// At first we check if there are any relocations that override the call address
// Note, that the relocation overrides only the part of the instruction
ut8 buf[CALL_BUF_SIZE] = { 0 };
RzAnalysisOp op = { 0 };
if (!rz_io_read_at_mapped(core->io, addr, buf, sizeof(buf))) {
RZ_LOG_INFO("analysis: Fail to load %d bytes of data at 0x%08" PFMT64x "\n", CALL_BUF_SIZE, addr);
return true;
}
rz_analysis_op_init(&op);
if (rz_analysis_op(core->analysis, &op, addr, buf, CALL_BUF_SIZE, 0) > 0) {
RzBinReloc *rel = rz_core_getreloc(core, addr, op.size);
if (rel) {
// Find the block that has an instruction at exactly the reference addr
RzAnalysisBlock *block = find_block_at_xref_addr(core, addr);
if (!block) {
rz_analysis_op_fini(&op);
return true;
}
relocation_noreturn_process(core, noretl, todo, block, rel, op.size, addr);
}
}
rz_analysis_op_fini(&op);
return true;
}
static bool process_refs_cb(void *u, const ut64 k, const void *v) {
HtUP *ht = (HtUP *)v;
ht_up_foreach(ht, process_reference_noreturn_cb, u);
return true;
}
static bool reanalyze_fcns_cb(void *u, const ut64 k, const void *v) {
RzCore *core = u;
RzAnalysisFunction *fcn = (RzAnalysisFunction *)(size_t)k;
if (fcn->addr && analyze_noreturn_function(core, fcn)) {
fcn->is_noreturn = true;
rz_analysis_noreturn_add(core->analysis, NULL, fcn->addr);
}
return true;
}
RZ_API void rz_core_analysis_propagate_noreturn_relocs(RzCore *core, ut64 addr) {
// Processing every reference calls rz_analysis_op() which sometimes changes the
// state of `asm.bits` variable, thus we save it to restore after the processing
// is finished.
int bits = rz_asm_get_bits(core->rasm);
// find known noreturn functions to propagate
RzList *noretl = rz_analysis_noreturn_functions(core->analysis);
// List of the potentially noreturn functions
RzSetU *todo = rz_set_u_new();
struct core_noretl u = { core, noretl, todo };
HtUP *ht_xrefs_to = rz_analysis_get_xrefs_to(core->analysis);
ht_up_foreach(ht_xrefs_to, process_refs_cb, &u);
rz_list_free(noretl);
rz_asm_set_bits(core->rasm, bits);
rz_analysis_set_bits(core->analysis, bits);
// For every function in todo list analyze if it's potentially become noreturn
ht_up_foreach(todo, reanalyze_fcns_cb, core);
rz_set_u_free(todo);
}
RZ_API void rz_core_analysis_propagate_noreturn(RzCore *core, ut64 addr) {
RzList *fcns = rz_analysis_function_list(core->analysis);
RzList *todo = rz_list_newf(free);
if (!todo) {
return;
}
HtUU *done = ht_uu_new();
if (!done) {
rz_list_free(todo);
return;
}
RzAnalysisFunction *request_fcn = NULL;
if (addr != UT64_MAX) {
request_fcn = rz_analysis_get_function_at(core->analysis, addr);
if (!request_fcn) {
rz_list_free(todo);
ht_uu_free(done);
return;
}
}
// At first we propagate all noreturn functions that are imports or symbols
// via the relocations
rz_core_analysis_propagate_noreturn_relocs(core, addr);
// find known noreturn functions to propagate
RzListIter *iter;
RzAnalysisFunction *f;
rz_list_foreach (fcns, iter, f) {
if (f->is_noreturn) {
ut64 *n = ut64_new(f->addr);
rz_list_append(todo, n);
}
}
while (!rz_list_empty(todo)) {
ut64 *paddr = (ut64 *)rz_list_pop(todo);
ut64 noret_addr = *paddr;
free(paddr);
if (rz_cons_is_breaked()) {
break;
}
RzList *xrefs = rz_analysis_xrefs_get_to(core->analysis, noret_addr);
RzAnalysisXRef *xref;
rz_list_foreach (xrefs, iter, xref) {
RzAnalysisOp *xrefop = rz_core_op_analysis(core, xref->from, RZ_ANALYSIS_OP_MASK_ALL);
if (!xrefop) {
RZ_LOG_ERROR("core: cannot analyze opcode at 0x%08" PFMT64x "\n", xref->from);
continue;
}
ut64 call_addr = xref->from;
ut64 chop_addr = call_addr + xrefop->size;
rz_analysis_op_free(xrefop);
if (xref->type != RZ_ANALYSIS_XREF_TYPE_CALL) {
continue;
}
// Find the block that has an instruction at exactly the xref addr
RzAnalysisBlock *block = find_block_at_xref_addr(core, call_addr);
if (!block) {
continue;
}
RzList *block_fcns = rz_list_clone(block->fcns);
if (request_fcn) {
// specific function requested, check if it contains the bb
if (!rz_list_contains(block->fcns, request_fcn)) {
goto kontinue;
}
} else {
// rz_analysis_block_chop_noreturn() might free the block!
block = rz_analysis_block_chop_noreturn(block, chop_addr);
}
RzListIter *fit;
rz_list_foreach (block_fcns, fit, f) {
bool found = ht_uu_find(done, f->addr, NULL) != 0;
if (f->addr && !found && analyze_noreturn_function(core, f)) {
f->is_noreturn = true;
rz_analysis_noreturn_add(core->analysis, NULL, f->addr);
ut64 *n = malloc(sizeof(ut64));
*n = f->addr;
rz_list_append(todo, n);
ht_uu_insert(done, *n, 1);
}
}
kontinue:
if (block) {
rz_analysis_block_unref(block);
}
rz_list_free(block_fcns);
}
rz_list_free(xrefs);
}
rz_list_free(todo);
ht_uu_free(done);
}
RZ_IPI bool rz_core_analysis_var_rename(RzCore *core, const char *name, const char *newname) {
RzAnalysisOp *op = rz_core_analysis_op(core, core->offset, RZ_ANALYSIS_OP_MASK_BASIC);
if (!name) {
RzAnalysisVar *var = op ? rz_analysis_get_used_function_var(core->analysis, op->addr) : NULL;
if (var) {
name = var->name;
} else {
RZ_LOG_ERROR("core: cannot find var at 0x%08" PFMT64x "\n", core->offset);
rz_analysis_op_free(op);
return false;
}
}
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, -1);
if (fcn) {
RzAnalysisVar *v1 = rz_analysis_function_get_var_byname(fcn, name);
if (v1) {
rz_analysis_var_rename(v1, newname, true);
} else {
RZ_LOG_ERROR("core: cannot find var by name (%s)\n", name);
return false;
}
} else {
RZ_LOG_ERROR("core: cannot find function at 0x%08" PFMT64x "\n", core->offset);
rz_analysis_op_free(op);
return false;
}
rz_analysis_op_free(op);
return true;
}
static bool is_in_data_map(RzCore *core, const ut64 address) {
if (address < 256 || address == UT64_MAX) {
// prevents a lot of false positives when it comes
// to invalid addresses, especially in x86 arch with
// mov and jmp instructions.
return false;
}
RzBinObject *o = rz_bin_cur_object(core->bin);
if (!o) {
return false;
}
const RzBinMap *map = rz_bin_object_get_map_at(o, address, core->io->va);
return map && map->psize > 2 && rz_bin_map_is_data(map);
}
static ut32 add_data_pointer(RzCore *core, const ut8 *bytes, const ut32 size, ut64 pc, ut32 min_op_size) {
RzAnalysis *analysis = core->analysis;
RzAnalysisOp aop = { 0 };
ut32 isize = 0;
ut64 pointer = 0;
rz_analysis_op_init(&aop);
if (rz_analysis_op(analysis, &aop, pc, bytes, size, RZ_ANALYSIS_OP_MASK_DISASM) < 1) {
rz_analysis_op_fini(&aop);
return min_op_size;
}
isize = aop.size;
pointer = aop.ptr;
rz_analysis_op_fini(&aop);
if (pointer == pc ||
!is_in_data_map(core, pointer) ||
rz_flag_get_list(core->flags, pointer) ||
rz_analysis_get_function_at(analysis, pointer)) {
return isize;
}
char *flagname = rz_str_newf("data.%08" PFMT64x, pointer);
if (!flagname) {
RZ_LOG_ERROR("Failed allocate flag name buffer for pointer to data\n");
return 0;
}
rz_flag_space_push(core->flags, RZ_FLAGS_FS_POINTERS);
int bits = rz_analysis_get_bits(core->analysis);
rz_flag_set(core->flags, flagname, pointer, bits / 8);
rz_flag_space_pop(core->flags);
free(flagname);
rz_analysis_xrefs_set(analysis, pc, pointer, RZ_ANALYSIS_XREF_TYPE_DATA);
return isize;
}
/**
* \brief Tries to resolve all the constant pointers and adds flags named data.XXXXXX
*
* \param core The RzCore to analyze
*/
RZ_IPI void rz_core_analysis_resolve_pointers_to_data(RzCore *core) {
RzAnalysis *analysis = core->analysis;
RzList *fcns = rz_analysis_function_list(analysis);
RzCoreBind *coreb = rz_analysis_get_core_bind(analysis);
bool can_use_pointers = rz_analysis_archinfo(analysis, RZ_ANALYSIS_ARCHINFO_CAN_USE_POINTERS);
if (!can_use_pointers) {
// never run this for archs that does not use pointers
return;
}
RzListIter *it;
RzAnalysisFunction *func = NULL;
RzAnalysisBlock *block = NULL;
ut8 *bytes = NULL;
void *archbits = NULL;
ut32 isize = 0, bsize = 0;
ut64 pc = 0;
ut32 min_op_size = rz_analysis_archinfo(analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
// ignore any hint.
RZ_PTR_MOVE(archbits, coreb->archbits);
rz_list_foreach (fcns, it, func) {
if (rz_cons_is_breaked()) {
break;
}
void **vit;
rz_pvector_foreach (func->bbs, vit) {
block = (RzAnalysisBlock *)*vit;
if (block->size < 1) {
continue;
}
bytes = malloc(block->size);
if (!bytes) {
RZ_LOG_ERROR("Failed allocate basic block bytes buffer\n");
goto end;
} else if (rz_io_nread_at(core->io, block->addr, bytes, block->size) < 0) {
free(bytes);
RZ_LOG_ERROR("Failed to read function basic block at address %" PFMT64x "\n", block->addr);
goto end;
}
for (ut32 i = 0; i < block->size;) {
pc = block->addr + i;
bsize = block->size - i;
if (!(isize = add_data_pointer(core, bytes + i, bsize, pc, min_op_size))) {
free(bytes);
goto end;
}
i += isize;
}
free(bytes);
}
}
end:
coreb->archbits = archbits;
}
static bool is_unknown_file(RzCore *core) {
if (core->bin->cur && core->bin->cur->o) {
return (rz_pvector_empty(core->bin->cur->o->sections));
}
return true;
}
static bool is_apple_target(RzCore *core) {
const char *arch = rz_config_get(core->config, "asm.arch");
if (!strstr(arch, "ppc") && !strstr(arch, "arm") && !strstr(arch, "x86")) {
return false;
}
RzBinObject *bo = rz_bin_cur_object(core->bin);
rz_return_val_if_fail(!bo || (bo->plugin && bo->plugin->name), false);
return bo ? strstr(bo->plugin->name, "mach") : false;
}
static void core_analysis_using_plugins(RzCore *core) {
RzIterator *it = ht_sp_as_iter(core->plugins);
RzCorePlugin **val;
rz_iterator_foreach(it, val) {
RzCorePlugin *plugin = *val;
if (plugin->analysis) {
plugin->analysis(core, rz_core_plugin_context_get(core, plugin));
}
}
rz_iterator_free(it);
}
static void core_analysis_analyze_local_var_and_arg(RzCore *core) {
RzList *fcns = rz_analysis_function_list(core->analysis);
RzAnalysisFunction *fcni;
RzListIter *iter;
rz_list_foreach (fcns, iter, fcni) {
if (rz_cons_is_breaked()) {
break;
}
RzList *list = rz_analysis_var_list(fcni, RZ_ANALYSIS_VAR_STORAGE_REG);
if (!rz_list_empty(list)) {
rz_list_free(list);
continue;
}
// extract only reg based var here
rz_core_recover_vars(core, fcni, true);
rz_list_free(list);
}
}
static void analysis_global_vars_from_symbols(RzCore *core) {
// DWARF/PDB already enumerate globals with accurate types; only fall back
// to symbol-table inference when no debug info is present.
RzAnalysisDebugInfo *dbg_info = rz_analysis_get_debug_info(core->analysis);
if (dbg_info && dbg_info->dw) {
return;
}
RzBinObject *obj = rz_bin_cur_object(core->bin);
if (!obj) {
return;
}
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
RzPVector *symbols = (RzPVector *)rz_bin_object_get_symbols(obj);
if (!symbols) {
return;
}
bool virt_addr = rz_config_get_b(core->config, "io.va");
void **it;
rz_pvector_foreach (symbols, it) {
RzBinSymbol *sym = *it;
if (!sym->name || sym->is_imported) {
continue;
}
if (!sym->type || strcmp(sym->type, RZ_BIN_TYPE_OBJECT_STR)) {
continue;
}
if (!sym->size) {
continue;
}
ut64 addr = virt_addr ? rz_bin_object_get_vaddr(obj, sym->paddr, sym->vaddr) : sym->paddr;
if (addr == UT64_MAX || addr == 0) {
continue;
}
if (rz_analysis_var_global_get_byaddr_in(core->analysis, addr)) {
continue;
}
RzType *type = sym->size == 1
? rz_type_identifier_of_base_type_str(typedb, "uint8_t")
: rz_type_array_of_base_type_str(typedb, "uint8_t", sym->size);
if (!type) {
continue;
}
// rz_analysis_var_global_create takes effective ownership of type
rz_analysis_var_global_create(core->analysis, sym->name, type, addr);
}
}
static int cmp_ut64s(const void *a, const void *b, RZ_UNUSED void *user) {
ut64 va = *(const ut64 *)a;
ut64 vb = *(const ut64 *)b;
return (va > vb) - (va < vb);
}
// rz_vector_upper_bound comparator: x is a raw ut64 key, y is a pointer to a ut64 element
#define CMP_UT64_VAL(x, y) ((int)((x) > *(const ut64 *)(y)) - (int)((x) < *(const ut64 *)(y)))
static bool collect_ht_keys_cb(void *user, const ut64 k, const void *v) {
RzVector *vec = user;
ut64 key = k;
rz_vector_push(vec, &key);
return true;
}
static bool addr_in_exec_section(RzBinObject *bo, ut64 addr) {
RzBinSection *sec = rz_bin_get_section_at(bo, addr, true);
return sec && (sec->perm & RZ_PERM_X);
}
static bool addr_in_exec_segment(RzBinObject *bo, ut64 addr) {
RzBinSection *seg = rz_bin_get_segment_at(bo, addr, true);
return seg && (seg->perm & RZ_PERM_X);
}
/** \brief How a DATA xref's instruction relates to its target address. */
typedef enum {
XREF_REF_OTHER = 0, ///< address computation, control flow, etc. — handled as before
XREF_REF_MEM_ACCESS, ///< load/store of the target: an unambiguous data access
XREF_REF_COINCIDENTAL_IMM, ///< immediate equals the target with no data access (e.g. `sub sp, sp, 0x810`)
} XrefRefKind;
/** \brief Classify how the instruction at \p from references \p target. */
static XrefRefKind xref_ref_kind(RzCore *core, ut64 from, ut64 target) {
RzAnalysisOp *op = rz_core_analysis_op(core, from, RZ_ANALYSIS_OP_MASK_VAL);
if (!op) {
return XREF_REF_OTHER;
}
XrefRefKind kind = XREF_REF_OTHER;
switch (op->type & RZ_ANALYSIS_OP_TYPE_MASK) {
case RZ_ANALYSIS_OP_TYPE_LOAD:
case RZ_ANALYSIS_OP_TYPE_STORE:
kind = XREF_REF_MEM_ACCESS;
break;
case RZ_ANALYSIS_OP_TYPE_MOV:
case RZ_ANALYSIS_OP_TYPE_LEA:
break; // address into a register: leave to the heuristic below
default:
if (op->val == target) {
kind = XREF_REF_COINCIDENTAL_IMM;
}
break;
}
rz_analysis_op_free(op);
return kind;
}
static void analysis_mark_xrefs_as_data(RzCore *core) {
int bits = rz_asm_get_bits(core->rasm);
ut64 ptr_size = bits == 64 ? 8 : 4;
RzList *all_xrefs = rz_analysis_xrefs_list(core->analysis);
if (!all_xrefs) {
return;
}
// collect every target address that has at least one CODE/CALL xref
RzSetU *code_call_targets = rz_set_u_new();
if (!code_call_targets) {
rz_list_free(all_xrefs);
return;
}
RzListIter *iter;
RzAnalysisXRef *xref;
rz_list_foreach (all_xrefs, iter, xref) {
if (xref->type == RZ_ANALYSIS_XREF_TYPE_CODE ||
xref->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
rz_set_u_add(code_call_targets, xref->to);
}
}
// collect unique DATA/STRING targets outside functions
RzSetU *data_targets = rz_set_u_new();
if (!data_targets) {
rz_set_u_free(code_call_targets);
rz_list_free(all_xrefs);
return;
}
// subset of data_targets that are in exec sections (pool entries): size capped at ptr_size
RzSetU *exec_targets = rz_set_u_new();
if (!exec_targets) {
rz_set_u_free(data_targets);
rz_set_u_free(code_call_targets);
rz_list_free(all_xrefs);
return;
}
rz_list_foreach (all_xrefs, iter, xref) {
if (xref->type != RZ_ANALYSIS_XREF_TYPE_DATA &&
xref->type != RZ_ANALYSIS_XREF_TYPE_STRING) {
continue;
}
ut64 target = xref->to;
if (rz_set_u_contains(code_call_targets, target)) {
continue;
}
// only consider xrefs that originate from inside an analyzed function
RzAnalysisFunction *fcn_from = rz_analysis_get_fcn_in(core->analysis, xref->from, 0);
if (!fcn_from) {
continue;
}
// take bins/arm/elf/K64F-RIOT-SPI.elf for an example
// the function dbg.sched_run at 0x00000490 has data xref with 0x000004e0
// we take the content of 0x000004e0 as address and check if it is in non-exec section.
// [0x000004be]> s 0x000004e0
// [0x000004e0]> pd 1
// ; DATA XREF from dbg.sched_run @ 0x490
// ;-- data.000004e0:
// 0x000004e0 .dword 0x1fff0274 ; runqueue_bitcache ; section..bss ; sym..bss ; obj.runqueue_bitcache ; loc._sbss ; loc._szero ; loc._erelocate ; sched.c:135
RzBinObject *bo = rz_bin_cur_object(core->bin);
if (!bo) {
continue;
}
// Only executable-region targets need classifying: reject constants that merely
// equal a code address (e.g. `sub sp, sp, 0x810`), while skipping the decode for
// the common data-section case.
bool in_exec_segment = addr_in_exec_segment(bo, target);
XrefRefKind ref_kind = in_exec_segment ? xref_ref_kind(core, xref->from, target) : XREF_REF_OTHER;
if (ref_kind == XREF_REF_COINCIDENTAL_IMM) {
continue;
}
bool target_in_exec = addr_in_exec_section(bo, target);
if (target_in_exec) {
ut8 buf[8] = { 0 };
if (!rz_io_read_at_mapped(core->io, target, buf, ptr_size)) {
continue;
}
bool big_endian = rz_config_get_b(core->config, "cfg.bigendian");
ut64 stored_val;
if (ptr_size == 8) {
stored_val = big_endian ? rz_read_be64(buf) : rz_read_le64(buf);
} else {
stored_val = big_endian ? rz_read_be32(buf) : rz_read_le32(buf);
}
RzBinSection *val_sec = rz_bin_get_section_at(bo, stored_val, true);
if (val_sec && (val_sec->perm & RZ_PERM_X)) {
// stored value is a code pointer — skip
continue;
}
// A load/store marks the target directly, covering whole literal pools
// regardless of distance (e.g. K64F sym.poweroff entries at 0x11d4/0x11d8).
// Other refs keep the conservative heuristic: accept only padding right
// after the referencing function.
if (ref_kind != XREF_REF_MEM_ACCESS && !val_sec) {
ut64 fcn_end = fcn_from->addr + rz_analysis_function_linear_size(fcn_from);
if (target < fcn_end || target - fcn_end >= ptr_size) {
continue;
}
}
}
// skip if already annotated by any other analysis
if (rz_meta_get_at(core->analysis, target, RZ_META_TYPE_ANY, NULL)) {
continue;
}
if (rz_analysis_get_fcn_in(core->analysis, target, -1)) {
continue;
}
rz_set_u_add(data_targets, target);
if (target_in_exec) {
rz_set_u_add(exec_targets, target);
}
}
rz_list_free(all_xrefs);
rz_set_u_free(code_call_targets);
if (!rz_set_u_size(data_targets)) {
rz_set_u_free(exec_targets);
rz_set_u_free(data_targets);
return;
}
// convert set to sorted vector
RzVector *data_addrs = rz_vector_new(sizeof(ut64), NULL, NULL);
if (!data_addrs) {
rz_set_u_free(exec_targets);
rz_set_u_free(data_targets);
return;
}
ht_up_foreach(data_targets, collect_ht_keys_cb, data_addrs);
rz_set_u_free(data_targets);
rz_vector_sort(data_addrs, cmp_ut64s, false, NULL);
// sorted vector of function start addresses
RzVector *fcn_starts = rz_vector_new(sizeof(ut64), NULL, NULL);
if (fcn_starts) {
RzList *fcns = rz_analysis_function_list(core->analysis);
RzAnalysisFunction *fcn;
rz_list_foreach (fcns, iter, fcn) {
rz_vector_push(fcn_starts, &fcn->addr);
}
rz_vector_sort(fcn_starts, cmp_ut64s, false, NULL);
}
// mark each target up to the nearest of: next data target, next function, or ptr_size
// for exec-section pool entries the size is capped at ptr_size to avoid consuming code;
// for data-section targets the natural boundary size is used.
size_t n = rz_vector_len(data_addrs);
for (size_t i = 0; i < n; i++) {
ut64 target = *(ut64 *)rz_vector_index_ptr(data_addrs, i);
ut64 next_data = (i + 1 < n) ? *(ut64 *)rz_vector_index_ptr(data_addrs, i + 1) : UT64_MAX;
ut64 next_fcn = UT64_MAX;
if (fcn_starts) {
size_t fi;
rz_vector_upper_bound(fcn_starts, target, fi, CMP_UT64_VAL);
if (fi < rz_vector_len(fcn_starts)) {
next_fcn = *(ut64 *)rz_vector_index_ptr(fcn_starts, fi);
}
}
ut64 upper = RZ_MIN(next_data, next_fcn);
ut64 size;
if (upper != UT64_MAX) {
size = rz_set_u_contains(exec_targets, target)
? RZ_MIN(upper - target, ptr_size)
: upper - target;
} else {
size = ptr_size;
}
rz_meta_set(core->analysis, RZ_META_TYPE_DATA, target, size, NULL);
}
rz_set_u_free(exec_targets);
rz_vector_free(data_addrs);
rz_vector_free(fcn_starts);
}
/**
* Runs all the steps of the deep analysis.
*
* Returns true if all steps were finished and false if it was interrupted.
*
* \param core RzCore reference
* \param experimental Enable more experimental analysis stages ("aaaa" command)
* \param dh_orig Name of the debug handler, e.g. "esil"
*/
RZ_API bool rz_core_analysis_everything(RzCore *core, bool experimental, char *dh_orig) {
bool didAap = false;
const char *notify = NULL;
ut64 curseek = core->offset;
bool cfg_debug = rz_config_get_b(core->config, "cfg.debug");
bool plugin_supports_esil = rz_analysis_plugin_support_esil(core->analysis);
bool is_apple = is_apple_target(core);
RzAnalysisOptions *aopts = rz_analysis_get_options(core->analysis);
if (rz_str_startswith(rz_config_get(core->config, "bin.lang"), "go")) {
rz_core_notify_done(core, "Find function and symbol names from golang binaries");
if (rz_core_analysis_recover_golang_functions(core)) {
rz_core_analysis_resolve_golang_strings(core);
}
rz_core_task_yield(&core->tasks);
if (rz_cons_is_breaked()) {
return false;
}
}
if (is_apple) {
notify = "Recover all Objective-C selector stub names";
rz_core_notify_begin(core, "%s", notify);
rz_core_analysis_objc_stubs(core); // "aalos"
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
if (rz_cons_is_breaked()) {
return false;
}
}
rz_core_task_yield(&core->tasks);
if (!cfg_debug) {
if (dh_orig && strcmp(dh_orig, "esil")) {
rz_config_set(core->config, "dbg.backend", "esil");
rz_core_task_yield(&core->tasks);
}
}
int c = rz_config_get_i(core->config, "analysis.calls");
rz_config_set_i(core->config, "analysis.calls", 1);
ut64 t = rz_num_math(core->num, "$S");
rz_core_seek(core, t, true);
if (rz_cons_is_breaked()) {
return false;
}
notify = "Analyze function calls";
rz_core_notify_begin(core, "%s", notify);
(void)rz_core_analysis_calls(core, false); // "aac"
rz_core_seek(core, curseek, true);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
if (rz_cons_is_breaked()) {
return false;
}
if (!rz_str_startswith(rz_config_get(core->config, "asm.arch"), "x86")) {
notify = "find and analyze function preludes";
rz_core_notify_begin(core, "%s", notify);
(void)rz_core_search_preludes(core, false); // "aap"
didAap = true;
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
if (rz_cons_is_breaked()) {
return false;
}
}
notify = "Analyze len bytes of instructions for references";
rz_core_notify_begin(core, "%s", notify);
(void)rz_core_analysis_refs(core, 0); // "aar"
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
if (rz_cons_is_breaked()) {
return false;
}
if (is_apple) {
notify = "Check for objc references";
rz_core_notify_begin(core, "%s", notify);
rz_core_analysis_objc_refs(core, true); // "aalor"
rz_core_notify_done(core, "%s", notify);
}
rz_core_task_yield(&core->tasks);
rz_config_set_i(core->config, "analysis.calls", c);
rz_core_task_yield(&core->tasks);
if (rz_cons_is_breaked()) {
return false;
}
if (!rz_asm_is_arch(core->rasm, "x86") &&
!rz_asm_is_arch(core->rasm, "hexagon")) {
rz_core_analysis_value_pointers(core, RZ_OUTPUT_MODE_STANDARD);
rz_core_task_yield(&core->tasks);
bool pcache = rz_config_get_b(core->config, "io.pcache");
rz_config_set_b(core->config, "io.pcache", false);
notify = "Emulate functions to find computed references";
rz_core_notify_begin(core, "%s", notify);
if (plugin_supports_esil) {
rz_core_analysis_esil_references_all_functions(core);
}
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
rz_config_set_b(core->config, "io.pcache", pcache);
if (rz_cons_is_breaked()) {
return false;
}
}
if (rz_config_get_i(core->config, "analysis.autoname")) {
notify = "Speculatively constructing a function name "
"for fcn.* and sym.func.* functions (aan)";
rz_core_notify_begin(core, "%s", notify);
rz_core_analysis_autoname_all_fcns(core);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
}
if (aopts->vars) {
notify = "Analyze local variables and arguments";
rz_core_notify_begin(core, "%s", notify);
core_analysis_analyze_local_var_and_arg(core);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
}
if (plugin_supports_esil) {
notify = "Type matching analysis for all functions";
rz_core_notify_begin(core, "%s", notify);
rz_core_analysis_types_propagation(core);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
}
if (rz_config_get_b(core->config, "analysis.apply.signature")) {
int n_applied = 0;
rz_core_notify_begin(core, "Applying signatures from sigdb");
rz_core_analysis_sigdb_apply(core, &n_applied, NULL);
rz_core_notify_done(core, "Applied %d FLIRT signatures via sigdb", n_applied);
rz_core_task_yield(&core->tasks);
}
notify = "Propagate noreturn information";
rz_core_notify_begin(core, "%s", notify);
rz_core_analysis_propagate_noreturn(core, UT64_MAX);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
notify = "Check for classes";
rz_core_notify_begin(core, "%s", notify);
rz_analysis_class_recover_all(core->analysis);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
// Apply DWARF function information
if (rz_analysis_get_debug_info(core->analysis)) {
notify = "Integrate dwarf function information.";
rz_core_notify_begin(core, "%s", notify);
rz_analysis_dwarf_integrate_functions(core->analysis, core->flags);
rz_core_notify_done(core, "%s", notify);
}
notify = "Recover global variables from symbols";
rz_core_notify_begin(core, "%s", notify);
analysis_global_vars_from_symbols(core);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
if (rz_cons_is_breaked()) {
return false;
}
if (rz_config_get_b(core->config, "analysis.resolve.pointers")) {
notify = "Resolve pointers to data sections";
rz_core_notify_begin(core, "%s", notify);
rz_core_analysis_resolve_pointers_to_data(core);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
}
notify = "Mark data-referenced bytes outside functions";
rz_core_notify_begin(core, "%s", notify);
analysis_mark_xrefs_as_data(core);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
if (rz_cons_is_breaked()) {
return false;
}
if (experimental) {
if (!didAap) {
notify = "Finding function preludes";
rz_core_notify_begin(core, "%s", notify);
(void)rz_core_search_preludes(core, false); // "aap"
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
}
notify = "Enable constraint types analysis for variables";
rz_core_notify_begin(core, "%s", notify);
rz_config_set(core->config, "analysis.types.constraint", "true");
rz_core_notify_done(core, "%s", notify);
} else {
rz_core_notify_done(core, "Use -AA or aaaa to perform additional experimental analysis.");
}
rz_core_seek_undo(core);
if (dh_orig) {
rz_config_set(core->config, "dbg.backend", dh_orig);
rz_core_task_yield(&core->tasks);
}
if (!is_unknown_file(core)) {
rz_analysis_add_device_peripheral_map(core->bin->cur->o, core->analysis);
}
core_analysis_using_plugins(core);
return true;
}
static void analysis_sigdb_add(RzSigDb *sigs, const char *path, bool with_details) {
if (RZ_STR_ISEMPTY(path) || !rz_file_is_directory(path)) {
return;
}
RzSigDb *tmp = rz_sign_sigdb_load_database(path, with_details);
if (tmp) {
rz_sign_sigdb_merge(sigs, tmp);
rz_sign_sigdb_free(tmp);
}
}
/**
* \brief Returns all the signatures found in the default path.
*
* Scans for signature in the following paths:
* - home path + RZ_SIGDB
* - system install prefix path + RZ_SIGDB
* - flirt.sigdb.path user custom sigdb path
*
* \param core The RzCore to use.
* \param[in] with_details The reads the signature details and sets them in RzSigDBEntry
* \return On success a RzList containing RzSigDBEntry entries, otherwise NULL.
*/
RZ_API RZ_OWN RzList /*<RzSigDBEntry *>*/ *rz_core_analysis_sigdb_list(RZ_NONNULL RzCore *core, bool with_details) {
rz_return_val_if_fail(core, NULL);
RzSigDb *sigs = rz_sign_sigdb_new();
if (!sigs) {
return NULL;
}
if (rz_config_get_b(core->config, "flirt.sigdb.load.home")) {
char *home_sigdb = rz_path_home_prefix(RZ_SIGDB);
analysis_sigdb_add(sigs, home_sigdb, with_details);
free(home_sigdb);
}
if (rz_config_get_b(core->config, "flirt.sigdb.load.system")) {
char *system_sigdb = rz_path_system(core->sys_path, RZ_SIGDB);
if (!system_sigdb) {
rz_sign_sigdb_free(sigs);
return rz_list_new();
}
analysis_sigdb_add(sigs, system_sigdb, with_details);
free(system_sigdb);
}
if (rz_config_get_b(core->config, "flirt.sigdb.load.extra")) {
char *extra_sigdb = rz_path_extra(RZ_SIGDB);
analysis_sigdb_add(sigs, extra_sigdb, with_details);
free(extra_sigdb);
}
const char *user_sigdb = rz_config_get(core->config, "flirt.sigdb.path");
analysis_sigdb_add(sigs, user_sigdb, with_details);
RzList *lst = rz_sign_sigdb_list(sigs);
sigs->entries->opt.finiKV = NULL;
rz_sign_sigdb_free(sigs);
return lst;
}
/**
* \brief Adds all the signatures to a RzTable structure.
*
* \param[in] core The RzCore to use
* \param[in] table The RzTable to use
*/
RZ_API void rz_core_analysis_sigdb_print(RZ_NONNULL RzCore *core, RZ_NONNULL RzTable *table) {
rz_return_if_fail(core && table);
RzList *sigdb = rz_core_analysis_sigdb_list(core, true);
if (!sigdb) {
return;
}
rz_table_set_columnsf(table, "ssnsns", "bin", "arch", "bits", "name", "modules", "details");
RzSigDBEntry *sig = NULL;
RzListIter *iter = NULL;
ut64 bits, nmods;
rz_list_foreach (sigdb, iter, sig) {
bits = sig->arch_bits;
nmods = sig->n_modules;
rz_table_add_rowf(table, "ssnsns", sig->bin_name, sig->arch_name, bits, sig->base_name, nmods, sig->details);
}
rz_list_free(sigdb);
}
/**
* \brief tries to apply the signatures in the flirt.sigdb.path
*
* \param core The RzCore instance
* \param n_applied Returns the number of successfully applied signatures
* \param filter Filters the signatures found following the user input
* \return fail when an error occurs otherwise true
*/
RZ_API bool rz_core_analysis_sigdb_apply(RZ_NONNULL RzCore *core, RZ_NULLABLE int *n_applied, RZ_NULLABLE const char *filter) {
rz_return_val_if_fail(core, false);
const char *bin = NULL;
const char *arch = NULL;
ut64 bits = 32;
RzSigDBEntry *sig = NULL;
RzList *sigdb = NULL;
RzListIter *iter = NULL;
RzBinObject *obj = NULL;
int n_flags_new, n_flags_old;
ut8 arch_id = RZ_FLIRT_SIG_ARCH_ANY;
if (RZ_STR_ISEMPTY(filter)) {
obj = core->bin ? rz_bin_cur_object(core->bin) : NULL;
if ((!obj || !obj->plugin)) {
RZ_LOG_INFO("Cannot apply signatures due unknown bin type\n");
return false;
} else if (!strcmp(obj->plugin->name, "elf64")) {
bin = "elf";
} else if (!strcmp(obj->plugin->name, "pe64")) {
bin = "pe";
} else if (!strcmp(obj->plugin->name, "coff")) {
// coff files are used also for PE, we can use the same signatures.
bin = "pe";
} else {
bin = obj->plugin->name;
}
}
arch = rz_config_get(core->config, "asm.arch");
bits = rz_config_get_i(core->config, "asm.bits");
arch_id = rz_core_flirt_arch_from_name(arch);
if (RZ_STR_ISEMPTY(filter) && arch_id >= RZ_FLIRT_SIG_ARCH_ANY) {
RZ_LOG_INFO("Cannot apply signatures due unknown arch (%s)\n", arch);
return false;
}
sigdb = rz_core_analysis_sigdb_list(core, false);
if (!sigdb) {
return false;
}
n_flags_old = rz_flag_count(core->flags, "flirt");
rz_list_foreach (sigdb, iter, sig) {
if (rz_cons_is_breaked()) {
break;
}
if (RZ_STR_ISEMPTY(filter)) {
// apply signatures automatically based on bin, arch and bits
if (strcmp(bin, sig->bin_name) || strcmp(arch, sig->arch_name) || bits != sig->arch_bits) {
continue;
} else if (strstr(sig->base_name, "c++") &&
obj->lang != RZ_BIN_LANGUAGE_CXX &&
obj->lang != RZ_BIN_LANGUAGE_RUST) {
// C++ libs can create many false positives, especially on C binaries.
// So their usage is limited to C++ and RUST lang
continue;
}
RZ_LOG_INFO("Applying %s signature file\n", sig->short_path);
} else {
// apply signatures based on filter value
if (!strstr(sig->short_path, filter)) {
continue;
}
rz_cons_printf("Applying %s/%s/%u/%s signature file\n",
sig->bin_name, sig->arch_name, sig->arch_bits, sig->base_name);
}
rz_sign_flirt_apply(core->analysis, sig->file_path, arch_id);
}
rz_list_free(sigdb);
n_flags_new = rz_flag_count(core->flags, "flirt");
if (n_applied) {
*n_applied = n_flags_new - n_flags_old;
}
return true;
}
RZ_IPI bool rz_core_analysis_function_delete_var(RzCore *core, RzAnalysisFunction *fcn, RzAnalysisVarStorageType kind, const char *id) {
RzAnalysisVar *var = NULL;
if (kind == RZ_ANALYSIS_VAR_STORAGE_STACK && IS_DIGIT(*id)) {
st64 delta = rz_num_math(core->num, id);
var = rz_analysis_function_get_stack_var_at(fcn, delta);
} else {
var = rz_analysis_function_get_var_byname(fcn, id);
}
if (!var || var->storage.type != kind) {
return false;
}
rz_analysis_var_delete(var);
return true;
}
/**
* \brief Get the address of a stack variable.
*
* The address for stack backed variables is computed from the
* current base pointer register. Register backed variables will always
* return UT64_MAX.
*
* \param var Pointer to a \ref RzAnalysisVar.
*/
RZ_API ut64 rz_core_analysis_var_addr(RZ_NONNULL RzCore *core, RZ_NONNULL RzAnalysisVar *var) {
rz_return_val_if_fail(core && var, UT64_MAX);
if (var->storage.type == RZ_ANALYSIS_VAR_STORAGE_STACK) {
// TODO: If bp is not available, we can also get the address from the sp
// through info available from rz_analysis_block_get_sp_at()
ut64 stack = rz_core_reg_getv_by_role_or_name(core, "BP");
return stack + var->fcn->bp_off + var->storage.stack_off;
}
return UT64_MAX;
}
RZ_API RZ_OWN char *rz_core_analysis_var_display(RZ_NONNULL RzCore *core, RZ_NONNULL RzAnalysisVar *var, bool add_name) {
RzStrBuf *sb = rz_strbuf_new(NULL);
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
char *fmt = rz_type_as_format(typedb, var->type);
if (!fmt) {
return rz_strbuf_drain(sb);
}
bool use_hexval = rz_type_is_strictly_atomic(typedb, var->type) && rz_type_atomic_str_eq(typedb, var->type, "int");
if (add_name) {
rz_strbuf_appendf(sb, "%s %s = ", rz_analysis_var_is_arg(var) ? "arg" : "var", var->name);
}
switch (var->storage.type) {
case RZ_ANALYSIS_VAR_STORAGE_REG: {
char *r;
if (use_hexval) {
int wordsize = rz_analysis_get_address_bits(core->analysis) / 8;
// Read register value
ut64 regval = rz_debug_reg_get(core->dbg, var->storage.reg);
r = rz_core_print_hexdump_refs(core, wordsize, wordsize, regval);
} else {
/* The legacy `r (regname)` pf specifier was retired by the
* pf-parser rewrite. The new DSL is purely byte-buffer-
* driven and has no notion of a "read this register" op,
* so we reproduce the legacy MUSTSEE output ourselves:
* leading column padding (matches the namefmt the legacy
* formatter emitted for an empty field name), the register
* name, and its 64-bit value as a hex literal.
*
* Use rz_reg_get_value() against the core's default RzReg
* (the same data source the legacy `r (...)` path went
* through) instead of rz_debug_reg_get(), so the value is
* read correctly in non-debug sessions (e.g. ESIL-only). */
RzReg *reg = rz_core_reg_default(core);
RzRegItem *ri = reg ? rz_reg_get(reg, var->storage.reg, RZ_REG_TYPE_ANY) : NULL;
ut64 regval = ri ? rz_reg_get_value(reg, ri) : 0;
r = rz_str_newf(" : %s : 0x%08" PFMT64x "\n",
var->storage.reg, regval);
}
rz_strbuf_append(sb, r);
free(r);
break;
}
case RZ_ANALYSIS_VAR_STORAGE_STACK: {
ut64 addr = rz_core_analysis_var_addr(core, var);
char *r;
if (use_hexval) {
int wordsize = rz_analysis_get_address_bits(core->analysis) / 8;
r = rz_core_print_hexdump_refs(core, wordsize, wordsize, addr);
} else {
r = rz_core_print_format(core, fmt, RZ_PRINT_MUSTSEE, addr);
}
rz_strbuf_append(sb, r);
free(r);
} break;
default:
rz_strbuf_append(sb, "unimplemented");
}
free(fmt);
return rz_strbuf_drain(sb);
}
RZ_IPI char *rz_core_analysis_all_vars_display(RzCore *core, RzAnalysisFunction *fcn, bool add_name) {
RzStrBuf *sb = rz_strbuf_new(NULL);
void **it;
rz_pvector_foreach (&fcn->vars, it) {
RzAnalysisVar *p = *it;
char *r = rz_core_analysis_var_display(core, p, add_name);
rz_strbuf_append(sb, r);
free(r);
}
return rz_strbuf_drain(sb);
}
static void var_global_show(RzAnalysis *analysis, RzAnalysisVarGlobal *glob, RzCmdStateOutput *state) {
RzTypeDB *typedb = rz_analysis_get_type_db(analysis);
char *var_type = rz_type_as_string(typedb, glob->type);
if (!var_type) {
return;
}
ut64 var_size = rz_type_db_get_bitsize(typedb, glob->type) / 8;
switch (state->mode) {
case RZ_OUTPUT_MODE_QUIET:
rz_cons_println(glob->name);
break;
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("global %s %s @ 0x%" PFMT64x, var_type, glob->name, glob->addr);
if (RZ_STR_ISNOTEMPTY(glob->coord.decl_file)) {
rz_cons_printf(" %s:%" PFMT32d "%" PFMT32d "\n",
glob->coord.decl_file, glob->coord.decl_line, glob->coord.decl_col);
} else {
rz_cons_print("\n");
}
break;
case RZ_OUTPUT_MODE_JSON: {
PJ *pj = state->d.pj;
pj_o(pj);
pj_ks(pj, "name", glob->name);
pj_ks(pj, "type", var_type);
pj_kn(pj, "size", var_size);
char addr[32];
rz_strf(addr, "0x%" PFMT64x, glob->addr);
pj_ks(pj, "addr", addr);
if (RZ_STR_ISNOTEMPTY(glob->coord.decl_file))
pj_ks(pj, "decl_file", glob->coord.decl_file);
if (glob->coord.decl_line != UT32_MAX)
pj_kn(pj, "decl_line", glob->coord.decl_line);
if (glob->coord.decl_col != UT32_MAX)
pj_kn(pj, "decl_col", glob->coord.decl_col);
pj_end(pj);
break;
}
case RZ_OUTPUT_MODE_TABLE: {
rz_table_add_rowf(state->d.t, "ssxxsdd", glob->name, var_type, var_size, glob->addr,
RZ_STR_ISNOTEMPTY(glob->coord.decl_file) ? glob->coord.decl_file : "-",
glob->coord.decl_line, glob->coord.decl_col);
break;
}
default:
break;
}
free(var_type);
}
RZ_IPI bool rz_analysis_var_global_list_show(RzAnalysis *analysis, RzCmdStateOutput *state, RZ_NULLABLE const char *name) {
rz_return_val_if_fail(analysis && state, false);
rz_cmd_state_output_array_start(state);
rz_cmd_state_output_set_columnsf(state, "ssxxsnn",
"name", "type", "size", "address", "decl_file", "decl_line", "decl_col");
if (name) {
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byname(analysis, name);
if (!glob) {
RZ_LOG_ERROR("Global variable '%s' does not exist!\n", name);
goto beach;
}
var_global_show(analysis, glob, state);
} else {
RBIter it;
RzAnalysisVarGlobal *var;
RBTree *rbtree = rz_analysis_get_global_var_tree(analysis);
rz_rbtree_foreach ((*rbtree), it, var, RzAnalysisVarGlobal, rb) {
var_global_show(analysis, var, state);
}
}
beach:
rz_cmd_state_output_array_end(state);
return true;
}
static int check_rom_exists(const void *value, const void *data, void *user) {
const char *name = (const char *)value;
const RzBinSection *sections = (const RzBinSection *)data;
return strcmp(name, sections->name);
}
/**
* \brief Maps the device peripherals as sections
*
* Gets the ROM_ADDRESS and ROM_SIZE from the corresponding CPU Profile
* and adds it as a section (RzBinSection) named ".rom" which will appear
* when you run `iS`.
*
* \param o reference to RzBinObject
* \param analysis reference to RzAnalysis
*/
RZ_API bool rz_analysis_add_device_peripheral_map(RzBinObject *o, RzAnalysis *analysis) {
rz_return_val_if_fail(o && analysis, false);
if (!o || !analysis) {
return false;
}
RzPlatformTarget *arch_target = rz_analysis_get_arch_target(analysis);
ut64 rom_size = arch_target->profile->rom_size;
ut64 rom_address = arch_target->profile->rom_address;
if (rom_address == 0 || rom_size == 0) {
return false;
}
if (!o->sections) {
return false;
}
if (rz_pvector_find(o->sections, ".rom", check_rom_exists, NULL)) {
return false;
}
RzBinSection *s = RZ_NEW0(RzBinSection);
if (!s) {
return false;
}
s->name = rz_str_dup(".rom");
s->vaddr = rom_address;
s->vsize = rom_size;
s->size = rom_size;
s->paddr = rom_address;
s->perm = RZ_PERM_RX;
rz_pvector_push(o->sections, s);
return true;
}
RZ_IPI bool rz_core_analysis_types_propagation(RzCore *core) {
RzReg *rreg = rz_analysis_get_reg(core->analysis);
RzAnalysisEsil *esil = rz_analysis_get_esil(core->analysis);
RzList *fcns = rz_analysis_function_list(core->analysis);
RzListIter *it;
RzAnalysisFunction *fcn;
ut64 seek;
if (rz_config_get_b(core->config, "cfg.debug")) {
RZ_LOG_WARN("core: analysis propagation type can't be exectured when in debugger mode.\n");
return false;
}
RzConfigHold *hold = rz_config_hold_new(core->config);
rz_config_hold_var(hold, "io.va", "io.pcache.write", NULL);
bool io_cache = rz_config_get_b(core->config, "io.pcache.write");
if (!io_cache) {
// XXX. we shouldnt need this, but it breaks 'rizin -c aaa -w ls'
rz_config_set_b(core->config, "io.pcache.write", true);
}
const bool delete_regs = !rz_flag_space_count(core->flags, RZ_FLAGS_FS_REGISTERS);
seek = core->offset;
rz_reg_arena_push(rreg);
rz_reg_arena_zero(rreg, RZ_REG_TYPE_ANY);
rz_core_analysis_esil_init(core);
rz_core_analysis_esil_init_mem(core, NULL, UT64_MAX, UT32_MAX);
ut8 *saved_arena = rz_reg_arena_peek(rreg);
// loop count of rz_core_analysis_type_match
// TODO : figure out the reason to hold a `LOOP COUNT` in type_match
// HtUU <addr->loop_count>
HtUU *loop_table = ht_uu_new();
// Iterating Reverse so that we get function in top-bottom call order
rz_list_foreach_prev(fcns, it, fcn) {
int ret = rz_core_seek(core, fcn->addr, true);
if (!ret) {
continue;
}
rz_reg_arena_poke(rreg, saved_arena);
rz_analysis_esil_set_pc(esil, fcn->addr);
rz_core_analysis_type_match(core, fcn, loop_table);
if (rz_cons_is_breaked()) {
break;
}
rz_analysis_fcn_vars_add_types(core->analysis, fcn);
}
if (delete_regs) {
rz_core_debug_clear_register_flags(core);
}
rz_core_seek(core, seek, true);
rz_reg_arena_pop(rreg);
rz_core_analysis_esil_init_mem_del(core, NULL, UT64_MAX, UT32_MAX);
rz_config_hold_restore(hold);
rz_config_hold_free(hold);
free(saved_arena);
ht_uu_free(loop_table);
return true;
}
RZ_IPI bool rz_core_analysis_function_set_signature(RzCore *core, RzAnalysisFunction *fcn, const char *newsig) {
bool res = false;
char *fcnname = NULL;
char *fcnstr = rz_str_newf("%s;", newsig);
char *fcnstr_copy = rz_str_dup(fcnstr);
char *fcnname_aux = strtok(fcnstr_copy, "(");
if (!fcnname_aux) {
goto err;
}
rz_str_trim_tail(fcnname_aux);
const char *ls = rz_str_lchr(fcnname_aux, ' ');
fcnname = rz_str_dup(ls ? ls : fcnname_aux);
if (!fcnname) {
goto err;
}
// TODO: move this into rz_analysis_function_set_type_str()
if (strcmp(fcn->name, fcnname)) {
(void)rz_core_analysis_function_rename(core, fcn->addr, fcnname);
fcn = rz_analysis_get_fcn_in(core->analysis, fcn->addr, -1);
}
rz_analysis_function_set_type_str(core->analysis, fcn, fcnstr);
res = true;
err:
free(fcnname);
free(fcnstr_copy);
free(fcnstr);
return res;
}
RZ_IPI void rz_core_analysis_function_signature_editor(RzCore *core, ut64 addr) {
RzAnalysisFunction *f = rz_analysis_get_fcn_in(core->analysis, addr, -1);
if (!f) {
RZ_LOG_ERROR("core: cannot find function in 0x%08" PFMT64x "\n", addr);
return;
}
char *sig = rz_analysis_function_get_signature(f);
char *data = rz_core_editor(core, NULL, sig);
if (sig && data) {
rz_core_analysis_function_set_signature(core, f, data);
}
free(sig);
free(data);
}
RZ_IPI void rz_core_analysis_function_until(RzCore *core, ut64 addr_end) {
rz_return_if_fail(core->offset <= addr_end);
ut64 addr = core->offset;
int depth = 1;
ut64 a, b;
const char *c;
a = rz_config_get_i(core->config, "analysis.from");
b = rz_config_get_i(core->config, "analysis.to");
c = rz_config_get(core->config, "analysis.limits");
rz_config_set_i(core->config, "analysis.from", addr);
rz_config_set_i(core->config, "analysis.to", addr_end);
rz_config_set(core->config, "analysis.limits", "true");
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
if (fcn) {
rz_analysis_function_resize(fcn, addr_end - addr);
}
rz_core_analysis_fcn(core, addr, UT64_MAX,
RZ_ANALYSIS_XREF_TYPE_NULL, depth);
fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
if (fcn) {
rz_analysis_function_resize(fcn, addr_end - addr);
}
rz_config_set_i(core->config, "analysis.from", a);
rz_config_set_i(core->config, "analysis.to", b);
rz_config_set(core->config, "analysis.limits", c ? c : "");
}
static void cb_in_range_aav(RzCore *core, ut64 from, ut64 to, int vsize, void *user) {
int arch_align = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN);
bool vinfun = rz_config_get_b(core->config, "analysis.vinfun");
int searchAlign = rz_config_get_i(core->config, "search.align");
int align = (arch_align < 1) ? searchAlign : arch_align;
if (arch_align < 0 || (!aligns(from, align) || !aligns(to, align))) {
// If archaling < 0, the arch has no alignment defined. Hence it is
// ignored if isFine stays false.
bool itsFine = false;
if (archIsThumbable(core)) {
if ((from & 1) || (to & 1)) {
itsFine = true;
}
}
if (!itsFine) {
return;
}
RZ_LOG_DEBUG("Warning: aav: false positive in 0x%08" PFMT64x "\n", from);
}
if (!vinfun) {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, from, -1);
if (fcn) {
return;
}
}
rz_analysis_xrefs_set(core->analysis, from, to, RZ_ANALYSIS_XREF_TYPE_NULL);
rz_meta_set(core->analysis, RZ_META_TYPE_DATA, from, vsize, NULL);
if (!rz_flag_get_at(core->flags, to, false)) {
char *name = rz_str_newf("aav.0x%08" PFMT64x, to);
rz_flag_set(core->flags, name, to, vsize);
free(name);
}
}
RZ_IPI void rz_core_analysis_value_pointers(RzCore *core, RzOutputMode mode) {
ut64 o_align = rz_config_get_i(core->config, "search.align");
const char *analysisin = rz_config_get(core->config, "analysis.in");
char *tmp = rz_str_dup(analysisin);
bool is_debug = rz_config_get_b(core->config, "cfg.debug");
int archAlign = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_TEXT_ALIGN);
rz_config_set_i(core->config, "search.align", archAlign < 1 ? 1 : archAlign);
rz_config_set(core->config, "analysis.in", "io.maps.x");
rz_core_notify_done(core, "Finding xrefs in noncode section with analysis.in=io.maps");
const char *arch = rz_config_get(core->config, "asm.arch");
if (RZ_STR_EQ(arch, "mips")) {
// forbid aav on mips
return;
}
int vsize = 4; // 32bit dword
int bits = rz_asm_get_bits(core->rasm);
if (bits == 64) {
vsize = 8;
}
// body
rz_core_notify_done(core, "Analyze value pointers (aav)");
rz_cons_break_push(NULL, NULL);
if (is_debug) {
RzInterval interval;
interval.addr = rz_config_get_i(core->config, "analysis.from");
interval.size = rz_config_get_i(core->config, "analysis.to") - interval.addr;
RzList *list = rz_core_get_boundaries_all_debug_maps(core, interval);
RzListIter *iter;
RzIOMap *map;
if (!list) {
goto beach;
}
rz_list_foreach (list, iter, map) {
if (rz_cons_is_breaked()) {
break;
}
rz_core_notify_done(core, "from 0x%" PFMT64x " to 0x%" PFMT64x " (aav)", map->itv.addr, rz_itv_end(map->itv));
(void)rz_core_search_value_in_range(core, map->itv,
map->itv.addr, rz_itv_end(map->itv), vsize, cb_in_range_aav, (void *)&mode);
}
rz_list_free(list);
} else {
RzList *list = rz_core_get_boundaries_select(core, "analysis.from", "analysis.to", "analysis.in");
if (!list) {
goto beach;
}
RzListIter *iter, *iter2;
RzIOMap *map, *map2;
ut64 from = UT64_MAX;
ut64 to = UT64_MAX;
// find values pointing to non-executable regions
rz_list_foreach (list, iter2, map2) {
if (rz_cons_is_breaked()) {
break;
}
// TODO: Reduce multiple hits for same addr
from = rz_itv_begin(map2->itv);
to = rz_itv_end(map2->itv);
if ((to - from) > MAX_SCAN_SIZE) {
rz_core_notify_done(core, "Skipping large region (from 0x%08" PFMT64x " to 0x%08" PFMT64x ")", from, to);
continue;
}
rz_core_notify_done(core, "Value from 0x%08" PFMT64x " to 0x%08" PFMT64x " (aav)", from, to);
rz_list_foreach (list, iter, map) {
ut64 begin = rz_itv_begin(map->itv);
ut64 end = rz_itv_end(map->itv);
if (rz_cons_is_breaked()) {
break;
}
if (end - begin > UT32_MAX) {
rz_core_notify_done(core, "Skipping huge range");
continue;
}
rz_core_notify_done(core, "0x%08" PFMT64x "-0x%08" PFMT64x " in 0x%" PFMT64x "-0x%" PFMT64x " (aav)", from, to, begin, end);
(void)rz_core_search_value_in_range(core, map->itv, from, to, vsize, cb_in_range_aav, (void *)&mode);
}
}
rz_list_free(list);
}
beach:
rz_cons_break_pop();
// end
rz_config_set(core->config, "analysis.in", tmp);
free(tmp);
rz_config_set_i(core->config, "search.align", o_align < 1 ? 1 : o_align);
}
RZ_API int rz_core_get_stacksz(RzCore *core, ut64 from, ut64 to) {
int stack = 0, maxstack = 0;
ut64 at = from;
if (from >= to) {
return 0;
}
const int mininstrsz = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
const int minopcode = RZ_MAX(1, mininstrsz);
while (at < to) {
RzAnalysisOp *op = rz_core_analysis_op(core, at, RZ_ANALYSIS_OP_MASK_BASIC);
if (!op || op->size <= 0) {
at += minopcode;
rz_analysis_op_free(op);
continue;
}
if ((op->stackop == RZ_ANALYSIS_STACK_INC) && RZ_ABS(op->stackptr) < 8096) {
stack += op->stackptr;
if (stack > maxstack) {
maxstack = stack;
}
}
at += op->size;
rz_analysis_op_free(op);
}
return maxstack;
}
RZ_API void rz_core_analysis_type_init(RzCore *core) {
rz_return_if_fail(core && core->analysis);
int bits = rz_asm_get_bits(core->rasm);
const char *analysis_arch = rz_config_get(core->config, "analysis.arch");
const char *os = rz_config_get(core->config, "asm.os");
char *types_dir = rz_path_system(core->sys_path, RZ_SDB_TYPES);
if (!types_dir) {
return;
}
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
rz_type_db_init(typedb, types_dir, analysis_arch, bits, os);
free(types_dir);
}
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 void rz_core_analysis_cc_init_by_path(RzCore *core, RZ_NULLABLE const char *path, RZ_NULLABLE const char *homepath) {
const char *analysis_arch = rz_analysis_get_arch(core->analysis);
Sdb *cc = rz_analysis_get_sdb_cc(core->analysis);
if (!strcmp(analysis_arch, "null")) {
sdb_reset(cc);
RZ_FREE(cc->path);
return;
}
char buf[40];
int bits = rz_analysis_get_bits(core->analysis);
char *dbpath = rz_file_path_join(path ? path : "", rz_strf(buf, "cc-%s-%d.sdb", analysis_arch, bits));
char *dbhomepath = rz_file_path_join(homepath ? homepath : "", rz_strf(buf, "cc-%s-%d.sdb", analysis_arch, bits));
// Avoid sdb reloading
if (cc->path && (!strcmp(cc->path, dbpath) || !strcmp(cc->path, dbhomepath))) {
free(dbpath);
free(dbhomepath);
return;
}
sdb_reset(cc);
RZ_FREE(cc->path);
if (rz_file_exists(dbpath)) {
sdb_concat_by_path(cc, dbpath);
cc->path = rz_str_dup(dbpath);
}
if (rz_file_exists(dbhomepath)) {
sdb_concat_by_path(cc, dbhomepath);
free(cc->path);
cc->path = rz_str_dup(dbhomepath);
}
free(dbpath);
free(dbhomepath);
// same as "tcc `arcc`"
RzReg *rreg = rz_analysis_get_reg(core->analysis);
char *s = rz_reg_profile_to_cc(rreg);
if (s && !rz_analysis_cc_set(core->analysis, s)) {
RZ_LOG_ERROR("core: invalid CC from reg profile.\n");
} else if (!s) {
RZ_LOG_ERROR("core: cannot derive CC from reg profile.\n");
}
free(s);
if (sdb_isempty(cc)) {
RZ_LOG_WARN("core: missing calling conventions for '%s'. Deriving it from the regprofile.\n", analysis_arch);
}
}
RZ_API void rz_core_analysis_cc_init(RzCore *core) {
char *types_dir = rz_path_system(core->sys_path, RZ_SDB_TYPES);
if (!types_dir) {
return;
}
char *home_types_dir = rz_path_home_prefix(RZ_SDB_TYPES);
rz_core_analysis_cc_init_by_path(core, types_dir, home_types_dir);
free(types_dir);
free(home_types_dir);
}
/**
* \brief Print Calling Convention info
*
* \param core The RzCore instance
* \param cc Calling Convention name
* \param pj Optional PJ instance for JSON mode
*/
RZ_IPI void rz_core_analysis_cc_print(RzCore *core, RZ_NONNULL const char *cc, RZ_NULLABLE PJ *pj) {
rz_return_if_fail(core && cc);
if (pj) {
pj_o(pj);
}
if (pj) {
pj_ks(pj, "name", cc);
} else {
rz_cons_printf("name: %s\n", cc);
}
const char *regname = rz_analysis_cc_ret(core->analysis, cc);
if (regname) {
if (pj) {
pj_ks(pj, "ret", regname);
} else {
rz_cons_printf("ret: %s\n", regname);
}
}
if (pj) {
pj_ka(pj, "args");
}
int maxargs = rz_analysis_cc_max_arg(core->analysis, cc);
for (int i = 0; i < maxargs; i++) {
regname = rz_analysis_cc_arg(core->analysis, cc, i);
if (pj) {
pj_s(pj, regname);
} else {
rz_cons_printf("arg%d: %s\n", i, regname);
}
}
if (pj) {
pj_end(pj);
}
regname = rz_analysis_cc_self(core->analysis, cc);
if (regname) {
if (pj) {
pj_ks(pj, "self", regname);
} else {
rz_cons_printf("self: %s\n", regname);
}
}
regname = rz_analysis_cc_error(core->analysis, cc);
if (regname) {
if (pj) {
pj_ks(pj, "error", regname);
} else {
rz_cons_printf("error: %s\n", regname);
}
}
if (pj) {
pj_end(pj);
}
}
/**
* \brief Start ESIL trace session
*
* \param core The RzCore instance
* \return false when an error occurs otherwise true
*/
RZ_API bool rz_core_analysis_esil_trace_start(RzCore *core) {
RzAnalysisEsil *esil = rz_analysis_get_esil(core->analysis);
if (!esil) {
RZ_LOG_ERROR("ESIL is not initialized. Use `aeim` first.\n");
return false;
}
if (esil->trace) {
RZ_LOG_ERROR("ESIL trace already started\n");
return false;
}
esil->trace = rz_analysis_esil_trace_new(esil);
if (!esil->trace) {
return false;
}
rz_config_set_i(core->config, "dbg.trace", true);
return true;
}
/**
* \brief Stop ESIL trace session
*
* \param core The RzCore instance
* \return false when an error occurs otherwise true
*/
RZ_API bool rz_core_analysis_esil_trace_stop(RzCore *core) {
RzAnalysisEsil *esil = rz_analysis_get_esil(core->analysis);
if (!esil) {
RZ_LOG_ERROR("ESIL is not initialized. Use `aeim` first.\n");
return false;
}
if (!esil->trace) {
RZ_LOG_ERROR("No ESIL trace started\n");
return false;
}
rz_analysis_esil_trace_free(esil->trace);
esil->trace = NULL;
rz_config_set_i(core->config, "dbg.trace", false);
return true;
}
static ut64 analysis_bytes_oplen(RzCore *core, const ut8 *ptr, ut64 addr, int len, int min_op_size, int mask) {
int oplen = 0;
RzAsmOp asmop = { 0 };
RzAnalysisOp op;
rz_asm_op_init(&asmop);
rz_asm_set_pc(core->rasm, addr);
rz_analysis_op_init(&op);
int reta = rz_analysis_op(core->analysis, &op, addr, ptr, len, mask);
rz_analysis_op_fini(&op);
int ret = rz_asm_disassemble(core->rasm, &asmop, ptr, len);
if (reta < 1 || ret < 1) {
return min_op_size;
}
oplen = rz_asm_op_get_size(&asmop);
rz_analysis_op_fini(&op);
rz_core_asm_bb_middle(core, addr, &oplen, &ret);
return oplen;
}
/**
*
* Analyze and disassemble bytes use rz_analysis_op and rz_asm_disassemble
* and return how many bytes were consumed
*
* \param core The RzCore instance
* \param buf data to analysis
* \param len analysis len bytes
* \param nops analysis n ops
* \return amount of the bytes consumed
*/
RZ_API ut64 rz_core_analysis_ops_size(
RZ_NONNULL RzCore *core, ut64 start_addr, RZ_NONNULL const ut8 *buf, ut64 len, ut64 nops) {
static const int mask = RZ_ANALYSIS_OP_MASK_HINT;
int min_op_size = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
ut64 end_offset = start_addr + len;
ut64 offset = start_addr;
int consumed = 0;
int remain = len;
while (offset < end_offset && nops > 0) {
const ut8 *ptr = buf + consumed;
remain = len - consumed;
consumed += analysis_bytes_oplen(core, ptr, offset, remain, min_op_size, mask);
offset += consumed;
nops--;
}
return consumed;
}
typedef struct {
RzCore *core;
RzInterval itv;
ut64 max_ops;
ut64 ops_count;
ut8 bytes[256];
ut64 current;
RzAnalysisOpMask mask;
RzAnalysisOp op;
} AnalysisOpContext;
static bool analysis_op_context_init(AnalysisOpContext *ctx, RzCore *core, ut64 start_addr, ut64 n_bytes, ut64 max_ops, RzAnalysisOpMask mask) {
if (n_bytes < 1 && max_ops < 1) {
RZ_LOG_ERROR("core: n_bytes & max_ops are zeros. One of them must be > 0.");
return false;
}
ctx->core = core;
ctx->itv.addr = start_addr;
ctx->itv.size = n_bytes;
ctx->current = start_addr;
ctx->max_ops = max_ops;
ctx->ops_count = 0;
ctx->mask = mask;
memset(&ctx->op, 0, sizeof(ctx->op));
return true;
}
static bool analysis_op_context_can_continue(AnalysisOpContext *ctx) {
if (ctx->max_ops > 0) {
return ctx->ops_count < ctx->max_ops;
}
return rz_itv_contain(ctx->itv, ctx->current);
}
static RzAnalysisOp *analysis_op_context_iter_next(RzIterator *it) {
AnalysisOpContext *ctx = it->u;
if (!analysis_op_context_can_continue(ctx)) {
return NULL;
}
RzCore *core = ctx->core;
int read = rz_io_nread_at(core->io, ctx->current, ctx->bytes, sizeof(ctx->bytes));
if (read < 1) {
ut64 n_bytes = sizeof(ctx->bytes);
RZ_LOG_ERROR("core: failed to read at 0x%08" PFMT64x " size %" PFMT64u ".", ctx->current, n_bytes);
return NULL;
}
rz_analysis_op_init(&ctx->op);
if (rz_analysis_op(core->analysis, &ctx->op, ctx->current, ctx->bytes, read, ctx->mask) < 1) {
RZ_LOG_ERROR("core: invalid instruction at 0x%08" PFMT64x "...\n", ctx->current);
return NULL;
}
ctx->current += RZ_MAX(ctx->op.size, 1);
ctx->ops_count++;
return &ctx->op;
}
/**
* \brief Parse \p len bytes and \p nops RzAnalysisOps,
* restricted by \p len and \p nops at the same time
*
* \param core RzCore
* \param len Maximum length read from \p buf in bytes. set to 0 to disable it (only use \p nops).
* \param nops Maximum number of instruction, set to 0 to disable it (only use \p len).
* \param mask The which analysis details should be disassembled.
* \return RzIterator of RzAnalysisOp
*/
RZ_API RZ_OWN RzIterator *rz_core_analysis_op_chunk_iter(
RZ_NONNULL RzCore *core, ut64 start_addr, ut64 n_bytes, ut64 max_ops, RzAnalysisOpMask mask) {
rz_return_val_if_fail(core, NULL);
AnalysisOpContext *ctx = RZ_NEW0(AnalysisOpContext);
if (!ctx || !analysis_op_context_init(ctx, core, start_addr, n_bytes, max_ops, mask)) {
free(ctx);
return NULL;
}
return rz_iterator_new((rz_iterator_next_cb)analysis_op_context_iter_next, (rz_iterator_free_cb)rz_analysis_op_fini, free, ctx);
}
typedef struct core_decoded_bytes_s {
RzCore *core;
bool asm_sub_var;
bool big_endian;
RzInterval itv;
ut64 current;
ut64 max_ops;
ut64 ops_count;
const ut8 *bytes;
RzCoreDecodedBytes cdb;
} CoreDecodedBytes;
static bool core_decoded_bytes_can_continue(CoreDecodedBytes *ctx) {
if (ctx->max_ops > 0) {
return ctx->ops_count < ctx->max_ops;
}
return rz_itv_contain(ctx->itv, ctx->current);
}
static void core_decoded_bytes_set_mnemonic(RzCoreDecodedBytes *cdb, bool is_x86) {
const char *opcode = rz_asm_op_get_asm(&cdb->as_op);
if (!opcode) {
opcode = cdb->an_op.mnemonic;
}
if (!opcode) {
return;
}
const char *space = rz_str_trim_head_wp(opcode);
if (cdb->an_op.prefix) {
// skip prefix
opcode = rz_str_trim_head_ro(space);
// find new space
space = rz_str_trim_head_wp(opcode);
}
cdb->mnemonic = rz_str_ndup(opcode, space - opcode);
}
static RzCoreDecodedBytes *core_decoded_bytes_next(RzIterator *it) {
CoreDecodedBytes *ctx = it->u;
if (!core_decoded_bytes_can_continue(ctx)) {
return NULL;
}
static const RzAnalysisOpMask mask = RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_OPEX | RZ_ANALYSIS_OP_MASK_HINT;
RzCore *core = ctx->core;
RzCoreDecodedBytes *cdb = &ctx->cdb;
char disasm[512] = { 0 };
size_t pos = ctx->current - rz_itv_begin(ctx->itv);
size_t left = rz_itv_end(ctx->itv) - ctx->current;
const ut8 *ptr = ctx->bytes + pos;
rz_asm_set_pc(core->rasm, ctx->current);
rz_analysis_op_init(&cdb->an_op);
rz_asm_op_init(&cdb->as_op);
cdb->hint = rz_analysis_hint_get(core->analysis, ctx->current);
int ret_an = rz_analysis_op(core->analysis, &cdb->an_op, ctx->current, ptr, left, mask);
int ret_as = rz_asm_disassemble(core->rasm, &cdb->as_op, ptr, left);
if (ret_an < 1 || ret_as < 1) {
cdb->oplen = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
cdb->opcode = rz_str_dup("invalid");
cdb->disasm = rz_str_dup("invalid");
cdb->mnemonic = rz_str_dup("invalid");
cdb->bytes = rz_hex_bin2strdup(ptr, cdb->oplen);
ctx->current += RZ_MAX(cdb->oplen, 1);
ctx->ops_count++;
return cdb;
}
cdb->oplen = rz_asm_op_get_size(&cdb->as_op);
core->parser->subrel_addr = 0;
if (core->parser->subrel) {
ut64 subrel_addr = UT64_MAX;
if (rz_io_read_i(core->io, cdb->an_op.ptr, &subrel_addr, cdb->an_op.refptr, ctx->big_endian)) {
core->parser->subrel_addr = subrel_addr;
}
}
bool is_x86 = rz_asm_is_arch(core->rasm, "x86");
char *opcode = rz_strbuf_get(&cdb->as_op.buf_asm);
size_t opcode_len = rz_strbuf_length(&cdb->as_op.buf_asm);
cdb->opcode = rz_str_ndup(opcode, opcode_len);
core_decoded_bytes_set_mnemonic(cdb, is_x86);
if (ctx->asm_sub_var) {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, ctx->current, RZ_ANALYSIS_FCN_TYPE_NULL);
rz_parse_subvar(core->parser, fcn, &cdb->an_op, opcode, disasm, sizeof(disasm));
}
if (!*disasm) {
// nothing was written to disasm, so we copy opcode.
memcpy(disasm, opcode, RZ_MIN(opcode_len, sizeof(disasm)));
}
// input and output must be different for rz_parse_filter.
char *tmp = rz_str_dup(disasm);
rz_parse_filter(core->parser, ctx->current, core->flags, cdb->hint, tmp, disasm, sizeof(disasm), ctx->big_endian);
free(tmp);
ut8 *amask = rz_analysis_mask(core->analysis, left, ptr, ctx->current);
cdb->mask = rz_hex_bin2strdup(amask, cdb->oplen);
free(amask);
cdb->bytes = rz_hex_bin2strdup(ptr, cdb->oplen);
// apply pseudo if needed
cdb->pseudo = rz_parse_pseudocode(core->parser, disasm);
cdb->description = rz_asm_describe(core->rasm, cdb->mnemonic);
cdb->disasm = rz_str_ndup(disasm, sizeof(disasm));
// if the next op is in the middle, then we should shorten oplen.
rz_core_asm_bb_middle(core, ctx->current, &cdb->oplen, &ret_as);
ctx->current += RZ_MAX(cdb->oplen, 1);
ctx->ops_count++;
return cdb;
}
static void analysis_bytes_iter_fini(RzCoreDecodedBytes *cdb) {
if (!cdb) {
return;
}
// this pointer is owned by CoreDecodedBytes
rz_analysis_op_fini(&cdb->an_op);
rz_asm_op_fini(&cdb->as_op);
rz_analysis_hint_free(cdb->hint);
free(cdb->opcode);
free(cdb->pseudo);
free(cdb->disasm);
free(cdb->description);
free(cdb->mask);
free(cdb->mnemonic);
free(cdb->bytes);
memset(cdb, 0, sizeof(RzCoreDecodedBytes));
}
static bool core_decoded_bytes_init(CoreDecodedBytes *ctx, RzCore *core, ut64 start_addr, const ut8 *buf, ut64 n_bytes, ut64 max_ops) {
if (n_bytes < 1) {
RZ_LOG_ERROR("core: n_bytes must be > 0.");
return false;
}
ctx->core = core;
ctx->itv.addr = start_addr;
ctx->itv.size = n_bytes;
ctx->current = start_addr;
ctx->max_ops = max_ops;
ctx->ops_count = 0;
ctx->bytes = buf;
ctx->asm_sub_var = rz_config_get_i(core->config, "asm.sub.var");
ctx->big_endian = rz_asm_is_big_endian_set(core->rasm);
memset(&ctx->cdb, 0, sizeof(ctx->cdb));
return true;
}
/**
*
* Analyze and disassemble bytes use rz_analysis_op and rz_asm_disassemble
*
* \param core The RzCore instance
* \param buf data to analysis
* \param n_bytes analysis len bytes
* \param max_ops analysis n ops
* \return RzIterator of RzCoreDecodedBytes
*/
RZ_API RZ_OWN RzIterator *rz_core_analysis_bytes(
RZ_NONNULL RzCore *core, ut64 start_addr, RZ_NONNULL const ut8 *buf, ut64 n_bytes, ut64 max_ops) {
rz_return_val_if_fail(core && buf, NULL);
// TODO: this should be removed once rz_config is refactored.
core->parser->subrel = rz_config_get_i(core->config, "asm.sub.rel");
core->parser->localvar_only = rz_config_get_i(core->config, "asm.sub.varonly");
CoreDecodedBytes *ctx = RZ_NEW0(CoreDecodedBytes);
if (!ctx || !core_decoded_bytes_init(ctx, core, start_addr, buf, n_bytes, max_ops)) {
free(ctx);
return NULL;
}
return rz_iterator_new((rz_iterator_next_cb)core_decoded_bytes_next, (rz_iterator_free_cb)analysis_bytes_iter_fini, free, ctx);
}
/**
* \brief Parse RzAnalysisOps of function at core->offset
*
* \param core RzCore
* \param fcn Pointer to `RzAnalysisFunction` used to analysis.
* \param mask The which analysis details should be disassembled.
* \return RzIterator of RzAnalysisOp
*/
RZ_API RZ_OWN RzIterator *rz_core_analysis_op_function_iter(RZ_NONNULL RzCore *core, RZ_NONNULL RZ_BORROW RzAnalysisFunction *fcn, RzAnalysisOpMask mask) {
rz_return_val_if_fail(core && fcn, NULL);
RzIterator *ops = NULL;
ut64 start = fcn->addr;
ut64 end = rz_analysis_function_max_addr(fcn);
if (end <= start) {
RZ_LOG_ERROR("Cannot print function because the end offset is less or equal to the start offset\n");
goto exit;
}
ut64 size = end - start;
ops = rz_core_analysis_op_chunk_iter(core, start, size, 0, mask);
exit:
return ops;
}
/**
* \brief Set analysis hint for the first immediate of the instruction at current offset to \p struct_member.
* \param core The RzCore instance
* \param struct_member struct.member
*/
RZ_API bool rz_core_analysis_hint_set_offset(RZ_NONNULL RzCore *core, RZ_NONNULL const char *struct_member) {
rz_return_val_if_fail(core && struct_member, false);
RzAnalysisOp op = { 0 };
ut8 code[128] = { 0 };
if (!rz_io_read_at_mapped(core->io, core->offset, code, sizeof(code))) {
return false;
}
bool res = false;
rz_analysis_op_init(&op);
int ret = rz_analysis_op(core->analysis, &op, core->offset, code, sizeof(code), RZ_ANALYSIS_OP_MASK_VAL);
if (ret < 1) {
goto exit;
}
// HACK: Just convert only the first imm seen
ut64 offimm = 0;
for (int i = 0; i < 3; i++) {
if (op.src[i]) {
if (op.src[i]->imm) {
offimm = op.src[i]->imm;
} else if (op.src[i]->delta) {
offimm = op.src[i]->delta;
}
}
}
if (!offimm && op.dst) {
if (op.dst->imm) {
offimm = op.dst->imm;
} else if (op.dst->delta) {
offimm = op.dst->delta;
}
}
if (!offimm) {
goto exit;
}
// TODO: Allow to select from multiple choices
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
RzList *otypes = rz_type_db_get_by_offset(typedb, offimm);
RzListIter *iter;
RzTypePath *tpath;
rz_list_foreach (otypes, iter, tpath) {
// TODO: Support also arrays and pointers
if (tpath->typ->kind == RZ_TYPE_KIND_IDENTIFIER) {
if (!strcmp(struct_member, tpath->path)) {
rz_analysis_hint_set_offset(core->analysis, core->offset, tpath->path);
break;
}
}
}
rz_list_free(otypes);
res = true;
exit:
rz_analysis_op_fini(&op);
return res;
}
/**
* \brief Continue until syscall
* \param core The RzCore instance
* \return success
*/
RZ_API bool rz_core_analysis_continue_until_syscall(RZ_NONNULL RzCore *core) {
rz_return_val_if_fail(core, false);
RzAnalysisEsil *esil = rz_analysis_get_esil(core->analysis);
RzReg *rreg = rz_analysis_get_reg(core->analysis);
const char *pc = rz_reg_get_name(rreg, RZ_REG_NAME_PC);
RzAnalysisOp *op = NULL;
while (!rz_cons_is_breaked()) {
if (!rz_core_esil_step(core, UT64_MAX, NULL, NULL, false)) {
break;
}
rz_core_reg_update_flags(core);
ut64 addr = rz_num_get(core->num, pc);
op = rz_core_analysis_op(core, addr, RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_HINT);
if (!op) {
break;
}
if (op->type == RZ_ANALYSIS_OP_TYPE_SWI) {
RZ_LOG_ERROR("syscall at 0x%08" PFMT64x "\n", addr);
break;
} else if (op->type == RZ_ANALYSIS_OP_TYPE_TRAP) {
RZ_LOG_ERROR("trap at 0x%08" PFMT64x "\n", addr);
break;
}
rz_analysis_op_free(op);
op = NULL;
if (esil->trap || esil->trap_code) {
break;
}
}
rz_analysis_op_free(op);
return true;
}
/**
* \brief Continue until call
* \param core The RzCore instance
* \return success
*/
RZ_API bool rz_core_analysis_continue_until_call(RZ_NONNULL RzCore *core) {
rz_return_val_if_fail(core, false);
RzAnalysisEsil *esil = rz_analysis_get_esil(core->analysis);
RzReg *rreg = rz_analysis_get_reg(core->analysis);
const char *pc = rz_reg_get_name(rreg, RZ_REG_NAME_PC);
RzAnalysisOp *op = NULL;
while (!rz_cons_is_breaked()) {
if (!rz_core_esil_step(core, UT64_MAX, NULL, NULL, false)) {
break;
}
rz_core_reg_update_flags(core);
ut64 addr = rz_num_get(core->num, pc);
op = rz_core_analysis_op(core, addr, RZ_ANALYSIS_OP_MASK_BASIC);
if (!op) {
break;
}
if (op->type == RZ_ANALYSIS_OP_TYPE_CALL || op->type == RZ_ANALYSIS_OP_TYPE_UCALL) {
RZ_LOG_ERROR("call at 0x%08" PFMT64x "\n", addr);
break;
}
rz_analysis_op_free(op);
op = NULL;
if (esil->trap || esil->trap_code) {
break;
}
}
rz_analysis_op_free(op);
return true;
}
/**
* \brief Compute analysis coverage count
* \param core The RzCore instance
* \return Total size of coverage. SIZE_MAX on failure.
*/
RZ_API size_t rz_core_analysis_coverage_count(RZ_NONNULL RzCore *core) {
rz_return_val_if_fail(core && core->analysis, SIZE_MAX);
RzListIter *iter;
RzAnalysisFunction *fcn;
size_t cov = 0;
cov += (size_t)rz_meta_get_size(core->analysis, RZ_META_TYPE_DATA);
RzList *fcns = rz_analysis_function_list(core->analysis);
rz_list_foreach (fcns, iter, fcn) {
void **it;
RzPVector *maps = rz_io_maps(core->io);
rz_pvector_foreach (maps, it) {
RzIOMap *map = *it;
if (map->perm & RZ_PERM_X) {
ut64 section_end = map->itv.addr + map->itv.size;
ut64 s = rz_analysis_function_realsize(fcn);
if (fcn->addr >= map->itv.addr && (fcn->addr + s) < section_end) {
cov += (size_t)s;
}
}
}
}
return cov;
}
/**
* \brief Compute analysis code count
* \param core The RzCore instance
* \return Total size of code regions. SIZE_MAX on failure.
*/
RZ_API size_t rz_core_analysis_code_count(RZ_NONNULL RzCore *core) {
rz_return_val_if_fail(core, SIZE_MAX);
size_t code = 0;
code += (size_t)rz_meta_get_size(core->analysis, RZ_META_TYPE_DATA);
void **it;
RzPVector *maps = rz_io_maps(core->io);
rz_pvector_foreach (maps, it) {
RzIOMap *map = *it;
if (map->perm & RZ_PERM_X) {
code += (size_t)map->itv.size;
}
}
return code;
}
/**
* \brief Compute analysis function xrefs count
* \param core The RzCore instance
* \return Total calls from all functions. SIZE_MAX on failure.
*/
RZ_API size_t rz_core_analysis_calls_count(RZ_NONNULL RzCore *core) {
rz_return_val_if_fail(core && core->analysis, SIZE_MAX);
RzListIter *iter;
RzAnalysisFunction *fcn;
size_t cov = 0;
RzList *fcns = rz_analysis_function_list(core->analysis);
rz_list_foreach (fcns, iter, fcn) {
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
if (xrefs) {
cov += rz_list_length(xrefs);
rz_list_free(xrefs);
}
}
return cov;
}
static const char *RzCoreAnalysisNameTypeStrs[] = {
"var",
"function",
"flag",
"address",
};
/**
* \brief Convert \p typ to string (const char*)
*/
RZ_API RZ_BORROW const char *rz_core_analysis_name_type_to_str(RzCoreAnalysisNameType typ) {
switch (typ) {
case RZ_CORE_ANALYSIS_NAME_TYPE_VAR:
case RZ_CORE_ANALYSIS_NAME_TYPE_FUNCTION:
case RZ_CORE_ANALYSIS_NAME_TYPE_FLAG:
case RZ_CORE_ANALYSIS_NAME_TYPE_ADDRESS:
return RzCoreAnalysisNameTypeStrs[typ];
default:
rz_warn_if_reached();
return NULL;
}
}
RZ_API void rz_core_analysis_name_free(RZ_NULLABLE RzCoreAnalysisName *p) {
if (!p) {
return;
}
free(p->name);
free(p->realname);
free(p);
}
/**
* \brief Rename whatever var/flag/function is used at \p addr to \p name
* \return success?
*/
RZ_API bool rz_core_analysis_rename(RZ_NONNULL RzCore *core, RZ_NONNULL const char *name, ut64 addr) {
rz_return_val_if_fail(core && core->analysis && RZ_STR_ISNOTEMPTY(name), false);
ut8 buf[128];
if (!rz_io_read_at_mapped(core->io, addr, buf, sizeof(buf))) {
return false;
}
RzAnalysisOp op = { 0 };
rz_analysis_op_init(&op);
rz_analysis_op(core->analysis, &op, core->offset,
buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_BASIC);
RzAnalysisVar *var = rz_analysis_get_used_function_var(core->analysis, op.addr);
ut64 tgt_addr = op.jump != UT64_MAX ? op.jump : op.ptr;
rz_analysis_op_fini(&op);
bool result = false;
if (var) {
result = rz_analysis_var_rename(var, name, true);
} else if (tgt_addr != UT64_MAX) {
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, tgt_addr);
RzFlagItem *f = rz_flag_get_i(core->flags, tgt_addr);
if (fcn) {
result = rz_core_analysis_function_rename(core, tgt_addr, name);
} else if (f) {
result = rz_flag_rename(core->flags, f, name);
} else {
result = rz_flag_set(core->flags, name, tgt_addr, 1);
}
}
return result;
}
/**
* \brief Get information on whatever var/flag/function is used at \p addr
* \return RzAnalysisName
*/
RZ_API RZ_OWN RzCoreAnalysisName *rz_core_analysis_name(RZ_NONNULL RzCore *core, ut64 addr) {
rz_return_val_if_fail(core && core->analysis, NULL);
ut8 buf[128];
if (!rz_io_read_at_mapped(core->io, addr, buf, sizeof(buf))) {
return NULL;
}
RzCoreAnalysisName *p = RZ_NEW0(RzCoreAnalysisName);
if (!p) {
return NULL;
}
RzAnalysisOp op = { 0 };
rz_analysis_op_init(&op);
rz_analysis_op(core->analysis, &op, core->offset,
buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_BASIC);
RzAnalysisVar *var = rz_analysis_get_used_function_var(core->analysis, op.addr);
ut64 tgt_addr = op.jump != UT64_MAX ? op.jump : op.ptr;
rz_analysis_op_fini(&op);
if (var) {
p->type = RZ_CORE_ANALYSIS_NAME_TYPE_VAR;
p->name = rz_str_dup(var->name);
p->offset = op.addr;
} else if (tgt_addr != UT64_MAX) {
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, tgt_addr);
RzFlagItem *f = rz_flag_get_i(core->flags, tgt_addr);
if (fcn) {
p->type = RZ_CORE_ANALYSIS_NAME_TYPE_FUNCTION;
p->name = rz_str_dup(fcn->name);
p->offset = tgt_addr;
} else if (f) {
p->type = RZ_CORE_ANALYSIS_NAME_TYPE_FLAG;
p->name = rz_str_dup(f->name);
p->realname = rz_str_dup(f->realname);
p->offset = tgt_addr;
} else {
p->type = RZ_CORE_ANALYSIS_NAME_TYPE_ADDRESS;
p->offset = tgt_addr;
}
} else {
rz_core_analysis_name_free(p);
return NULL;
}
return p;
}
static void _analysis_calls(RzCore *core, ut64 addr, ut64 addr_end, bool imports_only) {
RzAnalysisOp op = { 0 };
int depth = rz_config_get_i(core->config, "analysis.depth");
const int bsz = 4096;
int bufi = 0;
int archbits = rz_asm_get_bits(core->rasm);
int bufi_max = bsz - 16;
if (addr_end - addr > UT32_MAX) {
return;
}
ut8 *buf = malloc(bsz);
ut8 *block0 = calloc(1, bsz);
ut8 *block1 = malloc(bsz);
if (!buf || !block0 || !block1) {
RZ_LOG_ERROR("core: cannot allocate buf or block\n");
free(buf);
free(block0);
free(block1);
return;
}
memset(block1, -1, bsz);
int minop = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
if (minop < 1) {
minop = 1;
}
int setBits = rz_config_get_i(core->config, "asm.bits");
rz_cons_break_push(NULL, NULL);
while (addr < addr_end && !rz_cons_is_breaked()) {
// TODO: too many ioreads here
if (bufi > bufi_max) {
bufi = 0;
}
if (!bufi) {
(void)rz_io_read_at_mapped(core->io, addr, buf, bsz);
}
if (!memcmp(buf, block0, bsz) || !memcmp(buf, block1, bsz)) {
// eprintf ("Error: skipping uninitialized block \n");
addr += bsz;
continue;
}
RzAnalysisHint *hint = rz_analysis_hint_get(core->analysis, addr);
if (hint && hint->bits) {
setBits = hint->bits;
}
rz_analysis_hint_free(hint);
if (setBits != archbits) {
rz_config_set_i(core->config, "asm.bits", setBits);
}
rz_analysis_op_init(&op);
if (rz_analysis_op(core->analysis, &op, addr, buf + bufi, bsz - bufi, 0) > 0) {
if (op.size < 1) {
op.size = minop;
}
if (op.type == RZ_ANALYSIS_OP_TYPE_CALL) {
bool isValidCall = true;
if (imports_only) {
RzFlagItem *f = rz_flag_get_i(core->flags, op.jump);
if (!f || !strstr(f->name, "imp.")) {
isValidCall = false;
}
}
RzBinReloc *rel = rz_core_getreloc(core, addr, op.size);
if (rel && (rel->import || rel->symbol)) {
isValidCall = false;
}
if (isValidCall) {
ut8 buf[4];
rz_io_read_at_mapped(core->io, op.jump, buf, 4);
isValidCall = memcmp(buf, "\x00\x00\x00\x00", 4);
}
if (isValidCall) {
// add xref here
rz_analysis_xrefs_set(core->analysis, addr, op.jump, RZ_ANALYSIS_XREF_TYPE_CALL);
if (rz_io_is_valid_offset(core->io, op.jump, 1)) {
rz_core_analysis_fcn(core, op.jump, addr, RZ_ANALYSIS_XREF_TYPE_CALL, depth);
}
}
}
} else {
op.size = minop;
}
if ((int)op.size < 1) {
op.size = minop;
}
addr += op.size;
bufi += op.size;
rz_analysis_op_fini(&op);
}
rz_cons_break_pop();
free(buf);
free(block0);
free(block1);
}
/*
* \brief Performs analysis on each call sight, creates new functions whenever necessary.
* \param core RzCore instance
* \param imports_only if true it analyses calls only of imported functions, otherwise - every flag
*/
RZ_API void rz_core_analysis_calls(RZ_NONNULL RzCore *core, bool imports_only) {
rz_return_if_fail(core);
RzList *ranges = NULL;
RzIOMap *r;
ut64 addr;
RzBinFile *binfile = rz_bin_cur(core->bin);
addr = core->offset;
if (binfile) {
ranges = rz_core_get_boundaries_select(core, "analysis.from", "analysis.to", "analysis.in");
}
rz_cons_break_push(NULL, NULL);
if (!binfile || rz_list_length(ranges) < 1) {
RzListIter *iter;
RzIOMap *map;
rz_list_free(ranges);
ranges = rz_core_get_boundaries_select(core, "analysis.from", "analysis.to", "analysis.in");
if (ranges) {
rz_list_foreach (ranges, iter, map) {
ut64 addr = map->itv.addr;
_analysis_calls(core, addr, rz_itv_end(map->itv), imports_only);
}
}
} else {
RzListIter *iter;
if (binfile) {
rz_list_foreach (ranges, iter, r) {
addr = r->itv.addr;
// this normally will happen on fuzzed binaries, dunno if with huge
// binaries as well
if (rz_cons_is_breaked()) {
break;
}
_analysis_calls(core, addr, rz_itv_end(r->itv), imports_only);
}
}
}
rz_cons_break_pop();
rz_list_free(ranges);
}
/**
* Try to guess the address of the instruction before addr
*/
RZ_IPI ut64 rz_core_prevop_addr_heuristic(RzCore *core, ut64 addr) {
#define OPDELTA 32
ut8 buf[OPDELTA * 2];
ut64 target, base;
RzAnalysisBlock *bb;
RzAnalysisOp op = { 0 };
int len, ret, i;
int minop = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
int maxop = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE);
if (minop == maxop) {
if (minop == -1) {
return addr - 4;
}
return addr - minop;
}
// let's see if we can use analysis info to get the previous instruction
// TODO: look in the current basicblock, then in the current function
// and search in all functions only as a last chance, to try to speed
// up the process.
bb = rz_analysis_find_most_relevant_block_in(core->analysis, addr - minop);
if (bb) {
ut64 res = rz_analysis_block_get_op_addr_in(bb, addr - minop);
if (res != UT64_MAX) {
return res;
}
}
// if we analysis info didn't help then fallback to the dumb solution.
int midflags = rz_config_get_i(core->config, "asm.flags.middle");
target = addr;
base = target > OPDELTA ? target - OPDELTA : 0;
rz_io_read_at_mapped(core->io, base, buf, sizeof(buf));
for (i = 0; i < sizeof(buf); i++) {
rz_analysis_op_init(&op);
ret = rz_analysis_op(core->analysis, &op, base + i,
buf + i, sizeof(buf) - i, RZ_ANALYSIS_OP_MASK_BASIC);
if (ret > 0) {
len = op.size;
if (len < 1) {
len = 1;
}
if (midflags >= RZ_MIDFLAGS_REALIGN) {
int skip_bytes = rz_core_flag_in_middle(core, base + i, len, &midflags);
if (skip_bytes && base + i + skip_bytes < target) {
i += skip_bytes - 1;
rz_analysis_op_fini(&op);
continue;
}
}
} else {
len = 1;
}
rz_analysis_op_fini(&op);
if (target <= base + i + len) {
return base + i;
}
i += len - 1;
}
return target > 4 ? target - 4 : 0;
}
/**
* Search of the numinstrs-th instruction before start_addr.
*
* Sets prev_addr to the value of the instruction numinstrs back.
* If we can't use the analysis, then sets prev_addr to UT64_MAX and returns false
*
* \return if analysis was able to find the previous instruction address
*/
RZ_API bool rz_core_prevop_addr(RzCore *core, ut64 start_addr, int numinstrs, RZ_OUT RZ_BORROW RZ_NONNULL ut64 *prev_addr) {
rz_return_val_if_fail(core && prev_addr, false);
RzAnalysisBlock *bb;
int i;
// Check that we're in a bb, otherwise this prevop stuff won't work.
bb = rz_analysis_find_most_relevant_block_in(core->analysis, start_addr);
if (bb) {
if (rz_analysis_block_get_op_addr_in(bb, start_addr) != UT64_MAX) {
// Do some analysis looping.
for (i = 0; i < numinstrs; i++) {
*prev_addr = rz_core_prevop_addr_heuristic(core, start_addr);
start_addr = *prev_addr;
}
return true;
}
}
// Dang! not in a bb, return false and fallback to other methods.
*prev_addr = UT64_MAX;
return false;
}
/**
* Like rz_core_prevop_addr(), but also uses heuristics as fallback if
* no concrete analysis info is available.
*/
RZ_API ut64 rz_core_prevop_addr_force(RzCore *core, ut64 start_addr, int numinstrs) {
rz_return_val_if_fail(core, UT64_MAX);
for (int i = 0; i < numinstrs; i++) {
start_addr = rz_core_prevop_addr_heuristic(core, start_addr);
}
return start_addr;
}
/**
* \brief Check if core is debugging.
* \param core RzCore instance performing analysis.
* \return true if core is debugging, false otherwise.
* */
RZ_API bool rz_core_is_debugging(RZ_NONNULL RzCore *core) {
return core && core->io && core->io->desc && core->io->desc->plugin && core->io->desc->plugin->isdbg;
}
/**
* \brief Perform auto analysis based on given analysis type.
* \param core RzCore instance that'll be used to perform the analysis.
* \param type Analysis type.
* */
RZ_API void rz_core_perform_auto_analysis(RZ_NONNULL RzCore *core, RzCoreAnalysisType type) {
rz_return_if_fail(core);
ut64 old_offset = core->offset;
const char *notify = "Analyze all flags starting with sym. and entry0 (aa)";
rz_core_notify_begin(core, "%s", notify);
rz_cons_break_push(NULL, NULL);
ut64 timeout = rz_config_get_i(core->config, "analysis.timeout");
rz_cons_break_timeout(timeout);
rz_core_analysis_all(core);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
// set debugger only if is debugging
char *debugger = NULL;
if (rz_core_is_debugging(core)) {
debugger = core->dbg->cur ? rz_str_dup(core->dbg->cur->name) : rz_str_dup("esil");
}
rz_cons_clear_line(stderr);
// if type was simple only then don't proceed further
if (type == RZ_CORE_ANALYSIS_SIMPLE || rz_cons_is_breaked()) {
goto finish;
}
// Run pending analysis immediately after analysis
// Usefull when running commands with ";" or via rizin -c,-i
rz_core_analysis_everything(core, type == RZ_CORE_ANALYSIS_EXPERIMENTAL, debugger);
finish:
rz_core_seek(core, old_offset, true);
// XXX this shouldnt be called. flags muts be created wheen the function is registered
rz_core_analysis_flag_every_function(core);
rz_cons_break_pop();
RZ_FREE(debugger);
}
/**
* \brief Get string representation of RzAnalysisVar.
*
* \param core RzCore instance
* \param var RzAnalysisVar to be converted to string
*/
RZ_API RZ_OWN char *rz_core_analysis_var_to_string(RZ_NONNULL RzCore *core, RZ_NONNULL RzAnalysisVar *var) {
rz_return_val_if_fail(core && var, NULL);
RzStrBuf *sb = rz_strbuf_new(NULL);
if (!sb) {
return NULL;
}
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
bool color = rz_config_get_i(core->config, "scr.color") > 0;
bool color_arg = color && rz_config_get_b(core->config, "scr.color.args");
RzConsPrintablePalette *pal = &core->cons->context->pal;
const char *pfx = rz_analysis_var_is_arg(var) ? "arg" : "var";
char *constr = rz_analysis_var_get_constraints_readable(var);
char *vartype = rz_type_as_string(typedb, var->type);
rz_strbuf_appendf(sb, "%s%s %s%s%s%s %s%s%s%s@ ",
color_arg ? pal->func_var : "", pfx,
color_arg ? pal->func_var_type : "", vartype,
rz_str_endswith(vartype, "*") ? "" : " ",
var->name,
color_arg ? pal->func_var_addr : "",
constr ? " { " : "",
constr ? constr : "",
constr ? "} " : "");
free(vartype);
free(constr);
rz_analysis_var_storage_dump(core->analysis, sb, var, &var->storage);
return rz_strbuf_drain(sb);
}
static const RzBinSourceLineSample *get_source_line_info(RzCore *core, ut64 addr) {
RzBinObject *o = rz_bin_cur_object(core->bin);
const RzBinSourceLineInfo *sl = o ? o->lines : NULL;
const RzBinSourceLineSample *s = rz_bin_source_line_info_get_first_at(sl, addr);
if (!(s && s->address == addr)) {
// consider only exact matches, not inside of samples
return NULL;
}
while (s && !s->file) {
s = rz_bin_source_line_info_get_next(sl, s);
}
if (!s) {
return NULL;
}
return s;
}
/**
* \brief Get debug source line information for the given function
*
* \param core RzCore instance
* \param var RzAnalysisFunction to get the source level information for
*/
RZ_API RZ_BORROW const RzBinSourceLineSample *rz_analysis_function_sourceline_information(RZ_NONNULL RzCore *core, RZ_NONNULL RzAnalysisFunction *fcn) {
rz_return_val_if_fail(core && fcn, NULL);
return get_source_line_info(core, fcn->addr);
}
/**
* \brief Get debug source line information for the given global variable
*
* \param core RzCore instance
* \param var RzAnalysisVarGlobal to get the source level information for
*/
RZ_API RZ_BORROW const RzBinSourceLineSample *rz_analysis_var_global_sourceline_information(RZ_NONNULL RzCore *core, RZ_NONNULL RzAnalysisVarGlobal *glb) {
rz_return_val_if_fail(core && glb, NULL);
return get_source_line_info(core, glb->addr);
}