699 lines
20 KiB
C
699 lines
20 KiB
C
// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
|
|
// SPDX-FileCopyrightText: 2009-2020 nibble <nibble.ds@gmail.com>
|
|
// SPDX-FileCopyrightText: 2021 heersin <teablearcher@gmail.com>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <string.h>
|
|
|
|
#include <rz_types.h>
|
|
#include <rz_list.h>
|
|
#include <rz_flag.h>
|
|
#include <rz_core.h>
|
|
#include <rz_bin.h>
|
|
#include <ht_uu.h>
|
|
#include <rz_util/rz_graph_drawable.h>
|
|
|
|
#include "core_private.h"
|
|
|
|
static void core_esil_init(RzCore *core) {
|
|
unsigned int addrsize = rz_config_get_i(core->config, "esil.addr.size");
|
|
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");
|
|
int verbose = rz_config_get_i(core->config, "esil.verbose");
|
|
RzAnalysisEsil *esil = NULL;
|
|
if (!(esil = rz_analysis_esil_new(stacksize, iotrap, addrsize))) {
|
|
return;
|
|
}
|
|
rz_analysis_esil_setup(esil, core->analysis, romem, stats, noNULL); // setup io
|
|
core->analysis->esil = esil;
|
|
esil->verbose = verbose;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_IPI void rz_core_analysis_esil_init(RzCore *core) {
|
|
if (core->analysis->esil) {
|
|
return;
|
|
}
|
|
core_esil_init(core);
|
|
}
|
|
|
|
/**
|
|
* \brief Reinitialize ESIL
|
|
* \param core RzCore reference
|
|
*/
|
|
RZ_API void rz_core_analysis_esil_reinit(RZ_NONNULL RzCore *core) {
|
|
rz_return_if_fail(core && core->analysis);
|
|
rz_analysis_esil_free(core->analysis->esil);
|
|
core_esil_init(core);
|
|
// reinitialize
|
|
rz_reg_set_value_by_role(core->analysis->reg, RZ_REG_NAME_PC, core->offset);
|
|
}
|
|
|
|
/**
|
|
* \brief Deinitialize ESIL
|
|
* \param core RzCore reference
|
|
*/
|
|
RZ_API void rz_core_analysis_esil_deinit(RZ_NONNULL RzCore *core) {
|
|
rz_return_if_fail(core && core->analysis);
|
|
RzAnalysisEsil *esil = core->analysis->esil;
|
|
if (esil) {
|
|
sdb_reset(esil->stats);
|
|
}
|
|
rz_analysis_esil_free(esil);
|
|
core->analysis->esil = NULL;
|
|
}
|
|
|
|
static void initialize_stack(RzCore *core, ut64 addr, ut64 size) {
|
|
const char *mode = rz_config_get(core->config, "esil.fillstack");
|
|
if (mode && *mode && *mode != '0') {
|
|
const ut64 bs = 4096 * 32;
|
|
ut64 i;
|
|
for (i = 0; i < size; i += bs) {
|
|
ut64 left = RZ_MIN(bs, size - i);
|
|
// rz_core_cmdf (core, "wx 10203040 @ 0x%llx", addr);
|
|
switch (*mode) {
|
|
case 'd': { // "debrujn"
|
|
ut8 *buf = (ut8 *)rz_debruijn_pattern(left, 0, NULL);
|
|
if (buf) {
|
|
if (!rz_core_write_at(core, addr + i, buf, left)) {
|
|
RZ_LOG_ERROR("core: cannot write at %" PFMT64x "\n", addr + i);
|
|
}
|
|
free(buf);
|
|
} else {
|
|
RZ_LOG_ERROR("core: cannot generate pattern of length %" PFMT64d "\n", left);
|
|
}
|
|
} break;
|
|
case 's': // "seq"
|
|
rz_core_cmdf(core, "woe 1 0xff 1 4 @ 0x%" PFMT64x "!0x%" PFMT64x, addr + i, left);
|
|
break;
|
|
case 'r': // "random"
|
|
rz_core_cmdf(core, "woR %" PFMT64u " @ 0x%" PFMT64x "!0x%" PFMT64x, left, addr + i, left);
|
|
break;
|
|
case 'z': // "zero"
|
|
case '0':
|
|
rz_core_cmdf(core, "wow 00 @ 0x%" PFMT64x "!0x%" PFMT64x, addr + i, left);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static char *get_esil_stack_name(RzCore *core, const char *name, ut64 *addr, ut32 *size) {
|
|
ut64 sx_addr = rz_config_get_i(core->config, "esil.stack.addr");
|
|
ut32 sx_size = rz_config_get_i(core->config, "esil.stack.size");
|
|
RzIOMap *map = rz_io_map_get(core->io, sx_addr);
|
|
if (map) {
|
|
sx_addr = UT64_MAX;
|
|
}
|
|
if (sx_addr == UT64_MAX) {
|
|
const ut64 align = 0x10000000;
|
|
sx_addr = rz_io_map_next_available(core->io, core->offset, sx_size, align);
|
|
}
|
|
if (*addr != UT64_MAX) {
|
|
sx_addr = *addr;
|
|
}
|
|
if (*size != UT32_MAX) {
|
|
sx_size = *size;
|
|
}
|
|
if (sx_size < 1) {
|
|
sx_size = 0xf0000;
|
|
}
|
|
*addr = sx_addr;
|
|
*size = sx_size;
|
|
if (RZ_STR_ISEMPTY(name)) {
|
|
return rz_str_newf("mem.0x%" PFMT64x "_0x%x", sx_addr, sx_size);
|
|
} else {
|
|
return rz_str_newf("mem.%s", name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize ESIL memory stack region.
|
|
*
|
|
* \param core RzCore reference
|
|
* \param name Optional name of the memory stack region. If NULL, a name is
|
|
* computed automatically based on \p addr and \p size
|
|
* \param addr Base address of the stack region, if UT64_MAX it is automatically computed
|
|
* \param size Size of the stack region, if UT32_MAX it is automatically computed
|
|
*/
|
|
RZ_API void rz_core_analysis_esil_init_mem(RZ_NONNULL RzCore *core, RZ_NULLABLE const char *name, ut64 addr, ut32 size) {
|
|
rz_return_if_fail(core && core->analysis);
|
|
ut64 current_offset = core->offset;
|
|
rz_core_analysis_esil_init(core);
|
|
RzAnalysisEsil *esil = core->analysis->esil;
|
|
if (!esil) {
|
|
RZ_LOG_ERROR("core: cannot initialize ESIL\n");
|
|
return;
|
|
}
|
|
RzIOMap *stack_map;
|
|
if (!name && addr == UT64_MAX && size == UT32_MAX) {
|
|
const char *fi = sdb_const_get(core->sdb, "aeim.fd", 0);
|
|
if (fi) {
|
|
// Close the fd associated with the aeim stack
|
|
ut64 fd = sdb_atoi(fi);
|
|
(void)rz_io_fd_close(core->io, fd);
|
|
}
|
|
}
|
|
const char *pattern = rz_config_get(core->config, "esil.stack.pattern");
|
|
char *stack_name = get_esil_stack_name(core, name, &addr, &size);
|
|
|
|
char uri[32];
|
|
rz_strf(uri, "malloc://%u", size);
|
|
esil->stack_fd = rz_io_fd_open(core->io, uri, RZ_PERM_RW, 0);
|
|
if (!(stack_map = rz_io_map_add(core->io, esil->stack_fd, RZ_PERM_RW, 0LL, addr, size))) {
|
|
rz_io_fd_close(core->io, esil->stack_fd);
|
|
RZ_LOG_ERROR("core: cannot create map for the stack, fd %d got closed again\n", esil->stack_fd);
|
|
free(stack_name);
|
|
esil->stack_fd = 0;
|
|
return;
|
|
}
|
|
rz_io_map_set_name(stack_map, stack_name);
|
|
free(stack_name);
|
|
char val[128], *v;
|
|
v = sdb_itoa(esil->stack_fd, val, 10);
|
|
sdb_set(core->sdb, "aeim.fd", v, 0);
|
|
|
|
rz_config_set_b(core->config, "io.va", true);
|
|
if (pattern && *pattern) {
|
|
switch (*pattern) {
|
|
case '0':
|
|
// do nothing
|
|
break;
|
|
case 'd':
|
|
rz_core_cmdf(core, "wopD %d @ 0x%" PFMT64x, size, addr);
|
|
break;
|
|
case 'i':
|
|
rz_core_cmdf(core, "woe 0 255 1 @ 0x%" PFMT64x "!%d", addr, size);
|
|
break;
|
|
case 'w':
|
|
rz_core_cmdf(core, "woe 0 0xffff 1 4 @ 0x%" PFMT64x "!%d", addr, size);
|
|
break;
|
|
}
|
|
}
|
|
rz_reg_set_value_by_role(core->analysis->reg, RZ_REG_NAME_SP, addr + (size / 2)); // size / 2 to have free space in both directions
|
|
rz_reg_set_value_by_role(core->analysis->reg, RZ_REG_NAME_BP, addr + (size / 2));
|
|
rz_reg_set_value_by_role(core->analysis->reg, RZ_REG_NAME_PC, current_offset);
|
|
rz_core_reg_update_flags(core);
|
|
esil->stack_addr = addr;
|
|
esil->stack_size = size;
|
|
initialize_stack(core, addr, size);
|
|
rz_core_seek(core, current_offset, false);
|
|
}
|
|
|
|
RZ_IPI void rz_core_analysis_esil_init_mem_p(RzCore *core) {
|
|
rz_core_analysis_esil_init(core);
|
|
RzAnalysisEsil *esil = core->analysis->esil;
|
|
ut64 addr = 0x100000;
|
|
ut32 size = 0xf0000;
|
|
RzFlagItem *fi = rz_flag_get(core->flags, "aeim.stack");
|
|
if (fi) {
|
|
addr = fi->offset;
|
|
size = fi->size;
|
|
} else {
|
|
rz_core_analysis_esil_init_mem(core, NULL, UT64_MAX, UT32_MAX);
|
|
}
|
|
if (esil) {
|
|
esil->stack_addr = addr;
|
|
esil->stack_size = size;
|
|
}
|
|
initialize_stack(core, addr, size);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* \brief Remove ESIL VM stack
|
|
* \param core RzCore reference
|
|
* \param name Optional name of the memory stack region. If NULL, a name is computed automatically based on \p addr
|
|
* and \p size
|
|
* \param addr Base address of the stack region, if UT64_MAX it is automatically computed
|
|
* \param size Size of the stack region, if UT32_MAX it is automatically computed
|
|
*/
|
|
RZ_API void rz_core_analysis_esil_init_mem_del(RZ_NONNULL RzCore *core, RZ_NULLABLE const char *name, ut64 addr, ut32 size) {
|
|
rz_return_if_fail(core && core->analysis);
|
|
rz_core_analysis_esil_init(core);
|
|
RzAnalysisEsil *esil = core->analysis->esil;
|
|
char *stack_name = get_esil_stack_name(core, name, &addr, &size);
|
|
if (esil && esil->stack_fd > 2) { // 0, 1, 2 are reserved for stdio/stderr
|
|
rz_io_fd_close(core->io, esil->stack_fd);
|
|
// no need to kill the maps, rz_io_map_cleanup does that for us in the close
|
|
esil->stack_fd = 0;
|
|
} else {
|
|
RZ_LOG_ERROR("core: cannot deinitialize %s\n", stack_name);
|
|
}
|
|
rz_flag_unset_name(core->flags, stack_name);
|
|
rz_flag_unset_name(core->flags, "aeim.stack");
|
|
sdb_unset(core->sdb, "aeim.fd", 0);
|
|
free(stack_name);
|
|
}
|
|
|
|
/**
|
|
* Initialize ESIL registers.
|
|
*
|
|
* \param core RzCore reference
|
|
*/
|
|
RZ_API void rz_core_analysis_esil_init_regs(RZ_NONNULL RzCore *core) {
|
|
rz_return_if_fail(core);
|
|
rz_core_analysis_set_reg(core, "PC", core->offset);
|
|
}
|
|
|
|
RZ_API void rz_core_analysis_esil_step_over(RZ_NONNULL RzCore *core) {
|
|
RzAnalysisOp *op = rz_core_analysis_op(core, rz_reg_getv(core->analysis->reg, rz_reg_get_name(core->analysis->reg, RZ_REG_NAME_PC)), RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_HINT);
|
|
ut64 until_addr = UT64_MAX;
|
|
if (op && op->type == RZ_ANALYSIS_OP_TYPE_CALL) {
|
|
until_addr = op->addr + op->size;
|
|
}
|
|
rz_core_esil_step(core, until_addr, NULL, NULL, false);
|
|
rz_analysis_op_free(op);
|
|
rz_core_reg_update_flags(core);
|
|
}
|
|
|
|
RZ_IPI void rz_core_analysis_esil_step_over_until(RzCore *core, ut64 addr) {
|
|
rz_core_esil_step(core, addr, NULL, NULL, true);
|
|
rz_core_reg_update_flags(core);
|
|
}
|
|
|
|
RZ_IPI void rz_core_analysis_esil_step_over_untilexpr(RzCore *core, const char *expr) {
|
|
rz_core_esil_step(core, UT64_MAX, expr, NULL, true);
|
|
rz_core_reg_update_flags(core);
|
|
}
|
|
|
|
RZ_IPI void rz_core_analysis_esil_references_all_functions(RzCore *core) {
|
|
RzListIter *it;
|
|
RzAnalysisFunction *fcn;
|
|
rz_list_foreach (core->analysis->fcns, it, fcn) {
|
|
ut64 from = rz_analysis_function_min_addr(fcn);
|
|
ut64 to = rz_analysis_function_max_addr(fcn);
|
|
rz_core_analysis_esil(core, from, to - from, fcn);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Emulate \p n_instr instructions from \p addr. If \p until_addr is
|
|
* specified and that address is met before all the instructions are emulated,
|
|
* stop there.
|
|
*/
|
|
RZ_IPI void rz_core_analysis_esil_emulate(RzCore *core, ut64 addr, ut64 until_addr, int off) {
|
|
RzAnalysisEsil *esil = core->analysis->esil;
|
|
int i = 0, j = 0;
|
|
ut8 *buf = NULL;
|
|
RzAnalysisOp aop = { 0 };
|
|
int ret, bsize = RZ_MAX(4096, core->blocksize);
|
|
const int mininstrsz = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
|
|
const int minopcode = RZ_MAX(1, mininstrsz);
|
|
const char *pc = rz_reg_get_name(core->analysis->reg, RZ_REG_NAME_PC);
|
|
int stacksize = rz_config_get_i(core->config, "esil.stack.depth");
|
|
int iotrap = rz_config_get_i(core->config, "esil.iotrap");
|
|
ut64 addrsize = rz_config_get_i(core->config, "esil.addr.size");
|
|
|
|
if (!esil) {
|
|
RZ_LOG_WARN("core: cmd_espc: creating new esil instance\n");
|
|
if (!(esil = rz_analysis_esil_new(stacksize, iotrap, addrsize))) {
|
|
return;
|
|
}
|
|
core->analysis->esil = esil;
|
|
}
|
|
buf = malloc(bsize);
|
|
if (!buf) {
|
|
RZ_LOG_ERROR("core: cannot allocate %d byte(s)\n", bsize);
|
|
return;
|
|
}
|
|
if (addr == -1) {
|
|
addr = rz_reg_getv(core->analysis->reg, pc);
|
|
}
|
|
(void)rz_analysis_esil_setup(core->analysis->esil, core->analysis, 0, 0, 0); // int romem, int stats, int nonull) {
|
|
ut64 cursp = rz_reg_getv(core->analysis->reg, "SP");
|
|
ut64 oldoff = core->offset;
|
|
const ut64 flags = RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_HINT | RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_DISASM;
|
|
for (i = 0, j = 0; j < off; i++, j++) {
|
|
if (rz_cons_is_breaked()) {
|
|
break;
|
|
}
|
|
if (i >= (bsize - 32)) {
|
|
i = 0;
|
|
}
|
|
if (!i) {
|
|
rz_io_read_at(core->io, addr, buf, bsize);
|
|
}
|
|
if (addr == until_addr) {
|
|
break;
|
|
}
|
|
ret = rz_analysis_op(core->analysis, &aop, addr, buf + i, bsize - i, flags);
|
|
if (ret < 1) {
|
|
RZ_LOG_ERROR("core: failed esil analysis at 0x%08" PFMT64x "\n", addr);
|
|
break;
|
|
}
|
|
// skip calls and such
|
|
if (aop.type == RZ_ANALYSIS_OP_TYPE_CALL) {
|
|
// nothing
|
|
} else {
|
|
rz_reg_setv(core->analysis->reg, "PC", aop.addr + aop.size);
|
|
const char *e = RZ_STRBUF_SAFEGET(&aop.esil);
|
|
if (e && *e) {
|
|
// eprintf (" 0x%08llx %d %s\n", aop.addr, ret, aop.mnemonic);
|
|
(void)rz_analysis_esil_parse(esil, e);
|
|
}
|
|
}
|
|
int inc = (core->search->align > 0) ? core->search->align - 1 : ret - 1;
|
|
if (inc < 0) {
|
|
inc = minopcode;
|
|
}
|
|
i += inc;
|
|
addr += ret; // aop.size;
|
|
rz_analysis_op_fini(&aop);
|
|
}
|
|
rz_core_seek(core, oldoff, true);
|
|
rz_reg_setv(core->analysis->reg, "SP", cursp);
|
|
free(buf);
|
|
}
|
|
|
|
RZ_IPI void rz_core_analysis_esil_emulate_bb(RzCore *core) {
|
|
RzAnalysisBlock *bb = rz_analysis_find_most_relevant_block_in(core->analysis, core->offset);
|
|
if (!bb) {
|
|
RZ_LOG_ERROR("core: cannot find basic block for 0x%08" PFMT64x "\n", core->offset);
|
|
return;
|
|
}
|
|
rz_core_analysis_esil_emulate(core, bb->addr, UT64_MAX, bb->ninstr);
|
|
}
|
|
|
|
RZ_IPI int rz_core_analysis_set_reg(RzCore *core, const char *regname, ut64 val) {
|
|
RzRegItem *r = rz_reg_get(core->analysis->reg, regname, -1);
|
|
if (!r) {
|
|
int role = rz_reg_get_name_idx(regname);
|
|
if (role != -1) {
|
|
const char *alias = rz_reg_get_name(core->analysis->reg, role);
|
|
if (alias) {
|
|
r = rz_reg_get(core->analysis->reg, alias, -1);
|
|
}
|
|
}
|
|
}
|
|
if (!r) {
|
|
RZ_LOG_ERROR("core: unknown register '%s'\n", regname);
|
|
return -1;
|
|
}
|
|
rz_reg_set_value(core->analysis->reg, r, val);
|
|
rz_core_reg_update_flags(core);
|
|
return 0;
|
|
}
|
|
|
|
RZ_IPI void rz_core_analysis_esil_default(RzCore *core) {
|
|
RzIOMap *map;
|
|
RzListIter *iter;
|
|
RzList *list = rz_core_get_boundaries_prot(core, -1, NULL, "analysis");
|
|
if (!list) {
|
|
return;
|
|
}
|
|
if (!strcmp("range", rz_config_get(core->config, "analysis.in"))) {
|
|
ut64 from = rz_config_get_i(core->config, "analysis.from");
|
|
ut64 to = rz_config_get_i(core->config, "analysis.to");
|
|
if (to > from) {
|
|
rz_core_analysis_esil(core, from, to - from, NULL);
|
|
} else {
|
|
RZ_LOG_ERROR("core: analysis.from > analysis.to\n");
|
|
}
|
|
} else {
|
|
rz_list_foreach (list, iter, map) {
|
|
if (map->perm & RZ_PERM_X) {
|
|
rz_core_analysis_esil(core, map->itv.addr, map->itv.size, NULL);
|
|
}
|
|
}
|
|
}
|
|
rz_list_free(list);
|
|
}
|
|
|
|
RZ_IPI void rz_core_analysis_il_reinit(RzCore *core) {
|
|
rz_analysis_il_vm_setup(core->analysis);
|
|
if (core->analysis->il_vm) {
|
|
// initialize the program counter with the current offset
|
|
rz_reg_set_value_by_role(core->analysis->reg, RZ_REG_NAME_PC, core->offset);
|
|
rz_core_reg_update_flags(core);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Set a vm variable from user input
|
|
* \return whether the set succeeded
|
|
*
|
|
* Sets the given var, or "PC" to the given value.
|
|
* The type of the variable is handled dynamically.
|
|
* This is intended for setting from user input only.
|
|
*/
|
|
RZ_IPI bool rz_core_analysis_il_vm_set(RzCore *core, const char *var_name, ut64 value) {
|
|
rz_return_val_if_fail(core && core->analysis && var_name, false);
|
|
|
|
RzAnalysisILVM *vm = core->analysis->il_vm;
|
|
if (!vm) {
|
|
RZ_LOG_ERROR("RzIL: Run 'aezi' first to initialize the VM\n");
|
|
return false;
|
|
}
|
|
|
|
if (!strcmp(var_name, "PC")) {
|
|
RzBitVector *bv = rz_bv_new_from_ut64(vm->vm->pc->len, value);
|
|
rz_bv_free(vm->vm->pc);
|
|
vm->vm->pc = bv;
|
|
return true;
|
|
}
|
|
|
|
RzILVar *var = rz_il_vm_get_var(vm->vm, RZ_IL_VAR_KIND_GLOBAL, var_name);
|
|
if (!var) {
|
|
return false;
|
|
}
|
|
RzILVal *val = NULL;
|
|
switch (var->sort.type) {
|
|
case RZ_IL_TYPE_PURE_BITVECTOR:
|
|
val = rz_il_value_new_bitv(rz_bv_new_from_ut64(var->sort.props.bv.length, value));
|
|
break;
|
|
case RZ_IL_TYPE_PURE_BOOL:
|
|
val = rz_il_value_new_bool(rz_il_bool_new(value != 0));
|
|
break;
|
|
}
|
|
if (val) {
|
|
rz_il_vm_set_global_var(vm->vm, var_name, val);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
typedef struct il_print_t {
|
|
RzOutputMode mode;
|
|
const char *name;
|
|
void *ptr;
|
|
} ILPrint;
|
|
#define p_sb(x) ((RzStrBuf *)x)
|
|
#define p_tbl(x) ((RzTable *)x)
|
|
#define p_pj(x) ((PJ *)x)
|
|
|
|
static void rzil_print_register_bool(bool value, ILPrint *p) {
|
|
switch (p->mode) {
|
|
case RZ_OUTPUT_MODE_STANDARD:
|
|
rz_strbuf_appendf(p_sb(p->ptr), " %s: %s", p->name, rz_str_bool(value));
|
|
break;
|
|
case RZ_OUTPUT_MODE_TABLE:
|
|
rz_table_add_rowf(p_tbl(p->ptr), "sss", p->name, "bool", rz_str_bool(value));
|
|
break;
|
|
case RZ_OUTPUT_MODE_JSON:
|
|
pj_kb(p_pj(p->ptr), p->name, value);
|
|
break;
|
|
default:
|
|
rz_cons_printf("%s\n", rz_str_bool(value));
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void rzil_print_register_bitv(RzBitVector *number, ILPrint *p) {
|
|
char *hex = rz_bv_as_hex_string(number, true);
|
|
switch (p->mode) {
|
|
case RZ_OUTPUT_MODE_STANDARD:
|
|
rz_strbuf_appendf(p_sb(p->ptr), " %s: %s", p->name, hex);
|
|
break;
|
|
case RZ_OUTPUT_MODE_TABLE:
|
|
rz_table_add_rowf(p_tbl(p->ptr), "sss", p->name, "bitv", hex);
|
|
break;
|
|
case RZ_OUTPUT_MODE_JSON:
|
|
pj_ks(p_pj(p->ptr), p->name, hex);
|
|
break;
|
|
default:
|
|
rz_cons_printf("%s\n", hex);
|
|
break;
|
|
}
|
|
free(hex);
|
|
}
|
|
|
|
RZ_IPI void rz_core_analysis_il_vm_status(RzCore *core, const char *var_name, RzOutputMode mode) {
|
|
RzAnalysisILVM *vm = core->analysis->il_vm;
|
|
if (!vm) {
|
|
RZ_LOG_ERROR("RzIL: Run 'aezi' first to initialize the VM\n");
|
|
return;
|
|
}
|
|
|
|
ILPrint p = { 0 };
|
|
p.mode = mode;
|
|
|
|
switch (mode) {
|
|
case RZ_OUTPUT_MODE_STANDARD:
|
|
p.ptr = rz_strbuf_new("");
|
|
break;
|
|
case RZ_OUTPUT_MODE_TABLE:
|
|
p.ptr = rz_table_new();
|
|
rz_table_set_columnsf(p_tbl(p.ptr), "sss", "variable", "type", "value");
|
|
break;
|
|
case RZ_OUTPUT_MODE_JSON:
|
|
p.ptr = pj_new();
|
|
pj_o(p_pj(p.ptr));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!var_name || !strcmp(var_name, "PC")) {
|
|
p.name = "PC";
|
|
rzil_print_register_bitv(vm->vm->pc, &p);
|
|
}
|
|
|
|
RzPVector *global_vars = rz_il_vm_get_all_vars(vm->vm, RZ_IL_VAR_KIND_GLOBAL);
|
|
if (global_vars) {
|
|
void **it;
|
|
rz_pvector_foreach (global_vars, it) {
|
|
RzILVar *var = *it;
|
|
if (var_name && strcmp(var_name, var->name)) {
|
|
continue;
|
|
}
|
|
p.name = var->name;
|
|
RzILVal *val = rz_il_vm_get_var_value(vm->vm, RZ_IL_VAR_KIND_GLOBAL, var->name);
|
|
if (!val) {
|
|
continue;
|
|
}
|
|
switch (val->type) {
|
|
case RZ_IL_TYPE_PURE_BITVECTOR:
|
|
rzil_print_register_bitv(val->data.bv, &p);
|
|
break;
|
|
case RZ_IL_TYPE_PURE_BOOL:
|
|
rzil_print_register_bool(val->data.b->b, &p);
|
|
break;
|
|
default:
|
|
rz_warn_if_reached();
|
|
break;
|
|
}
|
|
if (var_name) {
|
|
break;
|
|
}
|
|
if (rz_strbuf_length(p_sb(p.ptr)) > 95) {
|
|
rz_cons_printf("%s\n", rz_strbuf_get(p_sb(p.ptr)));
|
|
rz_strbuf_fini(p_sb(p.ptr));
|
|
}
|
|
}
|
|
rz_pvector_free(global_vars);
|
|
}
|
|
|
|
char *out = NULL;
|
|
switch (mode) {
|
|
case RZ_OUTPUT_MODE_STANDARD:
|
|
if (rz_strbuf_length(p_sb(p.ptr)) > 0) {
|
|
out = rz_strbuf_drain(p_sb(p.ptr));
|
|
} else {
|
|
rz_strbuf_free(p_sb(p.ptr));
|
|
return;
|
|
}
|
|
break;
|
|
case RZ_OUTPUT_MODE_TABLE:
|
|
out = rz_table_tostring((RzTable *)p.ptr);
|
|
rz_table_free(p_tbl(p.ptr));
|
|
break;
|
|
case RZ_OUTPUT_MODE_JSON:
|
|
pj_end(p_pj(p.ptr));
|
|
out = pj_drain(p_pj(p.ptr));
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
rz_cons_printf("%s\n", out);
|
|
free(out);
|
|
}
|
|
#undef p_sb
|
|
#undef p_tbl
|
|
#undef p_pj
|
|
|
|
/**
|
|
* Perform a single step at the PC given by analysis->reg in RzIL
|
|
* \return false if an error occured (e.g. invalid op)
|
|
*/
|
|
RZ_IPI bool rz_core_il_step(RzCore *core) {
|
|
if (!core->analysis || !core->analysis->il_vm) {
|
|
RZ_LOG_ERROR("RzIL: Run 'aezi' first to initialize the VM\n");
|
|
return false;
|
|
}
|
|
RzAnalysisILStepResult r = rz_analysis_il_vm_step(core->analysis, core->analysis->il_vm, core->analysis->reg);
|
|
switch (r) {
|
|
case RZ_ANALYSIS_IL_STEP_RESULT_SUCCESS:
|
|
rz_core_reg_update_flags(core);
|
|
return true;
|
|
case RZ_ANALYSIS_IL_STEP_INVALID_OP:
|
|
RZ_LOG_ERROR("RzIL: invalid instruction or lifting not implemented at address 0x%08" PFMT64x "\n",
|
|
rz_reg_get_value_by_role(core->analysis->reg, RZ_REG_NAME_PC));
|
|
break;
|
|
default:
|
|
RZ_LOG_ERROR("RzIL: stepping failed.\n");
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Perform a single step at the PC given by analysis->reg in RzIL and print any events that happened
|
|
* \return false if an error occured (e.g. invalid op)
|
|
*/
|
|
RZ_IPI bool rz_core_analysis_il_step_with_events(RzCore *core, PJ *pj) {
|
|
if (!rz_core_il_step(core)) {
|
|
return false;
|
|
}
|
|
|
|
if (!core->analysis || !core->analysis->il_vm) {
|
|
return false;
|
|
}
|
|
|
|
RzILVM *vm = core->analysis->il_vm->vm;
|
|
|
|
RzStrBuf *sb = NULL;
|
|
RzListIter *it;
|
|
RzILEvent *evt;
|
|
|
|
bool evt_read = rz_config_get_b(core->config, "rzil.step.events.read");
|
|
bool evt_write = rz_config_get_b(core->config, "rzil.step.events.write");
|
|
|
|
if (!evt_read && !evt_write) {
|
|
RZ_LOG_ERROR("RzIL: cannot print events when all the events are disabled.");
|
|
RZ_LOG_ERROR("RzIL: please set 'rzil.step.events.read' or/and 'rzil.step.events.write' to true and try again.");
|
|
return false;
|
|
}
|
|
|
|
if (!pj) {
|
|
sb = rz_strbuf_new("");
|
|
}
|
|
rz_list_foreach (vm->events, it, evt) {
|
|
if (!evt_read && (evt->type == RZ_IL_EVENT_MEM_READ || evt->type == RZ_IL_EVENT_VAR_READ)) {
|
|
continue;
|
|
} else if (!evt_write && (evt->type != RZ_IL_EVENT_MEM_READ && evt->type != RZ_IL_EVENT_VAR_READ)) {
|
|
continue;
|
|
}
|
|
if (!pj) {
|
|
rz_il_event_stringify(evt, sb);
|
|
rz_strbuf_append(sb, "\n");
|
|
} else {
|
|
rz_il_event_json(evt, pj);
|
|
}
|
|
}
|
|
if (!pj) {
|
|
rz_cons_print(rz_strbuf_get(sb));
|
|
rz_strbuf_free(sb);
|
|
}
|
|
return true;
|
|
}
|