rizin/librz/core/cmd/cmd_analysis.c
2022-08-05 15:10:24 +02:00

7632 lines
231 KiB
C

// SPDX-FileCopyrightText: 2009-2021 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2009-2021 maijin <maijin21@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_core.h>
#include <rz_util/rz_graph_drawable.h>
#include "../core_private.h"
#define MAX_SCAN_SIZE 0x7ffffff
HEAPTYPE(ut64);
static const char *help_msg_a[] = {
"Usage:", "a", "[abdefFghoprxstc] [...]",
"a*", "", "same as afl*;ah*;ax*",
"aa", "[?]", "analyze all (fcns + bbs) (aa0 to avoid sub renaming)",
"a8", " [hexpairs]", "analyze bytes",
"ab", "[?] [addr]", "analyze block",
"ad", "[?]", "analyze data trampoline (wip)",
"ad", " [from] [to]", "analyze data pointers to (from-to)",
"ae", "[?] [expr]", "analyze opcode eval expression (see ao)",
"af", "[?]", "analyze Functions",
"aF", "", "same as above, but using analysis.depth=1",
"ag", "[?] [options]", "draw graphs in various formats",
"ah", "[?]", "analysis hints (force opcode size, ...)",
"ai", " [addr]", "address information (show perms, stack, heap, ...)",
"aj", "", "same as a* but in json (aflj)",
"aL", "", "list all asm/analysis plugins (e asm.arch=?)",
"an", " [name] [@addr]", "show/rename/create whatever flag/function is used at addr",
"ao", "[?] [len]", "analyze Opcodes (or emulate it)",
"aO", "[?] [len]", "Analyze N instructions in M bytes",
"ap", "", "find prelude for current offset",
"ar", "[?]", "like 'dr' but for the esil vm. (registers)",
"as", "[?] [num]", "analyze syscall using dbg.reg",
"av", "[?] [.]", "show vtables",
"ax", "[?]", "manage refs/xrefs (see also afx?)",
NULL
};
static const char *help_msg_ad[] = {
"Usage:", "ad", "[kt] [...]",
"ad", " [N] [D]", "analyze N data words at D depth",
"ad4", " [N] [D]", "analyze N data words at D depth (asm.bits=32)",
"ad8", " [N] [D]", "analyze N data words at D depth (asm.bits=64)",
"adf", "", "analyze data in function (use like .adf @@=`afl~[0]`",
"adfg", "", "analyze data in function gaps",
"adt", "", "analyze data trampolines (wip)",
"adk", "", "analyze data kind (code, text, data, invalid, ...)",
NULL
};
static const char *help_msg_ae[] = {
"Usage:", "ae[idesr?] [arg]", "ESIL code emulation",
"ae", " [expr]", "evaluate ESIL expression",
"ae?", "", "show this help",
"ae??", "", "show ESIL help",
"ae[aA]", "[f] [count]", "analyse esil accesses (regs, mem..)",
"aeC", "[arg0 arg1..] @ addr", "appcall in esil",
"aec", "[?]", "continue until ^C",
"aecb", "", "continue back until breakpoint",
"aecs", "", "continue until syscall",
"aecc", "", "continue until call",
"aecu", " [addr]", "continue until address",
"aecue", " [esil]", "continue until esil expression match",
"aef", " [addr]", "emulate function",
"aefa", " [addr]", "emulate function to find out args in given or current offset",
"aei", "", "initialize ESIL VM state (aei- to deinitialize)",
"aeim", " [addr] [size] [name]", "initialize ESIL VM stack (aeim- remove)",
"aeip", "", "initialize ESIL program counter to curseek",
"aek", " [query]", "perform sdb query on ESIL.info",
"aek-", "", "resets the ESIL.info sdb instance",
"aeli", "", "list loaded ESIL interrupts",
"aeli", " [file]", "load ESIL interrupts from shared object",
"aelir", " [interrupt number]", "remove ESIL interrupt and free it if needed",
"aepc", " [addr]", "change esil PC to this address",
"aes", "", "perform emulated debugger step",
"aesp", " [X] [N]", "evaluate N instr from offset X",
"aesb", "", "step back",
"aeso", " ", "step over",
"aesou", " [addr]", "step over until given address",
"aess", " ", "step skip (in case of CALL, just skip, instead of step into)",
"aesu", " [addr]", "step until given address",
"aesue", " [esil]", "step until esil expression match",
"aesuo", " [optype]", "step until given opcode type",
"aets", "[?]", "ESIL Trace session",
"aex", " [hex]", "evaluate opcode expression",
"aez", "[?]", "RzIL Emulation",
NULL
};
static const char *help_detail_ae[] = {
"Examples:", "ESIL", " examples and documentation",
"=", "", "assign updating internal flags",
":=", "", "assign without updating internal flags",
"+=", "", "A+=B => B,A,+=",
"+", "", "A=A+B => B,A,+,A,=",
"++", "", "increment, 2,A,++ == 3 (see rsi,--=[1], ... )",
"--", "", "decrement, 2,A,-- == 1",
"*=", "", "A*=B => B,A,*=",
"/=", "", "A/=B => B,A,/=",
"%=", "", "A%=B => B,A,%=",
"&=", "", "and ax, bx => bx,ax,&=",
"|", "", "or r0, r1, r2 => r2,r1,|,r0,=",
"!=", "", "negate all bits",
"^=", "", "xor ax, bx => bx,ax,^=",
"", "[]", "mov eax,[eax] => eax,[],eax,=",
"=", "[]", "mov [eax+3], 1 => 1,3,eax,+,=[]",
"=", "[1]", "mov byte[eax],1 => 1,eax,=[1]",
"=", "[8]", "mov [rax],1 => 1,rax,=[8]",
"[]", "", "peek from random position",
"[N]", "", "peek word of N bytes from popped address",
"[*]", "", "peek some from random position",
"=", "[*]", "poke some at random position",
"$", "", "int 0x80 => 0x80,$",
"$$", "", "simulate a hardware trap",
"==", "", "pops twice, compare and update esil flags",
"<", "", "compare for smaller",
"<=", "", "compare for smaller or equal",
">", "", "compare for bigger",
">=", "", "compare bigger for or equal",
">>=", "", "shr ax, bx => bx,ax,>>= # shift right",
"<<=", "", "shl ax, bx => bx,ax,<<= # shift left",
">>>=", "", "ror ax, bx => bx,ax,>>>= # rotate right",
"<<<=", "", "rol ax, bx => bx,ax,<<<= # rotate left",
"?{", "", "if popped value != 0 run the block until }",
"POP", "", "drops last element in the esil stack",
"DUP", "", "duplicate last value in stack",
"NUM", "", "evaluate last item in stack to number",
"SWAP", "", "swap last two values in stack",
"TRAP", "", "stop execution",
"BITS", "", "16,BITS # change bits, useful for arm/thumb",
"TODO", "", "the instruction is not yet esilized",
"STACK", "", "show contents of stack",
"CLEAR", "", "clears the esil stack",
"REPEAT", "", "repeat n times",
"BREAK", "", "terminates the string parsing",
"SETJT", "", "set jump target",
"SETJTS", "", "set jump target set",
"SETD", "", "set delay slot",
"GOTO", "", "jump to the Nth word popped from the stack",
"$", "", "esil interrupt",
"$z", "", "internal flag: zero",
"$c", "", "internal flag: carry",
"$b", "", "internal flag: borrow",
"$p", "", "internal flag: parity",
"$s", "", "internal flag: sign",
"$o", "", "internal flag: overflow",
"$ds", "", "internal flag: delay-slot",
"$jt", "", "internal flag: jump-target",
"$js", "", "internal flag: jump-target-set",
// DEPRECATED "$r", "", "internal flag: jump-sign",
"$$", "", "internal flag: pc address",
NULL
};
static const char *help_msg_aea[] = {
"Examples:", "aea", " show regs and memory accesses used in a range",
"aea", " [ops]", "Show regs/memory accesses used in N instructions ",
"aea*", " [ops]", "Create mem.* flags for memory accesses",
"aeab", "", "Show regs used in current basic block",
"aeaf", "", "Show regs used in current function",
"aear", " [ops]", "Show regs read in N instructions",
"aeaw", " [ops]", "Show regs written in N instructions",
"aean", " [ops]", "Show regs not written in N instructions",
"aeaj", " [ops]", "Show aea output in JSON format",
"aeA", " [len]", "Show regs used in N bytes (subcommands are the same)",
"Legend:", "", "",
"I", "", "input registers (read before being set)",
"A", "", "all regs accessed",
"R", "", "register values read",
"W", "", "registers written",
"N", "", "read but never written",
"V", "", "values",
"@R", "", "memreads",
"@W", "", "memwrites",
"NOTE:", "", "mem{reads,writes} with PIC only fetch the offset",
NULL
};
static const char *help_msg_ag[] = {
"Usage:", "ag<graphtype><format> [addr]", "",
"Graph commands:", "", "",
"aga", "[format]", "Data references graph",
"agA", "[format]", "Global data references graph",
"agc", "[format]", "Function callgraph",
"agC", "[format]", "Global callgraph",
"agd", "[format] [fcn addr]", "Diff graph",
"agf", "[format]", "Basic blocks function graph",
"agi", "[format]", "Imports graph",
"agr", "[format]", "References graph",
"agR", "[format]", "Global references graph",
"agx", "[format]", "Cross references graph",
"agg", "[format]", "Custom graph",
"ag-", "", "Clear the custom graph",
"agn", "[?] title body", "Add a node to the custom graph",
"age", "[?] title1 title2", "Add an edge to the custom graph",
"", "", "",
"Output formats:", "", "",
"<blank>", "", "Ascii art",
"*", "", "rizin commands",
"d", "", "Graphviz dot",
"g", "", "Graph Modelling Language (gml)",
"j", "", "json ('J' for formatted disassembly)",
"k", "", "SDB key-value",
"t", "", "Tiny ascii art",
"v", "", "Interactive ascii art",
"w", " [path]", "Write to path or display graph image (see graph.gv.format)",
NULL
};
static const char *help_msg_age[] = {
"Usage:", "age [title1] [title2]", "",
"Examples:", "", "",
"age", " title1 title2", "Add an edge from the node with \"title1\" as title to the one with title \"title2\"",
"age", " \"title1 with spaces\" title2", "Add an edge from node \"title1 with spaces\" to node \"title2\"",
"age-", " title1 title2", "Remove an edge from the node with \"title1\" as title to the one with title \"title2\"",
"age?", "", "Show this help",
NULL
};
static const char *help_msg_agn[] = {
"Usage:", "agn [title] [body]", "",
"Examples:", "", "",
"agn", " title1 body1", "Add a node with title \"title1\" and body \"body1\"",
"agn", " \"title with space\" \"body with space\"", "Add a node with spaces in the title and in the body",
"agn", " title1 base64:Ym9keTE=", "Add a node with the body specified as base64",
"agn-", " title1", "Remove a node with title \"title1\"",
"agn?", "", "Show this help",
NULL
};
/**
* \brief Helper to get function in \p offset
*
* Case of overlapped functions is treated as an error
* if \p offset is not an entry point.
*/
static RzAnalysisFunction *analysis_get_function_in(RzAnalysis *analysis, ut64 offset) {
RzAnalysisFunction *fcn = rz_analysis_get_function_at(analysis, offset);
if (fcn) {
return fcn;
}
RzList *list = rz_analysis_get_functions_in(analysis, offset);
if (rz_list_empty(list)) {
RZ_LOG_ERROR("No function found in 0x%08" PFMT64x ".\n", offset);
goto exit;
}
if (rz_list_length(list) > 1) {
RZ_LOG_ERROR("Multiple overlapping functions found at 0x%08" PFMT64x ". "
"Re-run this command at the entrypoint of one of them to disambiguate.\n",
offset);
goto exit;
}
fcn = rz_list_first(list);
if (!fcn) {
rz_warn_if_reached();
}
exit:
rz_list_free(list);
return fcn;
}
static int cmpaddr(const void *_a, const void *_b) {
const RzAnalysisFunction *a = _a, *b = _b;
return (a->addr > b->addr) ? 1 : (a->addr < b->addr) ? -1
: 0;
}
static bool listOpDescriptions(void *_core, const char *k, const char *v) {
rz_cons_printf("%s=%s\n", k, v);
return true;
}
static void var_accesses_list(RzAnalysisFunction *fcn, RzAnalysisVar *var, PJ *pj, int access_type, const char *name) {
RzAnalysisVarAccess *acc;
bool first = true;
if (pj) {
pj_o(pj);
pj_ks(pj, "name", name);
pj_ka(pj, "addrs");
} else {
rz_cons_printf("%10s", name);
}
rz_vector_foreach(&var->accesses, acc) {
if (!(acc->type & access_type)) {
continue;
}
ut64 addr = fcn->addr + acc->offset;
if (pj) {
pj_n(pj, addr);
} else {
rz_cons_printf("%s0x%" PFMT64x, first ? " " : ",", addr);
}
first = false;
}
if (pj) {
pj_end(pj);
pj_end(pj);
} else {
rz_cons_newline();
}
}
typedef enum {
IS_VAR = 0,
IS_ARG,
IS_ARG_AND_VAR
} RzVarListType;
static void list_vars(RzCore *core, RzAnalysisFunction *fcn, PJ *pj, int type, const char *name, RzVarListType vlt) {
RzAnalysisVar *var = NULL;
RzListIter *iter;
RzList *list = rz_analysis_var_all_list(core->analysis, fcn);
if (type == '=') {
ut64 oaddr = core->offset;
rz_list_foreach (list, iter, var) {
rz_cons_printf("* %s\n", var->name);
RzAnalysisVarAccess *acc;
rz_vector_foreach(&var->accesses, acc) {
if (!(acc->type & RZ_ANALYSIS_VAR_ACCESS_TYPE_READ)) {
continue;
}
rz_cons_printf("R 0x%" PFMT64x " ", fcn->addr + acc->offset);
rz_core_seek(core, fcn->addr + acc->offset, 1);
rz_core_print_disasm_instructions(core, 0, 1);
}
rz_vector_foreach(&var->accesses, acc) {
if (!(acc->type & RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE)) {
continue;
}
rz_cons_printf("W 0x%" PFMT64x " ", fcn->addr + acc->offset);
rz_core_seek(core, fcn->addr + acc->offset, 1);
rz_core_print_disasm_instructions(core, 0, 1);
}
}
rz_core_seek(core, oaddr, 0);
return;
}
if (type == '*') {
const char *bp = rz_reg_get_name(core->analysis->reg, RZ_REG_NAME_BP);
rz_cons_printf("f-fcnvar*\n");
rz_list_foreach (list, iter, var) {
rz_cons_printf("f fcnvar.%s @ %s%s%d\n", var->name, bp,
var->delta >= 0 ? "+" : "", var->delta);
}
return;
}
if (type != 'W' && type != 'R') {
return;
}
int access_type = type == 'R' ? RZ_ANALYSIS_VAR_ACCESS_TYPE_READ : RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE;
if (pj) {
pj_a(pj);
}
if (name && *name) {
var = rz_analysis_function_get_var_byname(fcn, name);
if (var) {
if (var->isarg == vlt || vlt == IS_ARG_AND_VAR) {
var_accesses_list(fcn, var, pj, access_type, var->name);
}
}
} else {
rz_list_foreach (list, iter, var) {
if (var->isarg == vlt || vlt == IS_ARG_AND_VAR) {
var_accesses_list(fcn, var, pj, access_type, var->name);
}
}
}
if (pj) {
pj_end(pj);
}
}
#define PJ_KS(pj, key, value) \
{ \
const char *value_tmp = (value); \
if (RZ_STR_ISNOTEMPTY(value_tmp)) { \
pj_ks(pj, key, value_tmp); \
} \
}
#define PJ_KN(pj, key, value) \
{ \
const ut64 value_tmp = (value); \
if (value_tmp != UT64_MAX) { \
pj_kn(pj, key, value_tmp); \
} \
}
static bool core_analysis_name_print(RzCore *core, RzCmdStateOutput *state) {
RzCoreAnalysisName *p = rz_core_analysis_name(core, core->offset);
if (!p) {
return false;
}
PJ *pj = state->d.pj;
switch (state->mode) {
case RZ_OUTPUT_MODE_JSON: {
pj_a(pj);
pj_o(pj);
PJ_KS(pj, "name", p->name);
PJ_KS(pj, "realname", p->realname);
pj_ks(pj, "type", rz_core_analysis_name_type_to_str(p->type));
pj_kn(pj, "offset", p->offset);
pj_end(pj);
pj_end(pj);
break;
}
case RZ_OUTPUT_MODE_STANDARD: {
if (p->type == RZ_CORE_ANALYSIS_NAME_TYPE_ADDRESS) {
rz_cons_printf("0x%" PFMT64x "\n", p->offset);
} else {
rz_cons_println(p->name);
}
break;
}
default:
rz_warn_if_reached();
return false;
}
rz_core_analysis_name_free(p);
return true;
}
static void print_trampolines(RzCore *core, ut64 a, ut64 b, size_t element_size) {
int i;
for (i = 0; i < core->blocksize; i += element_size) {
ut32 n;
memcpy(&n, core->block + i, sizeof(ut32));
if (n >= a && n <= b) {
if (element_size == 4) {
rz_cons_printf("f trampoline.%x @ 0x%" PFMT64x "\n", n, core->offset + i);
} else {
rz_cons_printf("f trampoline.%" PFMT32x " @ 0x%" PFMT64x "\n", n, core->offset + i);
}
rz_cons_printf("Cd %zu @ 0x%" PFMT64x ":%zu\n", element_size, core->offset + i, element_size);
// TODO: add data xrefs
}
}
}
static void cmd_analysis_trampoline(RzCore *core, const char *input) {
int bits = rz_config_get_i(core->config, "asm.bits");
char *p, *inp = strdup(input);
p = strchr(inp, ' ');
if (p) {
*p = 0;
}
ut64 a = rz_num_math(core->num, inp);
ut64 b = p ? rz_num_math(core->num, p + 1) : 0;
free(inp);
switch (bits) {
case 32:
print_trampolines(core, a, b, 4);
break;
case 64:
print_trampolines(core, a, b, 8);
break;
}
}
static int mw(RzAnalysisEsil *esil, ut64 addr, const ut8 *buf, int len) {
int *ec = (int *)esil->user;
*ec += (len * 2);
return 1;
}
static int mr(RzAnalysisEsil *esil, ut64 addr, ut8 *buf, int len) {
int *ec = (int *)esil->user;
*ec += len;
return 1;
}
static int esil_cost(RzCore *core, ut64 addr, const char *expr) {
if (RZ_STR_ISEMPTY(expr)) {
return 0;
}
int ec = 0;
RzAnalysisEsil *e = rz_analysis_esil_new(256, 0, 0);
rz_analysis_esil_setup(e, core->analysis, false, false, false);
e->user = &ec;
e->cb.mem_read = mr;
e->cb.mem_write = mw;
rz_analysis_esil_parse(e, expr);
rz_analysis_esil_free(e);
return ec;
}
static void core_analysis_bytes_size(RzCore *core, const ut8 *buf, int len, int nops) {
core->parser->subrel = rz_config_get_i(core->config, "asm.sub.rel");
int ret, i, idx;
RzAnalysisOp op = { 0 };
ut64 addr;
int totalsize = 0;
for (i = idx = 0; idx < len && (!nops || (nops && i < nops)); i++, idx += ret) {
addr = core->offset + idx;
rz_asm_set_pc(core->rasm, addr);
ret = rz_analysis_op(core->analysis, &op, addr, buf + idx, len - idx,
RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_OPEX | RZ_ANALYSIS_OP_MASK_HINT);
if (ret < 1) {
RZ_LOG_ERROR("Invalid instruction at 0x%08" PFMT64x "...\n", core->offset + idx);
break;
}
totalsize += op.size;
rz_analysis_op_fini(&op);
}
rz_analysis_op_fini(&op);
rz_cons_printf("%d\n", totalsize);
}
static void core_analysis_bytes_desc(RzCore *core, const ut8 *buf, int len, int nops) {
core->parser->subrel = rz_config_get_i(core->config, "asm.sub.rel");
int ret, i, idx;
RzAsmOp asmop;
RzAnalysisOp op = { 0 };
ut64 addr;
for (i = idx = 0; idx < len && (!nops || (nops && i < nops)); i++, idx += ret) {
addr = core->offset + idx;
rz_asm_set_pc(core->rasm, addr);
ret = rz_analysis_op(core->analysis, &op, addr, buf + idx, len - idx,
RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_OPEX | RZ_ANALYSIS_OP_MASK_HINT);
(void)rz_asm_disassemble(core->rasm, &asmop, buf + idx, len - idx);
if (ret < 1) {
RZ_LOG_ERROR("Invalid instruction at 0x%08" PFMT64x "...\n", core->offset + idx);
break;
}
char *opname = strdup(rz_asm_op_get_asm(&asmop));
if (opname) {
rz_str_split(opname, ' ');
char *d = rz_asm_describe(core->rasm, opname);
if (d && *d) {
rz_cons_printf("%s: %s\n", opname, d);
free(d);
} else {
RZ_LOG_ERROR("mnemonic %s has no description\n", opname);
}
free(opname);
}
rz_analysis_op_fini(&op);
}
rz_analysis_op_fini(&op);
}
static void core_analysis_bytes_esil(RzCore *core, const ut8 *buf, int len, int nops) {
bool use_color = core->print->flags & RZ_PRINT_FLAGS_COLOR;
core->parser->subrel = rz_config_get_i(core->config, "asm.sub.rel");
int ret, i, idx;
const char *color = "";
const char *esilstr;
RzAnalysisEsil *esil = NULL;
RzAnalysisOp op = { 0 };
ut64 addr;
if (use_color) {
color = core->cons->context->pal.label;
}
for (i = idx = 0; idx < len && (!nops || (nops && i < nops)); i++, idx += ret) {
addr = core->offset + idx;
rz_asm_set_pc(core->rasm, addr);
ret = rz_analysis_op(core->analysis, &op, addr, buf + idx, len - idx,
RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_OPEX | RZ_ANALYSIS_OP_MASK_HINT);
esilstr = RZ_STRBUF_SAFEGET(&op.esil);
if (ret < 1) {
RZ_LOG_ERROR("Invalid instruction at 0x%08" PFMT64x "...\n", core->offset + idx);
break;
}
if (RZ_STR_ISNOTEMPTY(esilstr)) {
if (use_color) {
rz_cons_printf("%s0x%" PFMT64x Color_RESET " %s\n", color, core->offset + idx, esilstr);
} else {
rz_cons_printf("0x%" PFMT64x " %s\n", core->offset + idx, esilstr);
}
}
rz_analysis_op_fini(&op);
}
rz_analysis_op_fini(&op);
rz_analysis_esil_free(esil);
}
static void core_analysis_bytes_json(RzCore *core, const ut8 *buf, int len, int nops, PJ *pj) {
RzPVector *vec = rz_core_analysis_bytes(core, buf, len, nops);
if (!vec) {
return;
}
void **iter;
RzAnalysisBytes *ab;
pj_a(pj);
rz_pvector_foreach (vec, iter) {
if (!iter) {
break;
}
ab = *iter;
if (!ab || !ab->op) {
break;
}
RzAnalysisOp *op = ab->op;
const char *esilstr = RZ_STRBUF_SAFEGET(&op->esil);
const char *opexstr = RZ_STRBUF_SAFEGET(&op->opex);
RzAnalysisHint *hint = ab->hint;
pj_o(pj);
PJ_KS(pj, "opcode", ab->opcode);
PJ_KS(pj, "disasm", ab->disasm);
PJ_KS(pj, "pseudo", ab->pseudo);
PJ_KS(pj, "description", ab->description);
PJ_KS(pj, "mnemonic", op->mnemonic);
PJ_KS(pj, "mask", ab->mask);
if (hint) {
PJ_KS(pj, "ophint", hint->opcode);
}
PJ_KN(pj, "jump", op->jump);
PJ_KN(pj, "fail", op->fail);
PJ_KS(pj, "esil", (hint && hint->esil) ? hint->esil : esilstr);
if (op->il_op) {
pj_k(pj, "rzil");
rz_il_op_effect_json(op->il_op, pj);
}
pj_kb(pj, "sign", op->sign);
pj_kn(pj, "prefix", op->prefix);
pj_ki(pj, "id", op->id);
if (RZ_STR_ISNOTEMPTY(opexstr)) {
pj_k(pj, "opex");
pj_j(pj, opexstr);
}
PJ_KN(pj, "addr", op->addr);
PJ_KS(pj, "bytes", ab->bytes);
PJ_KN(pj, "val", op->val);
PJ_KN(pj, "disp", op->disp);
PJ_KN(pj, "ptr", op->ptr);
pj_ki(pj, "size", op->size);
PJ_KS(pj, "type", rz_analysis_optype_to_string((int)op->type));
PJ_KS(pj, "datatype", rz_analysis_datatype_to_string(op->datatype));
if (esilstr) {
pj_ki(pj, "esilcost", esil_cost(core, op->addr, esilstr));
}
PJ_KS(pj, "reg", op->reg);
PJ_KS(pj, "ireg", op->ireg);
pj_ki(pj, "scale", op->scale);
if (op->refptr != -1) {
pj_ki(pj, "refptr", op->refptr);
}
pj_ki(pj, "cycles", op->cycles);
pj_ki(pj, "failcycles", op->failcycles);
pj_ki(pj, "delay", op->delay);
const char *p1 = rz_analysis_stackop_tostring(op->stackop);
if (strcmp(p1, "null") != 0) {
PJ_KS(pj, "stack", p1);
}
pj_kn(pj, "stackptr", op->stackptr);
PJ_KS(pj, "cond", (op->type & RZ_ANALYSIS_OP_TYPE_COND) ? rz_type_cond_tostring(op->cond) : NULL);
PJ_KS(pj, "family", rz_analysis_op_family_to_string(op->family));
pj_end(pj);
}
pj_end(pj);
rz_pvector_free(vec);
}
#define PRINTF_LN(k, fmt, arg) \
{ \
if (use_color) \
rz_cons_printf("%s%s: " Color_RESET, color, k); \
else \
rz_cons_printf("%s: ", k); \
if (fmt) \
rz_cons_printf(fmt, arg); \
}
#define PRINTF_LN_NOT(k, fmt, arg, notv) \
if ((arg) != (notv)) { \
PRINTF_LN(k, fmt, arg) \
}
#define PRINTF_LN_STR(k, arg) \
{ \
const char *value = (arg); \
if (RZ_STR_ISNOTEMPTY(value)) { \
if (use_color) \
rz_cons_printf("%s%s: %s\n" Color_RESET, color, k, value); \
else \
rz_cons_printf("%s: %s\n", k, value); \
} \
}
static void core_analysis_bytes_standard(RzCore *core, const ut8 *buf, int len, int nops) {
RzPVector *vec = rz_core_analysis_bytes(core, buf, len, nops);
if (!vec) {
return;
}
bool use_color = core->print->flags & RZ_PRINT_FLAGS_COLOR;
const char *color = use_color ? core->cons->context->pal.label : "";
void **iter;
RzAnalysisBytes *ab;
rz_pvector_foreach (vec, iter) {
if (!(iter && *iter && ((RzAnalysisBytes *)*iter)->op)) {
break;
}
ab = *iter;
RzAnalysisOp *op = ab->op;
const char *esilstr = RZ_STRBUF_SAFEGET(&op->esil);
RzAnalysisHint *hint = ab->hint;
PRINTF_LN("address", "0x%" PFMT64x "\n", op->addr);
PRINTF_LN("opcode", "%s\n", ab->opcode);
if (esilstr) {
PRINTF_LN("esilcost", "%d\n", esil_cost(core, op->addr, esilstr));
}
PRINTF_LN("disasm", "%s\n", ab->disasm);
PRINTF_LN_STR("pseudo", ab->pseudo);
PRINTF_LN("mnemonic", "%s\n", op->mnemonic);
PRINTF_LN_STR("description", ab->description);
PRINTF_LN("mask", "%s\n", ab->mask);
PRINTF_LN_STR("ophint", hint ? hint->opcode : NULL);
PRINTF_LN("prefix", "%u\n", op->prefix);
PRINTF_LN("id", "%d\n", op->id);
PRINTF_LN_STR("bytes", ab->bytes);
PRINTF_LN_NOT("val", "0x%08" PFMT64x "\n", op->val, UT64_MAX);
PRINTF_LN_NOT("ptr", "0x%08" PFMT64x "\n", op->ptr, UT64_MAX);
PRINTF_LN_NOT("disp", "0x%08" PFMT64x "\n", op->disp, UT64_MAX);
PRINTF_LN_NOT("refptr", "%d\n", op->refptr, -1);
PRINTF_LN("size", "%d\n", op->size);
PRINTF_LN_STR("sign", rz_str_bool(op->sign));
PRINTF_LN_STR("type", rz_analysis_optype_to_string(op->type));
PRINTF_LN_STR("datatype", rz_analysis_datatype_to_string(op->datatype));
PRINTF_LN("cycles", "%d\n", op->cycles);
PRINTF_LN_NOT("failcycles", "%d\n", op->failcycles, 0);
PRINTF_LN_NOT("type2", "0x%x\n", op->type2, 0);
PRINTF_LN_STR("reg", op->reg);
PRINTF_LN_STR("ireg", op->ireg);
PRINTF_LN_NOT("scale", "%d\n", op->scale, 0);
PRINTF_LN_STR("esil", hint && hint->esil ? hint->esil : esilstr);
if (op->il_op) {
RzStrBuf *sbil = rz_strbuf_new("");
rz_il_op_effect_stringify(op->il_op, sbil);
PRINTF_LN_STR("rzil", rz_strbuf_get(sbil));
rz_strbuf_free(sbil);
}
PRINTF_LN_NOT("jump", "0x%08" PFMT64x "\n", op->jump, UT64_MAX);
if (op->direction != 0) {
const char *dir = op->direction == 1 ? "read"
: op->direction == 2 ? "write"
: op->direction == 4 ? "exec"
: op->direction == 8 ? "ref"
: "none";
PRINTF_LN("direction", "%s\n", dir);
}
PRINTF_LN_NOT("fail", "0x%08" PFMT64x "\n", op->fail, UT64_MAX);
PRINTF_LN_NOT("delay", "%d\n", op->delay, 0);
{
const char *arg = (op->type & RZ_ANALYSIS_OP_TYPE_COND) ? rz_type_cond_tostring(op->cond) : NULL;
PRINTF_LN_STR("cond", arg);
}
PRINTF_LN("family", "%s\n", rz_analysis_op_family_to_string(op->family));
PRINTF_LN_STR("stackop", op->stackop != RZ_ANALYSIS_STACK_NULL ? rz_analysis_stackop_tostring(op->stackop) : NULL);
PRINTF_LN_NOT("stackptr", "%" PFMT64u "\n", op->stackptr, 0);
}
rz_pvector_free(vec);
}
#undef PJ_KS
#undef PJ_KN
#undef PRINTF_LN
#undef PRINTF_LN_NOT
#undef PRINTF_LN_STR
static char *fcnjoin(RzList *list) {
RzAnalysisFunction *n;
RzListIter *iter;
RzStrBuf buf;
rz_strbuf_init(&buf);
rz_list_foreach (list, iter, n) {
rz_strbuf_appendf(&buf, " 0x%08" PFMT64x, n->addr);
}
char *s = strdup(rz_strbuf_get(&buf));
rz_strbuf_fini(&buf);
return s;
}
static char *ut64join(RzList *list) {
ut64 *n;
RzListIter *iter;
RzStrBuf buf;
rz_strbuf_init(&buf);
rz_list_foreach (list, iter, n) {
rz_strbuf_appendf(&buf, " 0x%08" PFMT64x, *n);
}
char *s = strdup(rz_strbuf_get(&buf));
rz_strbuf_fini(&buf);
return s;
}
static ut64 initializeEsil(RzCore *core) {
int romem = rz_config_get_i(core->config, "esil.romem");
int stats = rz_config_get_i(core->config, "esil.stats");
int iotrap = rz_config_get_i(core->config, "esil.iotrap");
int exectrap = rz_config_get_i(core->config, "esil.exectrap");
int stacksize = rz_config_get_i(core->config, "esil.stack.depth");
int noNULL = rz_config_get_i(core->config, "esil.noNULL");
unsigned int addrsize = rz_config_get_i(core->config, "esil.addr.size");
if (!(core->analysis->esil = rz_analysis_esil_new(stacksize, iotrap, addrsize))) {
return UT64_MAX;
}
ut64 addr;
RzAnalysisEsil *esil = core->analysis->esil;
esil->verbose = rz_config_get_i(core->config, "esil.verbose");
esil->cmd = rz_core_esil_cmd;
rz_analysis_esil_setup(esil, core->analysis, romem, stats, noNULL); // setup io
{
const char *cmd_esil_step = rz_config_get(core->config, "cmd.esil.step");
if (cmd_esil_step && *cmd_esil_step) {
esil->cmd_step = strdup(cmd_esil_step);
}
const char *cmd_esil_step_out = rz_config_get(core->config, "cmd.esil.stepout");
if (cmd_esil_step_out && *cmd_esil_step_out) {
esil->cmd_step_out = strdup(cmd_esil_step_out);
}
{
const char *s = rz_config_get(core->config, "cmd.esil.intr");
if (s) {
char *my = strdup(s);
if (my) {
rz_config_set(core->config, "cmd.esil.intr", my);
free(my);
}
}
}
}
esil->exectrap = exectrap;
RzList *entries = rz_bin_get_entries(core->bin);
RzBinAddr *entry = NULL;
RzBinInfo *info = NULL;
if (entries && !rz_list_empty(entries)) {
entry = (RzBinAddr *)rz_list_pop_head(entries);
info = rz_bin_get_info(core->bin);
addr = info->has_va ? entry->vaddr : entry->paddr;
rz_list_push(entries, entry);
} else {
addr = core->offset;
}
// set memory read only
return addr;
}
RZ_API int rz_core_esil_step(RzCore *core, ut64 until_addr, const char *until_expr, ut64 *prev_addr, bool stepOver) {
#define return_tail(x) \
{ \
tail_return_value = x; \
goto tail_return; \
}
int tail_return_value = 0;
int ret;
ut8 code[32];
RzAnalysisOp op = { 0 };
RzAnalysisEsil *esil = core->analysis->esil;
const char *name = rz_reg_get_name(core->analysis->reg, RZ_REG_NAME_PC);
ut64 addr = 0;
bool breakoninvalid = rz_config_get_i(core->config, "esil.breakoninvalid");
int esiltimeout = rz_config_get_i(core->config, "esil.timeout");
ut64 startTime;
if (esiltimeout > 0) {
startTime = rz_time_now_mono();
}
rz_cons_break_push(NULL, NULL);
repeat:
if (rz_cons_is_breaked()) {
eprintf("[+] ESIL emulation interrupted at 0x%08" PFMT64x "\n", addr);
return_tail(0);
}
// Break if we have exceeded esil.timeout
if (esiltimeout > 0) {
ut64 elapsedTime = rz_time_now_mono() - startTime;
elapsedTime >>= 20;
if (elapsedTime >= esiltimeout) {
eprintf("[ESIL] Timeout exceeded.\n");
return_tail(0);
}
}
if (!esil) {
addr = initializeEsil(core);
esil = core->analysis->esil;
if (!esil) {
return_tail(0);
}
} else {
esil->trap = 0;
addr = rz_reg_getv(core->analysis->reg, name);
// eprintf ("PC=0x%"PFMT64x"\n", (ut64)addr);
}
if (prev_addr) {
*prev_addr = addr;
}
if (esil->exectrap) {
if (!rz_io_is_valid_offset(core->io, addr, RZ_PERM_X)) {
esil->trap = RZ_ANALYSIS_TRAP_EXEC_ERR;
esil->trap_code = addr;
eprintf("[ESIL] Trap, trying to execute on non-executable memory\n");
return_tail(1);
}
}
rz_asm_set_pc(core->rasm, addr);
// run esil pin command here
int dataAlign = rz_analysis_archinfo(esil->analysis, RZ_ANALYSIS_ARCHINFO_DATA_ALIGN);
if (dataAlign > 1) {
if (addr % dataAlign) {
if (esil->cmd && esil->cmd_trap) {
esil->cmd(esil, esil->cmd_trap, addr, RZ_ANALYSIS_TRAP_UNALIGNED);
}
if (breakoninvalid) {
rz_cons_printf("[ESIL] Stopped execution in an unaligned instruction (see e??esil.breakoninvalid)\n");
return_tail(0);
}
}
}
(void)rz_io_read_at_mapped(core->io, addr, code, sizeof(code));
// TODO: sometimes this is dupe
ret = rz_analysis_op(core->analysis, &op, addr, code, sizeof(code), RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_HINT);
// if type is JMP then we execute the next N instructions
// update the esil pointer because RzAnalysis.op() can change it
esil = core->analysis->esil;
if (op.size < 1 || ret < 1) {
if (esil->cmd && esil->cmd_trap) {
esil->cmd(esil, esil->cmd_trap, addr, RZ_ANALYSIS_TRAP_INVALID);
}
if (breakoninvalid) {
eprintf("[ESIL] Stopped execution in an invalid instruction (see e??esil.breakoninvalid)\n");
return_tail(0);
}
op.size = 1; // avoid inverted stepping
}
if (stepOver) {
switch (op.type) {
case RZ_ANALYSIS_OP_TYPE_SWI:
case RZ_ANALYSIS_OP_TYPE_UCALL:
case RZ_ANALYSIS_OP_TYPE_CALL:
case RZ_ANALYSIS_OP_TYPE_JMP:
case RZ_ANALYSIS_OP_TYPE_RCALL:
case RZ_ANALYSIS_OP_TYPE_RJMP:
case RZ_ANALYSIS_OP_TYPE_CJMP:
case RZ_ANALYSIS_OP_TYPE_RET:
case RZ_ANALYSIS_OP_TYPE_CRET:
case RZ_ANALYSIS_OP_TYPE_UJMP:
if (addr == until_addr) {
return_tail(0);
} else {
rz_reg_setv(core->analysis->reg, "PC", op.addr + op.size);
}
return 1;
}
}
rz_reg_setv(core->analysis->reg, name, addr + op.size);
if (ret) {
rz_analysis_esil_set_pc(esil, addr);
const char *e = RZ_STRBUF_SAFEGET(&op.esil);
if (core->dbg->trace->enabled) {
RzReg *reg = core->dbg->reg;
core->dbg->reg = core->analysis->reg;
rz_debug_trace_op(core->dbg, &op);
core->dbg->reg = reg;
} else if (RZ_STR_ISNOTEMPTY(e)) {
rz_analysis_esil_parse(esil, e);
if (core->analysis->cur && core->analysis->cur->esil_post_loop) {
core->analysis->cur->esil_post_loop(esil, &op);
}
rz_analysis_esil_stack_free(esil);
}
bool isNextFall = false;
if (op.type == RZ_ANALYSIS_OP_TYPE_CJMP) {
ut64 pc = rz_reg_getv(core->analysis->reg, name);
if (pc == addr + op.size) {
// do not opdelay here
isNextFall = true;
}
}
// only support 1 slot for now
if (op.delay && !isNextFall) {
ut8 code2[32];
ut64 naddr = addr + op.size;
RzAnalysisOp op2 = { 0 };
// emulate only 1 instruction
rz_analysis_esil_set_pc(esil, naddr);
(void)rz_io_read_at(core->io, naddr, code2, sizeof(code2));
// TODO: sometimes this is dupe
ret = rz_analysis_op(core->analysis, &op2, naddr, code2, sizeof(code2), RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_HINT);
if (ret > 0) {
switch (op2.type) {
case RZ_ANALYSIS_OP_TYPE_CJMP:
case RZ_ANALYSIS_OP_TYPE_JMP:
case RZ_ANALYSIS_OP_TYPE_CRET:
case RZ_ANALYSIS_OP_TYPE_RET:
// branches are illegal in a delay slot
esil->trap = RZ_ANALYSIS_TRAP_EXEC_ERR;
esil->trap_code = addr;
eprintf("[ESIL] Trap, trying to execute a branch in a delay slot\n");
return_tail(1);
break;
}
const char *e = RZ_STRBUF_SAFEGET(&op2.esil);
if (RZ_STR_ISNOTEMPTY(e)) {
rz_analysis_esil_parse(esil, e);
}
} else {
eprintf("Invalid instruction at 0x%08" PFMT64x "\n", naddr);
}
rz_analysis_op_fini(&op2);
}
tail_return_value = 1;
}
// esil->verbose ?
// eprintf ("REPE 0x%llx %s => 0x%llx\n", addr, RZ_STRBUF_SAFEGET (&op.esil), rz_reg_getv (core->analysis->reg, "PC"));
ut64 pc = rz_reg_getv(core->analysis->reg, name);
if (core->analysis->pcalign > 0) {
pc -= (pc % core->analysis->pcalign);
rz_reg_setv(core->analysis->reg, name, pc);
}
st64 follow = (st64)rz_config_get_i(core->config, "dbg.follow");
if (follow > 0) {
ut64 pc = rz_reg_getv(core->analysis->reg, name);
if ((pc < core->offset) || (pc > (core->offset + follow))) {
rz_core_seek_to_register(core, "PC", false);
}
}
// check breakpoints
if (rz_bp_get_at(core->dbg->bp, pc)) {
rz_cons_printf("[ESIL] hit breakpoint at 0x%" PFMT64x "\n", pc);
return_tail(0);
}
// check addr
if (until_addr != UT64_MAX) {
if (pc == until_addr) {
return_tail(0);
}
goto repeat;
}
// check esil
if (esil && esil->trap) {
if (core->analysis->esil->verbose) {
eprintf("TRAP\n");
}
return_tail(0);
}
if (until_expr) {
if (rz_analysis_esil_condition(core->analysis->esil, until_expr)) {
if (core->analysis->esil->verbose) {
eprintf("ESIL BREAK!\n");
}
return_tail(0);
}
goto repeat;
}
tail_return:
rz_analysis_op_fini(&op);
rz_cons_break_pop();
return tail_return_value;
}
RZ_API int rz_core_esil_step_back(RzCore *core) {
rz_return_val_if_fail(core->analysis->esil && core->analysis->esil->trace, -1);
RzAnalysisEsil *esil = core->analysis->esil;
if (esil->trace->idx > 0) {
rz_analysis_esil_trace_restore(esil, esil->trace->idx - 1);
rz_core_reg_update_flags(core);
return 1;
}
return 0;
}
RZ_API bool rz_core_esil_continue_back(RZ_NONNULL RzCore *core) {
rz_return_val_if_fail(core->analysis->esil && core->analysis->esil->trace, false);
RzAnalysisEsil *esil = core->analysis->esil;
if (esil->trace->idx == 0) {
return true;
}
RzRegItem *ripc = rz_reg_get(esil->analysis->reg, "PC", -1);
RzVector *vreg = ht_up_find(esil->trace->registers, ripc->offset | (ripc->arena << 16), NULL);
if (!vreg) {
RZ_LOG_ERROR("failed to find PC change vector\n");
return false;
}
// Search for the nearest breakpoint in the tracepoints before the current position
bool bp_found = false;
int idx = 0;
RzAnalysisEsilRegChange *reg;
rz_vector_foreach_prev(vreg, reg) {
if (reg->idx >= esil->trace->idx) {
continue;
}
bp_found = rz_bp_get_in(core->dbg->bp, reg->data, RZ_PERM_X) != NULL;
if (bp_found) {
idx = reg->idx;
eprintf("hit breakpoint at: 0x%" PFMT64x " idx: %d\n", reg->data, reg->idx);
break;
}
}
// Return to the nearest breakpoint or jump back to the first index if a breakpoint wasn't found
rz_analysis_esil_trace_restore(esil, idx);
rz_core_reg_update_flags(core);
return true;
}
static void cmd_address_info(RzCore *core, const ut64 addr, RzCmdStateOutput *state) {
ut64 type;
PJ *pj;
type = rz_core_analysis_address(core, addr);
switch (state->mode) {
case RZ_OUTPUT_MODE_JSON:
pj = state->d.pj;
pj_o(pj);
if (type & RZ_ANALYSIS_ADDR_TYPE_PROGRAM)
pj_ks(pj, "program", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_LIBRARY)
pj_ks(pj, "library", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_EXEC)
pj_ks(pj, "exec", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_READ)
pj_ks(pj, "read", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_WRITE)
pj_ks(pj, "write", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_FLAG)
pj_ks(pj, "flag", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_FUNC)
pj_ks(pj, "func", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_STACK)
pj_ks(pj, "stack", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_HEAP)
pj_ks(pj, "heap", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_REG)
pj_ks(pj, "reg", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_ASCII)
pj_ks(pj, "ascii", "true");
if (type & RZ_ANALYSIS_ADDR_TYPE_SEQUENCE)
pj_ks(pj, "sequence", "true");
pj_end(pj);
break;
case RZ_OUTPUT_MODE_STANDARD:
if (type & RZ_ANALYSIS_ADDR_TYPE_PROGRAM)
rz_cons_printf("program\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_LIBRARY)
rz_cons_printf("library\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_EXEC)
rz_cons_printf("exec\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_READ)
rz_cons_printf("read\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_WRITE)
rz_cons_printf("write\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_FLAG)
rz_cons_printf("flag\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_FUNC)
rz_cons_printf("func\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_STACK)
rz_cons_printf("stack\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_HEAP)
rz_cons_printf("heap\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_REG)
rz_cons_printf("reg\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_ASCII)
rz_cons_printf("ascii\n");
if (type & RZ_ANALYSIS_ADDR_TYPE_SEQUENCE)
rz_cons_printf("sequence\n");
break;
default:
rz_warn_if_reached();
break;
}
}
typedef struct {
RzList *regs;
RzList *regread;
RzList *regwrite;
RzList *regvalues;
RzList *inputregs;
} AeaStats;
static void aea_stats_init(AeaStats *stats) {
stats->regs = rz_list_newf(free);
stats->regread = rz_list_newf(free);
stats->regwrite = rz_list_newf(free);
stats->regvalues = rz_list_newf(free);
stats->inputregs = rz_list_newf(free);
}
static void aea_stats_fini(AeaStats *stats) {
RZ_FREE(stats->regs);
RZ_FREE(stats->regread);
RZ_FREE(stats->regwrite);
RZ_FREE(stats->inputregs);
}
static bool contains(RzList *list, const char *name) {
RzListIter *iter;
const char *n;
rz_list_foreach (list, iter, n) {
if (!strcmp(name, n))
return true;
}
return false;
}
static char *oldregread = NULL;
static RzList *mymemxsr = NULL;
static RzList *mymemxsw = NULL;
#define RZ_NEW_DUP(x) memcpy((void *)malloc(sizeof(x)), &(x), sizeof(x))
typedef struct {
ut64 addr;
int size;
} AeaMemItem;
static int mymemwrite(RzAnalysisEsil *esil, ut64 addr, const ut8 *buf, int len) {
RzListIter *iter;
AeaMemItem *n;
rz_list_foreach (mymemxsw, iter, n) {
if (addr == n->addr) {
return len;
}
}
if (!rz_io_is_valid_offset(esil->analysis->iob.io, addr, 0)) {
return false;
}
n = RZ_NEW(AeaMemItem);
if (n) {
n->addr = addr;
n->size = len;
rz_list_push(mymemxsw, n);
}
return len;
}
static int mymemread(RzAnalysisEsil *esil, ut64 addr, ut8 *buf, int len) {
RzListIter *iter;
AeaMemItem *n;
rz_list_foreach (mymemxsr, iter, n) {
if (addr == n->addr) {
return len;
}
}
if (!rz_io_is_valid_offset(esil->analysis->iob.io, addr, 0)) {
return false;
}
n = RZ_NEW(AeaMemItem);
if (n) {
n->addr = addr;
n->size = len;
rz_list_push(mymemxsr, n);
}
return len;
}
static int myregwrite(RzAnalysisEsil *esil, const char *name, ut64 *val) {
AeaStats *stats = esil->user;
if (oldregread && !strcmp(name, oldregread)) {
rz_list_pop(stats->regread);
RZ_FREE(oldregread)
}
if (!IS_DIGIT(*name)) {
if (!contains(stats->regs, name)) {
rz_list_push(stats->regs, strdup(name));
}
if (!contains(stats->regwrite, name)) {
rz_list_push(stats->regwrite, strdup(name));
}
char *v = rz_str_newf("%" PFMT64d, *val);
if (!contains(stats->regvalues, v)) {
rz_list_push(stats->regvalues, strdup(v));
}
free(v);
}
return 0;
}
static int myregread(RzAnalysisEsil *esil, const char *name, ut64 *val, int *len) {
AeaStats *stats = esil->user;
if (!IS_DIGIT(*name)) {
if (!contains(stats->inputregs, name)) {
if (!contains(stats->regwrite, name)) {
rz_list_push(stats->inputregs, strdup(name));
}
}
if (!contains(stats->regs, name)) {
rz_list_push(stats->regs, strdup(name));
}
if (!contains(stats->regread, name)) {
rz_list_push(stats->regread, strdup(name));
}
}
return 0;
}
static void showregs(RzList *list) {
if (!rz_list_empty(list)) {
char *reg;
RzListIter *iter;
rz_list_foreach (list, iter, reg) {
rz_cons_print(reg);
if (iter->n) {
rz_cons_printf(" ");
}
}
}
rz_cons_newline();
}
static void showmem(RzList *list) {
if (!rz_list_empty(list)) {
AeaMemItem *item;
RzListIter *iter;
rz_list_foreach (list, iter, item) {
rz_cons_printf(" 0x%08" PFMT64x, item->addr);
}
}
rz_cons_newline();
}
static void showregs_json(RzList *list, PJ *pj) {
pj_a(pj);
if (!rz_list_empty(list)) {
char *reg;
RzListIter *iter;
rz_list_foreach (list, iter, reg) {
pj_s(pj, reg);
}
}
pj_end(pj);
}
static void showmem_json(RzList *list, PJ *pj) {
pj_a(pj);
if (!rz_list_empty(list)) {
RzListIter *iter;
AeaMemItem *item;
rz_list_foreach (list, iter, item) {
pj_n(pj, item->addr);
}
}
pj_end(pj);
}
static bool cmd_aea(RzCore *core, int mode, ut64 addr, int length) {
RzAnalysisEsil *esil;
int ptr, ops, ops_end = 0, len, buf_sz, maxopsize;
ut64 addr_end;
AeaStats stats;
const char *esilstr;
RzAnalysisOp aop = RZ_EMPTY;
ut8 *buf;
RzList *regnow;
PJ *pj = NULL;
if (!core) {
return false;
}
maxopsize = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MAX_OP_SIZE);
if (maxopsize < 1) {
maxopsize = 16;
}
if (mode & 1) {
// number of bytes / length
buf_sz = length;
} else {
// number of instructions / opcodes
ops_end = length;
if (ops_end < 1) {
ops_end = 1;
}
buf_sz = ops_end * maxopsize;
}
if (buf_sz < 1) {
buf_sz = maxopsize;
}
addr_end = addr + buf_sz;
buf = malloc(buf_sz);
if (!buf) {
return false;
}
(void)rz_io_read_at(core->io, addr, (ut8 *)buf, buf_sz);
aea_stats_init(&stats);
// esil_init (core);
// esil = core->analysis->esil;
rz_reg_arena_push(core->analysis->reg);
int stacksize = rz_config_get_i(core->config, "esil.stack.depth");
bool iotrap = rz_config_get_i(core->config, "esil.iotrap");
int romem = rz_config_get_i(core->config, "esil.romem");
int stats1 = rz_config_get_i(core->config, "esil.stats");
int noNULL = rz_config_get_i(core->config, "esil.noNULL");
unsigned int addrsize = rz_config_get_i(core->config, "esil.addr.size");
esil = rz_analysis_esil_new(stacksize, iotrap, addrsize);
rz_analysis_esil_setup(esil, core->analysis, romem, stats1, noNULL); // setup io
#define hasNext(x) (x & 1) ? (addr < addr_end) : (ops < ops_end)
mymemxsr = rz_list_new();
mymemxsw = rz_list_new();
esil->user = &stats;
esil->cb.hook_reg_write = myregwrite;
esil->cb.hook_reg_read = myregread;
esil->cb.hook_mem_write = mymemwrite;
esil->cb.hook_mem_read = mymemread;
esil->nowrite = true;
for (ops = ptr = 0; ptr < buf_sz && hasNext(mode); ops++, ptr += len) {
len = rz_analysis_op(core->analysis, &aop, addr + ptr, buf + ptr, buf_sz - ptr, RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_HINT);
esilstr = RZ_STRBUF_SAFEGET(&aop.esil);
if (RZ_STR_ISNOTEMPTY(esilstr)) {
if (len < 1) {
eprintf("Invalid 0x%08" PFMT64x " instruction %02x %02x\n",
addr + ptr, buf[ptr], buf[ptr + 1]);
break;
}
rz_analysis_esil_parse(esil, esilstr);
rz_analysis_esil_stack_free(esil);
}
rz_analysis_op_fini(&aop);
}
esil->nowrite = false;
esil->cb.hook_reg_write = NULL;
esil->cb.hook_reg_read = NULL;
// esil_fini (core);
rz_analysis_esil_free(esil);
rz_reg_arena_pop(core->analysis->reg);
regnow = rz_list_newf(free);
{
RzListIter *iter;
char *reg;
rz_list_foreach (stats.regs, iter, reg) {
if (!contains(stats.regwrite, reg)) {
rz_list_push(regnow, strdup(reg));
}
}
}
if ((mode >> 5) & 1) {
RzListIter *iter;
AeaMemItem *n;
int c = 0;
rz_cons_printf("f-mem.*\n");
rz_list_foreach (mymemxsr, iter, n) {
rz_cons_printf("f mem.read.%d 0x%08x @ 0x%08" PFMT64x "\n", c++, n->size, n->addr);
}
c = 0;
rz_list_foreach (mymemxsw, iter, n) {
rz_cons_printf("f mem.write.%d 0x%08x @ 0x%08" PFMT64x "\n", c++, n->size, n->addr);
}
}
/* show registers used */
if ((mode >> 1) & 1) {
showregs(stats.regread);
} else if ((mode >> 2) & 1) {
showregs(stats.regwrite);
} else if ((mode >> 3) & 1) {
showregs(regnow);
} else if ((mode >> 4) & 1) {
pj = pj_new();
if (!pj) {
return false;
}
pj_o(pj);
pj_k(pj, "A");
showregs_json(stats.regs, pj);
pj_k(pj, "I");
showregs_json(stats.inputregs, pj);
pj_k(pj, "R");
showregs_json(stats.regread, pj);
pj_k(pj, "W");
showregs_json(stats.regwrite, pj);
if (!rz_list_empty(stats.regvalues)) {
pj_k(pj, "V");
showregs_json(stats.regvalues, pj);
}
if (!rz_list_empty(regnow)) {
pj_k(pj, "N");
showregs_json(regnow, pj);
}
if (!rz_list_empty(mymemxsr)) {
pj_k(pj, "@R");
showmem_json(mymemxsr, pj);
}
if (!rz_list_empty(mymemxsw)) {
pj_k(pj, "@W");
showmem_json(mymemxsw, pj);
}
pj_end(pj);
rz_cons_println(pj_string(pj));
pj_free(pj);
} else if ((mode >> 5) & 1) {
// nothing
} else {
if (!rz_list_empty(stats.inputregs)) {
rz_cons_printf(" I: ");
showregs(stats.inputregs);
}
if (!rz_list_empty(stats.regs)) {
rz_cons_printf(" A: ");
showregs(stats.regs);
}
if (!rz_list_empty(stats.regread)) {
rz_cons_printf(" R: ");
showregs(stats.regread);
}
if (!rz_list_empty(stats.regwrite)) {
rz_cons_printf(" W: ");
showregs(stats.regwrite);
}
if (!rz_list_empty(stats.regvalues)) {
rz_cons_printf(" V: ");
showregs(stats.regvalues);
}
if (!rz_list_empty(regnow)) {
rz_cons_printf(" N: ");
showregs(regnow);
}
if (!rz_list_empty(mymemxsr)) {
rz_cons_printf("@R:");
showmem(mymemxsr);
}
if (!rz_list_empty(mymemxsw)) {
rz_cons_printf("@W:");
showmem(mymemxsw);
}
}
rz_list_free(mymemxsr);
rz_list_free(mymemxsw);
mymemxsr = NULL;
mymemxsw = NULL;
aea_stats_fini(&stats);
free(buf);
RZ_FREE(regnow);
return true;
}
// aeC
RZ_IPI RzCmdStatus rz_analysis_appcall_handler(RzCore *core, int argc, const char **argv) {
for (int i = 1; i < argc; ++i) {
const char *alias = rz_str_newf("A%d", i - 1);
rz_reg_setv(core->analysis->reg, alias, rz_num_math(core->num, argv[i]));
}
ut64 sp = rz_reg_getv(core->analysis->reg, "SP");
rz_reg_setv(core->analysis->reg, "SP", 0);
rz_reg_setv(core->analysis->reg, "PC", core->offset);
rz_core_esil_step(core, 0, NULL, NULL, false);
rz_core_reg_update_flags(core);
rz_reg_setv(core->analysis->reg, "SP", sp);
return RZ_CMD_STATUS_OK;
}
// aec
RZ_IPI RzCmdStatus rz_analysis_continue_until_except_handler(RzCore *core, int argc, const char **argv) {
rz_core_esil_step(core, UT64_MAX, "0", NULL, false);
rz_core_reg_update_flags(core);
return RZ_CMD_STATUS_OK;
}
// aecb
RZ_IPI RzCmdStatus rz_analysis_continue_until_breakpoint_handler(RzCore *core, int argc, const char **argv) {
if (!rz_core_esil_continue_back(core)) {
RZ_LOG_ERROR("cannot continue back\n");
return RZ_CMD_STATUS_ERROR;
}
rz_core_reg_update_flags(core);
return RZ_CMD_STATUS_OK;
}
// aecs
RZ_IPI RzCmdStatus rz_analysis_continue_until_syscall_handler(RzCore *core, int argc, const char **argv) {
return bool2status(rz_core_analysis_continue_until_syscall(core));
}
// aecc
RZ_IPI RzCmdStatus rz_analysis_continue_until_call_handler(RzCore *core, int argc, const char **argv) {
return bool2status(rz_core_analysis_continue_until_call(core));
}
// aecu
RZ_IPI RzCmdStatus rz_analysis_continue_until_addr_handler(RzCore *core, int argc, const char **argv) {
rz_core_esil_step(core, rz_num_math(core->num, argv[1]), NULL, NULL, false);
rz_core_reg_update_flags(core);
return RZ_CMD_STATUS_OK;
}
// aecue
RZ_IPI RzCmdStatus rz_analysis_continue_until_esil_handler(RzCore *core, int argc, const char **argv) {
rz_core_esil_step(core, UT64_MAX, argv[1], NULL, false);
rz_core_reg_update_flags(core);
return RZ_CMD_STATUS_OK;
}
// aei
RZ_IPI RzCmdStatus rz_analysis_esil_init_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_esil_reinit(core);
return RZ_CMD_STATUS_OK;
}
// aei-
RZ_IPI RzCmdStatus rz_analysis_esil_deinit_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_esil_deinit(core);
return RZ_CMD_STATUS_OK;
}
// aeip
RZ_IPI RzCmdStatus rz_analysis_esil_init_p_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_set_reg(core, "PC", core->offset);
return RZ_CMD_STATUS_OK;
}
// aeim
RZ_IPI RzCmdStatus rz_analysis_esil_init_mem_handler(RzCore *core, int argc, const char **argv) {
ut64 addr = argc > 1 ? rz_num_math(core->num, argv[1]) : UT64_MAX;
ut32 size = argc > 2 ? (ut32)rz_num_math(core->num, argv[2]) : UT32_MAX;
const char *name = argc > 3 ? argv[3] : NULL;
rz_core_analysis_esil_init_mem(core, name, addr, size);
return RZ_CMD_STATUS_OK;
}
// aeim-
RZ_IPI RzCmdStatus rz_analysis_esil_init_mem_remove_handler(RzCore *core, int argc, const char **argv) {
ut64 addr = argc > 1 ? rz_num_math(core->num, argv[1]) : UT64_MAX;
ut32 size = argc > 2 ? (ut32)rz_num_math(core->num, argv[2]) : UT32_MAX;
const char *name = argc > 3 ? argv[3] : NULL;
rz_core_analysis_esil_init_mem_del(core, name, addr, size);
return RZ_CMD_STATUS_OK;
}
// aeimp
RZ_IPI RzCmdStatus rz_analysis_esil_init_mem_p_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_esil_init_mem_p(core);
return RZ_CMD_STATUS_OK;
}
// aes
RZ_IPI RzCmdStatus rz_il_step_handler(RzCore *core, int argc, const char **argv) {
if (argc <= 1) {
rz_core_esil_step(core, UT64_MAX, NULL, NULL, false);
rz_core_reg_update_flags(core);
} else if (argc == 2) {
int n = (int)rz_num_math(core->num, argv[1]);
rz_core_analysis_esil_emulate(core, -1, -1, n);
}
return RZ_CMD_STATUS_OK;
}
// aesp
RZ_IPI RzCmdStatus rz_il_step_evaluate_handler(RzCore *core, int argc, const char **argv) {
int n = (int)rz_num_math(core->num, argv[1]);
rz_core_analysis_esil_emulate(core, core->offset, -1, n);
return RZ_CMD_STATUS_OK;
}
// aesb
RZ_IPI RzCmdStatus rz_il_step_back_handler(RzCore *core, int argc, const char **argv) {
if (!rz_core_esil_step_back(core)) {
RZ_LOG_ERROR("cannot step back\n");
return RZ_CMD_STATUS_ERROR;
}
rz_core_reg_update_flags(core);
return RZ_CMD_STATUS_OK;
}
// aeso
RZ_IPI RzCmdStatus rz_il_step_over_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_esil_step_over(core);
return RZ_CMD_STATUS_OK;
}
// aesou
RZ_IPI RzCmdStatus rz_il_step_over_until_addr_handler(RzCore *core, int argc, const char **argv) {
ut64 until_addr = rz_num_math(core->num, argv[1]);
rz_core_analysis_esil_step_over_until(core, until_addr);
return RZ_CMD_STATUS_OK;
}
// aess
RZ_IPI RzCmdStatus rz_il_step_skip_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_esil_step_over_until(core, UT64_MAX);
return RZ_CMD_STATUS_OK;
}
// aessu
RZ_IPI RzCmdStatus rz_il_step_skip_until_addr_handler(RzCore *core, int argc, const char **argv) {
ut64 until_addr = rz_num_math(core->num, argv[1]);
rz_core_analysis_esil_step_over_until(core, until_addr);
return RZ_CMD_STATUS_OK;
}
// aessue
RZ_IPI RzCmdStatus rz_il_step_skip_until_expr_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_esil_step_over_untilexpr(core, argv[1]);
return RZ_CMD_STATUS_OK;
}
// aesu
RZ_IPI RzCmdStatus rz_il_step_until_addr_handler(RzCore *core, int argc, const char **argv) {
ut64 until_addr = rz_num_math(core->num, argv[1]);
rz_core_esil_step(core, until_addr, NULL, NULL, false);
rz_core_reg_update_flags(core);
return RZ_CMD_STATUS_OK;
}
// aesue
RZ_IPI RzCmdStatus rz_il_step_until_expr_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_esil_step_over_untilexpr(core, argv[1]);
rz_core_reg_update_flags(core);
return RZ_CMD_STATUS_OK;
}
// aesuo
RZ_IPI RzCmdStatus rz_il_step_until_opt_handler(RzCore *core, int argc, const char **argv) {
RzList *optypes_list = rz_list_new_from_array((const void **)&argv[1], argc - 1);
step_until_optype(core, optypes_list);
rz_list_free(optypes_list);
rz_core_reg_update_flags(core);
return RZ_CMD_STATUS_OK;
}
// aets+
RZ_IPI RzCmdStatus rz_il_trace_start_handler(RzCore *core, int argc, const char **argv) {
return rz_core_analysis_esil_trace_start(core) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
// aets-
RZ_IPI RzCmdStatus rz_il_trace_stop_handler(RzCore *core, int argc, const char **argv) {
return rz_core_analysis_esil_trace_stop(core) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
static const char _handler_no_name[] = "<no name>";
static bool _aeli_iter(void *user, const ut64 key, const void *value) {
const RzAnalysisEsilInterrupt *interrupt = value;
rz_cons_printf("%3" PFMT64x ": %s\n", key, interrupt->handler->name ? interrupt->handler->name : _handler_no_name);
return true;
}
static void rz_analysis_aefa(RzCore *core, const char *arg) {
ut64 to = rz_num_math(core->num, arg);
ut64 at, from = core->offset;
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, to, -1);
if (!from || from == UT64_MAX) {
if (fcn) {
from = fcn->addr;
} else {
eprintf("Usage: aefa [from] # if no from address is given, uses fcn.addr\n");
return;
}
}
eprintf("Emulate from 0x%08" PFMT64x " to 0x%08" PFMT64x "\n", from, to);
eprintf("Resolve call args for 0x%08" PFMT64x "\n", to);
// emulate
rz_core_analysis_esil_init_mem(core, NULL, UT64_MAX, UT32_MAX);
ut64 off = core->offset;
for (at = from; at < to; at++) {
rz_core_analysis_set_reg(core, "PC", at);
rz_core_analysis_esil_step_over(core);
rz_core_seek(core, at, true);
int delta = rz_num_get(core->num, "$l");
if (delta < 1) {
break;
}
at += delta - 1;
}
rz_core_seek(core, off, true);
// the logic of identifying args by function types and
// show json format and arg name goes into arA
rz_core_cmd0(core, "arA");
}
RZ_API bool rz_core_esil_dumpstack(RzAnalysisEsil *esil) {
rz_return_val_if_fail(esil, false);
int i;
if (esil->trap) {
rz_cons_printf("ESIL TRAP type %d code 0x%08x %s\n",
esil->trap, esil->trap_code,
rz_analysis_esil_trapstr(esil->trap));
}
if (esil->stackptr < 1) {
return false;
}
for (i = esil->stackptr - 1; i >= 0; i--) {
rz_cons_printf("%s\n", esil->stack[i]);
}
return true;
}
static void __analysis_esil_function(RzCore *core, ut64 addr) {
RzListIter *iter;
RzAnalysisBlock *bb;
if (!core->analysis->esil) {
rz_core_analysis_esil_init_mem(core, NULL, UT64_MAX, UT32_MAX);
}
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis,
addr, RZ_ANALYSIS_FCN_TYPE_FCN | RZ_ANALYSIS_FCN_TYPE_SYM);
if (fcn) {
// emulate every instruction in the function recursively across all the basic blocks
rz_list_foreach (fcn->bbs, iter, bb) {
ut64 pc = bb->addr;
ut64 end = bb->addr + bb->size;
RzAnalysisOp op;
int ret, bbs = end - pc;
if (bbs < 1 || bbs > 0xfffff || pc >= end) {
eprintf("Invalid block size\n");
continue;
}
// eprintf ("[*] Emulating 0x%08"PFMT64x" basic block 0x%08" PFMT64x " - 0x%08" PFMT64x "\r[", fcn->addr, pc, end);
ut8 *buf = calloc(1, bbs + 1);
if (!buf) {
break;
}
rz_io_read_at(core->io, pc, buf, bbs);
int left;
bool opskip;
while (pc < end) {
left = RZ_MIN(end - pc, 32);
// rz_asm_set_pc (core->rasm, pc);
ret = rz_analysis_op(core->analysis, &op, pc, buf + pc - bb->addr, left, RZ_ANALYSIS_OP_MASK_HINT | RZ_ANALYSIS_OP_MASK_ESIL); // read overflow
opskip = false;
switch (op.type) {
case RZ_ANALYSIS_OP_TYPE_CALL:
case RZ_ANALYSIS_OP_TYPE_RET:
opskip = true;
break;
}
if (ret > 0) {
if (opskip) {
rz_reg_set_value_by_role(core->analysis->reg, RZ_REG_NAME_PC, pc);
rz_analysis_esil_parse(core->analysis->esil, RZ_STRBUF_SAFEGET(&op.esil));
rz_core_esil_dumpstack(core->analysis->esil);
rz_analysis_esil_stack_free(core->analysis->esil);
}
pc += op.size;
} else {
pc += 4; // XXX
}
rz_analysis_op_fini(&op);
}
free(buf);
}
} else {
eprintf("Cannot find function at 0x%08" PFMT64x "\n", addr);
}
rz_analysis_esil_free(core->analysis->esil);
}
static void cmd_analysis_esil(RzCore *core, const char *input) {
RzAnalysisEsil *esil = core->analysis->esil;
int stacksize = rz_config_get_i(core->config, "esil.stack.depth");
int iotrap = rz_config_get_i(core->config, "esil.iotrap");
int romem = rz_config_get_i(core->config, "esil.romem");
int stats = rz_config_get_i(core->config, "esil.stats");
int noNULL = rz_config_get_i(core->config, "esil.noNULL");
unsigned int addrsize = rz_config_get_i(core->config, "esil.addr.size");
switch (input[0]) {
case 'p':
switch (input[1]) {
case 'c': // "aepc"
if (input[2] == ' ' || input[2] == '=') {
// seek to this address
ut64 pc_val = rz_num_math(core->num, rz_str_trim_head_ro(input + 3));
rz_core_analysis_set_reg(core, "PC", pc_val);
} else {
eprintf("Missing argument\n");
}
break;
default:
rz_core_cmd_help(core, help_msg_ae);
break;
}
break;
case '*': // "ae*"
// XXX: this is wip, not working atm
if (core->analysis->esil) {
rz_cons_printf("trap: %d\n", core->analysis->esil->trap);
rz_cons_printf("trap-code: %d\n", core->analysis->esil->trap_code);
} else {
eprintf("esil vm not initialized. run `aei`\n");
}
break;
case ' ': // "ae "
// rz_analysis_esil_eval (core->analysis, input+1);
if (!esil && !(core->analysis->esil = esil = rz_analysis_esil_new(stacksize, iotrap, addrsize))) {
return;
}
rz_analysis_esil_setup(esil, core->analysis, romem, stats, noNULL); // setup io
rz_analysis_esil_set_pc(esil, core->offset);
rz_analysis_esil_parse(esil, input + 1);
rz_core_esil_dumpstack(esil);
rz_analysis_esil_stack_free(esil);
break;
case 'k': // "aek"
switch (input[1]) {
case '\0': // "aek"
input = "123*";
/* fall through */
case ' ': // "aek "
if (esil && esil->stats) {
char *out = sdb_querys(esil->stats, NULL, 0, input + 2);
if (out) {
rz_cons_println(out);
free(out);
}
} else {
eprintf("esil.stats is empty. Run 'aei'\n");
}
break;
case '-': // "aek-"
if (esil) {
sdb_reset(esil->stats);
}
break;
}
break;
case 'l': // ael commands
switch (input[1]) {
case 'i': // aeli interrupts
switch (input[2]) {
case ' ': // "aeli" with arguments
if (!rz_analysis_esil_load_interrupts_from_lib(esil, input + 3)) {
eprintf("Failed to load interrupts from '%s'.", input + 3);
}
break;
case 0: // "aeli" with no args
if (esil && esil->interrupts) {
ht_up_foreach(esil->interrupts, _aeli_iter, NULL);
}
break;
case 'r': // "aelir"
if (esil && esil->interrupts) {
ht_up_delete(esil->interrupts, rz_num_math(core->num, input + 3));
}
break;
}
}
break;
case 'b': // "aeb"
rz_core_analysis_esil_emulate_bb(core);
break;
case 'f': // "aef"
if (input[1] == 'a') { // "aefa"
rz_analysis_aefa(core, rz_str_trim_head_ro(input + 2));
} else { // This should be aefb -> because its emulating all the bbs
__analysis_esil_function(core, core->offset);
}
break;
case 'A': // "aeA"
if (input[1] == '?') {
rz_core_cmd_help(core, help_msg_aea);
} else if (input[1] == 'r') {
cmd_aea(core, 1 + (1 << 1), core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == 'w') {
cmd_aea(core, 1 + (1 << 2), core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == 'n') {
cmd_aea(core, 1 + (1 << 3), core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == 'j') {
cmd_aea(core, 1 + (1 << 4), core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == '*') {
cmd_aea(core, 1 + (1 << 5), core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == 'f') {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, -1);
if (fcn) {
cmd_aea(core, 1, rz_analysis_function_min_addr(fcn), rz_analysis_function_linear_size(fcn));
}
} else {
cmd_aea(core, 1, core->offset, (int)rz_num_math(core->num, input + 2));
}
break;
case 'a': // "aea"
{
RzReg *reg = core->analysis->reg;
ut64 pc = rz_reg_getv(reg, "PC");
RzAnalysisOp *op = rz_core_analysis_op(core, pc, 0);
if (!op) {
break;
}
ut64 newPC = core->offset + op->size;
rz_reg_setv(reg, "PC", newPC);
if (input[1] == '?') {
rz_core_cmd_help(core, help_msg_aea);
} else if (input[1] == 'r') {
cmd_aea(core, 1 << 1, core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == 'w') {
cmd_aea(core, 1 << 2, core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == 'n') {
cmd_aea(core, 1 << 3, core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == 'j') {
cmd_aea(core, 1 << 4, core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == '*') {
cmd_aea(core, 1 << 5, core->offset, rz_num_math(core->num, input + 2));
} else if (input[1] == 'b') { // "aeab"
bool json = input[2] == 'j';
int a = json ? 3 : 2;
ut64 addr = (input[a] == ' ') ? rz_num_math(core->num, input + a) : core->offset;
RzList *l = rz_analysis_get_blocks_in(core->analysis, addr);
RzAnalysisBlock *b;
RzListIter *iter;
rz_list_foreach (l, iter, b) {
int mode = json ? (1 << 4) : 1;
cmd_aea(core, mode, b->addr, b->size);
break;
}
} else if (input[1] == 'f') {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, -1);
// "aeafj"
if (fcn) {
switch (input[2]) {
case 'j': // "aeafj"
cmd_aea(core, 1 << 4, rz_analysis_function_min_addr(fcn), rz_analysis_function_linear_size(fcn));
break;
default:
cmd_aea(core, 1, rz_analysis_function_min_addr(fcn), rz_analysis_function_linear_size(fcn));
break;
}
break;
}
} else if (input[1] == 'b') { // "aeab"
RzAnalysisBlock *bb = rz_analysis_find_most_relevant_block_in(core->analysis, core->offset);
if (bb) {
switch (input[2]) {
case 'j': // "aeabj"
cmd_aea(core, 1 << 4, bb->addr, bb->size);
break;
default:
cmd_aea(core, 1, bb->addr, bb->size);
break;
}
}
} else {
const char *arg = input[1] ? input + 2 : "";
ut64 len = rz_num_math(core->num, arg);
cmd_aea(core, 0, core->offset, len);
}
rz_reg_setv(reg, "PC", pc);
} break;
case 'x': { // "aex"
char *hex;
int ret, bufsz;
input = rz_str_trim_head_ro(input + 1);
hex = strdup(input);
if (!hex) {
break;
}
RzAnalysisOp aop = RZ_EMPTY;
bufsz = rz_hex_str2bin(hex, (ut8 *)hex);
ret = rz_analysis_op(core->analysis, &aop, core->offset,
(const ut8 *)hex, bufsz, RZ_ANALYSIS_OP_MASK_ESIL);
if (ret > 0) {
const char *str = RZ_STRBUF_SAFEGET(&aop.esil);
char *str2 = rz_str_newf(" %s", str);
cmd_analysis_esil(core, str2);
free(str2);
}
rz_analysis_op_fini(&aop);
break;
}
case '?': // "ae?"
if (input[1] == '?') {
rz_core_cmd_help(core, help_detail_ae);
break;
}
/* fallthrough */
default:
rz_core_cmd_help(core, help_msg_ae);
break;
}
}
static bool print_cmd_analysis_after_traps_print(RZ_NONNULL RzCore *core, ut64 n_bytes) {
int bufi = 0, minop = 1; // 4
ut8 *buf = NULL;
RzAnalysisOp op = { 0 };
ut64 addr = core->offset, addr_end = 0;
if (n_bytes > 0xffffff) {
RZ_LOG_ERROR("number of bytes is too big (> 0xffffff)\n");
return false;
}
RzBinFile *bf = rz_bin_cur(core->bin);
if (!bf) {
return false;
}
if (!n_bytes) {
// ignore search.in to avoid problems. analysis != search
RzIOMap *map = rz_io_map_get(core->io, addr);
if (map && (map->perm & RZ_PERM_X)) {
// search in current section
if (map->itv.size > bf->size) {
addr = map->itv.addr;
if (bf->size > map->delta) {
n_bytes = bf->size - map->delta;
} else {
RZ_LOG_ERROR("aaT: binary size is smaller than map delta\n");
return false;
}
} else {
addr = map->itv.addr;
n_bytes = map->itv.size;
}
} else {
if (map && map->itv.addr != map->delta && bf->size > (core->offset - map->itv.addr + map->delta)) {
n_bytes = bf->size - (core->offset - map->itv.addr + map->delta);
} else {
if (bf->size > core->offset) {
n_bytes = bf->size - core->offset;
} else {
RZ_LOG_ERROR("aaT: found an invalid range where binary size > current offset\n");
return false;
}
}
}
}
addr_end = addr + n_bytes;
if (!(buf = malloc(4096))) {
RZ_LOG_ERROR("aaT: cannot allocate buffer\n");
return false;
}
bufi = 0;
int trapcount = 0;
int nopcount = 0;
rz_cons_break_push(NULL, NULL);
while (addr < addr_end) {
if (rz_cons_is_breaked()) {
break;
}
// TODO: too many ioreads here
if (bufi > 4000) {
bufi = 0;
}
if (!bufi) {
rz_io_read_at(core->io, addr, buf, 4096);
}
if (rz_analysis_op(core->analysis, &op, addr, buf + bufi, 4096 - bufi, RZ_ANALYSIS_OP_MASK_BASIC) > 0) {
if (op.size < 1) {
// XXX must be +4 on arm/mips/.. like we do in disasm.c
op.size = minop;
}
if (op.type == RZ_ANALYSIS_OP_TYPE_TRAP) {
trapcount++;
} else if (op.type == RZ_ANALYSIS_OP_TYPE_NOP) {
nopcount++;
} else {
if (nopcount > 1) {
rz_cons_printf("af @ 0x%08" PFMT64x "\n", addr);
nopcount = 0;
}
if (trapcount > 0) {
rz_cons_printf("af @ 0x%08" PFMT64x "\n", addr);
trapcount = 0;
}
}
} else {
op.size = minop;
}
addr += (op.size > 0) ? op.size : 1;
bufi += (op.size > 0) ? op.size : 1;
rz_analysis_op_fini(&op);
}
rz_cons_break_pop();
free(buf);
return true;
}
static void _analysis_calls(RzCore *core, ut64 addr, ut64 addr_end, bool importsOnly) {
RzAnalysisOp op;
int depth = rz_config_get_i(core->config, "analysis.depth");
const int addrbytes = core->io->addrbytes;
const int bsz = 4096;
int bufi = 0;
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) {
eprintf("Error: 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(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 != core->rasm->bits) {
rz_config_set_i(core->config, "asm.bits", setBits);
}
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 (importsOnly) {
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(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 += addrbytes * op.size;
rz_analysis_op_fini(&op);
}
rz_cons_break_pop();
free(buf);
free(block0);
free(block1);
}
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_prot(core, RZ_PERM_X, NULL, "analysis");
}
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_prot(core, 0, NULL, "analysis");
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);
}
RZ_IPI RzCmdStatus rz_analysis_syscall_show_handler(RzCore *core, int argc, const char **argv) {
st64 n = argc > 1 ? rz_num_math(core->num, argv[1]) : -1;
char *sysc = rz_core_syscall_as_string(core, n, core->offset);
if (!sysc) {
RZ_LOG_ERROR("Cannot resolve syscall: %" PFMT64d "\n", n);
return RZ_CMD_STATUS_ERROR;
}
rz_cons_println(sysc);
free(sysc);
return RZ_CMD_STATUS_OK;
}
static const char *syscallNumber(int n) {
return sdb_fmt(n > 1000 ? "0x%x" : "%d", n);
}
RZ_IPI RzCmdStatus rz_analysis_syscall_print_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzListIter *iter;
RzSyscallItem *si;
RzList *list = rz_syscall_list(core->analysis->syscall);
rz_cmd_state_output_array_start(state);
rz_list_foreach (list, iter, si) {
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("%s = 0x%02x.%s\n",
si->name, si->swi, syscallNumber(si->num));
break;
case RZ_OUTPUT_MODE_JSON:
pj_o(state->d.pj);
pj_ks(state->d.pj, "name", si->name);
pj_ki(state->d.pj, "swi", si->swi);
pj_ki(state->d.pj, "num", si->num);
pj_end(state->d.pj);
break;
default:
rz_warn_if_reached();
break;
}
}
rz_cmd_state_output_array_end(state);
rz_list_free(list);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_syscall_name_handler(RzCore *core, int argc, const char **argv) {
st64 num = rz_syscall_get_num(core->analysis->syscall, argv[1]);
if (num < 1) {
RZ_LOG_ERROR("Cannot resolve syscall: %s\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
rz_cons_println(syscallNumber(num));
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_syscall_number_handler(RzCore *core, int argc, const char **argv) {
st64 num = rz_num_math(NULL, argv[1]);
if (num < 1) {
RZ_LOG_ERROR("Cannot resolve syscall: %s\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
RzSyscallItem *si = rz_syscall_get(core->analysis->syscall, num, -1);
if (!si) {
RZ_LOG_ERROR("Cannot resolve syscall: %" PFMT64d "\n", num);
return RZ_CMD_STATUS_ERROR;
}
rz_cons_println(si->name);
return RZ_CMD_STATUS_OK;
}
static void syscall_dump(RzSyscallItem *si, bool is_c) {
if (is_c) {
rz_cons_printf("#define SYS_%s %s\n", si->name, syscallNumber(si->num));
} else {
rz_cons_printf(".equ SYS_%s %s\n", si->name, syscallNumber(si->num));
}
}
static RzCmdStatus syscalls_dump(RzCore *core, int argc, const char **argv, bool is_c) {
if (argc > 1) {
st64 n = rz_num_math(core->num, argv[1]);
if (n < 1) {
n = rz_syscall_get_num(core->analysis->syscall, argv[1]);
if (n == -1) {
RZ_LOG_ERROR("Cannot resolve syscall: %s\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
rz_cons_printf(".equ SYS_%s %s\n", argv[1], syscallNumber(n));
return RZ_CMD_STATUS_OK;
}
RzSyscallItem *si = rz_syscall_get(core->analysis->syscall, n, -1);
if (!si) {
RZ_LOG_ERROR("Cannot resolve syscall: %" PFMT64d "\n", n);
return RZ_CMD_STATUS_ERROR;
}
// Workaround until syscalls searching code is fixed
si->num = n;
syscall_dump(si, is_c);
rz_syscall_item_free(si);
} else {
RzListIter *iter;
RzSyscallItem *si;
RzList *list = rz_syscall_list(core->analysis->syscall);
rz_list_foreach (list, iter, si) {
syscall_dump(si, is_c);
}
rz_list_free(list);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_syscall_dump_assembly_handler(RzCore *core, int argc, const char **argv) {
return syscalls_dump(core, argc, argv, false);
}
RZ_IPI RzCmdStatus rz_analysis_syscall_dump_c_handler(RzCore *core, int argc, const char **argv) {
return syscalls_dump(core, argc, argv, true);
}
static void cmd_analysis_ucall_ref(RzCore *core, ut64 addr) {
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, addr);
if (fcn) {
rz_cons_printf(" ; %s", fcn->name);
} else {
rz_cons_printf(" ; 0x%" PFMT64x, addr);
}
}
static inline RzFlagItem *core_flag_get_at_as_ref_type(RzCore *core, RzAnalysisXRef *xrefi) {
switch (xrefi->type) {
case RZ_ANALYSIS_XREF_TYPE_CALL:
return rz_flag_get_by_spaces(core->flags, xrefi->to, RZ_FLAGS_FS_SYMBOLS, RZ_FLAGS_FS_CLASSES, RZ_FLAGS_FS_FUNCTIONS, NULL);
case RZ_ANALYSIS_XREF_TYPE_DATA:
return rz_flag_get_by_spaces(core->flags, xrefi->to, RZ_FLAGS_FS_STRINGS, RZ_FLAGS_FS_SYMBOLS, RZ_FLAGS_FS_IMPORTS, NULL);
case RZ_ANALYSIS_XREF_TYPE_STRING:
return rz_flag_get_by_spaces(core->flags, xrefi->to, RZ_FLAGS_FS_STRINGS, NULL);
default:
return rz_flag_get_at(core->flags, xrefi->to, true);
}
}
#define var_ref_list(a, d, t) sdb_fmt("var.0x%" PFMT64x ".%d.%d.%s", \
a, 1, d, (t == 'R') ? "reads" : "writes");
static char *getViewerPath(void) {
int i;
const char *viewers[] = {
#if __WINDOWS__
"explorer",
#else
"open",
"geeqie",
"gqview",
"eog",
"xdg-open",
#endif
NULL
};
for (i = 0; viewers[i]; i++) {
char *viewerPath = rz_file_path(viewers[i]);
if (viewerPath && strcmp(viewerPath, viewers[i])) {
return viewerPath;
}
free(viewerPath);
}
return NULL;
}
static char *dot_executable_path(void) {
const char *dot = "dot";
char *dotPath = rz_file_path(dot);
if (!strcmp(dotPath, dot)) {
free(dotPath);
dot = "xdot";
dotPath = rz_file_path(dot);
if (!strcmp(dotPath, dot)) {
free(dotPath);
return NULL;
}
}
return dotPath;
}
static bool convert_dot_to_image(RzCore *core, const char *dot_file, const char *save_path) {
char *dot = dot_executable_path();
bool result = false;
if (!dot) {
eprintf("Graphviz not found\n");
return false;
}
const char *ext = rz_config_get(core->config, "graph.gv.format");
char *cmd = NULL;
if (save_path && *save_path) {
cmd = rz_str_newf("!%s -T%s -o%s a.dot;", dot, ext, save_path);
} else {
char *viewer = getViewerPath();
if (viewer) {
cmd = rz_str_newf("!%s -T%s -oa.%s a.dot;!%s a.%s",
dot, ext, ext, viewer, ext);
free(viewer);
} else {
eprintf("Cannot find a valid picture viewer\n");
goto end;
}
}
rz_core_cmd0(core, cmd);
result = true;
end:
free(cmd);
free(dot);
return result;
}
static bool convert_dotcmd_to_image(RzCore *core, char *rz_cmd, const char *save_path) {
if (save_path && *save_path) {
rz_cons_printf("Saving to file '%s'...\n", save_path);
rz_cons_flush();
}
rz_core_cmdf(core, "%s > a.dot", rz_cmd); // TODO: check error here
return convert_dot_to_image(core, "a.dot", save_path);
}
static bool convert_dot_str_to_image(RzCore *core, char *str, const char *save_path) {
if (save_path && *save_path) {
rz_cons_printf("Saving to file '%s'...\n", save_path);
rz_cons_flush();
}
if (!rz_file_dump("a.dot", (const unsigned char *)str, -1, false)) {
return false;
}
return convert_dot_to_image(core, "a.dot", save_path);
}
RZ_IPI void rz_core_agraph_print_write(RzCore *core, const char *filename) {
convert_dotcmd_to_image(core, "aggd", filename);
}
static void cmd_agraph_node(RzCore *core, const char *input) {
switch (*input) {
case ' ': { // "agn"
int n_args = 0;
char **args = rz_str_argv(input, &n_args);
if (n_args < 1 || n_args > 3) {
rz_cons_printf("Wrong arguments\n");
rz_str_argv_free(args);
break;
}
const char *title = args[0];
const char *body = n_args > 1 ? args[1] : "";
int color = n_args > 2 ? atoi(args[2]) : -1;
rz_core_agraph_add_node(core, title, body, color);
rz_str_argv_free(args);
break;
}
case '-': { // "agn-"
char **args;
int n_args;
input++;
args = rz_str_argv(input, &n_args);
if (n_args != 1) {
rz_cons_printf("Wrong arguments\n");
rz_str_argv_free(args);
break;
}
rz_core_agraph_del_node(core, args[0]);
rz_str_argv_free(args);
break;
}
case '?': // "agn?"
default:
rz_core_cmd_help(core, help_msg_agn);
break;
}
}
static void cmd_agraph_edge(RzCore *core, const char *input) {
char **args;
int n_args;
switch (*input) {
case ' ': // "age"
args = rz_str_argv(input + 1, &n_args);
if (n_args != 2) {
rz_cons_printf("Wrong arguments\n");
rz_str_argv_free(args);
break;
}
rz_core_agraph_add_edge(core, args[0], args[1]);
rz_str_argv_free(args);
break;
case '-': // "age-"
args = rz_str_argv(input + 1, &n_args);
if (n_args != 2) {
rz_cons_printf("Wrong arguments\n");
rz_str_argv_free(args);
break;
}
rz_core_agraph_del_edge(core, args[0], args[1]);
rz_str_argv_free(args);
break;
case '?': // "age?"
default:
rz_core_cmd_help(core, help_msg_age);
break;
}
}
RZ_API void rz_core_agraph_print(RzCore *core, int use_utf, const char *input) {
if (use_utf != -1) {
rz_config_set_i(core->config, "scr.utf8", use_utf);
}
switch (*input) {
case 0:
rz_core_agraph_print_ascii(core);
break;
case 't': // "aggt" - tiny graph
rz_core_agraph_print_tiny(core);
break;
case 'k': // "aggk"
rz_core_agraph_print_sdb(core);
break;
case 'v': // "aggv"
case 'i': // "aggi" - open current core->graph in interactive mode
rz_core_agraph_print_interactive(core);
break;
case 'd': // "aggd" - dot format
rz_core_agraph_print_dot(core);
break;
case '*': // "agg*" -
rz_core_agraph_print_rizin(core);
break;
case 'J': // "aggJ"
case 'j': // "aggj"
rz_core_agraph_print_json(core);
break;
case 'g': // "aggg"
rz_core_agraph_print_gml(core);
break;
case 'w': { // "aggw"
const char *filename = rz_str_trim_head_ro(input + 1);
rz_core_agraph_print_write(core, filename);
break;
}
default:
eprintf("Usage: see ag?\n");
}
}
static void print_graph_agg(RzGraph /*<RzGraphNodeInfo *>*/ *graph) {
RzGraphNodeInfo *print_node;
RzGraphNode *node, *target;
RzListIter *it, *edge_it;
rz_list_foreach (graph->nodes, it, node) {
char *encbody;
int len;
print_node = node->data;
if (RZ_STR_ISNOTEMPTY(print_node->body)) {
len = strlen(print_node->body);
if (len > 0 && print_node->body[len - 1] == '\n') {
len--;
}
encbody = rz_base64_encode_dyn((const ut8 *)print_node->body, len);
rz_cons_printf("agn \"%s\" base64:%s\n", print_node->title, encbody);
free(encbody);
} else {
rz_cons_printf("agn \"%s\"\n", print_node->title);
}
}
rz_list_foreach (graph->nodes, it, node) {
print_node = node->data;
rz_list_foreach (node->out_nodes, edge_it, target) {
RzGraphNodeInfo *to = target->data;
rz_cons_printf("age \"%s\" \"%s\"\n", print_node->title, to->title);
}
}
}
static char *print_graph_dot(RzCore *core, RzGraph /*<RzGraphNodeInfo *>*/ *graph) {
const char *font = rz_config_get(core->config, "graph.font");
char *node_properties = rz_str_newf("fontname=\"%s\"", font);
char *result = rz_graph_drawable_to_dot(graph, node_properties, NULL);
free(node_properties);
return result;
}
static void rz_core_graph_print(RzCore *core, RzGraph /*<RzGraphNodeInfo *>*/ *graph, int use_utf, bool use_offset, const char *input) {
RzAGraph *agraph = NULL;
RzListIter *it;
RzListIter *edge_it;
RzGraphNode *graphNode, *target;
RzGraphNodeInfo *print_node;
if (use_utf != -1) {
rz_config_set_i(core->config, "scr.utf8", use_utf);
}
switch (*input) {
case 0:
case 't':
case 'k':
case 'v':
case 'i': {
agraph = create_agraph_from_graph(graph);
switch (*input) {
case 0:
agraph->can->linemode = rz_config_get_i(core->config, "graph.linemode");
agraph->can->color = rz_config_get_i(core->config, "scr.color");
rz_agraph_set_title(agraph,
rz_config_get(core->config, "graph.title"));
rz_agraph_print(agraph);
break;
case 't': { // "ag_t" - tiny graph
agraph->is_tiny = true;
int e = rz_config_get_i(core->config, "graph.edges");
rz_config_set_i(core->config, "graph.edges", 0);
rz_core_visual_graph(core, agraph, NULL, false);
rz_config_set_i(core->config, "graph.edges", e);
break;
}
case 'k': // "ag_k"
{
Sdb *db = rz_agraph_get_sdb(agraph);
char *o = sdb_querys(db, "null", 0, "*");
rz_cons_print(o);
free(o);
break;
}
case 'v': // "ag_v"
case 'i': // "ag_i" - open current core->graph in interactive mode
{
RzANode *ran = rz_agraph_get_first_node(agraph);
if (ran) {
ut64 oseek = core->offset;
rz_agraph_set_title(agraph, rz_config_get(core->config, "graph.title"));
rz_agraph_set_curnode(agraph, ran);
agraph->force_update_seek = true;
agraph->need_set_layout = true;
agraph->layout = rz_config_get_i(core->config, "graph.layout");
bool ov = rz_cons_is_interactive();
agraph->need_update_dim = true;
int update_seek = rz_core_visual_graph(core, agraph, NULL, true);
rz_config_set_i(core->config, "scr.interactive", ov);
rz_cons_show_cursor(true);
rz_cons_enable_mouse(false);
if (update_seek != -1) {
rz_core_seek(core, oseek, false);
}
} else {
eprintf("This graph contains no nodes\n");
}
break;
}
}
break;
}
case 'd': { // "ag_d" - dot format
char *dot_text = print_graph_dot(core, graph);
if (dot_text) {
rz_cons_print(dot_text);
free(dot_text);
}
break;
}
case '*': // "ag_*" -
print_graph_agg(graph);
break;
case 'J': // "ag_J"
case 'j': { // "ag_j"
PJ *pj = pj_new();
if (pj) {
rz_graph_drawable_to_json(graph, pj, use_offset);
rz_cons_println(pj_string(pj));
pj_free(pj);
}
} break;
case 'g': // "ag_g"
rz_cons_printf("graph\n[\n"
"hierarchic 1\n"
"label \"\"\n"
"directed 1\n");
rz_list_foreach (graph->nodes, it, graphNode) {
print_node = graphNode->data;
rz_cons_printf(" node [\n"
" id %d\n"
" label \"%s\"\n"
" ]\n",
graphNode->idx, print_node->title);
}
rz_list_foreach (graph->nodes, it, graphNode) {
rz_list_foreach (graphNode->out_nodes, edge_it, target) {
rz_cons_printf(" edge [\n"
" source %d\n"
" target %d\n"
" ]\n",
graphNode->idx, target->idx);
}
}
rz_cons_print("]\n");
break;
case 'w': { // "ag_w"
const char *filename = rz_str_trim_head_ro(input + 1);
char *dot_text = print_graph_dot(core, graph);
if (dot_text) {
convert_dot_str_to_image(core, dot_text, filename);
free(dot_text);
}
break;
}
default:
eprintf("Usage: see ag?\n");
}
}
static void cmd_analysis_graph(RzCore *core, const char *input) {
core->graph->show_node_titles = rz_config_get_i(core->config, "graph.ntitles");
rz_cons_enable_highlight(false);
switch (input[0]) {
case 'f': // "agf"
switch (input[1]) {
case 0: // "agf"
rz_core_visual_graph(core, NULL, NULL, false);
break;
case ' ': { // "agf "
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
rz_core_visual_graph(core, NULL, fcn, false);
break;
}
case 'v': // "agfv"
eprintf("\rRendering graph...\n");
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, RZ_ANALYSIS_FCN_TYPE_ROOT);
if (fcn) {
rz_core_visual_graph(core, NULL, fcn, 1);
}
rz_cons_enable_mouse(false);
rz_cons_show_cursor(true);
break;
case 't': { // "agft" - tiny graph
int e = rz_config_get_i(core->config, "graph.edges");
rz_config_set_i(core->config, "graph.edges", 0);
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
rz_core_visual_graph(core, NULL, fcn, 2);
rz_config_set_i(core->config, "graph.edges", e);
break;
}
case 'd': // "agfd"
if (input[2] == 'm') {
rz_core_analysis_graph(core, rz_num_math(core->num, input + 3),
RZ_CORE_ANALYSIS_GRAPHLINES);
} else {
rz_core_analysis_graph(core, rz_num_math(core->num, input + 2),
RZ_CORE_ANALYSIS_GRAPHBODY);
}
break;
case 'j': // "agfj"
rz_core_analysis_graph(core, rz_num_math(core->num, input + 2), RZ_CORE_ANALYSIS_JSON);
break;
case 'J': { // "agfJ"
// Honor asm.graph=false in json as well
RzConfigHold *hc = rz_config_hold_new(core->config);
rz_config_hold_i(hc, "asm.offset", NULL);
const bool o_graph_offset = rz_config_get_i(core->config, "graph.offset");
rz_config_set_i(core->config, "asm.offset", o_graph_offset);
rz_core_analysis_graph(core, rz_num_math(core->num, input + 2),
RZ_CORE_ANALYSIS_JSON | RZ_CORE_ANALYSIS_JSON_FORMAT_DISASM);
rz_config_hold_restore(hc);
rz_config_hold_free(hc);
break;
}
case 'g': { // "agfg"
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
rz_core_print_bb_gml(core, fcn);
break;
}
case 'k': // "agfk"
rz_core_agraph_reset(core);
rz_core_cmdf(core, ".agf* @ %" PFMT64u "", core->offset);
rz_core_agraph_print_sdb(core);
break;
case '*': { // "agf*"
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
rz_core_print_bb_custom(core, fcn);
break;
}
case 'w': { // "agfw"
char *cmdargs = rz_str_newf("agfd @ 0x%" PFMT64x, core->offset);
convert_dotcmd_to_image(core, cmdargs, input + 2);
free(cmdargs);
break;
}
default:
eprintf("Usage: see ag?\n");
break;
}
break;
case '-': // "ag-"
rz_core_agraph_reset(core);
break;
case 'n': // "agn"
cmd_agraph_node(core, input + 1);
break;
case 'e': // "age"
cmd_agraph_edge(core, input + 1);
break;
case 'g': // "agg"
rz_core_agraph_print(core, -1, input + 1);
break;
case 's': // "ags"
rz_core_analysis_graph(core, rz_num_math(core->num, input + 1), 0);
break;
case 'C': // "agC"
switch (input[1]) {
case 'v': // "agCv"
case 't': // "agCt"
case 'k': // "agCk"
case 'w': // "agCw"
case ' ': // "agC "
case 0: {
core->graph->is_callgraph = true;
rz_core_agraph_reset(core);
rz_core_cmdf(core, ".agC*;");
rz_core_agraph_print(core, -1, input + 1);
core->graph->is_callgraph = false;
break;
}
case 'J': // "agCJ"
case 'j': // "agCj"
rz_core_analysis_callgraph(core, UT64_MAX, RZ_GRAPH_FORMAT_JSON);
break;
case 'g': // "agCg"
rz_core_analysis_callgraph(core, UT64_MAX, RZ_GRAPH_FORMAT_GML);
break;
case 'd': // "agCd"
rz_core_analysis_callgraph(core, UT64_MAX, RZ_GRAPH_FORMAT_DOT);
break;
case '*': // "agC*"
rz_core_analysis_callgraph(core, UT64_MAX, RZ_GRAPH_FORMAT_CMD);
break;
default:
eprintf("Usage: see ag?\n");
break;
}
break;
case 'r': // "agr" references graph
switch (input[1]) {
case '*': { // "agr*"
rz_core_analysis_coderefs(core, core->offset);
} break;
default: {
core->graph->is_callgraph = true;
rz_core_agraph_reset(core);
rz_core_cmdf(core, ".agr* @ %" PFMT64u ";", core->offset);
rz_core_agraph_print(core, -1, input + 1);
core->graph->is_callgraph = false;
break;
}
}
break;
case 'R': // "agR" global refs
switch (input[1]) {
case '*': { // "agR*"
ut64 from = rz_config_get_i(core->config, "graph.from");
ut64 to = rz_config_get_i(core->config, "graph.to");
RzListIter *it;
RzAnalysisFunction *fcn;
rz_list_foreach (core->analysis->fcns, it, fcn) {
if ((from == UT64_MAX && to == UT64_MAX) || RZ_BETWEEN(from, fcn->addr, to)) {
rz_core_analysis_coderefs(core, fcn->addr);
}
}
break;
}
default: {
core->graph->is_callgraph = true;
rz_core_agraph_reset(core);
rz_core_cmdf(core, ".agR*;");
rz_core_agraph_print(core, -1, input + 1);
core->graph->is_callgraph = false;
break;
}
}
break;
case 'x': { // "agx" cross refs
RzGraph *graph = rz_core_analysis_codexrefs(core, core->offset);
if (!graph) {
eprintf("Couldn't create graph");
break;
}
rz_core_graph_print(core, graph, -1, true, input + 1);
rz_graph_free(graph);
break;
}
case 'i': { // "agi" import graph
RzGraph *graph = rz_core_analysis_importxrefs(core);
if (!graph) {
eprintf("Couldn't create graph");
break;
}
rz_core_graph_print(core, graph, -1, true, input + 1);
rz_graph_free(graph);
break;
}
case 'c': // "agc"
switch (input[1]) {
case 'v': // "agcv"
case 't': // "agct"
case 'k': // "agck"
case 'w': // "agcw"
case ' ': { // "agc "
core->graph->is_callgraph = true;
rz_core_agraph_reset(core);
rz_core_cmdf(core, ".agc* @ %" PFMT64u "; agg%s;", core->offset, input + 1);
core->graph->is_callgraph = false;
break;
}
case 0: // "agc "
core->graph->is_callgraph = true;
rz_core_agraph_reset(core);
rz_core_cmd0(core, ".agc* $$");
rz_core_agraph_print_ascii(core);
core->graph->is_callgraph = false;
break;
case 'g': { // "agg"
rz_core_analysis_callgraph(core, core->offset, RZ_GRAPH_FORMAT_GMLFCN);
break;
}
case 'd': { // "aggd"
rz_core_analysis_callgraph(core, core->offset, RZ_GRAPH_FORMAT_DOT);
break;
}
case 'J': // "aggJ"
case 'j': { // "aggj"
rz_core_analysis_callgraph(core, core->offset, RZ_GRAPH_FORMAT_JSON);
break;
}
case '*': { // "agg*"
rz_core_analysis_callgraph(core, core->offset, RZ_GRAPH_FORMAT_CMD);
break;
}
default:
eprintf("Usage: see ag?\n");
break;
}
break;
case 'j': // "agj" alias for agfj
rz_core_cmdf(core, "agfj%s", input + 1);
break;
case 'J': // "agJ" alias for agfJ
rz_core_cmdf(core, "agfJ%s", input + 1);
break;
case 'k': // "agk" alias for agfk
rz_core_cmdf(core, "agfk%s", input + 1);
break;
case 'l': // "agl"
rz_core_analysis_graph(core, rz_num_math(core->num, input + 1), RZ_CORE_ANALYSIS_GRAPHLINES);
break;
case 'a': // "aga"
switch (input[1]) {
case '*': {
rz_core_analysis_datarefs(core, core->offset);
break;
}
default:
rz_core_agraph_reset(core);
rz_core_cmdf(core, ".aga* @ %" PFMT64u ";", core->offset);
rz_core_agraph_print(core, -1, input + 1);
break;
}
break;
case 'A': // "agA" global data refs
switch (input[1]) {
case '*': {
ut64 from = rz_config_get_i(core->config, "graph.from");
ut64 to = rz_config_get_i(core->config, "graph.to");
RzListIter *it;
RzAnalysisFunction *fcn;
rz_list_foreach (core->analysis->fcns, it, fcn) {
if ((from == UT64_MAX && to == UT64_MAX) || RZ_BETWEEN(from, fcn->addr, to)) {
rz_core_analysis_datarefs(core, fcn->addr);
}
}
break;
}
default:
rz_core_agraph_reset(core);
rz_core_cmdf(core, ".agA*;");
rz_core_agraph_print(core, -1, input + 1);
break;
}
break;
case 'd': { // "agd"
int diff_opt = RZ_CORE_ANALYSIS_GRAPHBODY | RZ_CORE_ANALYSIS_GRAPHDIFF;
switch (input[1]) {
case 'j': { // "agdj"
ut64 addr = input[2] ? rz_num_math(core->num, input + 2) : core->offset;
rz_core_gdiff_function_1_file(core, addr, core->offset);
rz_core_analysis_graph(core, addr, diff_opt | RZ_CORE_ANALYSIS_JSON);
break;
}
case 'J': { // "agdJ"
ut64 addr = input[2] ? rz_num_math(core->num, input + 2) : core->offset;
rz_core_gdiff_function_1_file(core, addr, core->offset);
rz_core_analysis_graph(core, addr, diff_opt | RZ_CORE_ANALYSIS_JSON | RZ_CORE_ANALYSIS_JSON_FORMAT_DISASM);
break;
}
case '*': { // "agd*"
ut64 addr = input[2] ? rz_num_math(core->num, input + 2) : core->offset;
rz_core_gdiff_function_1_file(core, addr, core->offset);
rz_core_analysis_graph(core, addr, diff_opt | RZ_CORE_ANALYSIS_STAR);
break;
}
case ' ': // "agd "
case 0:
case 't': // "agdt"
case 'k': // "agdk"
case 'v': // "agdv"
case 'g': { // "agdg"
ut64 addr = input[2] ? rz_num_math(core->num, input + 2) : core->offset;
rz_core_agraph_reset(core);
rz_core_cmdf(core, ".agd* @ %" PFMT64u "; agg%s;", addr, input + 1);
break;
}
case 'd': { // "agdd"
ut64 addr = input[2] ? rz_num_math(core->num, input + 2) : core->offset;
rz_core_gdiff_function_1_file(core, addr, core->offset);
rz_core_analysis_graph(core, addr, diff_opt);
break;
}
case 'w': { // "agdw"
char *cmdargs = rz_str_newf("agdd 0x%" PFMT64x, core->offset);
convert_dotcmd_to_image(core, cmdargs, input + 2);
free(cmdargs);
break;
}
}
break;
}
case 'v': // "agv" alias for "agfv"
rz_core_cmdf(core, "agfv%s", input + 1);
break;
case 'w': { // "agw"
char *cmdargs = rz_str_newf("agfd @ 0x%" PFMT64x, core->offset);
convert_dotcmd_to_image(core, cmdargs, input + 1);
free(cmdargs);
break;
}
default:
rz_core_cmd_help(core, help_msg_ag);
break;
}
}
static bool analysis_fcn_data(RzCore *core, const char *input) {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, RZ_ANALYSIS_FCN_TYPE_ANY);
if (fcn) {
int i;
bool gap = false;
ut64 gap_addr = UT64_MAX;
ut32 fcn_size = rz_analysis_function_size_from_entry(fcn);
char *bitmap = calloc(1, fcn_size);
if (bitmap) {
RzAnalysisBlock *b;
RzListIter *iter;
rz_list_foreach (fcn->bbs, iter, b) {
int f = b->addr - fcn->addr;
int t = RZ_MIN(f + b->size, fcn_size);
if (f >= 0) {
while (f < t) {
bitmap[f++] = 1;
}
}
}
}
for (i = 0; i < fcn_size; i++) {
ut64 here = fcn->addr + i;
if (bitmap && bitmap[i]) {
if (gap) {
rz_cons_printf("Cd %" PFMT64u " @ 0x%08" PFMT64x "\n", here - gap_addr, gap_addr);
gap = false;
}
gap_addr = UT64_MAX;
} else {
if (!gap) {
gap = true;
gap_addr = here;
}
}
}
if (gap) {
rz_cons_printf("Cd %" PFMT64u " @ 0x%08" PFMT64x "\n", fcn->addr + fcn_size - gap_addr, gap_addr);
}
free(bitmap);
return true;
}
return false;
}
static bool analysis_fcn_data_gaps(RzCore *core, const char *input) {
ut64 end = UT64_MAX;
RzAnalysisFunction *fcn;
RzListIter *iter;
int i, wordsize = (core->rasm->bits == 64) ? 8 : 4;
rz_list_sort(core->analysis->fcns, cmpaddr);
rz_list_foreach (core->analysis->fcns, iter, fcn) {
if (end != UT64_MAX) {
int range = fcn->addr - end;
if (range > 0) {
for (i = 0; i + wordsize < range; i += wordsize) {
rz_cons_printf("Cd %d @ 0x%08" PFMT64x "\n", wordsize, end + i);
}
rz_cons_printf("Cd %d @ 0x%08" PFMT64x "\n", range - i, end + i);
// rz_cons_printf ("Cd %d @ 0x%08"PFMT64x"\n", range, end);
}
}
end = fcn->addr + rz_analysis_function_size_from_entry(fcn);
}
return true;
}
RZ_IPI RzCmdStatus rz_analysis_list_vtables_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
rz_analysis_list_vtables(core->analysis, mode);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_print_rtti_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
rz_analysis_rtti_print_at_vtable(core->analysis, core->offset, mode);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_print_rtti_all_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
rz_analysis_rtti_print_all(core->analysis, mode);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_recover_rtti_all_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_rtti_recover_all(core->analysis);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_rtti_demangle_class_name_handler(RzCore *core, int argc, const char **argv) {
if (argc == 2) {
char *classname = (char *)argv[1];
char *demangled = rz_analysis_rtti_demangle_class_name(core->analysis, classname);
if (demangled) {
rz_cons_println(demangled);
free(demangled);
}
return RZ_CMD_STATUS_OK;
}
return RZ_CMD_STATUS_ERROR;
}
RZ_IPI RzCmdStatus rz_analysis_print_global_variable_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
if (!rz_analysis_var_global_list_show(core->analysis, state, argv[1])) {
return RZ_CMD_STATUS_ERROR;
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_global_variable_add_handler(RzCore *core, int argc, const char **argv) {
const char *var_name = argv[1];
ut64 addr = rz_num_math(core->num, argv[2]);
const char *type = argv[3];
char *errmsg = NULL;
RzType *typ = rz_type_parse_string_single(core->analysis->typedb->parser, type, &errmsg);
if (errmsg) {
RZ_LOG_ERROR("%s : Error parsing type: \"%s\" message:\n%s\n", __FUNCTION__, type, errmsg);
free(errmsg);
return RZ_CMD_STATUS_ERROR;
}
RzAnalysisVarGlobal *glob = rz_analysis_var_global_new(var_name, addr);
if (!glob) {
return RZ_CMD_STATUS_ERROR;
}
rz_analysis_var_global_set_type(glob, typ);
if (!rz_analysis_var_global_add(core->analysis, glob)) {
rz_analysis_var_global_free(glob);
return RZ_CMD_STATUS_ERROR;
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_global_variable_delete_byaddr_handler(RzCore *core, int argc, const char **argv) {
ut64 addr = rz_num_math(core->num, argv[1]);
if (!rz_analysis_var_global_delete_byaddr_in(core->analysis, addr)) {
return RZ_CMD_STATUS_ERROR;
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_global_variable_delete_byname_handler(RzCore *core, int argc, const char **argv) {
if (!rz_analysis_var_global_delete_byname(core->analysis, argv[1])) {
return RZ_CMD_STATUS_ERROR;
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_global_variable_rename_handler(RzCore *core, int argc, const char **argv) {
const char *oldname = argv[1];
const char *newname = argv[2];
if (!rz_analysis_var_global_rename(core->analysis, oldname, newname)) {
return RZ_CMD_STATUS_ERROR;
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_global_variable_retype_handler(RzCore *core, int argc, const char **argv) {
const char *name = argv[1];
const char *type = argv[2];
RzAnalysisVarGlobal *glob = rz_analysis_var_global_get_byname(core->analysis, name);
if (!glob) {
RZ_LOG_ERROR("Global variable '%s' does not exist!\n", name);
return RZ_CMD_STATUS_ERROR;
}
char *errmsg = NULL;
RzType *typ = rz_type_parse_string_single(core->analysis->typedb->parser, type, &errmsg);
if (errmsg) {
RZ_LOG_ERROR("%s : Error parsing type: \"%s\" message:\n%s\n", __FUNCTION__, type, errmsg);
free(errmsg);
return RZ_CMD_STATUS_ERROR;
}
rz_analysis_var_global_set_type(glob, typ);
return RZ_CMD_STATUS_OK;
}
RZ_IPI int rz_cmd_analysis(void *data, const char *input) {
const char *r;
RzCore *core = (RzCore *)data;
ut32 tbs = core->blocksize;
switch (input[0]) {
case 'p': // "ap"
{
const ut8 *prelude = (const ut8 *)"\xe9\x2d"; //: fffff000";
const int prelude_sz = 2;
const int bufsz = 4096;
ut8 *buf = calloc(1, bufsz);
ut64 off = core->offset;
if (input[1] == ' ') {
off = rz_num_math(core->num, input + 1);
rz_io_read_at(core->io, off - bufsz + prelude_sz, buf, bufsz);
} else {
rz_io_read_at(core->io, off - bufsz + prelude_sz, buf, bufsz);
}
// const char *prelude = "\x2d\xe9\xf0\x47"; //:fffff000";
rz_mem_reverse(buf, bufsz);
// rz_print_hexdump (NULL, off, buf, bufsz, 16, -16);
const ut8 *pos = rz_mem_mem(buf, bufsz, prelude, prelude_sz);
if (pos) {
int delta = (size_t)(pos - buf);
eprintf("POS = %d\n", delta);
eprintf("HIT = 0x%" PFMT64x "\n", off - delta);
rz_cons_printf("0x%08" PFMT64x "\n", off - delta);
} else {
eprintf("Cannot find prelude\n");
}
free(buf);
} break;
case 'e': cmd_analysis_esil(core, input + 1); break; // "ae"
case 'F': // "aF"
rz_core_analysis_fcn(core, core->offset, UT64_MAX, RZ_ANALYSIS_XREF_TYPE_NULL, 1);
break;
case 'g': // "ag"
cmd_analysis_graph(core, input + 1);
break;
case '*': // "a*"
rz_core_cmd0_rzshell(core, "afl*");
rz_core_cmd0_rzshell(core, "ah*");
rz_core_cmd0_rzshell(core, "ax*");
break;
case 'd': // "ad"
switch (input[1]) {
case 'f': // "adf"
if (input[2] == 'g') {
analysis_fcn_data_gaps(core, rz_str_trim_head_ro(input + 1));
} else {
analysis_fcn_data(core, input + 1);
}
break;
case 't': // "adt"
cmd_analysis_trampoline(core, input + 2);
break;
case ' ': { // "ad"
const int default_depth = 1;
const char *p;
int a, b;
a = rz_num_math(core->num, input + 2);
p = strchr(input + 2, ' ');
b = p ? rz_num_math(core->num, p + 1) : default_depth;
if (a < 1) {
a = 1;
}
if (b < 1) {
b = 1;
}
rz_core_analysis_data(core, core->offset, a, b, 0);
} break;
case 'k': // "adk"
r = rz_analysis_data_kind(core->analysis,
core->offset, core->block, core->blocksize);
rz_cons_println(r);
break;
case '\0': // "ad"
rz_core_analysis_data(core, core->offset, 2 + (core->blocksize / 4), 1, 0);
break;
case '4': // "ad4"
rz_core_analysis_data(core, core->offset, 2 + (core->blocksize / 4), 1, 4);
break;
case '8': // "ad8"
rz_core_analysis_data(core, core->offset, 2 + (core->blocksize / 4), 1, 8);
break;
default:
rz_core_cmd_help(core, help_msg_ad);
break;
}
break;
default:
rz_core_cmd_help(core, help_msg_a);
break;
}
if (tbs != core->blocksize) {
rz_core_block_size(core, tbs);
}
if (rz_cons_is_breaked()) {
rz_cons_clear_line(1);
}
return 0;
}
RZ_IPI RzCmdStatus rz_analysis_function_blocks_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
rz_core_analysis_bbs_info_print(core, fcn, state);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_blocks_del_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisBlock *b = rz_analysis_find_most_relevant_block_in(core->analysis, core->offset);
if (!b) {
eprintf("Cannot find basic block\n");
return RZ_CMD_STATUS_ERROR;
}
RzAnalysisFunction *fcn = rz_list_first(b->fcns);
rz_analysis_function_remove_block(fcn, b);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_blocks_del_all_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
while (!rz_list_empty(fcn->bbs)) {
rz_analysis_function_remove_block(fcn, rz_list_first(fcn->bbs));
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_blocks_edge_handler(RzCore *core, int argc, const char **argv) {
ut64 switch_addr = rz_num_math(core->num, argv[1]);
ut64 case_addr = rz_num_math(core->num, argv[2]);
RzList *blocks = rz_analysis_get_blocks_in(core->analysis, switch_addr);
if (!rz_list_empty(blocks)) {
rz_list_free(blocks);
return RZ_CMD_STATUS_ERROR;
}
rz_analysis_block_add_switch_case(rz_list_first(blocks), switch_addr, 0, case_addr);
rz_list_free(blocks);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_blocks_switch_type_handler(RzCore *core, int argc, const char **argv) {
ut64 switch_addr = rz_num_math(core->num, argv[1]);
RzList *blocks = rz_analysis_get_blocks_in(core->analysis, switch_addr);
if (rz_list_empty(blocks)) {
RZ_LOG_ERROR("No basic block exists at '%s' (0x%" PFMT64x ")\n", argv[1], switch_addr);
rz_list_free(blocks);
return RZ_CMD_STATUS_ERROR;
}
RzAnalysisBlock *b = rz_list_first(blocks);
if (!b->switch_op) {
RZ_LOG_ERROR("Block does not have a switch case\n");
return RZ_CMD_STATUS_INVALID;
}
RzBaseType *e = rz_type_db_get_enum(core->analysis->typedb, argv[2]);
if (!e) {
RZ_LOG_ERROR("Enum '%s' does not exist\n", argv[2]);
return RZ_CMD_STATUS_INVALID;
}
rz_type_free(b->switch_op->enum_type);
b->switch_op->enum_type = rz_type_identifier_of_base_type(core->analysis->typedb, e, true);
rz_list_free(blocks);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_returns_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
rz_core_analysis_fcn_returns(core, fcn);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_blocks_asciiart_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
rz_core_analysis_bbs_asciiart(core, fcn);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_blocks_info_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisBlock *bb = rz_analysis_find_most_relevant_block_in(core->analysis, core->offset);
if (!bb) {
eprintf("No basic block at 0x%" PFMT64x, core->offset);
return RZ_CMD_STATUS_ERROR;
}
rz_core_analysis_bb_info_print(core, bb, core->offset, state);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_blocks_add_handler(RzCore *core, int argc, const char **argv) {
RzCmdStatus res = RZ_CMD_STATUS_ERROR;
ut64 fcn_addr = rz_num_math(core->num, argv[1]);
ut64 addr = rz_num_math(core->num, argv[2]);
ut64 size = rz_num_math(core->num, argv[3]);
ut64 jump = argc > 4 ? rz_num_math(core->num, argv[4]) : UT64_MAX;
ut64 fail = argc > 5 ? rz_num_math(core->num, argv[5]) : UT64_MAX;
RzAnalysisDiff *diff = NULL;
if (argc > 6) {
diff = rz_analysis_diff_new();
diff->type = argv[6][0] == 'm' ? RZ_ANALYSIS_DIFF_TYPE_MATCH : RZ_ANALYSIS_DIFF_TYPE_UNMATCH;
}
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, fcn_addr);
if (!fcn) {
eprintf("Cannot find function at 0x%" PFMT64x "\n", fcn_addr);
goto err;
}
if (!rz_analysis_fcn_add_bb(core->analysis, fcn, addr, size, jump, fail, diff)) {
eprintf("Cannot add basic block at 0x%" PFMT64x " to fcn at 0x%" PFMT64x "\n", addr, fcn_addr);
goto err;
}
res = RZ_CMD_STATUS_OK;
err:
rz_analysis_diff_free(diff);
return res;
}
RZ_IPI RzCmdStatus rz_analysis_function_blocks_color_handler(RzCore *core, int argc, const char **argv) {
ut64 addr = rz_num_math(core->num, argv[1]);
ut32 color = (ut32)rz_num_math(core->num, argv[2]);
RzAnalysisBlock *block = rz_analysis_find_most_relevant_block_in(core->analysis, addr);
if (!block) {
eprintf("No basic block at 0x%08" PFMT64x "\n", addr);
return RZ_CMD_STATUS_ERROR;
}
block->colorize = color;
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_setbits_handler(RzCore *core, int argc, const char **argv) {
int bits = atoi(argv[1]);
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
RzListIter *iter;
RzAnalysisBlock *bb;
rz_list_foreach (fcn->bbs, iter, bb) {
rz_analysis_hint_set_bits(core->analysis, bb->addr, bits);
rz_analysis_hint_set_bits(core->analysis, bb->addr + bb->size, core->analysis->bits);
}
fcn->bits = bits;
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_signature_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
RzAnalysisFunction *f = analysis_get_function_in(core->analysis, core->offset);
if (!f) {
return RZ_CMD_STATUS_ERROR;
}
if (argc > 1) {
// set signature
rz_core_analysis_function_set_signature(core, f, argv[1]);
} else {
// get signature
char *str = NULL;
switch (mode) {
case RZ_OUTPUT_MODE_STANDARD: {
str = rz_analysis_function_get_signature(f);
break;
}
case RZ_OUTPUT_MODE_JSON: {
str = rz_analysis_function_get_json(f);
break;
}
default:
rz_warn_if_reached();
return RZ_CMD_STATUS_ERROR;
}
rz_cons_println(str);
free(str);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_signature_editor_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_function_signature_editor(core, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_signature_type_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
char *error_msg = NULL;
RzType *ret_type = rz_type_parse_string_single(core->analysis->typedb->parser, argv[1], &error_msg);
if (!ret_type || error_msg) {
eprintf("Cannot parse type \"%s\":\n%s\n", argv[1], error_msg);
free(error_msg);
return RZ_CMD_STATUS_ERROR;
}
if (!rz_type_func_ret_set(core->analysis->typedb, fcn->name, ret_type)) {
eprintf("Cannot find type %s\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
fcn->ret_type = ret_type;
return RZ_CMD_STATUS_OK;
}
static void xref_print_to_json(RZ_UNUSED RzCore *core, RzAnalysisXRef *xref, PJ *pj) {
pj_o(pj);
pj_kn(pj, "from", xref->from);
pj_kn(pj, "to", xref->to);
pj_ks(pj, "type", rz_analysis_xrefs_type_tostring(xref->type));
pj_end(pj);
}
static void xref_list_print_to_json(RZ_UNUSED RzCore *core, RzList *list, PJ *pj) {
RzAnalysisXRef *xref;
RzListIter *iter;
pj_a(pj);
rz_list_foreach (list, iter, xref) {
xref_print_to_json(core, xref, pj);
}
pj_end(pj);
}
RZ_IPI RzCmdStatus rz_analysis_function_xrefs_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
ut64 oaddr = core->offset;
RzCmdStatus status = RZ_CMD_STATUS_OK;
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
rz_cmd_state_output_array_start(state);
RzAnalysisXRef *xref;
RzListIter *iter;
rz_list_foreach (xrefs, iter, xref) {
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("%c 0x%08" PFMT64x " -> ", xref->type, xref->from);
switch (xref->type) {
case RZ_ANALYSIS_XREF_TYPE_NULL:
rz_cons_printf("0x%08" PFMT64x " ", xref->to);
break;
case RZ_ANALYSIS_XREF_TYPE_CODE:
case RZ_ANALYSIS_XREF_TYPE_CALL:
case RZ_ANALYSIS_XREF_TYPE_DATA:
rz_cons_printf("0x%08" PFMT64x " ", xref->to);
rz_core_seek(core, xref->from, 1);
rz_core_print_disasm_instructions(core, 0, 1);
break;
case RZ_ANALYSIS_XREF_TYPE_STRING: {
char *s = rz_core_cmd_strf(core, "pxr 8 @ 0x%08" PFMT64x, xref->to);
char *nl = strchr(s, '\n');
if (nl) {
*nl = 0;
}
rz_cons_printf("%s\n", s);
free(s);
break;
}
}
break;
case RZ_OUTPUT_MODE_JSON:
xref_print_to_json(core, xref, state->d.pj);
break;
default:
rz_warn_if_reached();
status = RZ_CMD_STATUS_WRONG_ARGS;
goto exit;
}
}
rz_core_seek(core, oaddr, 1);
exit:
rz_list_free(xrefs);
rz_cmd_state_output_array_end(state);
return status;
}
RZ_IPI RzCmdStatus rz_analysis_function_stacksz_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
fcn->maxstack = rz_num_math(core->num, argv[1]);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_address_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
PJ *pj = NULL;
switch (mode) {
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("0x%08" PFMT64x "\n", fcn->addr);
break;
case RZ_OUTPUT_MODE_JSON:
pj = pj_new();
if (!pj) {
return RZ_CMD_STATUS_ERROR;
}
pj_o(pj);
if (fcn) {
pj_ki(pj, "address", fcn->addr);
}
pj_end(pj);
rz_cons_println(pj_string(pj));
pj_free(pj);
break;
default:
rz_return_val_if_reached(RZ_CMD_STATUS_ERROR);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_until_handler(RzCore *core, int argc, const char **argv) {
ut64 addr_end = rz_num_math(core->num, argv[1]);
if (addr_end < core->offset) {
eprintf("Invalid address ranges\n");
return RZ_CMD_STATUS_ERROR;
}
rz_core_analysis_function_until(core, addr_end);
return RZ_CMD_STATUS_OK;
}
static int var_comparator(const RzAnalysisVar *a, const RzAnalysisVar *b) {
return (a && b) ? (a->delta > b->delta) - (a->delta < b->delta) : 0;
}
static void core_analysis_var_list_show(RzAnalysis *analysis, RzAnalysisFunction *fcn, int kind, RzCmdStateOutput *state) {
RzAnalysisVar *var;
RzListIter *iter;
if (state->mode == RZ_OUTPUT_MODE_JSON) {
pj_a(state->d.pj);
}
RzList *list = rz_analysis_var_list(analysis, fcn, kind);
if (!list) {
goto fail;
}
rz_list_sort(list, (RzListComparator)var_comparator);
rz_list_foreach (list, iter, var) {
if (var->kind != kind) {
continue;
}
switch (state->mode) {
case RZ_OUTPUT_MODE_RIZIN: {
// we can't express all type info here :(
char *vartype = rz_type_as_string(analysis->typedb, var->type);
if (kind == RZ_ANALYSIS_VAR_KIND_REG) { // registers
RzRegItem *i = rz_reg_index_get(analysis->reg, var->delta);
if (!i) {
RZ_LOG_ERROR("Register not found");
free(vartype);
break;
}
rz_cons_printf("afv%c %s %s %s @ 0x%" PFMT64x "\n",
kind, i->name, var->name, vartype, fcn->addr);
} else {
int delta = kind == RZ_ANALYSIS_VAR_KIND_BPV
? var->delta + fcn->bp_off
: var->delta;
rz_cons_printf("afv%c %d %s %s @ 0x%" PFMT64x "\n",
kind, delta, var->name, vartype,
fcn->addr);
}
free(vartype);
break;
}
case RZ_OUTPUT_MODE_JSON:
switch (var->kind) {
case RZ_ANALYSIS_VAR_KIND_BPV: {
char *vartype = rz_type_as_string(analysis->typedb, var->type);
st64 delta = (st64)var->delta + fcn->bp_off;
pj_o(state->d.pj);
pj_ks(state->d.pj, "name", var->name);
pj_ks(state->d.pj, "kind", var->isarg ? "arg" : "var");
pj_ks(state->d.pj, "type", vartype);
pj_k(state->d.pj, "ref");
pj_o(state->d.pj);
pj_ks(state->d.pj, "base", analysis->reg->name[RZ_REG_NAME_BP]);
pj_kN(state->d.pj, "offset", delta);
pj_end(state->d.pj);
pj_end(state->d.pj);
free(vartype);
} break;
case RZ_ANALYSIS_VAR_KIND_REG: {
RzRegItem *i = rz_reg_index_get(analysis->reg, var->delta);
if (!i) {
RZ_LOG_ERROR("Register not found");
break;
}
char *vartype = rz_type_as_string(analysis->typedb, var->type);
pj_o(state->d.pj);
pj_ks(state->d.pj, "name", var->name);
pj_ks(state->d.pj, "kind", "reg");
pj_ks(state->d.pj, "type", vartype);
pj_ks(state->d.pj, "ref", i->name);
pj_end(state->d.pj);
free(vartype);
} break;
case RZ_ANALYSIS_VAR_KIND_SPV: {
st64 delta = (st64)var->delta + fcn->maxstack;
pj_o(state->d.pj);
pj_ks(state->d.pj, "name", var->name);
pj_ks(state->d.pj, "kind", var->isarg ? "arg" : "var");
char *vartype = rz_type_as_string(analysis->typedb, var->type);
pj_ks(state->d.pj, "type", vartype);
pj_k(state->d.pj, "ref");
pj_o(state->d.pj);
pj_ks(state->d.pj, "base", analysis->reg->name[RZ_REG_NAME_SP]);
pj_kN(state->d.pj, "offset", delta);
pj_end(state->d.pj);
pj_end(state->d.pj);
free(vartype);
} break;
}
break;
default:
switch (kind) {
case RZ_ANALYSIS_VAR_KIND_BPV: {
int delta = var->delta + fcn->bp_off;
char *vartype = rz_type_as_string(analysis->typedb, var->type);
if (var->isarg) {
rz_cons_printf("arg %s %s @ %s+0x%x\n",
vartype, var->name,
analysis->reg->name[RZ_REG_NAME_BP],
delta);
} else {
char sign = (-var->delta <= fcn->bp_off) ? '+' : '-';
rz_cons_printf("var %s %s @ %s%c0x%x\n",
vartype, var->name,
analysis->reg->name[RZ_REG_NAME_BP],
sign, RZ_ABS(delta));
}
free(vartype);
} break;
case RZ_ANALYSIS_VAR_KIND_REG: {
RzRegItem *i = rz_reg_index_get(analysis->reg, var->delta);
if (!i) {
RZ_LOG_ERROR("Register not found");
break;
}
char *vartype = rz_type_as_string(analysis->typedb, var->type);
rz_cons_printf("arg %s %s @ %s\n", vartype, var->name, i->name);
free(vartype);
} break;
case RZ_ANALYSIS_VAR_KIND_SPV: {
int delta = fcn->maxstack + var->delta;
char *vartype = rz_type_as_string(analysis->typedb, var->type);
if (!var->isarg) {
char sign = (-var->delta <= fcn->maxstack) ? '+' : '-';
rz_cons_printf("var %s %s @ %s%c0x%x\n",
vartype, var->name,
analysis->reg->name[RZ_REG_NAME_SP],
sign, RZ_ABS(delta));
} else {
rz_cons_printf("arg %s %s @ %s+0x%x\n",
vartype, var->name,
analysis->reg->name[RZ_REG_NAME_SP],
delta);
}
free(vartype);
} break;
}
}
}
fail:
if (state->mode == RZ_OUTPUT_MODE_JSON) {
pj_end(state->d.pj);
}
rz_list_free(list);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
const char *bp = NULL;
RzList *list;
RzListIter *iter;
RzAnalysisVar *var;
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
core_analysis_var_list_show(core->analysis, fcn, RZ_ANALYSIS_VAR_KIND_SPV, state);
core_analysis_var_list_show(core->analysis, fcn, RZ_ANALYSIS_VAR_KIND_BPV, state);
core_analysis_var_list_show(core->analysis, fcn, RZ_ANALYSIS_VAR_KIND_REG, state);
break;
case RZ_OUTPUT_MODE_RIZIN:
bp = rz_reg_get_name(core->analysis->reg, RZ_REG_NAME_BP);
rz_cons_printf("f-fcnvar*\n");
list = rz_analysis_var_all_list(core->analysis, fcn);
rz_list_foreach (list, iter, var) {
rz_cons_printf("f fcnvar.%s @ %s%s%d\n", var->name, bp,
var->delta >= 0 ? "+" : "", var->delta);
}
break;
case RZ_OUTPUT_MODE_JSON:
pj_o(state->d.pj);
pj_k(state->d.pj, "sp");
core_analysis_var_list_show(core->analysis, fcn, RZ_ANALYSIS_VAR_KIND_SPV, state);
pj_k(state->d.pj, "bp");
core_analysis_var_list_show(core->analysis, fcn, RZ_ANALYSIS_VAR_KIND_BPV, state);
pj_k(state->d.pj, "reg");
core_analysis_var_list_show(core->analysis, fcn, RZ_ANALYSIS_VAR_KIND_REG, state);
pj_end(state->d.pj);
break;
default:
rz_return_val_if_reached(RZ_CMD_STATUS_ERROR);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_dis_refs_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
ut64 oaddr = core->offset;
RzListIter *iter;
RzAnalysisVar *var;
RzList *list = rz_analysis_var_all_list(core->analysis, fcn);
rz_list_foreach (list, iter, var) {
rz_cons_printf("* %s\n", var->name);
RzAnalysisVarAccess *acc;
rz_vector_foreach(&var->accesses, acc) {
if (!(acc->type & RZ_ANALYSIS_VAR_ACCESS_TYPE_READ)) {
continue;
}
rz_cons_printf("R 0x%" PFMT64x " ", fcn->addr + acc->offset);
rz_core_seek(core, fcn->addr + acc->offset, 1);
rz_core_print_disasm_instructions(core, 0, 1);
}
rz_vector_foreach(&var->accesses, acc) {
if (!(acc->type & RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE)) {
continue;
}
rz_cons_printf("W 0x%" PFMT64x " ", fcn->addr + acc->offset);
rz_core_seek(core, fcn->addr + acc->offset, 1);
rz_core_print_disasm_instructions(core, 0, 1);
}
}
rz_core_seek(core, oaddr, 0);
rz_list_free(list);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_del_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
rz_core_analysis_function_delete_var(core, fcn, RZ_ANALYSIS_VAR_KIND_SPV, argv[1]);
rz_core_analysis_function_delete_var(core, fcn, RZ_ANALYSIS_VAR_KIND_BPV, argv[1]);
rz_core_analysis_function_delete_var(core, fcn, RZ_ANALYSIS_VAR_KIND_REG, argv[1]);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_detect_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
rz_analysis_function_delete_all_vars(fcn);
rz_core_recover_vars(core, fcn, false);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_display_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
RzAnalysisVar *v;
char *r = NULL;
switch (argc) {
case 1:
r = rz_core_analysis_all_vars_display(core, fcn, true);
break;
case 2:
v = rz_analysis_function_get_var_byname(fcn, argv[1]);
if (!v) {
eprintf("Cannot find variable '%s' in current function\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
r = rz_core_analysis_var_display(core, v, true);
break;
default:
rz_return_val_if_reached(RZ_CMD_STATUS_ERROR);
}
rz_cons_print(r);
free(r);
return RZ_CMD_STATUS_OK;
}
// EBP BASED
static int delta_cmp(const void *a, const void *b) {
const RzAnalysisVar *va = a;
const RzAnalysisVar *vb = b;
return vb->delta - va->delta;
}
static int delta_cmp2(const void *a, const void *b) {
const RzAnalysisVar *va = a;
const RzAnalysisVar *vb = b;
return va->delta - vb->delta;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_stackframe_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
RzListIter *iter;
RzAnalysisVar *p;
RzList *list = rz_analysis_var_all_list(core->analysis, fcn);
rz_list_sort(list, delta_cmp2);
rz_list_foreach (list, iter, p) {
if (p->isarg || p->delta > 0) {
continue;
}
const char *pad = rz_str_pad(' ', 10 - strlen(p->name));
char *ptype = rz_type_as_string(core->analysis->typedb, p->type);
rz_cons_printf("0x%08" PFMT64x " %s:%s%s\n", (ut64)-p->delta, p->name, pad, ptype);
free(ptype);
}
rz_list_sort(list, delta_cmp);
rz_list_foreach (list, iter, p) {
if (!p->isarg && p->delta < 0) {
continue;
}
// TODO: only stack vars if (p->kind == 's') { }
const char *pad = rz_str_pad(' ', 10 - strlen(p->name));
char *ptype = rz_type_as_string(core->analysis->typedb, p->type);
// XXX this 0x6a is a hack
rz_cons_printf("0x%08" PFMT64x " %s:%s%s\n", ((ut64)p->delta) - 0x6a, p->name, pad, ptype);
free(ptype);
}
rz_list_free(list);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_rename_handler(RzCore *core, int argc, const char **argv) {
const char *newname = argv[1];
const char *oldname = argv[2];
bool result = rz_core_analysis_var_rename(core, oldname, newname);
return result ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_OK;
}
static RzCmdStatus analysis_function_vars_accesses(RzCore *core, int access_type, const char *varname) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
if (!varname) {
RzList *list = rz_analysis_var_all_list(core->analysis, fcn);
RzListIter *iter;
RzAnalysisVar *var;
rz_list_foreach (list, iter, var) {
var_accesses_list(fcn, var, NULL, access_type, var->name);
}
rz_list_free(list);
} else {
RzAnalysisVar *var = rz_analysis_function_get_var_byname(fcn, varname);
if (!var) {
eprintf("Cannot find variable %s\n", varname);
return RZ_CMD_STATUS_ERROR;
}
var_accesses_list(fcn, var, NULL, access_type, var->name);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_reads_handler(RzCore *core, int argc, const char **argv) {
return analysis_function_vars_accesses(core, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ, argv[1]);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_writes_handler(RzCore *core, int argc, const char **argv) {
return analysis_function_vars_accesses(core, RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE, argv[1]);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_type_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
RzAnalysisVar *v = rz_analysis_function_get_var_byname(fcn, argv[1]);
if (!v) {
eprintf("Cannot find variable %s\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
char *error_msg = NULL;
RzType *v_type = rz_type_parse_string_single(core->analysis->typedb->parser, argv[2], &error_msg);
if (!v_type || error_msg) {
eprintf("Cannot parse type \"%s\":\n%s\n", argv[2], error_msg);
free(error_msg);
return RZ_CMD_STATUS_ERROR;
}
rz_analysis_var_set_type(v, v_type, true);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_args_and_vars_xrefs_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode, bool use_args, bool use_vars) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
PJ *pj = NULL;
if (mode == RZ_OUTPUT_MODE_JSON) {
pj = pj_new();
pj_o(pj);
pj_k(pj, "reads");
} else {
rz_cons_printf("afvR\n");
}
if (use_args) {
list_vars(core, fcn, pj, 'R', argv[1], IS_ARG);
}
if (use_vars) {
list_vars(core, fcn, pj, 'R', argv[1], IS_VAR);
}
if (mode == RZ_OUTPUT_MODE_JSON) {
pj_k(pj, "writes");
} else {
rz_cons_printf("afvW\n");
}
if (use_args) {
list_vars(core, fcn, pj, 'W', argv[1], IS_ARG);
}
if (use_vars) {
list_vars(core, fcn, pj, 'W', argv[1], IS_VAR);
}
if (mode == RZ_OUTPUT_MODE_JSON) {
pj_end(pj);
char *j = pj_drain(pj);
rz_cons_printf("%s\n", j);
free(j);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_xrefs_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
return rz_analysis_function_args_and_vars_xrefs_handler(core, argc, argv, mode, true, true);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_xrefs_args_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
return rz_analysis_function_args_and_vars_xrefs_handler(core, argc, argv, mode, true, false);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_xrefs_vars_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
return rz_analysis_function_args_and_vars_xrefs_handler(core, argc, argv, mode, false, true);
}
static RzCmdStatus analysis_function_vars_kind_list(RzCore *core, RzAnalysisFunction *fcn, RzAnalysisVarKind kind, RzCmdStateOutput *state) {
core_analysis_var_list_show(core->analysis, fcn, kind, state);
return RZ_CMD_STATUS_OK;
}
static RzCmdStatus analysis_function_vars_del(RzCore *core, RzAnalysisVarKind kind, const char *varname) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
rz_core_analysis_function_delete_var(core, fcn, kind, varname);
return RZ_CMD_STATUS_OK;
}
static RzCmdStatus analysis_function_vars_del_all(RzCore *core, RzAnalysisVarKind kind) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
rz_analysis_function_delete_vars_by_kind(fcn, kind);
return RZ_CMD_STATUS_OK;
}
static RzCmdStatus analysis_function_vars_getsetref(RzCore *core, int delta, ut64 addr, RzAnalysisVarKind kind, RzAnalysisVarAccessType access_type) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
RzAnalysisVar *var = rz_analysis_function_get_var(fcn, kind, delta);
if (!var) {
eprintf("Cannot find variable with delta %d\n", delta);
return RZ_CMD_STATUS_ERROR;
}
RzAnalysisOp *op = rz_core_analysis_op(core, addr, 0);
const char *ireg = op ? op->ireg : NULL;
if (kind == RZ_ANALYSIS_VAR_KIND_SPV) {
delta -= fcn->maxstack;
}
rz_analysis_var_set_access(var, ireg, addr, access_type, delta);
rz_analysis_op_free(op);
return RZ_CMD_STATUS_OK;
}
/// --------- Base pointer based variable handlers -------------
RZ_IPI RzCmdStatus rz_analysis_function_vars_bp_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
if (argc == 1) {
return analysis_function_vars_kind_list(core, fcn, RZ_ANALYSIS_VAR_KIND_BPV, state);
} else {
const char *varname = argv[2];
const char *vartype = argc > 3 ? argv[3] : "int";
int delta = (int)rz_num_math(core->num, argv[1]) - fcn->bp_off;
bool isarg = delta > 0;
char *error_msg = NULL;
RzType *var_type = rz_type_parse_string_single(core->analysis->typedb->parser, vartype, &error_msg);
if (!var_type || error_msg) {
eprintf("Cannot parse type \"%s\":\n%s\n", vartype, error_msg);
free(error_msg);
return RZ_CMD_STATUS_ERROR;
}
rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_BPV, var_type, 4, isarg, varname);
rz_type_free(var_type);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_bp_del_handler(RzCore *core, int argc, const char **argv) {
return analysis_function_vars_del(core, RZ_ANALYSIS_VAR_KIND_BPV, argv[1]);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_bp_del_all_handler(RzCore *core, int argc, const char **argv) {
return analysis_function_vars_del_all(core, RZ_ANALYSIS_VAR_KIND_BPV);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_bp_getref_handler(RzCore *core, int argc, const char **argv) {
int delta = (int)rz_num_math(core->num, argv[1]);
ut64 addr = rz_num_math(core->num, argv[2]);
return analysis_function_vars_getsetref(core, delta, addr, RZ_ANALYSIS_VAR_KIND_BPV, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_bp_setref_handler(RzCore *core, int argc, const char **argv) {
int delta = (int)rz_num_math(core->num, argv[1]);
ut64 addr = rz_num_math(core->num, argv[2]);
return analysis_function_vars_getsetref(core, delta, addr, RZ_ANALYSIS_VAR_KIND_BPV, RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE);
}
/// --------- Register-based variable handlers -------------
RZ_IPI RzCmdStatus rz_analysis_function_vars_regs_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
if (argc == 1) {
return analysis_function_vars_kind_list(core, fcn, RZ_ANALYSIS_VAR_KIND_REG, state);
} else {
const char *varname = argv[2];
const char *vartype = argc > 3 ? argv[3] : "int";
RzRegItem *i = rz_reg_get(core->analysis->reg, argv[1], -1);
if (!i) {
eprintf("Register not found");
return RZ_CMD_STATUS_ERROR;
}
int delta = i->index;
bool isarg = true;
char *error_msg = NULL;
RzType *var_type = rz_type_parse_string_single(core->analysis->typedb->parser, vartype, &error_msg);
if (!var_type || error_msg) {
eprintf("Cannot parse type \"%s\":\n%s\n", vartype, error_msg);
free(error_msg);
return RZ_CMD_STATUS_ERROR;
}
rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_REG, var_type, 4, isarg, varname);
rz_type_free(var_type);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_regs_del_handler(RzCore *core, int argc, const char **argv) {
return analysis_function_vars_del(core, RZ_ANALYSIS_VAR_KIND_REG, argv[1]);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_regs_del_all_handler(RzCore *core, int argc, const char **argv) {
return analysis_function_vars_del_all(core, RZ_ANALYSIS_VAR_KIND_REG);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_regs_getref_handler(RzCore *core, int argc, const char **argv) {
RzRegItem *i = rz_reg_get(core->analysis->reg, argv[1], -1);
if (!i) {
eprintf("Register not found");
return RZ_CMD_STATUS_ERROR;
}
int delta = i->index;
ut64 addr = rz_num_math(core->num, argv[2]);
return analysis_function_vars_getsetref(core, delta, addr, RZ_ANALYSIS_VAR_KIND_REG, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_regs_setref_handler(RzCore *core, int argc, const char **argv) {
RzRegItem *i = rz_reg_get(core->analysis->reg, argv[1], -1);
if (!i) {
eprintf("Register not found");
return RZ_CMD_STATUS_ERROR;
}
int delta = i->index;
ut64 addr = rz_num_math(core->num, argv[2]);
return analysis_function_vars_getsetref(core, delta, addr, RZ_ANALYSIS_VAR_KIND_REG, RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE);
}
/// --------- Stack-based variable handlers -------------
RZ_IPI RzCmdStatus rz_analysis_function_vars_sp_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
if (argc == 1) {
return analysis_function_vars_kind_list(core, fcn, RZ_ANALYSIS_VAR_KIND_SPV, state);
} else {
const char *varname = argv[2];
const char *vartype = argc > 3 ? argv[3] : "int";
int delta = (int)rz_num_math(core->num, argv[1]);
bool isarg = delta > fcn->maxstack;
char *error_msg = NULL;
RzType *var_type = rz_type_parse_string_single(core->analysis->typedb->parser, vartype, &error_msg);
if (!var_type || error_msg) {
eprintf("Cannot parse type \"%s\":\n%s\n", vartype, error_msg);
free(error_msg);
return RZ_CMD_STATUS_ERROR;
}
rz_analysis_function_set_var(fcn, delta, RZ_ANALYSIS_VAR_KIND_SPV, var_type, 4, isarg, varname);
rz_type_free(var_type);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_sp_del_handler(RzCore *core, int argc, const char **argv) {
return analysis_function_vars_del(core, RZ_ANALYSIS_VAR_KIND_SPV, argv[1]);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_sp_getref_handler(RzCore *core, int argc, const char **argv) {
int delta = (int)rz_num_math(core->num, argv[1]);
ut64 addr = rz_num_math(core->num, argv[2]);
return analysis_function_vars_getsetref(core, delta, addr, RZ_ANALYSIS_VAR_KIND_SPV, RZ_ANALYSIS_VAR_ACCESS_TYPE_READ);
}
RZ_IPI RzCmdStatus rz_analysis_function_vars_sp_setref_handler(RzCore *core, int argc, const char **argv) {
int delta = (int)rz_num_math(core->num, argv[1]);
ut64 addr = rz_num_math(core->num, argv[2]);
return analysis_function_vars_getsetref(core, delta, addr, RZ_ANALYSIS_VAR_KIND_SPV, RZ_ANALYSIS_VAR_ACCESS_TYPE_WRITE);
}
static RzCmdStatus xrefs_set(RzCore *core, int argc, const char **argv, RzAnalysisXRefType type) {
ut64 from = core->offset;
ut64 to = rz_num_math(core->num, argv[1]);
return rz_analysis_xrefs_set(core->analysis, from, to, type) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_set_0_handler(RzCore *core, int argc, const char **argv) {
return xrefs_set(core, argc, argv, RZ_ANALYSIS_XREF_TYPE_NULL);
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_set_c_handler(RzCore *core, int argc, const char **argv) {
return xrefs_set(core, argc, argv, RZ_ANALYSIS_XREF_TYPE_CODE);
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_set_C_handler(RzCore *core, int argc, const char **argv) {
return xrefs_set(core, argc, argv, RZ_ANALYSIS_XREF_TYPE_CALL);
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_set_d_handler(RzCore *core, int argc, const char **argv) {
return xrefs_set(core, argc, argv, RZ_ANALYSIS_XREF_TYPE_DATA);
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_set_s_handler(RzCore *core, int argc, const char **argv) {
return xrefs_set(core, argc, argv, RZ_ANALYSIS_XREF_TYPE_STRING);
}
static void xrefs_list_print(RzCore *core, RzList *list) {
RzListIter *iter;
RzAnalysisXRef *xref;
rz_list_foreach (list, iter, xref) {
char *name = core->analysis->coreb.getNameDelta(core->analysis->coreb.core, xref->from);
if (name) {
rz_str_replace_ch(name, ' ', 0, true);
rz_cons_printf("%40s", name);
free(name);
} else {
rz_cons_printf("%40s", "?");
}
rz_cons_printf(" 0x%" PFMT64x " -> %9s -> 0x%" PFMT64x, xref->from, rz_analysis_xrefs_type_tostring(xref->type), xref->to);
name = core->analysis->coreb.getNameDelta(core->analysis->coreb.core, xref->to);
if (name) {
rz_str_replace_ch(name, ' ', 0, true);
rz_cons_printf(" %s\n", name);
free(name);
} else {
rz_cons_printf("\n");
}
}
}
static const char *xref_type2cmd(RzAnalysisXRefType type) {
switch (type) {
case RZ_ANALYSIS_XREF_TYPE_CODE:
return "axc";
case RZ_ANALYSIS_XREF_TYPE_CALL:
return "axC";
case RZ_ANALYSIS_XREF_TYPE_DATA:
return "axd";
case RZ_ANALYSIS_XREF_TYPE_STRING:
return "axs";
case RZ_ANALYSIS_XREF_TYPE_NULL:
return "ax";
}
return "ax";
}
static void xref_list_print_as_cmd(RZ_UNUSED RzCore *core, RzList *list) {
RzListIter *iter;
RzAnalysisXRef *xref;
rz_list_foreach (list, iter, xref) {
rz_cons_printf("%s 0x%" PFMT64x " @ 0x%" PFMT64x "\n", xref_type2cmd(xref->type), xref->to, xref->from);
}
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzListIter *iter;
RzAnalysisXRef *xref;
RzCmdStatus status = RZ_CMD_STATUS_OK;
RzList *list = rz_analysis_xrefs_list(core->analysis);
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
xrefs_list_print(core, list);
break;
case RZ_OUTPUT_MODE_QUIET:
rz_list_foreach (list, iter, xref) {
rz_cons_printf("0x%08" PFMT64x " -> 0x%08" PFMT64x " %s\n", xref->from, xref->to, rz_analysis_xrefs_type_tostring(xref->type));
}
break;
case RZ_OUTPUT_MODE_JSON:
xref_list_print_to_json(core, list, state->d.pj);
break;
case RZ_OUTPUT_MODE_RIZIN:
xref_list_print_as_cmd(core, list);
break;
default:
rz_warn_if_reached();
status = RZ_CMD_STATUS_WRONG_ARGS;
break;
}
rz_list_free(list);
return status;
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_to_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisXRef *xref;
RzListIter *iter;
RzCmdStatus status = RZ_CMD_STATUS_OK;
RzList *list = rz_analysis_xrefs_get_to(core->analysis, core->offset);
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
rz_list_foreach (list, iter, xref) {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, xref->from, 0);
char *buf_asm = rz_core_disasm_instruction(core, xref->from, core->offset, fcn, true);
const char *comment = rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, xref->from);
char *print_comment = NULL;
const char *nl = comment ? strchr(comment, '\n') : NULL;
if (nl) { // display only until the first newline
comment = print_comment = rz_str_ndup(comment, nl - comment);
}
char *buf_fcn = comment
? rz_str_newf("%s; %s", fcn ? fcn->name : "(nofunc)", comment)
: rz_str_newf("%s", fcn ? fcn->name : "(nofunc)");
free(print_comment);
rz_cons_printf("%s 0x%" PFMT64x " [%s] %s\n",
buf_fcn, xref->from, rz_analysis_xrefs_type_tostring(xref->type), buf_asm);
free(buf_asm);
free(buf_fcn);
}
break;
case RZ_OUTPUT_MODE_QUIET:
rz_list_foreach (list, iter, xref) {
rz_cons_printf("0x%08" PFMT64x "\n", xref->from);
}
break;
case RZ_OUTPUT_MODE_JSON:
xref_list_print_to_json(core, list, state->d.pj);
break;
case RZ_OUTPUT_MODE_RIZIN:
xref_list_print_as_cmd(core, list);
break;
default:
rz_warn_if_reached();
status = RZ_CMD_STATUS_WRONG_ARGS;
break;
}
rz_list_free(list);
return status;
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_from_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisXRef *xref;
RzListIter *iter;
char str[512];
RzCmdStatus status = RZ_CMD_STATUS_OK;
RzList *list = rz_analysis_xrefs_get_from(core->analysis, core->offset);
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
rz_list_foreach (list, iter, xref) {
ut8 buf[16];
char *desc;
RzAsmOp asmop;
RzFlagItem *flag = rz_flag_get_at(core->flags, xref->to, false);
if (flag) {
desc = flag->name;
} else {
rz_io_read_at(core->io, xref->to, buf, sizeof(buf));
rz_asm_set_pc(core->rasm, xref->to);
rz_asm_disassemble(core->rasm, &asmop, buf, sizeof(buf));
RzAnalysisHint *hint = rz_analysis_hint_get(core->analysis, xref->to);
rz_parse_filter(core->parser, xref->from, core->flags, hint, rz_asm_op_get_asm(&asmop),
str, sizeof(str), core->print->big_endian);
rz_analysis_hint_free(hint);
desc = str;
}
rz_cons_printf("%c 0x%" PFMT64x " %s",
xref->type ? xref->type : ' ', xref->to, desc);
if (xref->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
RzAnalysisOp aop = { 0 };
rz_analysis_op(core->analysis, &aop, xref->to, buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_BASIC);
if (aop.type == RZ_ANALYSIS_OP_TYPE_UCALL) {
cmd_analysis_ucall_ref(core, xref->to);
}
}
rz_cons_newline();
}
break;
case RZ_OUTPUT_MODE_QUIET:
rz_list_foreach (list, iter, xref) {
rz_cons_printf("0x%08" PFMT64x "\n", xref->to);
}
break;
case RZ_OUTPUT_MODE_JSON:
xref_list_print_to_json(core, list, state->d.pj);
break;
case RZ_OUTPUT_MODE_RIZIN:
xref_list_print_as_cmd(core, list);
break;
default:
rz_warn_if_reached();
status = RZ_CMD_STATUS_WRONG_ARGS;
break;
}
rz_list_free(list);
return status;
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_to_graph_cmd_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisXRef *xref;
RzListIter *iter;
ut64 addr = core->offset;
RzList *list = rz_analysis_xrefs_get_to(core->analysis, addr);
rz_list_foreach (list, iter, xref) {
char *str = rz_core_cmd_strf(core, "fd @ 0x%" PFMT64x, xref->from);
if (!str) {
str = strdup("?\n");
}
rz_str_trim_tail(str);
rz_cons_printf("agn 0x%" PFMT64x " \"%s\"\n", xref->from, str);
free(str);
}
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, addr, 0);
rz_cons_printf("agn 0x%" PFMT64x " \"%s\"\n", addr, fcn ? fcn->name : "$$");
rz_list_foreach (list, iter, xref) {
rz_cons_printf("age 0x%" PFMT64x " 0x%" PFMT64x "\n", xref->from, addr);
}
rz_list_free(list);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_del_handler(RzCore *core, int argc, const char **argv) {
bool all = true;
ut64 from = 0;
ut64 to = rz_num_math(core->num, argv[1]);
if (argc == 3) {
from = rz_num_math(core->num, argv[2]);
all = false;
}
RzAnalysisXRef *xref;
RzListIter *iter;
RzList *list = rz_analysis_xrefs_get_to(core->analysis, to);
rz_list_foreach (list, iter, xref) {
if (all || from == xref->from) {
rz_analysis_xref_del(core->analysis, xref->from, xref->to);
}
}
rz_list_free(list);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_del_all_handler(RzCore *core, int argc, const char **argv) {
return rz_analysis_xrefs_init(core->analysis) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_copy_handler(RzCore *core, int argc, const char **argv) {
ut64 src = rz_num_math(core->num, argv[1]);
RzAnalysisXRef *xref;
RzListIter *iter;
RzList *list = rz_analysis_xrefs_get_to(core->analysis, src);
rz_list_foreach (list, iter, xref) {
rz_cons_printf("0x%08" PFMT64x " %s\n", xref->from, rz_analysis_xrefs_type_tostring(xref->type));
rz_analysis_xrefs_set(core->analysis, xref->from, core->offset, xref->type);
}
rz_list_free(list);
return RZ_CMD_STATUS_OK;
}
static void xrefs_graph_fcn_start_json(PJ *pj, RzAnalysisFunction *fcn, ut64 addr) {
char taddr[64];
pj_o(pj);
pj_k(pj, rz_strf(taddr, "%" PFMT64u, addr));
pj_o(pj);
pj_ks(pj, "type", "fcn");
pj_kn(pj, "fcn_addr", fcn->addr);
pj_ks(pj, "name", fcn->name);
pj_k(pj, "refs");
pj_a(pj);
}
static void xrefs_graph(RzCore *core, ut64 addr, int level, HtUU *ht, RzOutputMode mode, PJ *pj) {
char pre[128];
RzListIter *iter;
RzAnalysisXRef *xref;
bool is_json = mode == RZ_OUTPUT_MODE_JSON;
bool is_rz = mode == RZ_OUTPUT_MODE_RIZIN;
int spaces = (level + 1) * 2;
if (spaces > sizeof(pre) - 4) {
spaces = sizeof(pre) - 4;
}
memset(pre, ' ', sizeof(pre));
strcpy(pre + spaces, "- ");
RzList *xrefs = rz_analysis_xrefs_get_to(core->analysis, addr);
bool open_object = false;
if (!rz_list_empty(xrefs)) {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in_bounds(core->analysis, addr, -1);
if (fcn) {
if (is_rz) {
rz_cons_printf("agn 0x%08" PFMT64x " %s\n", fcn->addr, fcn->name);
} else if (is_json) {
xrefs_graph_fcn_start_json(pj, fcn, addr);
open_object = true;
} else {
rz_cons_printf("%s0x%08" PFMT64x " fcn 0x%08" PFMT64x " %s\n",
pre + 2, addr, fcn->addr, fcn->name);
}
} else {
if (is_rz) {
rz_cons_printf("age 0x%08" PFMT64x "\n", addr);
} else if (is_json) {
char taddr[64];
pj_o(pj);
pj_k(pj, sdb_itoa(addr, taddr, 10));
pj_o(pj);
pj_k(pj, "refs");
pj_a(pj);
open_object = true;
} else {
rz_cons_printf("%s0x%08" PFMT64x "\n", pre + 2, addr);
}
}
}
rz_list_foreach (xrefs, iter, xref) {
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in_bounds(core->analysis, xref->from, -1);
if (fcn) {
if (is_rz) {
rz_cons_printf("agn 0x%08" PFMT64x " %s\n", fcn->addr, fcn->name);
rz_cons_printf("age 0x%08" PFMT64x " 0x%08" PFMT64x "\n", fcn->addr, addr);
} else if (is_json) {
xrefs_graph_fcn_start_json(pj, fcn, xref->from);
} else {
rz_cons_printf("%s0x%08" PFMT64x " fcn 0x%08" PFMT64x " %s\n", pre, xref->from, fcn->addr, fcn->name);
}
if (ht_uu_insert(ht, fcn->addr, 1)) {
xrefs_graph(core, fcn->addr, level + 1, ht, mode, pj);
}
if (is_json) {
pj_end(pj);
pj_end(pj);
pj_end(pj);
}
} else {
if (is_rz) {
rz_cons_printf("agn 0x%08" PFMT64x " ???\n", xref->from);
rz_cons_printf("age 0x%08" PFMT64x " 0x%08" PFMT64x "\n", xref->from, addr);
} else if (is_json) {
char taddr[64];
pj_o(pj);
pj_k(pj, sdb_itoa(xref->from, taddr, 10));
pj_o(pj);
pj_ks(pj, "type", "???");
pj_k(pj, "refs");
pj_a(pj);
} else {
rz_cons_printf("%s0x%08" PFMT64x " ???\n", pre, xref->from);
}
if (ht_uu_insert(ht, xref->from, 1)) {
xrefs_graph(core, xref->from, level + 1, ht, mode, pj);
}
if (is_json) {
pj_end(pj);
pj_end(pj);
pj_end(pj);
}
}
}
if (is_json) {
if (open_object) {
pj_end(pj);
pj_end(pj);
pj_end(pj);
}
}
rz_list_free(xrefs);
}
RZ_IPI RzCmdStatus rz_analysis_xrefs_graph_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
HtUU *ht = ht_uu_new0();
if (!ht) {
return RZ_CMD_STATUS_ERROR;
}
PJ *pj = state->mode == RZ_OUTPUT_MODE_JSON ? state->d.pj : NULL;
xrefs_graph(core, core->offset, 0, ht, state->mode, pj);
ht_uu_free(ht);
return RZ_CMD_STATUS_OK;
}
#define CMD_REGS_PREFIX analysis
#define CMD_REGS_REG_PATH analysis->reg
#define CMD_REGS_SYNC NULL
#include "cmd_regs_meta.inc"
#undef CMD_REGS_PREFIX
#undef CMD_REGS_REG_PATH
#undef CMD_REGS_SYNC
static int RzAnalysisRef_cmp(const RzAnalysisXRef *xref1, const RzAnalysisXRef *xref2) {
return xref1->to != xref2->to;
}
static void function_list_print_to_table(RzCore *core, RzList *list, RzTable *t, bool verbose) {
RzTableColumnType *typeString = rz_table_type("string");
RzTableColumnType *typeNumber = rz_table_type("number");
rz_table_add_column(t, typeNumber, "addr", 0);
rz_table_add_column(t, typeString, "name", 0);
rz_table_add_column(t, typeNumber, "size", 0);
rz_table_add_column(t, typeNumber, "xrefsTo", 0);
rz_table_add_column(t, typeNumber, "xrefsFrom", 0);
rz_table_add_column(t, typeNumber, "calls", 0);
rz_table_add_column(t, typeNumber, "nbbs", 0);
rz_table_add_column(t, typeNumber, "edges", 0);
rz_table_add_column(t, typeNumber, "cc", 0);
rz_table_add_column(t, typeNumber, "cost", 0);
rz_table_add_column(t, rz_table_type("boolean"), "noreturn", 0);
if (verbose) {
rz_table_add_column(t, typeNumber, "min bound", 0);
rz_table_add_column(t, typeNumber, "range", 0);
rz_table_add_column(t, typeNumber, "max bound", 0);
rz_table_add_column(t, typeNumber, "locals", 0);
rz_table_add_column(t, typeNumber, "args", 0);
rz_table_add_column(t, typeNumber, "frame", 0);
rz_table_add_column(t, typeNumber, "loops", 0);
}
RzListIter *iter;
RzAnalysisFunction *fcn;
rz_list_foreach (list, iter, fcn) {
RzList *xrefs_to = rz_analysis_function_get_xrefs_to(fcn);
ut32 xref_to_num = rz_list_length(xrefs_to);
rz_list_free(xrefs_to);
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
ut32 xref_from_num = rz_list_length(xrefs);
rz_list_free(xrefs);
RzList *calls = rz_core_analysis_fcn_get_calls(core, fcn);
// Uniquify the list by ref->addr
RzList *uniq_calls = rz_list_uniq(calls, (RzListComparator)RzAnalysisRef_cmp);
ut32 calls_num = rz_list_length(uniq_calls);
rz_list_free(uniq_calls);
rz_list_free(calls);
if (verbose) {
int locals = rz_analysis_var_count(core->analysis, fcn, 's', 0);
locals += rz_analysis_var_count(core->analysis, fcn, 'b', 0);
locals += rz_analysis_var_count(core->analysis, fcn, 'r', 0);
int args = rz_analysis_var_count(core->analysis, fcn, 's', 1);
args += rz_analysis_var_count(core->analysis, fcn, 'b', 1);
args += rz_analysis_var_count(core->analysis, fcn, 'r', 1);
rz_table_add_rowf(t, "XsndddddddbXnXdddd", fcn->addr,
fcn->name, rz_analysis_function_realsize(fcn),
xref_to_num, xref_from_num, calls_num,
rz_list_length(fcn->bbs), rz_analysis_function_count_edges(fcn, NULL),
rz_analysis_function_complexity(fcn), rz_analysis_function_cost(fcn),
fcn->is_noreturn, rz_analysis_function_min_addr(fcn),
rz_analysis_function_linear_size(fcn), rz_analysis_function_max_addr(fcn),
locals, args, fcn->maxstack, rz_analysis_function_loops(fcn), NULL);
} else {
rz_table_add_rowf(t, "Xsndddddddb", fcn->addr,
fcn->name, rz_analysis_function_realsize(fcn),
xref_to_num, xref_from_num, calls_num,
rz_list_length(fcn->bbs), rz_analysis_function_count_edges(fcn, NULL),
rz_analysis_function_complexity(fcn), rz_analysis_function_cost(fcn),
fcn->is_noreturn, NULL);
}
}
}
static void function_list_print(RzCore *core, RzList *list) {
RzListIter *it;
RzAnalysisFunction *fcn;
rz_list_foreach (list, it, fcn) {
char *msg, *name = rz_core_analysis_fcn_name(core, fcn);
ut64 realsize = rz_analysis_function_realsize(fcn);
ut64 size = rz_analysis_function_linear_size(fcn);
if (realsize == size) {
msg = rz_str_newf("%-12" PFMT64u, size);
} else {
msg = rz_str_newf("%-4" PFMT64u " -> %-4" PFMT64u, size, realsize);
}
rz_cons_printf("0x%08" PFMT64x " %4d %4s %s\n",
fcn->addr, rz_list_length(fcn->bbs), msg, name);
free(name);
free(msg);
}
}
static void function_list_print_quiet(RZ_UNUSED RzCore *core, RzList *list) {
RzListIter *it;
RzAnalysisFunction *fcn;
rz_list_foreach (list, it, fcn) {
rz_cons_printf("0x%08" PFMT64x " %s\n", fcn->addr, fcn->name);
}
}
static char function_type_to_char(RzAnalysisFunction *fcn) {
switch (fcn->type) {
case RZ_ANALYSIS_FCN_TYPE_LOC:
return 'l';
case RZ_ANALYSIS_FCN_TYPE_SYM:
return 's';
case RZ_ANALYSIS_FCN_TYPE_IMP:
return 'i';
default:
break;
}
return 'f';
}
static const char *diff_type_to_str(RzAnalysisDiff *diff) {
if (diff->type == RZ_ANALYSIS_DIFF_TYPE_NULL) {
return "new";
}
return diff->type == RZ_ANALYSIS_DIFF_TYPE_MATCH ? "match" : "unmatch";
}
static char diff_type_to_char(RzAnalysisDiff *diff) {
return diff->type == RZ_ANALYSIS_DIFF_TYPE_NULL ? 'n' : diff->type;
}
static void fcn_list_bbs(RzAnalysisFunction *fcn) {
RzAnalysisBlock *bbi;
RzListIter *iter;
rz_list_foreach (fcn->bbs, iter, bbi) {
rz_cons_printf("afb+ 0x%08" PFMT64x " 0x%08" PFMT64x " %" PFMT64u " ",
fcn->addr, bbi->addr, bbi->size);
rz_cons_printf("0x%08" PFMT64x " ", bbi->jump);
rz_cons_printf("0x%08" PFMT64x, bbi->fail);
if (bbi->diff) {
rz_cons_printf(" %c", diff_type_to_char(bbi->diff));
}
rz_cons_printf("\n");
}
}
static void function_list_print_as_cmd(RzCore *core, RzList *list, RzCmdStateOutput *state) {
RzListIter *it;
RzAnalysisFunction *fcn;
rz_list_foreach (list, it, fcn) {
const char *defaultCC = rz_analysis_cc_default(core->analysis);
char *name = rz_core_analysis_fcn_name(core, fcn);
rz_cons_printf("\"f %s %" PFMT64u " @ 0x%08" PFMT64x "\"\n", name, rz_analysis_function_linear_size(fcn), fcn->addr);
rz_cons_printf("\"af+ %s %c %c @ 0x%08" PFMT64x "\"\n",
name, // rz_analysis_fcn_size (fcn), name,
function_type_to_char(fcn),
diff_type_to_char(fcn->diff),
fcn->addr);
// FIXME: this command prints something annoying. Does it have important side-effects?
fcn_list_bbs(fcn);
if (fcn->bits != 0) {
rz_cons_printf("afB %d @ 0x%08" PFMT64x "\n", fcn->bits, fcn->addr);
}
// FIXME command injection vuln here
if (fcn->cc || defaultCC) {
rz_cons_printf("afc %s @ 0x%08" PFMT64x "\n", fcn->cc ? fcn->cc : defaultCC, fcn->addr);
}
/* show variables and arguments */
core_analysis_var_list_show(core->analysis, fcn, 'b', state);
core_analysis_var_list_show(core->analysis, fcn, 'r', state);
core_analysis_var_list_show(core->analysis, fcn, 's', state);
/* Show references */
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
xref_list_print_as_cmd(core, xrefs);
rz_list_free(xrefs);
/*Saving Function stack frame*/
rz_cons_printf("afS %d @ 0x%" PFMT64x "\n", fcn->maxstack, fcn->addr);
free(name);
}
}
static void function_print_to_json(RzCore *core, RzAnalysisFunction *fcn, RzCmdStateOutput *state) {
int ebbs = 0;
pj_o(state->d.pj);
pj_kn(state->d.pj, "offset", fcn->addr);
char *name = rz_core_analysis_fcn_name(core, fcn);
if (name) {
pj_ks(state->d.pj, "name", name);
}
free(name);
pj_kn(state->d.pj, "size", rz_analysis_function_linear_size(fcn));
pj_kb(state->d.pj, "is-pure", rz_analysis_function_purity(fcn));
pj_kn(state->d.pj, "realsz", rz_analysis_function_realsize(fcn));
pj_kb(state->d.pj, "noreturn", fcn->is_noreturn);
pj_ki(state->d.pj, "stackframe", fcn->maxstack);
if (fcn->cc) {
pj_ks(state->d.pj, "calltype", fcn->cc); // calling conventions
}
pj_ki(state->d.pj, "cost", rz_analysis_function_cost(fcn)); // execution cost
pj_ki(state->d.pj, "cc", rz_analysis_function_complexity(fcn)); // cyclic cost
pj_ki(state->d.pj, "loops", rz_analysis_function_loops(fcn));
pj_ki(state->d.pj, "bits", fcn->bits);
pj_ks(state->d.pj, "type", rz_analysis_fcntype_tostring(fcn->type));
pj_ki(state->d.pj, "nbbs", rz_list_length(fcn->bbs));
pj_ki(state->d.pj, "edges", rz_analysis_function_count_edges(fcn, &ebbs));
pj_ki(state->d.pj, "ebbs", ebbs);
{
char *sig = rz_core_analysis_function_signature(core, RZ_OUTPUT_MODE_STANDARD, fcn->name);
if (sig) {
rz_str_trim(sig);
pj_ks(state->d.pj, "signature", sig);
free(sig);
}
}
pj_kn(state->d.pj, "minbound", rz_analysis_function_min_addr(fcn));
pj_kn(state->d.pj, "maxbound", rz_analysis_function_max_addr(fcn));
int outdegree = 0;
RzListIter *iter;
RzAnalysisXRef *xrefi;
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
if (!rz_list_empty(xrefs)) {
pj_k(state->d.pj, "callrefs");
pj_a(state->d.pj);
rz_list_foreach (xrefs, iter, xrefi) {
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
outdegree++;
}
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_CODE ||
xrefi->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
xref_print_to_json(core, xrefi, state->d.pj);
}
}
pj_end(state->d.pj);
pj_k(state->d.pj, "datarefs");
pj_a(state->d.pj);
rz_list_foreach (xrefs, iter, xrefi) {
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_DATA ||
xrefi->type == RZ_ANALYSIS_XREF_TYPE_STRING) {
xref_print_to_json(core, xrefi, state->d.pj);
}
}
pj_end(state->d.pj);
}
rz_list_free(xrefs);
int indegree = 0;
xrefs = rz_analysis_function_get_xrefs_to(fcn);
if (!rz_list_empty(xrefs)) {
pj_k(state->d.pj, "codexrefs");
pj_a(state->d.pj);
rz_list_foreach (xrefs, iter, xrefi) {
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_CODE ||
xrefi->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
indegree++;
xref_print_to_json(core, xrefi, state->d.pj);
}
}
pj_end(state->d.pj);
pj_k(state->d.pj, "dataxrefs");
pj_a(state->d.pj);
rz_list_foreach (xrefs, iter, xrefi) {
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_DATA) {
xref_print_to_json(core, xrefi, state->d.pj);
}
}
pj_end(state->d.pj);
}
rz_list_free(xrefs);
pj_ki(state->d.pj, "indegree", indegree);
pj_ki(state->d.pj, "outdegree", outdegree);
if (fcn->type == RZ_ANALYSIS_FCN_TYPE_FCN || fcn->type == RZ_ANALYSIS_FCN_TYPE_SYM) {
pj_ki(state->d.pj, "nlocals", rz_analysis_var_count(core->analysis, fcn, 'b', 0) + rz_analysis_var_count(core->analysis, fcn, 'r', 0) + rz_analysis_var_count(core->analysis, fcn, 's', 0));
pj_ki(state->d.pj, "nargs", rz_analysis_var_count(core->analysis, fcn, 'b', 1) + rz_analysis_var_count(core->analysis, fcn, 'r', 1) + rz_analysis_var_count(core->analysis, fcn, 's', 1));
pj_k(state->d.pj, "bpvars");
core_analysis_var_list_show(core->analysis, fcn, 'b', state);
pj_k(state->d.pj, "spvars");
core_analysis_var_list_show(core->analysis, fcn, 's', state);
pj_k(state->d.pj, "regvars");
core_analysis_var_list_show(core->analysis, fcn, 'r', state);
pj_ks(state->d.pj, "difftype", diff_type_to_str(fcn->diff));
if (fcn->diff->addr != -1) {
pj_kn(state->d.pj, "diffaddr", fcn->diff->addr);
}
if (fcn->diff->name) {
pj_ks(state->d.pj, "diffname", fcn->diff->name);
}
}
pj_end(state->d.pj);
}
static void function_list_print_to_json(RzCore *core, RzList *list, RzCmdStateOutput *state) {
RzListIter *it;
RzAnalysisFunction *fcn;
pj_a(state->d.pj);
rz_list_foreach (list, it, fcn) {
function_print_to_json(core, fcn, state);
}
pj_end(state->d.pj);
}
RZ_IPI RzCmdStatus rz_analysis_function_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzCmdStatus res = RZ_CMD_STATUS_OK;
RzList *list = rz_analysis_function_list(core->analysis);
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
function_list_print(core, list);
break;
case RZ_OUTPUT_MODE_LONG:
rz_cmd_state_output_fini(state);
if (rz_cmd_state_output_init(state, RZ_OUTPUT_MODE_TABLE)) {
function_list_print_to_table(core, list, state->d.t, true);
} else {
res = RZ_CMD_STATUS_ERROR;
}
break;
case RZ_OUTPUT_MODE_QUIET:
function_list_print_quiet(core, list);
break;
case RZ_OUTPUT_MODE_RIZIN:
function_list_print_as_cmd(core, list, state);
break;
case RZ_OUTPUT_MODE_JSON:
function_list_print_to_json(core, list, state);
break;
case RZ_OUTPUT_MODE_TABLE:
function_list_print_to_table(core, list, state->d.t, false);
break;
default:
rz_warn_if_reached();
res = RZ_CMD_STATUS_WRONG_ARGS;
break;
}
return res;
}
RZ_IPI RzCmdStatus rz_analysis_function_list_in_handler(RzCore *core, int argc, const char **argv) {
RzList *list = rz_analysis_get_functions_in(core->analysis, core->offset);
function_list_print_quiet(core, list);
rz_list_free(list);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_count_handler(RzCore *core, int argc, const char **argv) {
rz_cons_printf("%d\n", rz_list_length(core->analysis->fcns));
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_size_sum_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn;
RzListIter *iter;
ut64 total = 0;
rz_list_foreach (core->analysis->fcns, iter, fcn) {
total += rz_analysis_function_realsize(fcn);
}
rz_cons_printf("%" PFMT64u "\n", total);
return RZ_CMD_STATUS_OK;
}
// Lists function names and their calls (uniqified)
static void function_print_calls(RzCore *core, RzList *fcns, RzCmdStateOutput *state) {
PJ *pj = state->d.pj;
if (state->mode == RZ_OUTPUT_MODE_JSON) {
pj_a(pj);
}
RzListIter *fcniter;
RzAnalysisFunction *fcn;
rz_list_foreach (fcns, fcniter, fcn) {
// Get all refs for a function
RzList *xrefs = rz_core_analysis_fcn_get_calls(core, fcn);
// Uniquify the list by ref->addr
RzList *uniq_xrefs = rz_list_uniq(xrefs, (RzListComparator)RzAnalysisRef_cmp);
// don't enter for functions with 0 refs
if (!rz_list_empty(uniq_xrefs)) {
if (state->mode == RZ_OUTPUT_MODE_JSON) { // begin json output of function
pj_o(pj);
pj_ks(pj, "name", fcn->name);
pj_kn(pj, "addr", fcn->addr);
pj_k(pj, "calls");
pj_a(pj);
} else {
rz_cons_printf("%s", fcn->name);
}
if (state->mode == RZ_OUTPUT_MODE_STANDARD) {
rz_cons_printf(":\n");
} else if (state->mode == RZ_OUTPUT_MODE_QUIET) {
rz_cons_printf(" -> ");
}
RzListIter *refiter;
RzAnalysisXRef *xrefi;
rz_list_foreach (uniq_xrefs, refiter, xrefi) {
RzFlagItem *f = rz_flag_get_i(core->flags, xrefi->to);
char *dst = rz_str_newf((f ? f->name : "0x%08" PFMT64x), xrefi->to);
if (state->mode == RZ_OUTPUT_MODE_JSON) { // Append calee json item
pj_o(pj);
pj_ks(pj, "name", dst);
pj_kn(pj, "addr", xrefi->from);
pj_end(pj); // close referenced item
} else if (state->mode == RZ_OUTPUT_MODE_QUIET) {
rz_cons_printf("%s ", dst);
} else {
rz_cons_printf(" %s\n", dst);
}
free(dst);
}
if (state->mode == RZ_OUTPUT_MODE_JSON) {
pj_end(pj); // close list of calls
pj_end(pj); // close function item
} else {
rz_cons_newline();
}
}
rz_list_free(xrefs);
rz_list_free(uniq_xrefs);
}
if (state->mode == RZ_OUTPUT_MODE_JSON) {
pj_end(pj);
}
}
RZ_IPI RzCmdStatus rz_analysis_function_list_calls_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzCmdStatus res = RZ_CMD_STATUS_OK;
RzList *list = rz_analysis_function_list(core->analysis);
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
case RZ_OUTPUT_MODE_QUIET:
case RZ_OUTPUT_MODE_JSON:
function_print_calls(core, list, state);
break;
default:
rz_warn_if_reached();
res = RZ_CMD_STATUS_WRONG_ARGS;
break;
}
return res;
}
RZ_IPI RzCmdStatus rz_analysis_function_list_ascii_handler(RzCore *core, int argc, const char **argv) {
RzList *fcns = rz_list_clone(rz_analysis_function_list(core->analysis));
if (!fcns) {
return RZ_CMD_STATUS_ERROR;
}
rz_list_sort(fcns, fcn_cmpaddr);
RzList *flist = rz_list_newf((RzListFree)rz_listinfo_free);
if (!flist) {
rz_list_free(fcns);
return RZ_CMD_STATUS_ERROR;
}
char temp[32];
RzListIter *iter;
RzAnalysisFunction *fcn;
rz_list_foreach (fcns, iter, fcn) {
RzInterval inter = { rz_analysis_function_min_addr(fcn), rz_analysis_function_linear_size(fcn) };
char *fcn_name = rz_core_analysis_fcn_name(core, fcn);
RzListInfo *info = rz_listinfo_new(fcn_name, inter, inter, -1, rz_strf(temp, "%d", fcn->bits));
free(fcn_name);
if (!info) {
break;
}
rz_list_append(flist, info);
}
RzTable *table = rz_core_table(core);
rz_table_visual_list(table, flist, core->offset, core->blocksize,
rz_cons_get_size(NULL), rz_config_get_i(core->config, "scr.color"));
char *tablestr = rz_table_tostring(table);
rz_cons_printf("\n%s\n", tablestr);
free(tablestr);
rz_table_free(table);
rz_list_free(flist);
rz_list_free(fcns);
return RZ_CMD_STATUS_OK;
}
static void fcn_print_trace_info(RzDebugTrace *traced, RzAnalysisFunction *fcn) {
int tag = traced->tag;
RzListIter *iter;
RzDebugTracepoint *trace;
rz_list_foreach (traced->traces, iter, trace) {
if (!trace->tag || (tag & trace->tag)) {
if (rz_analysis_function_contains(fcn, trace->addr)) {
rz_cons_printf("\ntraced: %d\n", trace->times);
return;
}
}
}
}
static void fcn_print_info(RzCore *core, RzAnalysisFunction *fcn, RzCmdStateOutput *state) {
RzListIter *iter;
RzAnalysisXRef *xrefi;
int ebbs = 0;
char *name = rz_core_analysis_fcn_name(core, fcn);
rz_cons_printf("#\noffset: 0x%08" PFMT64x "\nname: %s\nsize: %" PFMT64u,
fcn->addr, name, rz_analysis_function_linear_size(fcn));
rz_cons_printf("\nis-pure: %s", rz_str_bool(rz_analysis_function_purity(fcn)));
rz_cons_printf("\nrealsz: %" PFMT64d, rz_analysis_function_realsize(fcn));
rz_cons_printf("\nstackframe: %d", fcn->maxstack);
if (fcn->cc) {
rz_cons_printf("\ncall-convention: %s", fcn->cc);
}
rz_cons_printf("\ncyclomatic-cost: %d", rz_analysis_function_cost(fcn));
rz_cons_printf("\ncyclomatic-complexity: %d", rz_analysis_function_complexity(fcn));
rz_cons_printf("\nloops: %d", rz_analysis_function_loops(fcn));
rz_cons_printf("\nbits: %d", fcn->bits);
rz_cons_printf("\ntype: %s", rz_analysis_fcntype_tostring(fcn->type));
if (fcn->type == RZ_ANALYSIS_FCN_TYPE_FCN || fcn->type == RZ_ANALYSIS_FCN_TYPE_SYM) {
rz_cons_printf(" [%s]", diff_type_to_str(fcn->diff));
}
rz_cons_printf("\nnum-bbs: %d", rz_list_length(fcn->bbs));
rz_cons_printf("\nedges: %d", rz_analysis_function_count_edges(fcn, &ebbs));
rz_cons_printf("\nend-bbs: %d", ebbs);
rz_cons_printf("\ncall-refs:");
int outdegree = 0;
RzList *xrefs = rz_analysis_function_get_xrefs_from(fcn);
rz_list_foreach (xrefs, iter, xrefi) {
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
outdegree++;
}
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_CODE || xrefi->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
rz_cons_printf(" 0x%08" PFMT64x " %c", xrefi->to,
xrefi->type == RZ_ANALYSIS_XREF_TYPE_CALL ? 'C' : 'J');
}
}
rz_cons_printf("\ndata-refs:");
rz_list_foreach (xrefs, iter, xrefi) {
// global or local?
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_DATA) {
rz_cons_printf(" 0x%08" PFMT64x, xrefi->to);
}
}
rz_list_free(xrefs);
int indegree = 0;
rz_cons_printf("\ncode-xrefs:");
xrefs = rz_analysis_function_get_xrefs_to(fcn);
rz_list_foreach (xrefs, iter, xrefi) {
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_CODE || xrefi->type == RZ_ANALYSIS_XREF_TYPE_CALL) {
indegree++;
rz_cons_printf(" 0x%08" PFMT64x " %c", xrefi->from,
xrefi->type == RZ_ANALYSIS_XREF_TYPE_CALL ? 'C' : 'J');
}
}
rz_cons_printf("\nnoreturn: %s", rz_str_bool(fcn->is_noreturn));
rz_cons_printf("\nin-degree: %d", indegree);
rz_cons_printf("\nout-degree: %d", outdegree);
rz_cons_printf("\ndata-xrefs:");
rz_list_foreach (xrefs, iter, xrefi) {
if (xrefi->type == RZ_ANALYSIS_XREF_TYPE_DATA) {
rz_cons_printf(" 0x%08" PFMT64x, xrefi->from);
}
}
rz_list_free(xrefs);
if (fcn->type == RZ_ANALYSIS_FCN_TYPE_FCN || fcn->type == RZ_ANALYSIS_FCN_TYPE_SYM) {
int args_count = rz_analysis_var_count(core->analysis, fcn, 'b', 1);
args_count += rz_analysis_var_count(core->analysis, fcn, 's', 1);
args_count += rz_analysis_var_count(core->analysis, fcn, 'r', 1);
int var_count = rz_analysis_var_count(core->analysis, fcn, 'b', 0);
var_count += rz_analysis_var_count(core->analysis, fcn, 's', 0);
var_count += rz_analysis_var_count(core->analysis, fcn, 'r', 0);
rz_cons_printf("\nlocals: %d\nargs: %d\n", var_count, args_count);
core_analysis_var_list_show(core->analysis, fcn, 'b', state);
core_analysis_var_list_show(core->analysis, fcn, 's', state);
core_analysis_var_list_show(core->analysis, fcn, 'r', state);
rz_cons_printf("diff: type: %s", diff_type_to_str(fcn->diff));
if (fcn->diff->addr != -1) {
rz_cons_printf("addr: 0x%" PFMT64x, fcn->diff->addr);
}
if (fcn->diff->name) {
rz_cons_printf("function: %s", fcn->diff->name);
}
}
free(name);
// traced
if (core->dbg->trace->enabled) {
fcn_print_trace_info(core->dbg->trace, fcn);
}
}
static void fcn_list_print_info(RzCore *core, RzList *fcns, RzCmdStateOutput *state) {
RzListIter *iter;
RzAnalysisFunction *fcn;
rz_list_foreach (fcns, iter, fcn) {
fcn_print_info(core, fcn, state);
}
rz_cons_newline();
}
RZ_IPI RzCmdStatus rz_analysis_function_info_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzCmdStatus res = RZ_CMD_STATUS_OK;
RzList *list = rz_analysis_get_functions_in(core->analysis, core->offset);
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
fcn_list_print_info(core, list, state);
break;
case RZ_OUTPUT_MODE_RIZIN:
function_list_print_as_cmd(core, list, state);
break;
case RZ_OUTPUT_MODE_JSON:
function_list_print_to_json(core, list, state);
break;
default:
rz_warn_if_reached();
res = RZ_CMD_STATUS_WRONG_ARGS;
break;
}
rz_list_free(list);
return res;
}
RZ_IPI RzCmdStatus rz_analysis_function_import_list_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
if (argc == 2) {
if (!fcn->imports) {
fcn->imports = rz_list_newf((RzListFree)free);
if (!fcn->imports) {
return RZ_CMD_STATUS_ERROR;
}
}
char *import = strdup(argv[1]);
if (!import || !rz_list_append(fcn->imports, import)) {
free(import);
return RZ_CMD_STATUS_ERROR;
}
} else {
char *imp;
RzListIter *iter;
rz_list_foreach (fcn->imports, iter, imp) {
rz_cons_printf("%s\n", imp);
}
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_import_list_del_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
rz_list_free(fcn->imports);
fcn->imports = NULL;
return RZ_CMD_STATUS_OK;
}
static void ht_inc(HtPU *ht, const char *key) {
bool found;
HtPUKv *kv = ht_pu_find_kv(ht, key, &found);
if (kv) {
kv->value++;
} else {
ht_pu_insert(ht, key, 1);
}
}
enum STATS_MODE {
STATS_MODE_DEF,
STATS_MODE_FML,
STATS_MODE_TYPE
};
static void update_stat_for_op(RzCore *core, HtPU *ht, ut64 addr, int mode) {
RzAnalysisOp *op = rz_core_analysis_op(core, addr, RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_HINT | RZ_ANALYSIS_OP_MASK_DISASM);
if (!op) {
return;
}
RzStrBuf buf;
const char *key;
rz_strbuf_init(&buf);
if (mode == STATS_MODE_FML) {
key = rz_analysis_op_family_to_string(op->family);
} else if (mode == STATS_MODE_TYPE) {
key = rz_analysis_optype_to_string(op->type);
} else {
char *sp = strchr(op->mnemonic, ' ');
if (sp) {
rz_strbuf_setbin(&buf, (ut8 *)op->mnemonic, sp - op->mnemonic);
} else {
rz_strbuf_set(&buf, op->mnemonic);
}
key = rz_strbuf_get(&buf);
}
ht_inc(ht, key);
rz_strbuf_fini(&buf);
rz_analysis_op_free(op);
}
static void gather_opcode_stat_for_fcn(RzCore *core, HtPU *ht, RzAnalysisFunction *fcn, int mode) {
RzAnalysisBlock *bb;
RzListIter *iter;
rz_list_foreach (fcn->bbs, iter, bb) {
update_stat_for_op(core, ht, bb->addr, mode);
for (int i = 0; i < bb->op_pos_size; i++) {
ut16 op_pos = bb->op_pos[i];
update_stat_for_op(core, ht, bb->addr + op_pos, mode);
}
}
}
static bool list_keys_cb(RzList *list, char *k, RZ_UNUSED ut64 v) {
rz_list_push(list, k);
return true;
}
static void print_stats(RzCore *core, HtPU *ht, RzAnalysisFunction *fcn, RzCmdStateOutput *state) {
const char *name;
RzListIter *iter;
RzList *list = rz_list_newf(NULL);
ht_pu_foreach(ht, (HtPUForeachCallback)list_keys_cb, list);
rz_list_sort(list, (RzListComparator)strcmp);
if (state->mode == RZ_OUTPUT_MODE_TABLE) {
RzTable *t = state->d.t;
RzTableColumnType *typeString = rz_table_type("string");
RzTableColumnType *typeNumber = rz_table_type("number");
rz_table_add_column(t, typeString, "name", 0);
rz_list_foreach (list, iter, name) {
rz_table_add_column(t, typeNumber, name, 0);
}
RzPVector *items = rz_pvector_new(free);
if (!items) {
RZ_LOG_ERROR("Failed to allocate memory.\n");
rz_list_free(list);
return;
}
rz_pvector_push(items, strdup(fcn->name));
rz_list_foreach (list, iter, name) {
int nv = (int)ht_pu_find(ht, name, NULL);
rz_pvector_push(items, rz_str_newf("%d", nv));
}
rz_table_add_row_vec(t, items);
} else {
rz_list_foreach (list, iter, name) {
ut32 nv = (ut32)ht_pu_find(ht, name, NULL);
rz_cons_printf("%4u %s\n", nv, name);
}
}
rz_list_free(list);
}
RZ_IPI RzCmdStatus rz_analysis_function_opcode_stat_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
int mode = STATS_MODE_DEF;
if (argc > 1) {
mode = !strcmp(argv[1], "family") ? STATS_MODE_FML : STATS_MODE_TYPE;
}
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
HtPU *ht = ht_pu_new0();
if (!ht) {
return RZ_CMD_STATUS_ERROR;
}
gather_opcode_stat_for_fcn(core, ht, fcn, mode);
print_stats(core, ht, fcn, state);
ht_pu_free(ht);
return RZ_CMD_STATUS_OK;
}
static bool add_keys_to_set_cb(HtPU *ht, const char *k, RZ_UNUSED const ut64 v) {
if (strcmp(k, ".addr")) {
ht_pu_insert(ht, k, 1);
}
return true;
}
RZ_IPI RzCmdStatus rz_analysis_function_all_opcode_stat_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
if (state->mode != RZ_OUTPUT_MODE_TABLE) {
return RZ_CMD_STATUS_ERROR;
}
RzCmdStatus res = RZ_CMD_STATUS_ERROR;
int mode = STATS_MODE_DEF;
if (argc > 1) {
mode = !strcmp(argv[1], "family") ? STATS_MODE_FML : STATS_MODE_TYPE;
}
RzList *keys = rz_list_newf(NULL);
HtPU *keys_set = ht_pu_new0();
RzList *dbs = rz_list_newf((RzListFree)ht_pu_free);
if (!keys || !keys_set || !dbs) {
goto exit;
}
RzListIter *iter;
RzAnalysisFunction *fcn;
rz_list_foreach (core->analysis->fcns, iter, fcn) {
HtPU *db = ht_pu_new0();
if (!db) {
break;
}
gather_opcode_stat_for_fcn(core, db, fcn, mode);
ht_pu_insert(db, ".addr", fcn->addr);
rz_list_append(dbs, db);
}
HtPU *db;
rz_list_foreach (dbs, iter, db) {
ht_pu_foreach(db, (HtPUForeachCallback)add_keys_to_set_cb, keys_set);
}
ht_pu_foreach(keys_set, (HtPUForeachCallback)list_keys_cb, keys);
rz_list_sort(keys, (RzListComparator)strcmp);
RzTable *t = state->d.t;
RzTableColumnType *typeString = rz_table_type("string");
RzTableColumnType *typeNumber = rz_table_type("number");
rz_table_add_column(t, typeString, "name", 0);
rz_table_add_column(t, typeNumber, "addr", 0);
char *key;
rz_list_foreach (keys, iter, key) {
rz_table_add_column(t, typeNumber, key, 0);
}
RzListIter *iter2;
rz_list_foreach (dbs, iter2, db) {
RzPVector *items = rz_pvector_new(free);
if (!items) {
break;
}
ut64 fcnAddr = ht_pu_find(db, ".addr", NULL);
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, fcnAddr);
rz_pvector_push(items, fcn ? strdup(fcn->name) : strdup(""));
rz_pvector_push(items, fcn ? rz_str_newf("0x%08" PFMT64x, fcnAddr) : strdup("0"));
rz_list_foreach (keys, iter, key) {
ut32 n = (ut32)ht_pu_find(db, key, NULL);
rz_pvector_push(items, rz_str_newf("%u", n));
}
rz_table_add_row_vec(t, items);
}
res = RZ_CMD_STATUS_OK;
exit:
rz_list_free(keys);
rz_list_free(dbs);
ht_pu_free(keys_set);
return res;
}
RZ_IPI RzCmdStatus rz_analysis_function_rename_handler(RzCore *core, int argc, const char **argv) {
return rz_core_analysis_function_rename(core, core->offset, argv[1]) ? RZ_CMD_STATUS_OK : RZ_CMD_STATUS_ERROR;
}
RZ_IPI RzCmdStatus rz_analysis_function_autoname_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
char *name = rz_core_analysis_function_autoname(core, fcn);
if (!name) {
return RZ_CMD_STATUS_ERROR;
}
rz_cons_printf("%s\n", name);
free(name);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_strings_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
PJ *pj = state->mode == RZ_OUTPUT_MODE_JSON ? state->d.pj : NULL;
rz_core_analysis_function_strings_print(core, fcn, pj);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_type_matching_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
ut64 seek = core->offset;
rz_analysis_esil_set_pc(core->analysis->esil, fcn->addr);
rz_core_analysis_type_match(core, fcn, NULL);
rz_core_seek(core, seek, true);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_functions_map_handler(RzCore *core, int argc, const char **argv) {
int show_color = rz_config_get_i(core->config, "scr.color");
int cols = rz_config_get_i(core->config, "hex.cols") * 4;
ut64 code_size = rz_num_get(core->num, "$SS");
ut64 base_addr = rz_num_get(core->num, "$S");
if (code_size < 1) {
return RZ_CMD_STATUS_WRONG_ARGS;
}
char *bitmap = calloc(1, code_size + 64);
if (!bitmap) {
return RZ_CMD_STATUS_ERROR;
}
RzListIter *iter, *iter2;
RzAnalysisFunction *fcn;
RzAnalysisBlock *b;
// for each function
rz_list_foreach (core->analysis->fcns, iter, fcn) {
// for each basic block in the function
rz_list_foreach (fcn->bbs, iter2, b) {
// if it is not within range, continue
if ((fcn->addr < base_addr) || (fcn->addr >= base_addr + code_size))
continue;
// otherwise mark each byte in the BB in the bitmap
int counter = 1;
for (counter = 0; counter < b->size; counter++) {
bitmap[b->addr + counter - base_addr] = '=';
}
bitmap[fcn->addr - base_addr] = 'F';
}
}
// print the bitmap
int assigned = 0;
if (cols < 1) {
cols = 1;
}
for (ut64 i = 0; i < code_size; i += 1) {
if (!(i % cols)) {
rz_cons_printf("\n0x%08" PFMT64x " ", base_addr + i);
}
if (bitmap[i]) {
assigned++;
}
if (show_color) {
if (bitmap[i]) {
rz_cons_printf("%s%c\x1b[0m", Color_GREEN, bitmap[i]);
} else {
rz_cons_printf(".");
}
} else {
rz_cons_printf("%c", bitmap[i] ? bitmap[i] : '.');
}
}
rz_cons_printf("\n%d / %" PFMT64u " (%.2lf%%) bytes assigned to a function\n", assigned, code_size, 100.0 * ((float)assigned) / code_size);
free(bitmap);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_functions_merge_handler(RzCore *core, int argc, const char **argv) {
ut64 addr = rz_num_math(core->num, argv[1]);
rz_core_analysis_fcn_merge(core, core->offset, addr);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_cc_set_get_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
if (argc == 1) {
rz_cons_println(fcn->cc);
return RZ_CMD_STATUS_OK;
}
if (!rz_analysis_cc_exist(core->analysis, argv[1])) {
RZ_LOG_ERROR("Unknown calling convention. See `afcl` for available ones.\n");
return RZ_CMD_STATUS_WRONG_ARGS;
}
fcn->cc = rz_str_constpool_get(&core->analysis->constpool, argv[1]);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_cc_list_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
rz_core_types_calling_conventions_print(core, mode);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_cc_load_handler(RzCore *core, int argc, const char **argv) {
if (!rz_file_exists(argv[1])) {
RZ_LOG_ERROR("File \"%s\" does not exist\n", argv[1]);
return RZ_CMD_STATUS_WRONG_ARGS;
}
Sdb *db = sdb_new(0, argv[1], 0);
if (!db) {
return RZ_CMD_STATUS_ERROR;
}
sdb_merge(core->analysis->sdb_cc, db);
sdb_close(db);
sdb_free(db);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_cc_reg_usage_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisFunction *fcn = analysis_get_function_in(core->analysis, core->offset);
if (!fcn) {
return RZ_CMD_STATUS_ERROR;
}
PJ *pj = state->mode == RZ_OUTPUT_MODE_JSON ? state->d.pj : NULL;
rz_core_analysis_cc_print(core, fcn->cc, pj);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_describe_offset_handler(RzCore *core, int argc, const char **argv) {
RzList *list = rz_analysis_get_functions_in(core->analysis, core->offset);
if (rz_list_empty(list)) {
RZ_LOG_ERROR("No function found in 0x%08" PFMT64x ".\n", core->offset);
rz_list_free(list);
return RZ_CMD_STATUS_ERROR;
}
RzListIter *it;
RzAnalysisFunction *fcn;
rz_list_foreach (list, it, fcn) {
st64 delta = core->offset - fcn->addr;
if (delta > 0) {
rz_cons_printf("%s + %" PFMT64d "\n", fcn->name, delta);
} else if (delta < 0) {
rz_cons_printf("%s - %" PFMT64d "\n", fcn->name, -delta);
} else {
rz_cons_printf("%s\n", fcn->name);
}
}
rz_list_free(list);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_add_recu_handler(RzCore *core, int argc, const char **argv) {
const char *name = argc == 2 ? argv[1] : NULL;
bool analyze_recursively = true;
if (!strcmp(argv[0], "af")) {
analyze_recursively = rz_config_get_b(core->config, "analysis.calls");
}
return bool2status(rz_core_analysis_function_add(core, name, core->offset, analyze_recursively));
}
RZ_IPI RzCmdStatus rz_analysis_function_create_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisDiff *diff = rz_analysis_diff_new();
if (!diff) {
RZ_LOG_ERROR("Cannot init RzAnalysisDiff\n");
return RZ_CMD_STATUS_ERROR;
}
if (argc == 4) {
switch (argv[3][0]) {
case 'm':
diff->type = RZ_ANALYSIS_DIFF_TYPE_MATCH;
break;
case 'u':
diff->type = RZ_ANALYSIS_DIFF_TYPE_UNMATCH;
break;
default:
break;
}
}
int type = RZ_ANALYSIS_FCN_TYPE_FCN;
if (argc >= 3) {
switch (argv[2][0]) {
case 'l':
type = RZ_ANALYSIS_FCN_TYPE_LOC;
break;
case 'i':
type = RZ_ANALYSIS_FCN_TYPE_IMP;
break;
case 's':
type = RZ_ANALYSIS_FCN_TYPE_SYM;
break;
default:
break;
}
}
RzAnalysisFunction *fcn = rz_analysis_create_function(core->analysis, argv[1], core->offset, type, diff);
if (!fcn) {
rz_analysis_diff_free(diff);
RZ_LOG_ERROR("Cannot add function (duplicated)\n");
return RZ_CMD_STATUS_ERROR;
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_del_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *f = analysis_get_function_in(core->analysis, core->offset);
if (!f) {
return RZ_CMD_STATUS_ERROR;
}
ut64 addr = f->addr;
rz_core_analysis_undefine(core, addr);
rz_analysis_fcn_del_locs(core->analysis, addr);
rz_analysis_fcn_del(core->analysis, addr);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_del_all_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisFunction *f;
RzListIter *iter, *iter_tmp;
rz_list_foreach_safe (core->analysis->fcns, iter, iter_tmp, f) {
rz_analysis_del_jmprefs(core->analysis, f);
rz_core_analysis_undefine(core, f->addr);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_analyze_jmptable_handler(RzCore *core, int argc, const char **argv) {
RzList *blocks = rz_analysis_get_blocks_in(core->analysis, core->offset);
if (!blocks) {
return RZ_CMD_STATUS_ERROR;
}
RzAnalysisBlock *block = rz_list_first(blocks);
if (block && !rz_list_empty(block->fcns)) {
ut64 table = rz_num_math(core->num, argv[1]);
ut64 elements = rz_num_math(core->num, argv[2]);
rz_analysis_jmptbl(core->analysis, rz_list_first(block->fcns), block, core->offset, table, elements, UT64_MAX);
} else {
RZ_LOG_ERROR("No function defined here\n");
}
rz_list_free(blocks);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_function_analyze_args_handler(RzCore *core, int argc, const char **argv) {
if (!strcmp(argv[0], "afa") || rz_config_get_b(core->config, "dbg.funcarg")) {
rz_core_print_func_args(core);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_il_vm_initialize_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_il_reinit(core);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_il_vm_step_handler(RzCore *core, int argc, const char **argv) {
ut64 repeat_times = argc == 1 ? 1 : rz_num_math(NULL, argv[1]);
for (ut64 i = 0; i < repeat_times; ++i) {
if (!rz_core_il_step(core)) {
break;
}
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_il_vm_step_with_events_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
ut64 repeat_times = argc == 1 ? 1 : rz_num_math(NULL, argv[1]);
PJ *pj = NULL;
if (mode == RZ_OUTPUT_MODE_JSON) {
pj = pj_new();
if (!pj) {
RZ_LOG_ERROR("cannot allocate PJ.\n");
return RZ_CMD_STATUS_ERROR;
}
pj_a(pj);
}
for (ut64 i = 0; i < repeat_times; ++i) {
if (!rz_core_analysis_il_step_with_events(core, pj)) {
break;
}
}
if (mode == RZ_OUTPUT_MODE_JSON) {
pj_end(pj);
rz_cons_println(pj_string(pj));
pj_free(pj);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_il_vm_step_until_addr_handler(RzCore *core, int argc, const char **argv) {
ut64 address = rz_num_math(core->num, argv[1]);
if (!core->analysis->il_vm) {
RZ_LOG_ERROR("RzIL: the VM is not initialized.\n");
return RZ_CMD_STATUS_ERROR;
}
while (1) {
ut64 pc = rz_reg_get_value_by_role(core->analysis->reg, RZ_REG_NAME_PC);
if (pc == address) {
break;
}
if (rz_cons_is_breaked()) {
rz_cons_printf("CTRL+C was pressed.\n");
break;
}
if (!rz_core_il_step(core)) {
break;
}
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_il_vm_status_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) {
if (argc == 3) {
ut64 value = rz_num_math(core->num, argv[2]);
if (rz_core_analysis_il_vm_set(core, argv[1], value)) {
rz_cons_printf("%s = 0x%" PFMT64x "\n", argv[1], value);
}
} else {
// print variable or all variables
rz_core_analysis_il_vm_status(core, argc == 2 ? argv[1] : NULL, mode);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
rz_core_analysis_hint_list_print(core->analysis, state);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_list_at_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
rz_core_analysis_hint_print(core->analysis, core->offset, state);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_handler(RzCore *core, int argc, const char **argv) {
ut64 size = 1;
if (argc == 2) {
size = rz_num_math(core->num, argv[1]);
}
rz_analysis_hint_del(core->analysis, core->offset, size);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_all_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_clear(core->analysis);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_arch_handler(RzCore *core, int argc, const char **argv) {
const char *arch = !strcmp(argv[1], "0") ? NULL : argv[1];
rz_analysis_hint_set_arch(core->analysis, core->offset, arch);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_arch_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_arch(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_bits_handler(RzCore *core, int argc, const char **argv) {
ut64 bits = rz_num_math(core->num, argv[1]);
rz_analysis_hint_set_bits(core->analysis, core->offset, bits);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_bits_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_bits(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_high_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_set_high(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_high_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_high(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_jump_handler(RzCore *core, int argc, const char **argv) {
ut64 jump = rz_num_math(core->num, argv[1]);
rz_analysis_hint_set_jump(core->analysis, core->offset, jump);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_jump_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_jump(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_esil_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_set_esil(core->analysis, core->offset, argv[1]);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_esil_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_esil(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_opcode_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_set_opcode(core->analysis, core->offset, argv[1]);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_opcode_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_opcode(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_size_handler(RzCore *core, int argc, const char **argv) {
ut64 size = rz_num_math(core->num, argv[1]);
rz_analysis_hint_set_size(core->analysis, core->offset, size);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_size_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_size(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_fail_handler(RzCore *core, int argc, const char **argv) {
ut64 fail = rz_num_math(core->num, argv[1]);
rz_analysis_hint_set_fail(core->analysis, core->offset, fail);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_fail_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_fail(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_stackframe_handler(RzCore *core, int argc, const char **argv) {
ut64 size = rz_num_math(core->num, argv[1]);
rz_analysis_hint_set_stackframe(core->analysis, core->offset, size);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_stackframe_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_stackframe(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_syntax_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_set_syntax(core->analysis, core->offset, argv[1]);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_syntax_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_syntax(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_ptr_handler(RzCore *core, int argc, const char **argv) {
ut64 ptr = rz_num_math(core->num, argv[1]);
rz_analysis_hint_set_pointer(core->analysis, core->offset, ptr);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_ptr_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_pointer(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_ret_handler(RzCore *core, int argc, const char **argv) {
ut64 ret = rz_num_math(core->num, argv[1]);
rz_analysis_hint_set_ret(core->analysis, core->offset, ret);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_ret_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_ret(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_val_handler(RzCore *core, int argc, const char **argv) {
ut64 val = rz_num_math(core->num, argv[1]);
rz_analysis_hint_set_val(core->analysis, core->offset, val);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_val_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_val(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_optype_handler(RzCore *core, int argc, const char **argv) {
int type = rz_analysis_optype_from_string(argv[1]);
if (type < 0) {
return RZ_CMD_STATUS_WRONG_ARGS;
}
rz_analysis_hint_set_type(core->analysis, core->offset, type);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_optype_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_type(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_immbase_handler(RzCore *core, int argc, const char **argv) {
int base = rz_num_base_of_string(core->num, argv[1]);
if (argc == 3) {
ut64 nword = rz_num_math(core->num, argv[2]);
rz_analysis_hint_set_nword(core->analysis, core->offset, (int)(nword));
}
rz_analysis_hint_set_immbase(core->analysis, core->offset, base);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_immbase_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_immbase(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_hint_set_offset_handler(RzCore *core, int argc, const char **argv) {
return bool2status(rz_core_analysis_hint_set_offset(core, argv[1]));
}
RZ_IPI RzCmdStatus rz_analysis_hint_del_offset_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_hint_unset_offset(core->analysis, core->offset);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_list_struct_offsets_handler(RzCore *core, int argc, const char **argv) {
ut64 toff = rz_num_math(core->num, argv[1]);
if (!toff) {
return RZ_CMD_STATUS_WRONG_ARGS;
}
RzList *typeoffs = rz_type_db_get_by_offset(core->analysis->typedb, toff);
RzListIter *iter;
RzTypePath *ty;
// We only print type paths here
rz_list_foreach (typeoffs, iter, ty) {
rz_cons_printf("%s\n", ty->path);
}
rz_list_free(typeoffs);
return RZ_CMD_STATUS_OK;
}
static void analysis_class_print(RzAnalysis *analysis, const char *class_name, bool detailed) {
rz_cons_printf("[%s", class_name);
RzVector *bases = rz_analysis_class_base_get_all(analysis, class_name);
if (bases) {
RzAnalysisBaseClass *base;
bool first = true;
rz_vector_foreach(bases, base) {
if (first) {
rz_cons_print(": ");
first = false;
} else {
rz_cons_print(", ");
}
rz_cons_print(base->class_name);
}
rz_vector_free(bases);
}
rz_cons_print("]\n");
if (detailed) {
RzVector *vtables = rz_analysis_class_vtable_get_all(analysis, class_name);
if (vtables) {
RzAnalysisVTable *vtable;
rz_vector_foreach(vtables, vtable) {
rz_cons_printf(" (vtable at 0x%" PFMT64x, vtable->addr);
if (vtable->offset > 0) {
rz_cons_printf(" in class at +0x%" PFMT64x ")\n", vtable->offset);
} else {
rz_cons_print(")\n");
}
}
rz_vector_free(vtables);
}
RzVector *methods = rz_analysis_class_method_get_all(analysis, class_name);
if (methods && rz_vector_len(methods) > 0) {
RzTable *table = rz_table_new();
rz_table_set_columnsf(table, "dsxxs", "nth", "name", "addr", "vt_offset", "type");
rz_table_align(table, 2, RZ_TABLE_ALIGN_RIGHT);
char *method_type[] = { "DEFAULT", "VIRTUAL", "V_DESTRUCTOR", "DESTRUCTOR", "CONSTRUCTOR" };
RzAnalysisMethod *meth;
int i = 1;
rz_vector_foreach(methods, meth) {
RzPVector *row_vec = rz_pvector_new(free);
if (!row_vec) {
RZ_LOG_ERROR("Failed to allocate memory.\n");
rz_table_free(table);
rz_vector_free(methods);
return;
}
rz_pvector_push(row_vec, rz_str_newf("%d", i++));
rz_pvector_push(row_vec, rz_str_new(meth->real_name));
rz_pvector_push(row_vec, rz_str_newf("0x%" PFMT64x, meth->addr));
if (meth->vtable_offset >= 0) {
rz_pvector_push(row_vec, rz_str_newf("0x%" PFMT64x, meth->vtable_offset));
} else {
rz_pvector_push(row_vec, rz_str_new("-1"));
}
rz_pvector_push(row_vec, rz_str_new(method_type[meth->method_type]));
rz_table_add_row_vec(table, row_vec);
}
char *s = rz_table_tostring(table);
rz_cons_printf("%s\n", s);
free(s);
rz_table_free(table);
}
rz_vector_free(methods);
}
}
static void analysis_class_print_to_json(RzAnalysis *analysis, PJ *pj, const char *class_name) {
pj_o(pj);
pj_ks(pj, "name", class_name);
pj_k(pj, "bases");
pj_a(pj);
RzVector *bases = rz_analysis_class_base_get_all(analysis, class_name);
if (bases) {
RzAnalysisBaseClass *base;
rz_vector_foreach(bases, base) {
pj_o(pj);
pj_ks(pj, "id", base->id);
pj_ks(pj, "name", base->class_name);
pj_kn(pj, "offset", base->offset);
pj_end(pj);
}
rz_vector_free(bases);
}
pj_end(pj);
pj_k(pj, "vtables");
pj_a(pj);
RzVector *vtables = rz_analysis_class_vtable_get_all(analysis, class_name);
if (vtables) {
RzAnalysisVTable *vtable;
rz_vector_foreach(vtables, vtable) {
pj_o(pj);
pj_ks(pj, "id", vtable->id);
pj_kn(pj, "addr", vtable->addr);
pj_kn(pj, "offset", vtable->offset);
pj_end(pj);
}
}
pj_end(pj);
pj_k(pj, "methods");
pj_a(pj);
RzVector *methods = rz_analysis_class_method_get_all(analysis, class_name);
if (methods) {
char *method_type[] = { "DEFAULT", "VIRTUAL", "V_DESTRUCTOR", "DESTRUCTOR", "CONSTRUCTOR" };
RzAnalysisMethod *meth;
rz_vector_foreach(methods, meth) {
pj_o(pj);
pj_ks(pj, "name", meth->real_name);
pj_kn(pj, "addr", meth->addr);
pj_ks(pj, "type", method_type[meth->method_type]);
if (meth->vtable_offset >= 0) {
pj_kn(pj, "vtable_offset", (ut64)meth->vtable_offset);
}
pj_end(pj);
}
rz_vector_free(methods);
}
pj_end(pj);
pj_end(pj);
}
typedef struct {
RzAnalysis *analysis;
PJ *pj;
} ListJsonCtx;
static bool analysis_class_print_to_json_cb(void *user, const char *k, const char *v) {
ListJsonCtx *ctx = user;
analysis_class_print_to_json(ctx->analysis, ctx->pj, k);
return true;
}
static void analysis_class_list_print_to_json(RzAnalysis *analysis, PJ *pj) {
ListJsonCtx ctx;
ctx.analysis = analysis;
ctx.pj = pj;
pj_a(pj);
rz_analysis_class_foreach(analysis, analysis_class_print_to_json_cb, &ctx);
pj_end(pj);
return;
}
static void analysis_class_print_as_cmd(RzAnalysis *analysis, const char *class_name) {
RzVector *bases = rz_analysis_class_base_get_all(analysis, class_name);
if (bases) {
RzAnalysisBaseClass *base;
rz_vector_foreach(bases, base) {
rz_cons_printf("acb %s %s %" PFMT64u "\n", class_name, base->class_name, base->offset);
}
rz_vector_free(bases);
}
RzVector *vtables = rz_analysis_class_vtable_get_all(analysis, class_name);
if (vtables) {
RzAnalysisVTable *vtable;
rz_vector_foreach(vtables, vtable) {
rz_cons_printf("acv %s 0x%" PFMT64x " %" PFMT64u "\n", class_name, vtable->addr, vtable->offset);
}
rz_vector_free(vtables);
}
RzVector *methods = rz_analysis_class_method_get_all(analysis, class_name);
if (methods) {
RzAnalysisMethod *meth;
rz_vector_foreach(methods, meth) {
rz_cons_printf("acm %s %s 0x%" PFMT64x " %" PFMT64d "\n", class_name, meth->name, meth->addr, meth->vtable_offset);
}
rz_vector_free(methods);
}
}
RZ_IPI RzCmdStatus rz_analysis_class_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
if (state->mode == RZ_OUTPUT_MODE_JSON) {
analysis_class_list_print_to_json(core->analysis, state->d.pj);
return RZ_CMD_STATUS_OK;
}
SdbList *classes = rz_analysis_class_get_all(core->analysis, state->mode != RZ_OUTPUT_MODE_RIZIN);
SdbListIter *iter;
SdbKv *kv;
if (state->mode == RZ_OUTPUT_MODE_RIZIN) {
ls_foreach (classes, iter, kv) {
// need to create all classes first, so they can be referenced
rz_cons_printf("ac %s\n", sdbkv_key(kv));
}
ls_foreach (classes, iter, kv) {
analysis_class_print_as_cmd(core->analysis, sdbkv_key(kv));
}
} else {
ls_foreach (classes, iter, kv) {
analysis_class_print(core->analysis, sdbkv_key(kv), state->mode == RZ_OUTPUT_MODE_LONG);
}
}
ls_free(classes);
return RZ_CMD_STATUS_OK;
}
static inline void log_err_nonexist_class() {
RZ_LOG_ERROR("Class does not exist.\n");
}
RZ_IPI RzCmdStatus rz_analysis_class_info_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
const char *class_name = argv[1];
if (!rz_analysis_class_exists(core->analysis, class_name)) {
log_err_nonexist_class();
return RZ_CMD_STATUS_ERROR;
}
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
case RZ_OUTPUT_MODE_LONG:
analysis_class_print(core->analysis, class_name, state->mode == RZ_OUTPUT_MODE_LONG);
break;
case RZ_OUTPUT_MODE_JSON:
analysis_class_print_to_json(core->analysis, state->d.pj, class_name);
break;
default:
rz_warn_if_reached();
break;
}
return RZ_CMD_STATUS_OK;
}
static RzCmdStatus class_error(RzAnalysisClassErr err) {
RzCmdStatus status = RZ_CMD_STATUS_ERROR;
switch (err) {
case RZ_ANALYSIS_CLASS_ERR_SUCCESS:
status = RZ_CMD_STATUS_OK;
break;
case RZ_ANALYSIS_CLASS_ERR_NONEXISTENT_CLASS:
log_err_nonexist_class();
break;
case RZ_ANALYSIS_CLASS_ERR_CLASH:
RZ_LOG_ERROR("A class with this name already exists.\n");
break;
default:
break;
}
return status;
}
RZ_IPI RzCmdStatus rz_analysis_class_add_handler(RzCore *core, int argc, const char **argv) {
const char *class_name = argv[1];
if (strchr(class_name, ' ')) {
RZ_LOG_ERROR("Invalid class name.\n");
return RZ_CMD_STATUS_WRONG_ARGS;
}
RzAnalysisClassErr err = rz_analysis_class_create(core->analysis, class_name);
return class_error(err);
}
RZ_IPI RzCmdStatus rz_analysis_class_del_handler(RzCore *core, int argc, const char **argv) {
rz_analysis_class_delete(core->analysis, argv[1]);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_class_rename_handler(RzCore *core, int argc, const char **argv) {
const char *old_name = argv[1];
const char *new_name = argv[2];
if (strchr(new_name, ' ')) {
RZ_LOG_ERROR("Invalid class name.\n");
return RZ_CMD_STATUS_WRONG_ARGS;
}
RzAnalysisClassErr err = rz_analysis_class_rename(core->analysis, old_name, new_name);
return class_error(err);
}
RZ_IPI RzCmdStatus rz_analysis_class_graph_handler(RzCore *core, int argc, const char **argv) {
RzGraph *graph = rz_analysis_class_get_inheritance_graph(core->analysis);
if (!graph) {
RZ_LOG_ERROR("Couldn't create graph.\n");
return RZ_CMD_STATUS_ERROR;
}
rz_core_graph_print(core, graph, -1, false, "");
rz_graph_free(graph);
return RZ_CMD_STATUS_OK;
}
static RzCmdStatus class_method_error(RzAnalysisClassErr err) {
RzCmdStatus status = RZ_CMD_STATUS_ERROR;
switch (err) {
case RZ_ANALYSIS_CLASS_ERR_NONEXISTENT_ATTR:
RZ_LOG_ERROR("Method does not exist.\n");
break;
case RZ_ANALYSIS_CLASS_ERR_CLASH:
RZ_LOG_ERROR("Method already exists.\n");
break;
default:
status = class_error(err);
break;
}
return status;
}
RZ_IPI RzCmdStatus rz_analysis_class_method_add_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisMethod meth;
meth.name = strdup(argv[2]);
meth.real_name = strdup(argv[2]);
meth.method_type = RZ_ANALYSIS_CLASS_METHOD_DEFAULT;
meth.addr = rz_num_math(core->num, argv[3]);
meth.vtable_offset = -1;
if (argc == 5) {
meth.vtable_offset = (st64)rz_num_math(core->num, argv[4]);
}
RzAnalysisClassErr err = rz_analysis_class_method_set(core->analysis, argv[1], &meth);
rz_analysis_class_method_fini(&meth);
return class_method_error(err);
}
RZ_IPI RzCmdStatus rz_analysis_class_method_del_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisClassErr err = rz_analysis_class_method_delete(core->analysis, argv[1], argv[2]);
return class_method_error(err);
}
RZ_IPI RzCmdStatus rz_analysis_class_method_rename_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisClassErr err = rz_analysis_class_method_rename(core->analysis, argv[1], argv[2], argv[3]);
return class_method_error(err);
}
static RzCmdStatus class_base_error(RzAnalysisClassErr err) {
RzCmdStatus status = RZ_CMD_STATUS_ERROR;
switch (err) {
case RZ_ANALYSIS_CLASS_ERR_NONEXISTENT_ATTR:
RZ_LOG_ERROR("Base class does not exist.\n");
break;
case RZ_ANALYSIS_CLASS_ERR_CLASH:
RZ_LOG_ERROR("Base class already exists.\n");
break;
default:
status = class_error(err);
break;
}
return status;
}
RZ_IPI RzCmdStatus rz_analysis_class_base_add_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisBaseClass base;
base.id = NULL;
base.offset = 0;
base.class_name = strdup(argv[2]);
if (argc == 4) {
base.offset = rz_num_math(core->num, argv[3]);
}
RzAnalysisClassErr err = rz_analysis_class_base_set(core->analysis, argv[1], &base);
rz_analysis_class_base_fini(&base);
return class_base_error(err);
}
RZ_IPI RzCmdStatus rz_analysis_class_base_del_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisClassErr err = rz_analysis_class_base_delete(core->analysis, argv[1], argv[2]);
return class_base_error(err);
}
RZ_IPI RzCmdStatus rz_analysis_class_base_list_handler(RzCore *core, int argc, const char **argv) {
const char *class_name = argv[1];
if (!rz_analysis_class_exists(core->analysis, class_name)) {
log_err_nonexist_class();
return RZ_CMD_STATUS_ERROR;
}
char *class_name_sanitized = rz_str_sanitize_sdb_key(class_name);
if (!class_name_sanitized) {
return RZ_CMD_STATUS_ERROR;
}
rz_cons_printf("%s:\n", class_name_sanitized);
free(class_name_sanitized);
RzVector *bases = rz_analysis_class_base_get_all(core->analysis, class_name);
RzAnalysisBaseClass *base;
rz_vector_foreach(bases, base) {
rz_cons_printf(" %4s %s @ +0x%" PFMT64x "\n", base->id, base->class_name, base->offset);
}
rz_vector_free(bases);
return RZ_CMD_STATUS_OK;
}
static RzCmdStatus class_vtable_error(RzAnalysisClassErr err) {
RzCmdStatus status = RZ_CMD_STATUS_ERROR;
switch (err) {
case RZ_ANALYSIS_CLASS_ERR_NONEXISTENT_ATTR:
RZ_LOG_ERROR("Vtable does not exist.\n");
break;
case RZ_ANALYSIS_CLASS_ERR_CLASH:
RZ_LOG_ERROR("Vtable already exists.\n");
break;
default:
status = class_error(err);
break;
}
return status;
}
RZ_IPI RzCmdStatus rz_analysis_class_vtable_add_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisVTable vtable;
vtable.id = NULL;
vtable.addr = rz_num_math(core->num, argv[2]);
vtable.offset = 0;
vtable.size = 0;
if (argc >= 4) {
vtable.offset = rz_num_math(core->num, argv[3]);
}
if (argc == 5) {
vtable.size = rz_num_math(core->num, argv[4]);
}
RzAnalysisClassErr err = rz_analysis_class_vtable_set(core->analysis, argv[1], &vtable);
rz_analysis_class_vtable_fini(&vtable);
return class_vtable_error(err);
}
RZ_IPI RzCmdStatus rz_analysis_class_vtable_del_handler(RzCore *core, int argc, const char **argv) {
RzAnalysisClassErr err = rz_analysis_class_vtable_delete(core->analysis, argv[1], argv[2]);
return class_vtable_error(err);
}
RZ_IPI RzCmdStatus rz_analysis_class_vtable_list_handler(RzCore *core, int argc, const char **argv) {
const char *class_name = argv[1];
if (!rz_analysis_class_exists(core->analysis, class_name)) {
log_err_nonexist_class();
return RZ_CMD_STATUS_ERROR;
}
char *class_name_sanitized = rz_str_sanitize_sdb_key(class_name);
if (!class_name_sanitized) {
return RZ_CMD_STATUS_ERROR;
}
rz_cons_printf("%s:\n", class_name_sanitized);
free(class_name_sanitized);
RzVector *vtables = rz_analysis_class_vtable_get_all(core->analysis, class_name);
if (vtables) {
RzAnalysisVTable *vtable;
rz_vector_foreach(vtables, vtable) {
rz_cons_printf(" %4s vtable 0x%" PFMT64x " @ +0x%" PFMT64x " size:+0x%" PFMT64x "\n", vtable->id, vtable->addr, vtable->offset, vtable->size);
}
rz_vector_free(vtables);
}
return RZ_CMD_STATUS_OK;
}
static void list_all_functions_at_vtable_offset(RzAnalysis *analysis, const char *class_name, ut64 offset) {
RVTableContext vtableContext;
rz_analysis_vtable_begin(analysis, &vtableContext);
ut8 function_ptr_size = vtableContext.word_size;
RzVector *vtables = rz_analysis_class_vtable_get_all(analysis, class_name);
if (!vtables) {
return;
}
RzAnalysisVTable *vtable;
rz_vector_foreach(vtables, vtable) {
if (vtable->size < offset + function_ptr_size || offset % function_ptr_size) {
continue;
}
ut64 func_address;
if (vtableContext.read_addr(analysis, vtable->addr + offset, &func_address)) {
rz_cons_printf("Function address: 0x%08" PFMT64x ", in %s vtable %s\n", func_address, class_name, vtable->id);
}
}
rz_vector_free(vtables);
}
RZ_IPI RzCmdStatus rz_analysis_class_vtable_lookup_handler(RzCore *core, int argc, const char **argv) {
ut64 offset = rz_num_math(core->num, argv[1]);
const char *class_name = argc == 3 ? argv[2] : NULL;
if (class_name && !rz_analysis_class_exists(core->analysis, class_name)) {
log_err_nonexist_class();
return RZ_CMD_STATUS_ERROR;
}
if (class_name) {
list_all_functions_at_vtable_offset(core->analysis, class_name, offset);
return RZ_CMD_STATUS_OK;
}
SdbList *classes = rz_analysis_class_get_all(core->analysis, true);
SdbListIter *iter;
SdbKv *kv;
ls_foreach (classes, iter, kv) {
const char *name = sdbkv_key(kv);
list_all_functions_at_vtable_offset(core->analysis, name, offset);
}
ls_free(classes);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_bytes_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
ut8 *buf;
st32 len;
if (!(buf = malloc(strlen(argv[1]) + 1))) {
return RZ_CMD_STATUS_ERROR;
}
len = rz_hex_str2bin(argv[1], buf);
if (len <= 0) {
free(buf);
return RZ_CMD_STATUS_ERROR;
}
switch (state->mode) {
case RZ_OUTPUT_MODE_JSON:
core_analysis_bytes_json(core, buf, len, 0, state->d.pj);
break;
case RZ_OUTPUT_MODE_STANDARD:
core_analysis_bytes_standard(core, buf, len, 0);
break;
default:
rz_warn_if_reached();
break;
}
free(buf);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_n_bytes_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
switch (state->mode) {
case RZ_OUTPUT_MODE_JSON:
core_analysis_bytes_json(core, core->block, core->blocksize, 0, state->d.pj);
break;
case RZ_OUTPUT_MODE_STANDARD:
core_analysis_bytes_standard(core, core->block, core->blocksize, 0);
break;
default:
rz_warn_if_reached();
break;
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_n_bytes_esil_handler(RzCore *core, int argc, const char **argv) {
core_analysis_bytes_esil(core, core->block, core->blocksize, 0);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_n_bytes_desc_handler(RzCore *core, int argc, const char **argv) {
core_analysis_bytes_desc(core, core->block, core->blocksize, 0);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_n_bytes_size_handler(RzCore *core, int argc, const char **argv) {
core_analysis_bytes_size(core, core->block, core->blocksize, 0);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_n_ins_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
st32 l;
ut32 count = 1, obs = core->blocksize;
if (argc > 1) {
l = (st32)rz_num_math(core->num, argv[1]);
if (l <= 0) {
RZ_LOG_ERROR("Invalid zero or negative arguments.\n");
return RZ_CMD_STATUS_ERROR;
}
count = l;
l *= 8;
if (l > obs) {
rz_core_block_size(core, l);
}
}
switch (state->mode) {
case RZ_OUTPUT_MODE_JSON:
core_analysis_bytes_json(core, core->block, core->blocksize, count, state->d.pj);
break;
case RZ_OUTPUT_MODE_STANDARD:
core_analysis_bytes_standard(core, core->block, core->blocksize, count);
break;
default:
rz_warn_if_reached();
break;
}
if (obs != core->blocksize) {
rz_core_block_size(core, obs);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_n_ins_size_handler(RzCore *core, int argc, const char **argv) {
ut32 count = 1, obs = core->blocksize;
st32 l;
if (argc > 1) {
l = (st32)rz_num_math(core->num, argv[1]);
if (l <= 0) {
RZ_LOG_ERROR("Invalid zero or negative arguments.\n");
return RZ_CMD_STATUS_ERROR;
}
count = l;
l *= 8;
if (l > obs) {
rz_core_block_size(core, l);
}
}
core_analysis_bytes_size(core, core->block, core->blocksize, count);
if (obs != core->blocksize) {
rz_core_block_size(core, obs);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_n_ins_esil_handler(RzCore *core, int argc, const char **argv) {
ut32 count = 1, obs = core->blocksize;
st32 l;
if (argc > 1) {
l = (st32)rz_num_math(core->num, argv[1]);
if (l <= 0) {
RZ_LOG_ERROR("Invalid zero or negative arguments.\n");
return RZ_CMD_STATUS_ERROR;
}
count = l;
l *= 8;
if (l > obs) {
rz_core_block_size(core, l);
}
}
core_analysis_bytes_esil(core, core->block, core->blocksize, count);
if (obs != core->blocksize) {
rz_core_block_size(core, obs);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_opcode_handler(RzCore *core, int argc, const char **argv) {
int cur;
char *d;
if (argc < 2) {
cur = RZ_MAX(core->print->cur, 0);
core_analysis_bytes_desc(core, core->block + cur, core->blocksize, 1);
} else {
d = rz_asm_describe(core->rasm, argv[1]);
if (RZ_STR_ISEMPTY(d)) {
RZ_LOG_ERROR("Unknown mnemonic\n");
free(d);
return RZ_CMD_STATUS_ERROR;
}
rz_cons_println(d);
free(d);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_display_opcode_handler(RzCore *core, int argc, const char **argv) {
sdb_foreach(core->rasm->pair, listOpDescriptions, core);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_cycles_handler(RzCore *core, int argc, const char **argv) {
RzList *hooks;
RzListIter *iter;
RzAnalysisCycleHook *hook;
char *instr_tmp = NULL;
st32 ccl = 0;
RzConfigHold *hc = rz_config_hold_new(core->config);
rz_config_hold_i(hc, "asm.cmt.right", "asm.functions", "asm.lines", "asm.xrefs", NULL);
if (argc > 1) {
ccl = (st32)rz_num_get(core->num, argv[1]);
if (ccl < 0) {
RZ_LOG_ERROR("Invalid negative arguments.\n");
return RZ_CMD_STATUS_ERROR;
}
}
rz_config_set_i(core->config, "asm.cmt.right", true);
rz_config_set_i(core->config, "asm.functions", false);
rz_config_set_i(core->config, "asm.lines", false);
rz_config_set_i(core->config, "asm.xrefs", false);
hooks = rz_core_analysis_cycles(core, ccl); // analysisyse
rz_cons_clear_line(1);
rz_list_foreach (hooks, iter, hook) {
instr_tmp = rz_core_disassemble_instr(core, hook->addr, 1);
rz_cons_printf("After %4i cycles:\t%s", (ccl - hook->cycles), instr_tmp);
rz_cons_flush();
free(instr_tmp);
}
rz_list_free(hooks);
rz_config_hold_restore(hc);
rz_config_hold_free(hc);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_convert_mne_handler(RzCore *core, int argc, const char **argv) {
st32 id;
if (rz_str_isnumber(argv[1])) {
id = (st32)rz_num_math(core->num, argv[1]);
// id starts from 1
if (id <= 0) {
RZ_LOG_ERROR("Invalid negative or zero arguments.\n");
return RZ_CMD_STATUS_ERROR;
}
char *ops = rz_asm_mnemonics(core->rasm, id, false);
if (!ops) {
RZ_LOG_ERROR("Can not find mnemonic by id.\n");
return RZ_CMD_STATUS_ERROR;
}
rz_cons_println(ops);
free(ops);
} else {
id = rz_asm_mnemonics_byname(core->rasm, argv[1]);
if (id <= 0) {
RZ_LOG_ERROR("Can not find id by mnemonic.\n");
return RZ_CMD_STATUS_ERROR;
}
rz_cons_printf("%d\n", id);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_list_mne_handler(RzCore *core, int argc, const char **argv) {
char *nl, *ptr, *ops = rz_asm_mnemonics(core->rasm, -1, false);
if (!ops) {
return RZ_CMD_STATUS_ERROR;
}
ptr = ops;
nl = strchr(ptr, '\n');
while (nl) {
*nl = 0;
char *desc = rz_asm_describe(core->rasm, ptr);
if (desc) {
const char *pad = rz_str_pad(' ', 16 - strlen(ptr));
rz_cons_printf("%s%s%s\n", ptr, pad, desc);
free(desc);
} else {
rz_cons_printf("%s\n", ptr);
}
ptr = nl + 1;
nl = strchr(ptr, '\n');
}
free(ops);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_list_plugins_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
return rz_core_asm_plugins_print(core, NULL, state);
}
RZ_IPI RzCmdStatus rz_analyse_name_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
if (argc > 1) {
bool ret = rz_core_analysis_rename(core, argv[1], core->offset);
if (!ret) {
// name exists when error happens
RZ_LOG_ERROR("Error happens while handling name: %s\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
return RZ_CMD_STATUS_OK;
}
return bool2status(core_analysis_name_print(core, state));
}
RZ_IPI RzCmdStatus rz_analysis_all_esil_handler(RzCore *core, int argc, const char **argv) {
bool reg_flags_defined = rz_flag_space_count(core->flags, RZ_FLAGS_FS_REGISTERS) != 0;
if (argc > 1) {
rz_core_analysis_esil(core, core->offset, rz_num_get(core->num, argv[1]), NULL);
} else {
rz_core_analysis_esil_default(core);
}
if (!reg_flags_defined) {
// hack to not leak flags if not wanted
rz_flag_unset_all_in_space(core->flags, RZ_FLAGS_FS_REGISTERS);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_all_esil_functions_handler(RzCore *core, int argc, const char **argv) {
bool reg_flags_defined = rz_flag_space_count(core->flags, RZ_FLAGS_FS_REGISTERS) != 0;
rz_core_analysis_esil_references_all_functions(core);
if (!reg_flags_defined) {
// hack to not leak flags if not wanted
rz_flag_unset_all_in_space(core->flags, RZ_FLAGS_FS_REGISTERS);
}
return RZ_CMD_STATUS_OK;
}
static RzList *get_xrefs(RzAnalysisBlock *block) {
RzListIter *iter;
RzAnalysisXRef *xref;
RzList *list = NULL;
size_t i;
for (i = 0; i < block->ninstr; i++) {
ut64 ia = block->addr + block->op_pos[i];
RzList *xrefs = rz_analysis_xrefs_get_to(block->analysis, ia);
rz_list_foreach (xrefs, iter, xref) {
if (!list) {
list = rz_list_newf(free);
}
rz_list_push(list, ut64_new(xref->from));
}
}
return list;
}
static RzList *get_calls(RzAnalysisBlock *block) {
ut8 *data = malloc(block->size);
if (!data) {
return NULL;
}
RzList *list = NULL;
RzAnalysisOp op;
block->analysis->iob.read_at(block->analysis->iob.io, block->addr, data, block->size);
for (size_t i = 0; i < block->size; i++) {
int ret = rz_analysis_op(block->analysis, &op, block->addr + i, data + i, block->size - i, RZ_ANALYSIS_OP_MASK_HINT);
if (ret < 1) {
continue;
}
if (op.type == RZ_ANALYSIS_OP_TYPE_CALL) {
if (!list) {
list = rz_list_newf(free);
}
rz_list_push(list, ut64_new(op.jump));
}
rz_analysis_op_fini(&op);
if (op.size > 0) {
i += op.size - 1;
}
}
return list;
}
RZ_IPI RzCmdStatus rz_analysis_basic_block_info_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzAnalysisBlock *bb = rz_analysis_find_most_relevant_block_in(core->analysis, core->offset);
if (!bb) {
RZ_LOG_ERROR("No basic block at 0x%" PFMT64x "\n", core->offset);
return RZ_CMD_STATUS_ERROR;
}
rz_core_analysis_bb_info_print(core, bb, core->offset, state);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_basic_block_list_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
PJ *pj = state->mode == RZ_OUTPUT_MODE_JSON ? state->d.pj : NULL;
RzTable *table = state->mode == RZ_OUTPUT_MODE_TABLE ? state->d.t : NULL;
rz_cmd_state_output_array_start(state);
rz_cmd_state_output_set_columnsf(state, "xnddsssss", "addr", "size",
"traced", "ninstr", "jump", "fail", "fcns", "calls", "xrefs");
RzCmdStatus status = RZ_CMD_STATUS_OK;
RBIter iter;
RzAnalysisBlock *block;
rz_rbtree_foreach (core->analysis->bb_tree, iter, block, RzAnalysisBlock, _rb) {
RzList *xrefs = get_xrefs(block);
RzList *calls = get_calls(block);
switch (state->mode) {
case RZ_OUTPUT_MODE_JSON:
pj_o(pj);
pj_kn(pj, "addr", block->addr);
pj_kb(pj, "traced", block->traced);
pj_kn(pj, "ninstr", block->ninstr);
pj_kn(pj, "size", block->size);
if (block->jump != UT64_MAX) {
pj_kn(pj, "jump", block->jump);
}
if (block->fail != UT64_MAX) {
pj_kn(pj, "fail", block->fail);
}
if (xrefs) {
pj_ka(pj, "xrefs");
RzListIter *iter2;
ut64 *addr;
rz_list_foreach (xrefs, iter2, addr) {
pj_n(pj, *addr);
}
pj_end(pj);
}
if (calls) {
pj_ka(pj, "calls");
RzListIter *iter2;
ut64 *addr;
rz_list_foreach (calls, iter2, addr) {
pj_n(pj, *addr);
}
pj_end(pj);
}
pj_ka(pj, "fcns");
RzListIter *iter2;
RzAnalysisFunction *fcn;
rz_list_foreach (block->fcns, iter2, fcn) {
pj_n(pj, fcn->addr);
}
pj_end(pj);
pj_end(pj);
break;
case RZ_OUTPUT_MODE_TABLE: {
char *jump = block->jump != UT64_MAX ? rz_str_newf("0x%08" PFMT64x, block->jump) : strdup("");
char *fail = block->fail != UT64_MAX ? rz_str_newf("0x%08" PFMT64x, block->fail) : strdup("");
char *call = ut64join(calls);
char *xref = ut64join(calls);
char *fcns = fcnjoin(block->fcns);
rz_table_add_rowf(table, "xnddsssss",
block->addr,
block->size,
block->traced,
block->ninstr,
jump,
fail,
fcns,
call,
xref);
free(jump);
free(fail);
free(call);
free(xref);
free(fcns);
} break;
case RZ_OUTPUT_MODE_QUIET:
rz_cons_printf("0x%08" PFMT64x "\n", block->addr);
break;
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("0x%08" PFMT64x, block->addr);
if (block->jump != UT64_MAX) {
rz_cons_printf(" .j 0x%08" PFMT64x, block->jump);
}
if (block->fail != UT64_MAX) {
rz_cons_printf(" .f 0x%08" PFMT64x, block->fail);
}
if (xrefs) {
RzListIter *iter2;
rz_cons_printf(" .x");
ut64 *addr;
rz_list_foreach (xrefs, iter2, addr) {
rz_cons_printf(" 0x%08" PFMT64x, *addr);
}
}
if (calls) {
rz_cons_printf(" .c");
RzListIter *iter2;
ut64 *addr;
rz_list_foreach (calls, iter2, addr) {
rz_cons_printf(" 0x%08" PFMT64x, *addr);
}
}
if (block->fcns) {
RzListIter *iter2;
RzAnalysisFunction *fcn;
rz_list_foreach (block->fcns, iter2, fcn) {
rz_cons_printf(" .u 0x%" PFMT64x, fcn->addr);
}
}
rz_cons_printf(" .s %" PFMT64d "\n", block->size);
break;
default:
rz_warn_if_reached();
status = RZ_CMD_STATUS_WRONG_ARGS;
iter.len = 0;
}
rz_list_free(xrefs);
rz_list_free(calls);
}
rz_cmd_state_output_array_end(state);
return status;
}
RZ_IPI RzCmdStatus rz_analysis_basic_block_find_paths_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
ut64 addr = rz_num_math(core->num, argv[1]);
RzAnalysisBlock *block = rz_analysis_get_block_at(core->analysis, core->offset);
if (!block) {
RZ_LOG_ERROR("No basic block at 0x%" PFMT64x "\n", core->offset);
return RZ_CMD_STATUS_ERROR;
}
RzList *path = rz_analysis_block_shortest_path(block, addr);
if (!path) {
return RZ_CMD_STATUS_ERROR;
}
RzCmdStatus status = RZ_CMD_STATUS_OK;
rz_cmd_state_output_array_start(state);
RzListIter *it;
rz_list_foreach (path, it, block) {
switch (state->mode) {
case RZ_OUTPUT_MODE_JSON:
pj_n(state->d.pj, block->addr);
break;
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("0x%08" PFMT64x "\n", block->addr);
break;
default:
rz_warn_if_reached();
status = RZ_CMD_STATUS_WRONG_ARGS;
it = NULL;
}
}
rz_cmd_state_output_array_end(state);
rz_list_free(path);
return status;
}
typedef enum {
CORE_ANALYSIS_SIMPLE, ///< aa
CORE_ANALYSIS_DEEP, ///< aaa
CORE_ANALYSIS_EXPERIMENTAL, ///< aaaa
} CoreAnalysisType;
static void core_perform_auto_analysis(RzCore *core, CoreAnalysisType type) {
ut64 timeout = rz_config_get_i(core->config, "analysis.timeout");
char *debugger = NULL;
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);
rz_cons_break_timeout(timeout);
rz_core_analysis_all(core);
rz_core_notify_done(core, "%s", notify);
rz_core_task_yield(&core->tasks);
if (type == 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
debugger = core->dbg->cur ? strdup(core->dbg->cur->name) : strdup("esil");
if (core->io && core->io->desc && core->io->desc->plugin && !core->io->desc->plugin->isdbg) {
// set it only if is debugging
RZ_FREE(debugger);
}
rz_cons_clear_line(1);
rz_core_analysis_everything(core, type == 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);
}
RZ_IPI RzCmdStatus rz_analyze_simple_handler(RzCore *core, int argc, const char **argv) {
core_perform_auto_analysis(core, CORE_ANALYSIS_SIMPLE);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_everything_handler(RzCore *core, int argc, const char **argv) {
core_perform_auto_analysis(core, CORE_ANALYSIS_DEEP);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_everything_experimental_handler(RzCore *core, int argc, const char **argv) {
core_perform_auto_analysis(core, CORE_ANALYSIS_EXPERIMENTAL);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_all_function_calls_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_calls(core, false);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_all_function_calls_to_imports_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_calls(core, true);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_all_data_references_to_code_handler(RzCore *core, int argc, const char **argv) {
RzListIter *iter;
RzAnalysisXRef *xref;
RzList *list = rz_analysis_xrefs_get_from(core->analysis, UT64_MAX);
rz_list_foreach (list, iter, xref) {
if (xref->type == RZ_ANALYSIS_XREF_TYPE_DATA && rz_io_is_valid_offset(core->io, xref->to, false)) {
rz_core_analysis_fcn(core, xref->from, xref->to, RZ_ANALYSIS_XREF_TYPE_NULL, 1);
}
}
rz_list_free(list);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_all_functions_handler(RzCore *core, int argc, const char **argv) {
const bool old_hasnext = rz_config_get_b(core->config, "analysis.hasnext");
rz_config_set_b(core->config, "analysis.hasnext", true);
rz_core_cmd0(core, "afr @@c:isq"); // TODO: replace with C apis.
rz_config_set_b(core->config, "analysis.hasnext", old_hasnext);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_all_functions_esil_handler(RzCore *core, int argc, const char **argv) {
rz_core_cmd0(core, "aef @@F"); // TODO: replace with C apis.
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_all_consecutive_functions_in_section_handler(RzCore *core, int argc, const char **argv) {
ut64 old_offset = core->offset;
RzListIter *iter;
RzIOMap *map;
RzList *list = rz_core_get_boundaries_prot(core, RZ_PERM_X, NULL, "analysis");
if (!list) {
RZ_LOG_ERROR("Cannot find maps with exec permisions.\n");
return RZ_CMD_STATUS_ERROR;
}
const bool hasnext = rz_config_get_b(core->config, "analysis.hasnext");
rz_list_foreach (list, iter, map) {
rz_core_seek(core, map->itv.addr, true);
rz_config_set_b(core->config, "analysis.hasnext", true);
rz_core_analysis_function_add(core, NULL, core->offset, true);
rz_config_set_b(core->config, "analysis.hasnext", hasnext);
}
rz_list_free(list);
rz_core_seek(core, old_offset, true);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_recursively_all_function_types_handler(RzCore *core, int argc, const char **argv) {
return bool2status(rz_core_analysis_types_propagation(core));
}
RZ_IPI RzCmdStatus rz_apply_signatures_from_sigdb_handler(RzCore *core, int argc, const char **argv) {
const char *filter = argc == 2 ? argv[1] : NULL;
return bool2status(rz_core_analysis_sigdb_apply(core, NULL, filter));
}
RZ_IPI RzCmdStatus rz_list_signatures_in_sigdb_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
rz_core_analysis_sigdb_print(core, state->d.t);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_print_analysis_details_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
st64 fcns = rz_list_length(core->analysis->fcns);
st64 strs = rz_flag_count(core->flags, "str.*");
st64 syms = rz_flag_count(core->flags, "sym.*");
st64 imps = rz_flag_count(core->flags, "sym.imp.*");
st64 sigs = rz_flag_count(core->flags, "flirt.*");
st64 code = rz_core_analysis_code_count(core);
st64 covr = rz_core_analysis_coverage_count(core);
st64 call = rz_core_analysis_calls_count(core);
st64 xrfs = rz_analysis_xrefs_count(core->analysis);
double precentage = (code > 0) ? (covr * 100.0 / code) : 0;
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("functions: %" PFMT64d "\n", fcns);
rz_cons_printf("xrefs: %" PFMT64d "\n", xrfs);
rz_cons_printf("calls: %" PFMT64d "\n", call);
rz_cons_printf("strings: %" PFMT64d "\n", strs);
rz_cons_printf("symbols: %" PFMT64d "\n", syms);
rz_cons_printf("imports: %" PFMT64d "\n", imps);
rz_cons_printf("signatures: %" PFMT64d "\n", sigs);
rz_cons_printf("coverage: %" PFMT64d "\n", covr);
rz_cons_printf("code size: %" PFMT64d "\n", code);
rz_cons_printf("percentuage: %.2f%% (coverage on code size)\n", precentage);
break;
case RZ_OUTPUT_MODE_JSON:
pj_o(state->d.pj);
pj_ki(state->d.pj, "fcns", fcns);
pj_ki(state->d.pj, "xrefs", xrfs);
pj_ki(state->d.pj, "calls", call);
pj_ki(state->d.pj, "strings", strs);
pj_ki(state->d.pj, "symbols", syms);
pj_ki(state->d.pj, "imports", imps);
pj_ki(state->d.pj, "signatures", sigs);
pj_ki(state->d.pj, "covrage", covr);
pj_ki(state->d.pj, "codesz", code);
pj_ki(state->d.pj, "percent", precentage);
pj_end(state->d.pj);
break;
default:
rz_warn_if_reached();
return RZ_CMD_STATUS_ERROR;
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_all_unresolved_jumps_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_resolve_jumps(core);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_recover_all_golang_functions_strings_handler(RzCore *core, int argc, const char **argv) {
if (!rz_core_analysis_recover_golang_functions(core)) {
RZ_LOG_ERROR("cannot recover golang functions.\n");
return RZ_CMD_STATUS_ERROR;
}
rz_core_analysis_resolve_golang_strings(core);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_all_objc_references_handler(RzCore *core, int argc, const char **argv) {
return bool2status(cmd_analysis_objc(core, false));
}
RZ_IPI RzCmdStatus rz_autoname_all_functions_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_autoname_all_fcns(core);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_autoname_all_functions_noreturn_handler(RzCore *core, int argc, const char **argv) {
rz_core_analysis_propagate_noreturn(core, UT64_MAX);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_all_preludes_handler(RzCore *core, int argc, const char **argv) {
rz_core_search_preludes(core, true);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_xrefs_section_bytes_handler(RzCore *core, int argc, const char **argv) {
size_t n_bytes = argc == 2 ? rz_num_math(core->num, argv[1]) : 0;
return bool2status(rz_core_analysis_refs(core, n_bytes));
}
static bool analyze_function_at_flag(RzFlagItem *fi, RzCore *core) {
bool analyze_recursively = rz_config_get_b(core->config, "analysis.calls");
rz_core_analysis_function_add(core, NULL, fi->offset, analyze_recursively);
return true;
}
RZ_IPI RzCmdStatus rz_analyze_symbols_entries_handler(RzCore *core, int argc, const char **argv) {
bool analyze_recursively = rz_config_get_b(core->config, "analysis.calls");
RzBinObject *obj = rz_bin_cur_object(core->bin);
if (!obj) {
RZ_LOG_ERROR("Cannot get current bin object\n");
return RZ_CMD_STATUS_ERROR;
}
const RzList *symbols = rz_bin_object_get_symbols(obj);
RzListIter *it;
RzBinSymbol *symbol;
rz_list_foreach (symbols, it, symbol) {
rz_core_analysis_function_add(core, NULL, symbol->vaddr, analyze_recursively);
}
rz_flag_foreach_glob(core->flags, "entry", (RzFlagItemCb)analyze_function_at_flag, core);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_symbols_entries_flags_handler(RzCore *core, int argc, const char **argv) {
rz_flag_foreach_glob(core->flags, "sym.", (RzFlagItemCb)analyze_function_at_flag, core);
rz_flag_foreach_glob(core->flags, "entry", (RzFlagItemCb)analyze_function_at_flag, core);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_function_linked_offsets_handler(RzCore *core, int argc, const char **argv) {
ut64 func_offset = argc == 2 ? rz_num_math(core->num, argv[1]) : UT64_MAX;
RzAnalysisFunction *fcn;
if (func_offset != UT64_MAX) {
fcn = rz_analysis_get_function_at(core->analysis, func_offset);
if (!fcn) {
RZ_LOG_ERROR("Cannot find function '%s'\n", argv[1]);
return RZ_CMD_STATUS_ERROR;
}
rz_core_link_stroff(core, fcn);
return RZ_CMD_STATUS_OK;
} else if (rz_list_empty(core->analysis->fcns)) {
RZ_LOG_ERROR("Couldn't find any functions\n");
return RZ_CMD_STATUS_ERROR;
}
RzListIter *it;
rz_list_foreach (core->analysis->fcns, it, fcn) {
if (rz_cons_is_breaked()) {
break;
}
rz_core_link_stroff(core, fcn);
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_print_commands_after_traps_handler(RzCore *core, int argc, const char **argv) {
ut64 n_bytes = argc == 2 ? rz_num_math(core->num, argv[1]) : 0;
return bool2status(print_cmd_analysis_after_traps_print(core, n_bytes));
}
RZ_IPI RzCmdStatus rz_print_areas_no_functions_handler(RzCore *core, int argc, const char **argv) {
size_t min_len = argc == 2 ? rz_num_math(core->num, argv[1]) : 16;
if (min_len < 1) {
min_len = 1;
}
ut64 code_size = rz_num_get(core->num, "$SS");
ut64 base_addr = rz_num_get(core->num, "$S");
ut64 chunk_size, chunk_offset, i;
RzListIter *iter, *iter2;
RzAnalysisFunction *fcn;
RzAnalysisBlock *b;
char *bitmap;
int counter;
if (code_size < 1) {
RZ_LOG_ERROR("Invalid code size (size < 1)\n");
return RZ_CMD_STATUS_ERROR;
}
bitmap = calloc(1, code_size + 64);
if (!bitmap) {
RZ_LOG_ERROR("Cannot allocate bitmap buffer\n");
return RZ_CMD_STATUS_ERROR;
}
// for each function
rz_list_foreach (core->analysis->fcns, iter, fcn) {
// for each basic block in the function
rz_list_foreach (fcn->bbs, iter2, b) {
// if it is not withing range, continue
if ((fcn->addr < base_addr) || (fcn->addr >= base_addr + code_size))
continue;
// otherwise mark each byte in the BB in the bitmap
for (counter = 0; counter < b->size; counter++) {
bitmap[b->addr + counter - base_addr] = '=';
}
// finally, add a special marker to show the beginning of a
// function
bitmap[fcn->addr - base_addr] = 'F';
}
}
// Now we print the list of memory regions that are not assigned to a function
chunk_size = 0;
chunk_offset = 0;
for (i = 0; i < code_size; i++) {
if (bitmap[i]) {
// We only print a region is its size is bigger than 15 bytes
if (chunk_size >= min_len) {
fcn = rz_analysis_get_fcn_in(core->analysis, base_addr + chunk_offset, RZ_ANALYSIS_FCN_TYPE_FCN | RZ_ANALYSIS_FCN_TYPE_SYM);
if (fcn) {
rz_cons_printf("0x%08" PFMT64x " %6" PFMT64u " %s\n", base_addr + chunk_offset, chunk_size, fcn->name);
} else {
rz_cons_printf("0x%08" PFMT64x " %6" PFMT64u "\n", base_addr + chunk_offset, chunk_size);
}
}
chunk_size = 0;
chunk_offset = i + 1;
continue;
}
chunk_size += 1;
}
if (chunk_size >= 16) {
fcn = rz_analysis_get_fcn_in(core->analysis, base_addr + chunk_offset, RZ_ANALYSIS_FCN_TYPE_FCN | RZ_ANALYSIS_FCN_TYPE_SYM);
if (fcn) {
rz_cons_printf("0x%08" PFMT64x " %6" PFMT64u " %s\n", base_addr + chunk_offset, chunk_size, fcn->name);
} else {
rz_cons_printf("0x%08" PFMT64x " %6" PFMT64u "\n", base_addr + chunk_offset, chunk_size);
}
}
free(bitmap);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analyze_value_to_maps_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
rz_core_analysis_value_pointers(core, state->mode);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_analysis_info_show_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
cmd_address_info(core, core->offset, state);
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_global_imports_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
char *imp;
RzListIter *iter;
if (RZ_STR_ISNOTEMPTY(argv[1])) {
rz_analysis_add_import(core->analysis, argv[1]);
return RZ_CMD_STATUS_OK;
}
rz_list_foreach (core->analysis->imports, iter, imp) {
switch (state->mode) {
case RZ_OUTPUT_MODE_STANDARD:
rz_cons_printf("%s\n", imp);
break;
default:
rz_warn_if_reached();
break;
}
}
return RZ_CMD_STATUS_OK;
}
RZ_IPI RzCmdStatus rz_delete_global_imports_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
rz_analysis_purge_imports(core->analysis);
return RZ_CMD_STATUS_OK;
}