- Move trap/sw breakpoint into RzArch - Remove RzBreakpointPlugin & RzBreakpointArch - Add get_sw_breakpoint_at/get_sw_breakpoint_size_at binds - Remove `dbh-` - Change `dbh` to just list supported archs for sw breakpoints - Implement correct PPC traps - Add missing x86 breakpoint callback
2284 lines
61 KiB
C
2284 lines
61 KiB
C
// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <rz_config.h>
|
|
#include <rz_util/ht_sp.h>
|
|
#include <rz_util/rz_regex.h>
|
|
#include <rz_vector.h>
|
|
#include <rz_core.h>
|
|
#include <rz_socket.h>
|
|
#include <rz_cmp.h>
|
|
#include <rz_util.h>
|
|
#if __UNIX__
|
|
#include <signal.h>
|
|
#endif
|
|
#include "core_private.h"
|
|
|
|
#define DB core->sdb
|
|
|
|
RZ_LIB_VERSION(rz_core);
|
|
|
|
static ut64 letter_divs[RZ_CORE_ASMQJMPS_LEN_LETTERS - 1] = {
|
|
RZ_CORE_ASMQJMPS_LETTERS * RZ_CORE_ASMQJMPS_LETTERS * RZ_CORE_ASMQJMPS_LETTERS * RZ_CORE_ASMQJMPS_LETTERS,
|
|
RZ_CORE_ASMQJMPS_LETTERS *RZ_CORE_ASMQJMPS_LETTERS *RZ_CORE_ASMQJMPS_LETTERS,
|
|
RZ_CORE_ASMQJMPS_LETTERS *RZ_CORE_ASMQJMPS_LETTERS,
|
|
RZ_CORE_ASMQJMPS_LETTERS
|
|
};
|
|
|
|
extern bool rz_core_is_project(RzCore *core, const char *name);
|
|
|
|
/**
|
|
* \brief Prints a message definining the beginning of a task
|
|
*
|
|
* \param core The RzCore to use
|
|
* \param format The message to notify
|
|
*/
|
|
RZ_API void rz_core_notify_begin(RZ_NONNULL RzCore *core, RZ_NONNULL const char *format, ...) {
|
|
rz_return_if_fail(core && format);
|
|
va_list args;
|
|
bool use_color = rz_config_get_i(core->config, "scr.color") > 0;
|
|
bool verbose = rz_config_get_b(core->config, "scr.prompt");
|
|
if (!verbose) {
|
|
return;
|
|
}
|
|
va_start(args, format);
|
|
if (use_color) {
|
|
fprintf(stderr, "[ ] " Color_YELLOW);
|
|
vfprintf(stderr, format, args);
|
|
fprintf(stderr, "\r[" Color_RESET);
|
|
} else {
|
|
fprintf(stderr, "[ ] ");
|
|
vfprintf(stderr, format, args);
|
|
fprintf(stderr, "\r[");
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
/**
|
|
* \brief Prints a message definining the end of a task which succeeded
|
|
*
|
|
* \param core The RzCore to use
|
|
* \param format The message to notify
|
|
*/
|
|
RZ_API void rz_core_notify_done(RZ_NONNULL RzCore *core, RZ_NONNULL const char *format, ...) {
|
|
rz_return_if_fail(core && format);
|
|
va_list args;
|
|
bool use_color = rz_config_get_i(core->config, "scr.color") > 0;
|
|
bool verbose = rz_config_get_b(core->config, "scr.prompt");
|
|
if (!verbose) {
|
|
return;
|
|
}
|
|
va_start(args, format);
|
|
if (use_color) {
|
|
fprintf(stderr, "\r" Color_GREEN "[x]" Color_RESET " ");
|
|
vfprintf(stderr, format, args);
|
|
fprintf(stderr, "\n");
|
|
} else {
|
|
fprintf(stderr, "\r[x] ");
|
|
vfprintf(stderr, format, args);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
/**
|
|
* \brief Prints a message definining the end of a task which errored
|
|
*
|
|
* \param core The RzCore to use
|
|
* \param format The message to notify
|
|
*/
|
|
RZ_API void rz_core_notify_error(RZ_NONNULL RzCore *core, RZ_NONNULL const char *format, ...) {
|
|
rz_return_if_fail(core && format);
|
|
va_list args;
|
|
bool use_color = rz_config_get_i(core->config, "scr.color") > 0;
|
|
bool verbose = rz_config_get_b(core->config, "scr.prompt");
|
|
if (!verbose) {
|
|
return;
|
|
}
|
|
va_start(args, format);
|
|
if (use_color) {
|
|
fprintf(stderr, "\r" Color_RED "[!]" Color_RESET " ");
|
|
vfprintf(stderr, format, args);
|
|
fprintf(stderr, "\n");
|
|
} else {
|
|
fprintf(stderr, "\r[!] ");
|
|
vfprintf(stderr, format, args);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
/**
|
|
* \brief Prints a message definining the beginning of a task
|
|
*
|
|
* \param core The RzCore to use
|
|
* \param text The message to notify
|
|
*/
|
|
RZ_API void rz_core_notify_begin_str(RZ_NONNULL RzCore *core, RZ_NONNULL const char *text) {
|
|
rz_core_notify_begin(core, "%s", text);
|
|
}
|
|
|
|
/**
|
|
* \brief Prints a message definining the end of a task which succeeded
|
|
*
|
|
* \param core The RzCore to use
|
|
* \param text The message to notify
|
|
*/
|
|
RZ_API void rz_core_notify_done_str(RZ_NONNULL RzCore *core, RZ_NONNULL const char *text) {
|
|
rz_core_notify_done(core, "%s", text);
|
|
}
|
|
|
|
/**
|
|
* \brief Prints a message definining the end of a task which errored
|
|
*
|
|
* \param core The RzCore to use
|
|
* \param text The message to notify
|
|
*/
|
|
RZ_API void rz_core_notify_error_str(RZ_NONNULL RzCore *core, RZ_NONNULL const char *text) {
|
|
rz_core_notify_error(core, "%s", text);
|
|
}
|
|
|
|
/**
|
|
* \brief Delays printing of a warning until after command output
|
|
*
|
|
* \param core The RzCore to use
|
|
* \param warning The message to warn about (must be heap-allocated)
|
|
*/
|
|
RZ_API void rz_core_warn_after_output(RZ_NONNULL RzCore *core, RZ_NONNULL const char *warning) {
|
|
rz_return_if_fail(core && warning);
|
|
rz_list_append(core->warnings_after, (char *)warning);
|
|
}
|
|
|
|
RZ_IPI void rz_core_print_warnings_after(RZ_NONNULL RzCore *core) {
|
|
rz_return_if_fail(core);
|
|
for (ut32 i = 0; i < rz_list_length(core->warnings_after); i++) {
|
|
char *warn_str = rz_list_pop_head(core->warnings_after);
|
|
RZ_LOG_WARN("%s", warn_str);
|
|
free(warn_str);
|
|
}
|
|
}
|
|
|
|
static int on_fcn_new(RzAnalysis *_analysis, void *_user, RzAnalysisFunction *fcn) {
|
|
RzCore *core = (RzCore *)_user;
|
|
const char *cmd = rz_config_get(core->config, "cmd.fcn.new");
|
|
if (cmd && *cmd) {
|
|
ut64 oaddr = core->offset;
|
|
ut64 addr = fcn->addr;
|
|
rz_core_seek(core, addr, true);
|
|
rz_core_cmd0(core, cmd);
|
|
rz_core_seek(core, oaddr, true);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int on_fcn_delete(RzAnalysis *_analysis, void *_user, RzAnalysisFunction *fcn) {
|
|
RzCore *core = (RzCore *)_user;
|
|
const char *cmd = rz_config_get(core->config, "cmd.fcn.delete");
|
|
if (cmd && *cmd) {
|
|
ut64 oaddr = core->offset;
|
|
ut64 addr = fcn->addr;
|
|
rz_core_seek(core, addr, true);
|
|
rz_core_cmd0(core, cmd);
|
|
rz_core_seek(core, oaddr, true);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int on_fcn_rename(RzAnalysis *_analysis, void *_user, RzAnalysisFunction *fcn, const char *oname) {
|
|
RzCore *core = (RzCore *)_user;
|
|
const char *cmd = rz_config_get(core->config, "cmd.fcn.rename");
|
|
if (cmd && *cmd) {
|
|
// XXX: wat do with old name here?
|
|
ut64 oaddr = core->offset;
|
|
ut64 addr = fcn->addr;
|
|
rz_core_seek(core, addr, true);
|
|
rz_core_cmd0(core, cmd);
|
|
rz_core_seek(core, oaddr, true);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void rz_core_debug_breakpoint_hit(RzCore *core, RzBreakpointItem *bpi) {
|
|
const char *cmdbp = rz_config_get(core->config, "cmd.bp");
|
|
const bool cmdbp_exists = (cmdbp && *cmdbp);
|
|
const bool bpcmd_exists = (bpi->data && bpi->data[0]);
|
|
const bool may_output = (cmdbp_exists || bpcmd_exists);
|
|
if (may_output) {
|
|
rz_cons_push();
|
|
}
|
|
if (cmdbp_exists) {
|
|
rz_core_cmd0(core, cmdbp);
|
|
}
|
|
if (bpcmd_exists) {
|
|
rz_core_cmd0(core, bpi->data);
|
|
}
|
|
if (may_output) {
|
|
rz_cons_set_flush(true);
|
|
rz_cons_flush();
|
|
rz_cons_pop();
|
|
}
|
|
}
|
|
|
|
static void rz_core_debug_syscall_hit(RzCore *core) {
|
|
const char *cmdhit = rz_config_get(core->config, "cmd.onsyscall");
|
|
|
|
if (cmdhit && cmdhit[0] != 0) {
|
|
rz_core_cmd0(core, cmdhit);
|
|
rz_cons_flush();
|
|
}
|
|
}
|
|
|
|
RZ_API RzBinReloc *rz_core_getreloc(RzCore *core, ut64 addr, int size) {
|
|
if (size < 1 || addr == UT64_MAX) {
|
|
return NULL;
|
|
}
|
|
RzBinFile *bf = rz_bin_cur(core->bin);
|
|
if (!bf || !bf->o || !bf->o->relocs) {
|
|
return NULL;
|
|
}
|
|
return rz_bin_reloc_storage_get_reloc_in(bf->o->relocs, addr, size);
|
|
}
|
|
|
|
RZ_API RzBinReloc *rz_core_get_reloc_to(RzCore *core, ut64 addr) {
|
|
rz_return_val_if_fail(core, NULL);
|
|
RzBinFile *bf = rz_bin_cur(core->bin);
|
|
if (!bf || !bf->o || !bf->o->relocs) {
|
|
return NULL;
|
|
}
|
|
return rz_bin_reloc_storage_get_reloc_to(bf->o->relocs, addr);
|
|
}
|
|
|
|
/* returns the address of a jmp/call given a shortcut by the user or UT64_MAX
|
|
* if there's no valid shortcut. When is_asmqjmps_letter is true, the string
|
|
* should be of the form XYZWu, where XYZW are uppercase letters and u is a
|
|
* lowercase one. If is_asmqjmps_letter is false, the string should be a number
|
|
* between 1 and 9 included. */
|
|
RZ_API ut64 rz_core_get_asmqjmps(RzCore *core, const char *str) {
|
|
if (!core->asmqjmps) {
|
|
return UT64_MAX;
|
|
}
|
|
if (core->is_asmqjmps_letter) {
|
|
int i, pos = 0;
|
|
int len = strlen(str);
|
|
for (i = 0; i < len - 1; i++) {
|
|
if (!isupper((ut8)str[i])) {
|
|
return UT64_MAX;
|
|
}
|
|
pos *= RZ_CORE_ASMQJMPS_LETTERS;
|
|
pos += str[i] - 'A' + 1;
|
|
}
|
|
if (!islower((ut8)str[i])) {
|
|
return UT64_MAX;
|
|
}
|
|
pos *= RZ_CORE_ASMQJMPS_LETTERS;
|
|
pos += str[i] - 'a';
|
|
if (pos < core->asmqjmps_count) {
|
|
return core->asmqjmps[pos + 1];
|
|
}
|
|
} else if (str[0] > '0' && str[1] <= '9') {
|
|
int pos = str[0] - '0';
|
|
if (pos <= core->asmqjmps_count) {
|
|
return core->asmqjmps[pos];
|
|
}
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
/**
|
|
* Takes addr and returns already saved shortcut or a new one
|
|
* The returned buffer needs to be freed
|
|
*/
|
|
RZ_API char *rz_core_add_asmqjmp(RzCore *core, ut64 addr) {
|
|
bool found = false;
|
|
if (!core->asmqjmps) {
|
|
return NULL;
|
|
}
|
|
if (core->is_asmqjmps_letter) {
|
|
if (core->asmqjmps_count >= RZ_CORE_ASMQJMPS_MAX_LETTERS) {
|
|
return NULL;
|
|
}
|
|
if (core->asmqjmps_count >= core->asmqjmps_size - 2) {
|
|
core->asmqjmps = realloc(core->asmqjmps, core->asmqjmps_size * 2 * sizeof(ut64));
|
|
if (!core->asmqjmps) {
|
|
return NULL;
|
|
}
|
|
core->asmqjmps_size *= 2;
|
|
}
|
|
}
|
|
if (core->asmqjmps_count < core->asmqjmps_size - 1) {
|
|
int i = 0;
|
|
char t[RZ_CORE_ASMQJMPS_LEN_LETTERS + 1] = { 0 };
|
|
for (i = 0; i < core->asmqjmps_count + 1; i++) {
|
|
if (core->asmqjmps[i] == addr) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
i = ++core->asmqjmps_count;
|
|
core->asmqjmps[i] = addr;
|
|
}
|
|
// This check makes pos never be <1, thefor not fill 't' with trash
|
|
if (i < 1) {
|
|
return NULL;
|
|
}
|
|
rz_core_set_asmqjmps(core, t, sizeof(t), i);
|
|
return rz_str_dup(t);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* returns in str a string that represents the shortcut to access the asmqjmp
|
|
* at position pos. When is_asmqjmps_letter is true, pos is converted into a
|
|
* multiletter shortcut of the form XYWZu and returned (see rz_core_get_asmqjmps
|
|
* for more info). Otherwise, the shortcut is the string representation of pos. */
|
|
RZ_API void rz_core_set_asmqjmps(RzCore *core, char *str, size_t len, int pos) {
|
|
if (core->is_asmqjmps_letter) {
|
|
int i, j = 0;
|
|
// if (pos > 0) {
|
|
pos--;
|
|
//// }
|
|
for (i = 0; i < RZ_CORE_ASMQJMPS_LEN_LETTERS - 1; i++) {
|
|
int div = pos / letter_divs[i];
|
|
pos %= letter_divs[i];
|
|
if (div > 0 && j < len) {
|
|
str[j++] = 'A' + div - 1;
|
|
}
|
|
}
|
|
if (j < len) {
|
|
int div = pos % RZ_CORE_ASMQJMPS_LETTERS;
|
|
str[j++] = 'a' + div;
|
|
}
|
|
str[j] = '\0';
|
|
} else {
|
|
snprintf(str, len, "%d", pos);
|
|
}
|
|
}
|
|
|
|
static void setab(RzCore *core, const char *arch, int bits) {
|
|
if (arch) {
|
|
rz_config_set(core->config, "asm.arch", arch);
|
|
}
|
|
if (bits > 0) {
|
|
rz_config_set_i(core->config, "asm.bits", bits);
|
|
}
|
|
}
|
|
|
|
static const char *getName(RzCore *core, ut64 addr) {
|
|
RzFlagItem *item = rz_flag_get_i(core->flags, addr);
|
|
if (item) {
|
|
if (core->flags->realnames) {
|
|
return item->realname
|
|
? item->realname
|
|
: item->name;
|
|
}
|
|
return item->name;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static char *getNameDelta(RzCore *core, ut64 addr) {
|
|
RzFlagItem *item = rz_flag_get_at(core->flags, addr, true);
|
|
if (item) {
|
|
if (item->offset != addr) {
|
|
const char *name = core->flags->realnames
|
|
? item->realname
|
|
: item->name;
|
|
return rz_str_newf("%s+%" PFMT64u, name, addr - item->offset);
|
|
}
|
|
return rz_str_dup(item->name);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void archbits(RzCore *core, ut64 addr) {
|
|
rz_core_seek_arch_bits(core, addr);
|
|
}
|
|
|
|
static ut64 cfggeti(RzCore *core, const char *k) {
|
|
return rz_config_get_i(core->config, k);
|
|
}
|
|
|
|
static const char *cfgget(RzCore *core, const char *k) {
|
|
return rz_config_get(core->config, k);
|
|
}
|
|
|
|
static bool cfgseti(RzCore *core, const char *k, ut64 v) {
|
|
return rz_config_set_i(core->config, k, v);
|
|
}
|
|
|
|
static bool cfgset(RzCore *core, const char *k, const char *v) {
|
|
return rz_config_set(core->config, k, v);
|
|
}
|
|
|
|
static ut64 numget(RzCore *core, const char *k) {
|
|
return rz_num_math(core->num, k);
|
|
}
|
|
|
|
static const RzList /*<RzFlagItem *>*/ *__flagsGet(RzCore *core, ut64 offset) {
|
|
return rz_flag_get_list(core->flags, offset);
|
|
}
|
|
|
|
RZ_API int rz_core_bind(RzCore *core, RzCoreBind *bnd) {
|
|
bnd->core = core;
|
|
bnd->bphit = (RzCoreDebugBpHit)rz_core_debug_breakpoint_hit;
|
|
bnd->syshit = (RzCoreDebugSyscallHit)rz_core_debug_syscall_hit;
|
|
bnd->cmd = (RzCoreCmd)rz_core_cmd0;
|
|
bnd->cmdf = (RzCoreCmdF)rz_core_cmdf;
|
|
bnd->cmdstr = (RzCoreCmdStr)rz_core_cmd_str;
|
|
bnd->cmdstrf = (RzCoreCmdStrF)rz_core_cmd_strf;
|
|
bnd->puts = (RzCorePuts)rz_cons_strcat;
|
|
bnd->setab = (RzCoreSetArchBits)setab;
|
|
bnd->getName = (RzCoreGetName)getName;
|
|
bnd->getNameDelta = (RzCoreGetNameDelta)getNameDelta;
|
|
bnd->archbits = (RzCoreSeekArchBits)archbits;
|
|
bnd->cfggeti = (RzCoreConfigGetI)cfggeti;
|
|
bnd->cfgGet = (RzCoreConfigGet)cfgget;
|
|
bnd->cfgSetI = (RzCoreConfigSetI)cfgseti;
|
|
bnd->cfgSet = (RzCoreConfigSet)cfgset;
|
|
bnd->numGet = (RzCoreNumGet)numget;
|
|
bnd->flagsGet = (RzCoreFlagsGet)__flagsGet;
|
|
bnd->applyBinInfo = (RzCoreBinApplyInfo)rz_core_bin_apply_info;
|
|
return true;
|
|
}
|
|
|
|
RZ_API RzCore *rz_core_ncast(ut64 p) {
|
|
return (RzCore *)(size_t)p;
|
|
}
|
|
|
|
RZ_API RzCore *rz_core_cast(void *p) {
|
|
return (RzCore *)p;
|
|
}
|
|
|
|
static ut64 getref(RzCore *core, int n, char t, int type) {
|
|
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
|
|
RzListIter *iter;
|
|
RzAnalysisXRef *r;
|
|
RzList *list;
|
|
int i = 0;
|
|
if (!fcn) {
|
|
return UT64_MAX;
|
|
}
|
|
if (t == 'r') {
|
|
list = rz_analysis_function_get_xrefs_from(fcn);
|
|
} else {
|
|
list = rz_analysis_function_get_xrefs_to(fcn);
|
|
}
|
|
rz_list_foreach (list, iter, r) {
|
|
if (r->type == type) {
|
|
if (i == n) {
|
|
ut64 addr = t == 'r' ? r->to : r->from;
|
|
rz_list_free(list);
|
|
return addr;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
rz_list_free(list);
|
|
|
|
return UT64_MAX;
|
|
}
|
|
|
|
static ut64 bbInstructions(RzAnalysisFunction *fcn, ut64 addr) {
|
|
void **vit;
|
|
RzAnalysisBlock *bb;
|
|
rz_pvector_foreach (fcn->bbs, vit) {
|
|
bb = (RzAnalysisBlock *)*vit;
|
|
if (RZ_BETWEEN(bb->addr, addr, bb->addr + bb->size - 1)) {
|
|
return bb->ninstr;
|
|
}
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
static ut64 bbBegin(RzAnalysisFunction *fcn, ut64 addr) {
|
|
void **vit;
|
|
RzAnalysisBlock *bb;
|
|
rz_pvector_foreach (fcn->bbs, vit) {
|
|
bb = (RzAnalysisBlock *)*vit;
|
|
if (RZ_BETWEEN(bb->addr, addr, bb->addr + bb->size - 1)) {
|
|
return bb->addr;
|
|
}
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
static ut64 bbJump(RzAnalysisFunction *fcn, ut64 addr) {
|
|
void **vit;
|
|
RzAnalysisBlock *bb;
|
|
rz_pvector_foreach (fcn->bbs, vit) {
|
|
bb = (RzAnalysisBlock *)*vit;
|
|
if (RZ_BETWEEN(bb->addr, addr, bb->addr + bb->size - 1)) {
|
|
return bb->jump;
|
|
}
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
static ut64 bbFail(RzAnalysisFunction *fcn, ut64 addr) {
|
|
void **vit;
|
|
RzAnalysisBlock *bb;
|
|
rz_pvector_foreach (fcn->bbs, vit) {
|
|
bb = (RzAnalysisBlock *)*vit;
|
|
if (RZ_BETWEEN(bb->addr, addr, bb->addr + bb->size - 1)) {
|
|
return bb->fail;
|
|
}
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
static ut64 bbSize(RzAnalysisFunction *fcn, ut64 addr) {
|
|
void **vit;
|
|
RzAnalysisBlock *bb;
|
|
rz_pvector_foreach (fcn->bbs, vit) {
|
|
bb = (RzAnalysisBlock *)*vit;
|
|
if (RZ_BETWEEN(bb->addr, addr, bb->addr + bb->size - 1)) {
|
|
return bb->size;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char *str_callback(RzNum *user, ut64 off, int *ok) {
|
|
RzFlag *f = (RzFlag *)user;
|
|
if (ok) {
|
|
*ok = 0;
|
|
}
|
|
if (f) {
|
|
RzFlagItem *item = rz_flag_get_i(f, off);
|
|
if (item) {
|
|
if (ok) {
|
|
*ok = true;
|
|
}
|
|
return item->name;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static ut64 num_callback(RzNum *userptr, const char *str, int *ok) {
|
|
RzCore *core = (RzCore *)userptr; // XXX ?
|
|
RzAnalysisFunction *fcn;
|
|
char *ptr, *bptr, *out = NULL;
|
|
RzFlagItem *flag;
|
|
RzBinSection *s;
|
|
RzAnalysisOp op = { 0 };
|
|
ut64 ret = 0;
|
|
|
|
if (ok) {
|
|
*ok = false;
|
|
}
|
|
switch (*str) {
|
|
case '.':
|
|
if (str[1] == '.') {
|
|
if (ok) {
|
|
*ok = true;
|
|
}
|
|
return rz_num_tail(core->num, core->offset, str + 2);
|
|
}
|
|
if (core->num->nc.curr_tok == '+') {
|
|
ut64 off = core->num->nc.number_value.n;
|
|
if (!off) {
|
|
off = core->offset;
|
|
}
|
|
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, off);
|
|
if (fcn) {
|
|
if (ok) {
|
|
*ok = true;
|
|
}
|
|
ut64 dst = rz_analysis_function_get_label(fcn, str + 1);
|
|
if (dst == UT64_MAX) {
|
|
dst = fcn->addr;
|
|
}
|
|
st64 delta = dst - off;
|
|
if (delta < 0) {
|
|
core->num->nc.curr_tok = '-';
|
|
delta = off - dst;
|
|
}
|
|
return delta;
|
|
}
|
|
}
|
|
break;
|
|
case '[': {
|
|
ut64 n = 0LL;
|
|
int refsz = core->rasm->bits / 8;
|
|
const char *p = NULL;
|
|
if (strlen(str) > 5) {
|
|
p = strchr(str + 5, ':');
|
|
}
|
|
if (p) {
|
|
refsz = atoi(str + 1);
|
|
str = p;
|
|
}
|
|
// push state
|
|
if (str[0] && str[1]) {
|
|
const char *q;
|
|
char *o = rz_str_dup(str + 1);
|
|
if (o) {
|
|
q = rz_num_calc_index(core->num, NULL);
|
|
if (q) {
|
|
if (rz_str_replace_char(o, ']', 0) > 0) {
|
|
n = rz_num_math(core->num, o);
|
|
if (core->num->nc.errors) {
|
|
return 0;
|
|
}
|
|
rz_num_calc_index(core->num, q);
|
|
}
|
|
}
|
|
free(o);
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
// pop state
|
|
if (ok) {
|
|
*ok = 1;
|
|
}
|
|
if (!rz_io_read_i(core->io, n, &n, refsz, core->print->big_endian)) {
|
|
return 0LL;
|
|
}
|
|
return n;
|
|
} break;
|
|
case '$':
|
|
if (ok) {
|
|
*ok = 1;
|
|
}
|
|
// TODO: group aop-dependant vars after a char, so i can filter
|
|
rz_analysis_op_init(&op);
|
|
rz_analysis_op(core->analysis, &op, core->offset, core->block, core->blocksize, RZ_ANALYSIS_OP_MASK_BASIC);
|
|
rz_analysis_op_fini(&op); // we don't need strings or pointers, just values, which are not nullified in fini
|
|
// XXX the above line is assuming op after fini keeps jump, fail, ptr, val, size and rz_analysis_op_is_eob()
|
|
switch (str[1]) {
|
|
case '.': // can use pc, sp, a0, a1, ...
|
|
return rz_debug_reg_get(core->dbg, str + 2);
|
|
case 'k': // $k{kv}
|
|
if (str[2] != '{') {
|
|
RZ_LOG_ERROR("core: expected '{' after 'k'.\n");
|
|
break;
|
|
}
|
|
bptr = rz_str_dup(str + 3);
|
|
ptr = strchr(bptr, '}');
|
|
if (!ptr) {
|
|
// invalid json
|
|
free(bptr);
|
|
break;
|
|
}
|
|
*ptr = '\0';
|
|
ret = 0LL;
|
|
out = sdb_querys(core->sdb, NULL, 0, bptr);
|
|
if (out && *out) {
|
|
if (strstr(out, "$k{")) {
|
|
RZ_LOG_ERROR("core: recursivity is not permitted here\n");
|
|
} else {
|
|
ret = rz_num_math(core->num, out);
|
|
}
|
|
}
|
|
free(bptr);
|
|
free(out);
|
|
return ret;
|
|
case '{': // ${ev} eval var
|
|
bptr = rz_str_dup(str + 2);
|
|
ptr = strchr(bptr, '}');
|
|
if (ptr) {
|
|
ptr[0] = '\0';
|
|
ut64 ret = rz_config_get_i(core->config, bptr);
|
|
free(bptr);
|
|
return ret;
|
|
}
|
|
// take flag here
|
|
free(bptr);
|
|
break;
|
|
case 'c': // $c console width
|
|
return rz_cons_get_size(NULL);
|
|
case 'r': // $r
|
|
if (str[2] == '{') {
|
|
bptr = rz_str_dup(str + 3);
|
|
ptr = strchr(bptr, '}');
|
|
if (!ptr) {
|
|
free(bptr);
|
|
break;
|
|
}
|
|
*ptr = 0;
|
|
return rz_core_reg_getv_by_role_or_name(core, bptr);
|
|
free(bptr);
|
|
return 0; // UT64_MAX;
|
|
} else {
|
|
int rows;
|
|
(void)rz_cons_get_size(&rows);
|
|
return rows;
|
|
}
|
|
break;
|
|
case 'e': // $e
|
|
if (str[2] == '{') { // $e{flag} flag off + size
|
|
char *flagName = rz_str_dup(str + 3);
|
|
int flagLength = strlen(flagName);
|
|
if (flagLength > 0) {
|
|
flagName[flagLength - 1] = 0;
|
|
}
|
|
RzFlagItem *flag = rz_flag_get(core->flags, flagName);
|
|
free(flagName);
|
|
if (flag) {
|
|
return flag->offset + flag->size;
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
return rz_analysis_op_is_eob(&op);
|
|
case 'j': // $j jump address
|
|
return op.jump;
|
|
case 'p': // $p
|
|
return rz_sys_getpid();
|
|
case 'P': // $P
|
|
return core->dbg->pid > 0 ? core->dbg->pid : 0;
|
|
case 'f': // $f jump fail address
|
|
if (str[2] == 'l') { // $fl flag length
|
|
RzFlagItem *fi = rz_flag_get_i(core->flags, core->offset);
|
|
if (fi) {
|
|
return fi->size;
|
|
}
|
|
return 0;
|
|
}
|
|
return op.fail;
|
|
case 'm': // $m memref
|
|
return op.ptr;
|
|
case 'B': // $B base address
|
|
case 'M': { // $M map address
|
|
ut64 lower = UT64_MAX;
|
|
ut64 size = 0LL;
|
|
RzIOMap *map = rz_io_map_get(core->io, core->offset);
|
|
if (map) {
|
|
lower = rz_itv_begin(map->itv);
|
|
size = rz_itv_size(map->itv);
|
|
}
|
|
|
|
if (str[1] == 'B') {
|
|
/* clear lower bits of the lowest map address to define the base address */
|
|
const int clear_bits = 16;
|
|
lower >>= clear_bits;
|
|
lower <<= clear_bits;
|
|
}
|
|
if (str[2] == 'M') {
|
|
return size;
|
|
}
|
|
return (lower == UT64_MAX) ? 0LL : lower;
|
|
} break;
|
|
case 'v': // $v immediate value
|
|
return op.val;
|
|
case 'l': // $l opcode length
|
|
return op.size;
|
|
case 'b': // $b
|
|
return core->blocksize;
|
|
case 's': // $s file size
|
|
if (str[2] == '{') { // $s{flag} flag size
|
|
bptr = rz_str_dup(str + 3);
|
|
ptr = strchr(bptr, '}');
|
|
if (!ptr) {
|
|
// invalid json
|
|
free(bptr);
|
|
break;
|
|
}
|
|
*ptr = '\0';
|
|
RzFlagItem *flag = rz_flag_get(core->flags, bptr);
|
|
ret = flag ? flag->size : 0LL; // flag
|
|
free(bptr);
|
|
free(out);
|
|
return ret;
|
|
} else if (core->file) {
|
|
return rz_io_fd_size(core->io, core->file->fd);
|
|
}
|
|
return 0LL;
|
|
case 'w': // $w word size
|
|
return rz_config_get_i(core->config, "asm.bits") / 8;
|
|
case 'S': // $S section offset
|
|
{
|
|
RzBinObject *bo = rz_bin_cur_object(core->bin);
|
|
if (bo && (s = rz_bin_get_section_at(bo, core->offset, true))) {
|
|
return (str[2] == 'S' ? s->size : s->vaddr);
|
|
}
|
|
}
|
|
return 0LL;
|
|
case 'D': // $D
|
|
if (str[2] == 'B') { // $DD
|
|
return rz_debug_get_baddr(core->dbg, NULL);
|
|
} else if (IS_DIGIT(str[2])) {
|
|
return getref(core, atoi(str + 2), 'r', RZ_ANALYSIS_XREF_TYPE_DATA);
|
|
} else {
|
|
RzDebugMap *map;
|
|
RzListIter *iter;
|
|
rz_list_foreach (core->dbg->maps, iter, map) {
|
|
if (core->offset >= map->addr && core->offset < map->addr_end) {
|
|
return (str[2] == 'D') ? map->size : map->addr;
|
|
}
|
|
}
|
|
}
|
|
return 0LL; // maybe // return UT64_MAX;
|
|
case '?': // $?
|
|
return core->num->value; // rc;
|
|
case '$': // $$ offset
|
|
return str[2] == '$' ? core->prompt_offset : core->offset;
|
|
case 'o': { // $o
|
|
RzBinSection *s = rz_bin_get_section_at(rz_bin_cur_object(core->bin), core->offset, true);
|
|
return s ? core->offset - s->vaddr + s->paddr : core->offset;
|
|
break;
|
|
}
|
|
case 'O': // $O
|
|
if (core->print->cur_enabled) {
|
|
return core->offset + core->print->cur;
|
|
}
|
|
return core->offset;
|
|
case 'C': // $C nth call
|
|
return getref(core, atoi(str + 2), 'r', RZ_ANALYSIS_XREF_TYPE_CALL);
|
|
case 'J': // $J nth jump
|
|
return getref(core, atoi(str + 2), 'r', RZ_ANALYSIS_XREF_TYPE_CODE);
|
|
case 'X': // $X nth xref
|
|
return getref(core, atoi(str + 2), 'x', RZ_ANALYSIS_XREF_TYPE_CALL);
|
|
case 'F': // $F function size
|
|
fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
|
|
if (fcn) {
|
|
switch (str[2]) {
|
|
/* function bounds (uppercase) */
|
|
case 'B': return fcn->addr; // begin
|
|
case 'E': return rz_analysis_function_max_addr(fcn); // end
|
|
case 'S': return (str[3] == 'S') ? rz_analysis_function_realsize(fcn) : rz_analysis_function_linear_size(fcn);
|
|
case 'I': return fcn->ninstr;
|
|
/* basic blocks (lowercase) */
|
|
case 'b': return bbBegin(fcn, core->offset);
|
|
case 'e': return bbBegin(fcn, core->offset) + bbSize(fcn, core->offset);
|
|
case 'i': return bbInstructions(fcn, core->offset);
|
|
case 's': return bbSize(fcn, core->offset);
|
|
case 'j': return bbJump(fcn, core->offset); // jump
|
|
case 'f': return bbFail(fcn, core->offset); // fail
|
|
}
|
|
return fcn->addr;
|
|
}
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
if (*str >= 'A') {
|
|
// NOTE: functions override flags
|
|
RzAnalysisFunction *fcn = rz_analysis_get_function_byname(core->analysis, str);
|
|
if (fcn) {
|
|
if (ok) {
|
|
*ok = true;
|
|
}
|
|
return fcn->addr;
|
|
}
|
|
|
|
if ((flag = rz_flag_get(core->flags, str))) {
|
|
ret = flag->offset;
|
|
if (ok) {
|
|
*ok = true;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// check for reg alias
|
|
RzReg *reg = rz_core_reg_default(core);
|
|
struct rz_reg_item_t *r = rz_reg_get(reg, str, -1);
|
|
if (!r) {
|
|
int role = rz_reg_get_name_idx(str);
|
|
if (role != -1) {
|
|
const char *alias = rz_reg_get_name(reg, role);
|
|
if (alias) {
|
|
r = rz_reg_get(reg, alias, -1);
|
|
if (r) {
|
|
if (ok) {
|
|
*ok = true;
|
|
}
|
|
ret = rz_reg_get_value(reg, r);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (ok) {
|
|
*ok = true;
|
|
}
|
|
ret = rz_reg_get_value(reg, r);
|
|
return ret;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
RZ_API RzCore *rz_core_new(void) {
|
|
RzCore *c = RZ_NEW0(RzCore);
|
|
if (c) {
|
|
rz_core_init(c);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static RzLineNSCompletionResult *rzshell_autocomplete(RzLineBuffer *buf, RzLinePromptType prompt_type, void *user) {
|
|
return rz_core_autocomplete_rzshell((RzCore *)user, buf, prompt_type);
|
|
}
|
|
|
|
RZ_API int rz_core_fgets(char *buf, int len, void *user) {
|
|
RzCore *core = (RzCore *)user;
|
|
RzCons *cons = core->cons;
|
|
RzLine *rzline = cons->line;
|
|
bool prompt = cons->context->is_interactive;
|
|
buf[0] = '\0';
|
|
if (prompt) {
|
|
rzline->ns_completion.run = rzshell_autocomplete;
|
|
rzline->ns_completion.run_user = core;
|
|
rzline->completion.run = NULL;
|
|
} else {
|
|
rzline->history.data = NULL;
|
|
rz_line_completion_set(&rzline->completion, 0, NULL);
|
|
rzline->completion.run = NULL;
|
|
rzline->completion.run_user = NULL;
|
|
}
|
|
const char *ptr = rz_line_readline(rzline);
|
|
if (!ptr) {
|
|
return -1;
|
|
}
|
|
return rz_str_ncpy(buf, ptr, len - 1);
|
|
}
|
|
|
|
static const char *rz_core_print_offname(void *p, ut64 addr) {
|
|
RzCore *c = (RzCore *)p;
|
|
RzFlagItem *item = rz_flag_get_i(c->flags, addr);
|
|
return item ? item->name : NULL;
|
|
}
|
|
|
|
static int rz_core_print_offsize(void *p, ut64 addr) {
|
|
RzCore *c = (RzCore *)p;
|
|
RzFlagItem *item = rz_flag_get_i(c->flags, addr);
|
|
return item ? item->size : -1;
|
|
}
|
|
|
|
/**
|
|
* Disassemble one instruction at specified address.
|
|
*/
|
|
static int __disasm(void *_core, ut64 addr) {
|
|
RzCore *core = _core;
|
|
ut64 prevaddr = core->offset;
|
|
|
|
rz_core_seek(core, addr, true);
|
|
int len = rz_core_print_disasm_instructions(core, 0, 1);
|
|
rz_core_seek(core, prevaddr, true);
|
|
|
|
return len;
|
|
}
|
|
|
|
static void update_sdb(RzCore *core) {
|
|
Sdb *d;
|
|
RzBinObject *o;
|
|
if (!core) {
|
|
return;
|
|
}
|
|
// SDB// analysis/
|
|
if (core->analysis && core->analysis->sdb) {
|
|
sdb_ns_set(DB, "analysis", core->analysis->sdb);
|
|
}
|
|
// SDB// bin/
|
|
if (core->bin && core->bin->sdb) {
|
|
sdb_ns_set(DB, "bin", core->bin->sdb);
|
|
}
|
|
// SDB// bin/info
|
|
o = rz_bin_cur_object(core->bin);
|
|
if (o) {
|
|
sdb_ns_set(sdb_ns(DB, "bin", 1), "info", o->kv);
|
|
}
|
|
// sdb_ns_set (core->sdb, "flags", core->flags->sdb);
|
|
// sdb_ns_set (core->sdb, "bin", core->bin->sdb);
|
|
// SDB// syscall/
|
|
if (core->rasm && core->rasm->syscall && core->rasm->syscall->db) {
|
|
sdb_ns_set(DB, "syscall", core->rasm->syscall->db);
|
|
}
|
|
d = sdb_ns(DB, "debug", 1);
|
|
if (core->dbg->sgnls) {
|
|
sdb_ns_set(d, "signals", core->dbg->sgnls);
|
|
}
|
|
}
|
|
|
|
static bool get_string(const ut8 *buf, int size, RzDetectedString **dstr, RzStrEnc encoding, bool big_endian) {
|
|
if (!buf || size < 1) {
|
|
return false;
|
|
}
|
|
|
|
RzUtilStrScanOptions opt = {
|
|
.max_str_length = size,
|
|
.min_str_length = RZ_BIN_STRING_SEARCH_MIN_STRING,
|
|
.prefer_big_endian = big_endian,
|
|
.check_ascii_freq = false,
|
|
};
|
|
|
|
if (rz_scan_strings_single_raw(buf, size, &opt, encoding, dstr) && (*dstr)->addr) {
|
|
rz_detected_string_free(*dstr);
|
|
*dstr = NULL;
|
|
}
|
|
return *dstr;
|
|
}
|
|
|
|
RZ_API char *rz_core_analysis_hasrefs(RzCore *core, ut64 value, int mode) {
|
|
if (mode) {
|
|
PJ *pj = (mode == 'j') ? pj_new() : NULL;
|
|
const int hex_depth = 1; // r_config_get_i (core->config, "hex.depth");
|
|
char *res = rz_core_analysis_hasrefs_to_depth(core, value, pj, hex_depth);
|
|
if (pj) {
|
|
free(res);
|
|
return pj_drain(pj);
|
|
}
|
|
return res;
|
|
}
|
|
RzFlagItem *fi = rz_flag_get_i(core->flags, value);
|
|
return fi ? rz_str_dup(fi->name) : NULL;
|
|
}
|
|
|
|
static char *getvalue(ut64 value, int bits) {
|
|
switch (bits) {
|
|
case 16: // umf, not in sync with pxr
|
|
{
|
|
st16 v = (st16)(value & UT16_MAX);
|
|
st16 h = UT16_MAX / 0x100;
|
|
if (v > -h && v < h) {
|
|
return rz_str_newf("%hd", v);
|
|
}
|
|
} break;
|
|
case 32: {
|
|
st32 v = (st32)(value & UT32_MAX);
|
|
st32 h = UT32_MAX / 0x10000;
|
|
if (v > -h && v < h) {
|
|
return rz_str_newf("%d", v);
|
|
}
|
|
} break;
|
|
case 64: {
|
|
st64 v = (st64)(value);
|
|
st64 h = UT64_MAX / 0x1000000;
|
|
if (v > -h && v < h) {
|
|
return rz_str_newf("%" PFMT64d, v);
|
|
}
|
|
} break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
pxr logic is dupplicated in other places
|
|
* ai, ad
|
|
* no json support
|
|
*/
|
|
RZ_API char *rz_core_analysis_hasrefs_to_depth(RzCore *core, ut64 value, PJ *pj, int depth) {
|
|
const int bits = core->rasm->bits;
|
|
const bool big_endian = rz_config_get_b(core->config, "cfg.bigendian");
|
|
rz_return_val_if_fail(core, NULL);
|
|
RzStrBuf *s = rz_strbuf_new(NULL);
|
|
if (pj) {
|
|
pj_o(pj);
|
|
pj_kn(pj, "addr", value);
|
|
}
|
|
if (depth < 1 || value == UT64_MAX) {
|
|
if (pj) {
|
|
pj_end(pj);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *val = getvalue(value, bits);
|
|
if (val) {
|
|
if (pj) {
|
|
pj_ks(pj, "value", val);
|
|
} else {
|
|
rz_strbuf_appendf(s, "%s ", val);
|
|
}
|
|
RZ_FREE(val);
|
|
}
|
|
|
|
if (value && value != UT64_MAX) {
|
|
RzDebugMap *map = rz_debug_map_get(core->dbg, value);
|
|
if (map && map->name && map->name[0]) {
|
|
if (pj) {
|
|
pj_ks(pj, "map", map->name);
|
|
} else {
|
|
rz_strbuf_appendf(s, "%s ", map->name);
|
|
}
|
|
}
|
|
}
|
|
ut64 type = rz_core_analysis_address(core, value);
|
|
RzBinObject *obj = rz_bin_cur_object(core->bin);
|
|
RzBinSection *sect = value && obj ? rz_bin_get_section_at(obj, value, true) : NULL;
|
|
if (!((type & RZ_ANALYSIS_ADDR_TYPE_HEAP) || (type & RZ_ANALYSIS_ADDR_TYPE_STACK))) {
|
|
// Do not repeat "stack" or "heap" words unnecessarily.
|
|
if (sect && sect->name[0]) {
|
|
if (pj) {
|
|
pj_ks(pj, "section", sect->name);
|
|
} else {
|
|
rz_strbuf_appendf(s, "%s ", sect->name);
|
|
}
|
|
}
|
|
}
|
|
if (value != 0 && value != UT64_MAX) {
|
|
if (pj) {
|
|
RzListIter *iter;
|
|
RzFlagItem *f;
|
|
const RzList *flags = rz_flag_get_list(core->flags, value);
|
|
if (flags && !rz_list_empty(flags)) {
|
|
pj_ka(pj, "flags");
|
|
rz_list_foreach (flags, iter, f) {
|
|
pj_s(pj, f->name);
|
|
}
|
|
pj_end(pj);
|
|
}
|
|
} else {
|
|
char *flags = rz_flag_get_liststr(core->flags, value);
|
|
if (flags) {
|
|
rz_strbuf_appendf(s, "%s ", flags);
|
|
free(flags);
|
|
}
|
|
}
|
|
}
|
|
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, value, 0);
|
|
if (fcn) {
|
|
if (pj) {
|
|
pj_ks(pj, "fcn", fcn->name);
|
|
} else {
|
|
rz_strbuf_appendf(s, "%s ", fcn->name);
|
|
}
|
|
}
|
|
if (type) {
|
|
const char *c = rz_core_analysis_optype_colorfor(core, value, true);
|
|
const char *cend = (c && *c) ? Color_RESET : "";
|
|
if (!c) {
|
|
c = "";
|
|
}
|
|
if (pj) {
|
|
pj_ka(pj, "attr");
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_HEAP) {
|
|
if (pj) {
|
|
pj_s(pj, "heap");
|
|
} else {
|
|
rz_strbuf_appendf(s, "%sheap%s ", c, cend);
|
|
}
|
|
} else if (type & RZ_ANALYSIS_ADDR_TYPE_STACK) {
|
|
if (pj) {
|
|
pj_s(pj, "stack");
|
|
} else {
|
|
rz_strbuf_appendf(s, "%sstack%s ", c, cend);
|
|
}
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_PROGRAM) {
|
|
if (pj) {
|
|
pj_s(pj, "program");
|
|
} else {
|
|
rz_strbuf_appendf(s, "%sprogram%s ", c, cend);
|
|
}
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_LIBRARY) {
|
|
if (pj) {
|
|
pj_s(pj, "library");
|
|
} else {
|
|
rz_strbuf_appendf(s, "%slibrary%s ", c, cend);
|
|
}
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_ASCII) {
|
|
if (pj) {
|
|
pj_s(pj, "ascii");
|
|
} else {
|
|
rz_strbuf_appendf(s, "%sascii%s ('%c') ", c, cend, (char)value);
|
|
}
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_SEQUENCE) {
|
|
if (pj) {
|
|
pj_s(pj, "sequence");
|
|
} else {
|
|
rz_strbuf_appendf(s, "%ssequence%s ", c, cend);
|
|
}
|
|
}
|
|
if (pj) {
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_READ) {
|
|
pj_s(pj, "R");
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_WRITE) {
|
|
pj_s(pj, "W");
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_EXEC) {
|
|
pj_s(pj, "X");
|
|
}
|
|
} else {
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_READ) {
|
|
rz_strbuf_appendf(s, "%sR%s ", c, cend);
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_WRITE) {
|
|
rz_strbuf_appendf(s, "%sW%s ", c, cend);
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_EXEC) {
|
|
RzAsmOp op = { 0 };
|
|
ut8 buf[32];
|
|
rz_strbuf_appendf(s, "%sX%s ", c, cend);
|
|
/* instruction disassembly */
|
|
rz_io_read_at(core->io, value, buf, sizeof(buf));
|
|
rz_asm_set_pc(core->rasm, value);
|
|
rz_asm_disassemble(core->rasm, &op, buf, sizeof(buf));
|
|
rz_strbuf_appendf(s, "'%s' ", rz_asm_op_get_asm(&op));
|
|
rz_asm_op_fini(&op);
|
|
/* get library name */
|
|
{ // NOTE: dup for mapname?
|
|
RzDebugMap *map;
|
|
RzListIter *iter;
|
|
rz_list_foreach (core->dbg->maps, iter, map) {
|
|
if ((value >= map->addr) &&
|
|
(value < map->addr_end)) {
|
|
const char *lastslash = rz_str_lchr(map->name, '/');
|
|
rz_strbuf_appendf(s, "'%s' ", lastslash ? lastslash + 1 : map->name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else if (type & RZ_ANALYSIS_ADDR_TYPE_READ) {
|
|
ut8 buf[8];
|
|
if (rz_io_read_at(core->io, value, buf, sizeof(buf))) {
|
|
ut64 n = rz_read_ble(buf, big_endian, bits);
|
|
rz_strbuf_appendf(s, "0x%" PFMT64x " ", n);
|
|
}
|
|
}
|
|
}
|
|
if (pj) {
|
|
pj_end(pj);
|
|
}
|
|
}
|
|
{
|
|
ut8 buf[128];
|
|
RzStrEnc encoding = core->bin->str_search_cfg.string_encoding;
|
|
const char *c = rz_config_get_i(core->config, "scr.color") ? core->cons->context->pal.ai_ascii : "";
|
|
const char *cend = (c && *c) ? Color_RESET : "";
|
|
RzDetectedString *dstr = NULL;
|
|
if (rz_io_read_at(core->io, value, buf, sizeof(buf)) &&
|
|
get_string(buf, sizeof(buf), &dstr, encoding, big_endian)) {
|
|
if (pj) {
|
|
pj_ks(pj, "string", dstr->string);
|
|
} else {
|
|
rz_strbuf_appendf(s, "%s%s%s ", c, dstr->string, cend);
|
|
}
|
|
rz_detected_string_free(dstr);
|
|
}
|
|
}
|
|
if ((type & RZ_ANALYSIS_ADDR_TYPE_READ) && !(type & RZ_ANALYSIS_ADDR_TYPE_EXEC) && depth) {
|
|
// Try to telescope further, but only several levels deep.
|
|
ut8 buf[8];
|
|
if (rz_io_read_at(core->io, value, buf, sizeof(buf))) {
|
|
ut64 n = rz_read_ble(buf, big_endian, bits);
|
|
if (n != value) {
|
|
if (pj) {
|
|
pj_k(pj, "ref");
|
|
}
|
|
char *rrstr = rz_core_analysis_hasrefs_to_depth(core, n, pj, depth - 1);
|
|
if (rrstr) {
|
|
if (!pj && rrstr[0]) {
|
|
rz_strbuf_appendf(s, " -> %s", rrstr);
|
|
}
|
|
free(rrstr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (pj) {
|
|
pj_end(pj);
|
|
}
|
|
char *res = rz_strbuf_drain(s);
|
|
rz_str_trim_tail(res);
|
|
return res;
|
|
}
|
|
|
|
RZ_API char *rz_core_analysis_get_comments(RzCore *core, ut64 addr) {
|
|
if (core) {
|
|
const char *type = rz_meta_get_string(core->analysis, RZ_META_TYPE_VARTYPE, addr);
|
|
const char *cmt = rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, addr);
|
|
if (type && cmt) {
|
|
return rz_str_newf("%s %s", type, cmt);
|
|
}
|
|
if (type) {
|
|
return rz_str_dup(type);
|
|
}
|
|
if (cmt) {
|
|
return rz_str_dup(cmt);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API const char *rz_core_analysis_optype_colorfor(RzCore *core, ut64 addr, bool verbose) {
|
|
ut64 type;
|
|
if (!(core->print->flags & RZ_PRINT_FLAGS_COLOR)) {
|
|
return NULL;
|
|
}
|
|
if (!rz_config_get_i(core->config, "scr.color")) {
|
|
return NULL;
|
|
}
|
|
type = rz_core_analysis_address(core, addr);
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_EXEC) {
|
|
return core->cons->context->pal.ai_exec; // Color_RED;
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_WRITE) {
|
|
return core->cons->context->pal.ai_write; // Color_BLUE;
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_READ) {
|
|
return core->cons->context->pal.ai_read; // Color_GREEN;
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_SEQUENCE) {
|
|
return core->cons->context->pal.ai_seq; // Color_MAGENTA;
|
|
}
|
|
if (type & RZ_ANALYSIS_ADDR_TYPE_ASCII) {
|
|
return core->cons->context->pal.ai_ascii; // Color_YELLOW;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int mywrite(const ut8 *buf, int len) {
|
|
return rz_cons_memcat((const char *)buf, len);
|
|
}
|
|
|
|
static bool exists_var(RzPrint *print, ut64 func_addr, char *str) {
|
|
RzAnalysis *analysis = ((RzCore *)(print->user))->analysis;
|
|
RzAnalysisFunction *fcn = rz_analysis_get_function_at(analysis, func_addr);
|
|
if (!fcn) {
|
|
return false;
|
|
}
|
|
return !!rz_analysis_function_get_var_byname(fcn, str);
|
|
}
|
|
|
|
static bool rz_core_analysis_read_at(struct rz_analysis_t *analysis, ut64 addr, ut8 *buf, int len) {
|
|
return rz_io_read_at(analysis->iob.io, addr, buf, len);
|
|
}
|
|
|
|
static void rz_core_break(RzCore *core) {
|
|
}
|
|
|
|
static void *rz_core_sleep_begin(RzCore *core) {
|
|
RzCoreTask *task = rz_core_task_self(&core->tasks);
|
|
if (task) {
|
|
rz_core_task_sleep_begin(task);
|
|
}
|
|
return task;
|
|
}
|
|
|
|
static void rz_core_sleep_end(RzCore *core, void *user) {
|
|
RzCoreTask *task = (RzCoreTask *)user;
|
|
if (task) {
|
|
rz_core_task_sleep_end(task);
|
|
}
|
|
}
|
|
|
|
static const char *colorfor_cb(void *user, ut64 addr, bool verbose) {
|
|
return rz_core_analysis_optype_colorfor((RzCore *)user, addr, verbose);
|
|
}
|
|
|
|
static char *hasrefs_cb(void *user, ut64 addr, int mode) {
|
|
return rz_core_analysis_hasrefs((RzCore *)user, addr, mode);
|
|
}
|
|
|
|
static const char *get_section_name(void *user, ut64 addr) {
|
|
return rz_core_get_section_name((RzCore *)user, addr);
|
|
}
|
|
|
|
static char *get_comments_cb(void *user, ut64 addr) {
|
|
return rz_core_analysis_get_comments((RzCore *)user, addr);
|
|
}
|
|
|
|
static RzFlagItem *core_flg_class_set(RzFlag *f, const char *name, ut64 addr, ut32 size) {
|
|
rz_flag_space_push(f, RZ_FLAGS_FS_CLASSES);
|
|
RzFlagItem *res = rz_flag_set(f, name, addr, size);
|
|
rz_flag_space_pop(f);
|
|
return res;
|
|
}
|
|
|
|
static RzFlagItem *core_flg_class_get(RzFlag *f, const char *name) {
|
|
rz_flag_space_push(f, RZ_FLAGS_FS_CLASSES);
|
|
RzFlagItem *res = rz_flag_get(f, name);
|
|
rz_flag_space_pop(f);
|
|
return res;
|
|
}
|
|
|
|
static RzFlagItem *core_flg_fcn_set(RzFlag *f, const char *name, ut64 addr, ut32 size) {
|
|
rz_flag_space_push(f, RZ_FLAGS_FS_FUNCTIONS);
|
|
RzFlagItem *res = rz_flag_set(f, name, addr, size);
|
|
rz_flag_space_pop(f);
|
|
return res;
|
|
}
|
|
|
|
RZ_API RzFlagItem *rz_core_flag_get_by_spaces(RzFlag *f, ut64 off) {
|
|
return rz_flag_get_by_spaces(f, off,
|
|
RZ_FLAGS_FS_FUNCTIONS,
|
|
RZ_FLAGS_FS_SIGNS,
|
|
RZ_FLAGS_FS_CLASSES,
|
|
RZ_FLAGS_FS_SYMBOLS,
|
|
RZ_FLAGS_FS_IMPORTS,
|
|
RZ_FLAGS_FS_RELOCS,
|
|
RZ_FLAGS_FS_STRINGS,
|
|
RZ_FLAGS_FS_RESOURCES,
|
|
RZ_FLAGS_FS_SYMBOLS_SECTIONS,
|
|
RZ_FLAGS_FS_SECTIONS,
|
|
RZ_FLAGS_FS_SEGMENTS,
|
|
NULL);
|
|
}
|
|
|
|
#if __WINDOWS__
|
|
// XXX move to rcons?
|
|
static int win_eprintf(const char *format, ...) {
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
rz_cons_win_vhprintf(STD_ERROR_HANDLE, false, format, ap);
|
|
va_end(ap);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static bool bp_is_mapped(ut64 addr, int perm, void *user) {
|
|
RzCore *core = user;
|
|
if (rz_core_is_debug(core)) {
|
|
// RzList *maps = core->dbg->maps;
|
|
RzDebugMap *map = NULL;
|
|
RzListIter *iter = NULL;
|
|
|
|
rz_list_foreach (core->dbg->maps, iter, map) {
|
|
if (addr < map->addr || addr >= map->addr_end) {
|
|
continue;
|
|
}
|
|
if (perm <= 0 || (map->perm & perm)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return rz_io_map_is_mapped(core->io, addr);
|
|
}
|
|
|
|
static void bp_maps_sync(void *user) {
|
|
RzCore *core = user;
|
|
if (rz_core_is_debug(core)) {
|
|
rz_debug_map_sync(core->dbg);
|
|
}
|
|
}
|
|
|
|
static void core_set_rz_asm_by_hint(RzCore *core, ut64 addr) {
|
|
int bits = 0;
|
|
const char *arch = NULL;
|
|
rz_core_arch_bits_at(core, addr, &bits, &arch);
|
|
rz_asm_set_arch(core->rasm, arch, bits);
|
|
}
|
|
|
|
static void core_set_rz_asm_by_config(RzCore *core) {
|
|
const char *arch = rz_config_get(core->config, "asm.arch");
|
|
int bits = rz_config_get_i(core->config, "asm.bits");
|
|
rz_asm_set_arch(core->rasm, arch, bits);
|
|
}
|
|
|
|
static RzStrBuf *bp_get_sw_breakpoint_at(ut64 addr, void *user) {
|
|
RzCore *core = (RzCore *)user;
|
|
RzStrBuf *opcode = NULL;
|
|
RzAsmOp op = { 0 };
|
|
|
|
core_set_rz_asm_by_hint(core, addr);
|
|
|
|
rz_asm_op_init(&op);
|
|
if (rz_asm_software_breakpoint(core->rasm, &op) &&
|
|
(opcode = rz_strbuf_new(NULL))) {
|
|
rz_strbuf_copy(opcode, &op.buf);
|
|
}
|
|
rz_asm_op_fini(&op);
|
|
|
|
core_set_rz_asm_by_config(core);
|
|
return opcode;
|
|
}
|
|
|
|
static size_t bp_get_sw_breakpoint_size_at(ut64 addr, void *user) {
|
|
RzCore *core = (RzCore *)user;
|
|
size_t length = 0;
|
|
RzAsmOp op = { 0 };
|
|
|
|
core_set_rz_asm_by_hint(core, addr);
|
|
|
|
rz_asm_op_init(&op);
|
|
if (rz_asm_software_breakpoint(core->rasm, &op)) {
|
|
length = rz_strbuf_length(&op.buf);
|
|
}
|
|
rz_asm_op_fini(&op);
|
|
|
|
core_set_rz_asm_by_config(core);
|
|
return length;
|
|
}
|
|
|
|
static void ev_iowrite_cb(RzEvent *ev, int type, void *user, void *data) {
|
|
RzCore *core = user;
|
|
RzEventIOWrite *iow = data;
|
|
if (rz_config_get_i(core->config, "analysis.detectwrites")) {
|
|
rz_analysis_update_analysis_range(core->analysis, iow->addr, iow->len);
|
|
if (core->cons->event_resize && core->cons->event_data) {
|
|
// Force a reload of the graph
|
|
core->cons->event_resize(core->cons->event_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_IPI void rz_core_file_io_desc_closed(RzCore *core, RzIODesc *desc);
|
|
RZ_IPI void rz_core_file_io_map_deleted(RzCore *core, RzIOMap *map);
|
|
RZ_IPI void rz_core_file_bin_file_deleted(RzCore *core, RzBinFile *bf);
|
|
RZ_IPI void rz_core_vfile_bin_file_deleted(RzCore *core, RzBinFile *bf);
|
|
|
|
static void ev_iodescclose_cb(RzEvent *ev, int type, void *user, void *data) {
|
|
RzEventIODescClose *ioc = data;
|
|
rz_core_file_io_desc_closed(user, ioc->desc);
|
|
}
|
|
|
|
static void ev_iomapdel_cb(RzEvent *ev, int type, void *user, void *data) {
|
|
RzEventIOMapDel *iod = data;
|
|
rz_core_file_io_map_deleted(user, iod->map);
|
|
}
|
|
|
|
static void ev_binfiledel_cb(RzEvent *ev, int type, void *user, void *data) {
|
|
RzEventBinFileDel *bev = data;
|
|
rz_core_file_bin_file_deleted(user, bev->bf);
|
|
rz_core_vfile_bin_file_deleted(user, bev->bf);
|
|
}
|
|
|
|
RZ_IPI void rz_core_task_ctx_switch(RzCoreTask *next, void *user);
|
|
RZ_IPI void rz_core_task_break_cb(RzCoreTask *task, void *user);
|
|
RZ_IPI void rz_core_file_free(RzCoreFile *cf);
|
|
|
|
RZ_IPI extern RzIOPlugin rz_core_io_plugin_vfile;
|
|
|
|
RZ_API bool rz_core_init(RzCore *core) {
|
|
core->blocksize = RZ_CORE_BLOCKSIZE;
|
|
core->block = (ut8 *)calloc(RZ_CORE_BLOCKSIZE + 1, 1);
|
|
if (!core->block) {
|
|
RZ_LOG_ERROR("core: cannot allocate %d byte(s)\n", RZ_CORE_BLOCKSIZE);
|
|
/* XXX memory leak */
|
|
return false;
|
|
}
|
|
core->ev = rz_event_new(core);
|
|
core->max_cmd_depth = RZ_CONS_CMD_DEPTH + 1;
|
|
core->sdb = sdb_new(NULL, "rzkv.sdb", 0); // XXX: path must be in home?
|
|
rz_core_seek_reset(core);
|
|
core->lastsearch = NULL;
|
|
core->cmdfilter = NULL;
|
|
core->curtheme = rz_str_dup("default");
|
|
core->switch_file_view = 0;
|
|
core->cmdremote = 0;
|
|
core->incomment = false;
|
|
core->config = NULL;
|
|
core->plugin_configs = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_config_free);
|
|
core->http_up = false;
|
|
ZERO_FILL(core->root_cmd_descriptor);
|
|
core->print = rz_print_new();
|
|
core->ropchain = rz_list_newf((RzListFree)free);
|
|
rz_core_bind(core, &(core->print->coreb));
|
|
core->print->user = core;
|
|
core->print->num = core->num;
|
|
core->print->offname = rz_core_print_offname;
|
|
core->print->offsize = rz_core_print_offsize;
|
|
core->print->cb_printf = rz_cons_printf;
|
|
core->print->cb_color = rz_cons_rainbow_get;
|
|
core->print->write = mywrite;
|
|
core->print->exists_var = exists_var;
|
|
core->print->disasm = __disasm;
|
|
core->print->colorfor = colorfor_cb;
|
|
core->print->hasrefs = hasrefs_cb;
|
|
core->print->get_comments = get_comments_cb;
|
|
core->print->get_section_name = get_section_name;
|
|
core->print->use_comments = false;
|
|
rz_core_rtr_init(core);
|
|
core->rtr_n = 0;
|
|
core->blocksize_max = RZ_CORE_BLOCKSIZE_MAX;
|
|
rz_core_task_scheduler_init(&core->tasks, rz_core_task_ctx_switch, NULL, rz_core_task_break_cb, NULL);
|
|
core->watchers = rz_list_new();
|
|
core->watchers->free = (RzListFree)rz_core_cmpwatch_free;
|
|
core->scriptstack = rz_list_new();
|
|
core->scriptstack->free = (RzListFree)free;
|
|
core->times = RZ_NEW0(RzCoreTimes);
|
|
core->vmode = false;
|
|
core->lastcmd = NULL;
|
|
core->cmdlog = NULL;
|
|
core->stkcmd = NULL;
|
|
core->cmdqueue = NULL;
|
|
core->cmdrepeat = true;
|
|
core->yank_buf = rz_buf_new_with_bytes(NULL, 0);
|
|
core->num = rz_num_new(&num_callback, &str_callback, core);
|
|
core->egg = rz_egg_new();
|
|
rz_egg_setup(core->egg, RZ_SYS_ARCH, RZ_SYS_BITS, 0, RZ_SYS_OS);
|
|
core->crypto = rz_crypto_new();
|
|
core->warnings_after = rz_list_newf((RzListFree)free);
|
|
|
|
core->fixedarch = false;
|
|
core->fixedbits = false;
|
|
|
|
/* initialize libraries */
|
|
core->cons = rz_cons_new();
|
|
if (core->cons->refcnt == 1) {
|
|
core->cons = rz_cons_singleton();
|
|
if (core->cons->line) {
|
|
core->cons->line->user = core;
|
|
core->cons->line->cb_editor =
|
|
(RzLineEditorCb)&rz_core_editor;
|
|
core->cons->line->cb_fkey = core->cons->cb_fkey;
|
|
}
|
|
#if __EMSCRIPTEN__
|
|
core->cons->user_fgets = NULL;
|
|
#else
|
|
core->cons->user_fgets = (void *)rz_core_fgets;
|
|
core->cons->user_fgets_user = core;
|
|
#endif
|
|
char *history = rz_path_home_history();
|
|
rz_line_hist_load(core->cons->line, history);
|
|
free(history);
|
|
}
|
|
core->print->cons = core->cons;
|
|
rz_cons_bind(&core->print->consbind);
|
|
|
|
// We save the old num ad user, in order to restore it after free
|
|
core->lang = rz_lang_new();
|
|
core->lang->cmd_str = (char *(*)(void *, const char *))rz_core_cmd_str;
|
|
core->lang->cmdf = (int (*)(void *, const char *, ...))rz_core_cmdf;
|
|
rz_core_bind_cons(core);
|
|
core->lang->cb_printf = rz_cons_printf;
|
|
rz_lang_define(core->lang, "RzCore", "core", core);
|
|
rz_lang_set_user_ptr(core->lang, core);
|
|
core->rasm = rz_asm_new();
|
|
core->rasm->num = core->num;
|
|
core->rasm->core = core;
|
|
core->analysis = rz_analysis_new();
|
|
core->gadgets = rz_list_newf((RzListFree)rz_core_gadget_free);
|
|
core->analysis->ev = core->ev;
|
|
core->analysis->read_at = rz_core_analysis_read_at;
|
|
core->analysis->flag_get = rz_core_flag_get_by_spaces;
|
|
core->analysis->cb.on_fcn_new = on_fcn_new;
|
|
core->analysis->cb.on_fcn_delete = on_fcn_delete;
|
|
core->analysis->cb.on_fcn_rename = on_fcn_rename;
|
|
core->rasm->syscall = rz_syscall_ref(core->analysis->syscall); // BIND syscall analysis/asm
|
|
core->analysis->core = core;
|
|
core->parser = rz_parse_new();
|
|
rz_analysis_bind(core->analysis, &(core->parser->analb));
|
|
core->parser->var_expr_for_reg_access = rz_analysis_function_var_expr_for_reg_access_at;
|
|
/// XXX shouhld be using coreb
|
|
rz_parse_set_user_ptr(core->parser, core);
|
|
core->bin = rz_bin_new();
|
|
rz_event_hook(core->bin->event, RZ_EVENT_BIN_FILE_DEL, ev_binfiledel_cb, core);
|
|
rz_cons_bind(&core->bin->consb);
|
|
// XXX we shuold use RzConsBind instead of this hardcoded pointer
|
|
core->bin->cb_printf = (PrintfCallback)rz_cons_printf;
|
|
rz_bin_set_user_ptr(core->bin, core);
|
|
core->io = rz_io_new();
|
|
rz_io_plugin_add(core->io, &rz_core_io_plugin_vfile);
|
|
rz_event_hook(core->io->event, RZ_EVENT_IO_WRITE, ev_iowrite_cb, core);
|
|
rz_event_hook(core->io->event, RZ_EVENT_IO_DESC_CLOSE, ev_iodescclose_cb, core);
|
|
rz_event_hook(core->io->event, RZ_EVENT_IO_MAP_DEL, ev_iomapdel_cb, core);
|
|
core->io->ff = 1;
|
|
core->search = rz_search_new(RZ_SEARCH_KEYWORD);
|
|
core->flags = rz_flag_new();
|
|
core->graph = rz_agraph_new(rz_cons_canvas_new(1, 1));
|
|
core->graph->need_reload_nodes = false;
|
|
core->asmqjmps_size = RZ_CORE_ASMQJMPS_NUM;
|
|
if (sizeof(ut64) * core->asmqjmps_size < core->asmqjmps_size) {
|
|
core->asmqjmps_size = 0;
|
|
core->asmqjmps = NULL;
|
|
} else {
|
|
core->asmqjmps = RZ_NEWS(ut64, core->asmqjmps_size);
|
|
}
|
|
core->hash = rz_hash_new();
|
|
|
|
rz_bin_bind(core->bin, &(core->rasm->binb));
|
|
rz_bin_bind(core->bin, &(core->analysis->binb));
|
|
rz_bin_bind(core->bin, &(core->analysis->binb));
|
|
|
|
rz_io_bind(core->io, &(core->search->iob));
|
|
rz_io_bind(core->io, &(core->print->iob));
|
|
rz_io_bind(core->io, &(core->analysis->iob));
|
|
rz_io_bind(core->io, &(core->analysis->typedb->iob));
|
|
rz_io_bind(core->io, &(core->bin->iob));
|
|
rz_flag_bind(core->flags, &(core->analysis->flb));
|
|
core->analysis->flg_class_set = core_flg_class_set;
|
|
core->analysis->flg_class_get = core_flg_class_get;
|
|
core->analysis->flg_fcn_set = core_flg_fcn_set;
|
|
rz_analysis_bind(core->analysis, &(core->parser->analb));
|
|
core->parser->flag_get = rz_core_flag_get_by_spaces;
|
|
core->parser->label_get = rz_analysis_function_get_label_at;
|
|
|
|
rz_core_bind(core, &(core->analysis->coreb));
|
|
|
|
core->file = NULL;
|
|
core->files = rz_list_newf((RzListFree)rz_core_file_free);
|
|
core->offset = 0LL;
|
|
core->prompt_offset = 0LL;
|
|
rz_core_cmd_init(core);
|
|
rz_core_plugin_init(core);
|
|
|
|
RzBreakpointContext bp_ctx = {
|
|
.user = core,
|
|
.is_mapped = bp_is_mapped,
|
|
.maps_sync = bp_maps_sync,
|
|
.get_sw_breakpoint_at = bp_get_sw_breakpoint_at,
|
|
.get_sw_breakpoint_size_at = bp_get_sw_breakpoint_size_at,
|
|
};
|
|
core->dbg = rz_debug_new(&bp_ctx);
|
|
|
|
rz_io_bind(core->io, &(core->dbg->iob));
|
|
rz_io_bind(core->io, &(core->dbg->bp->iob));
|
|
rz_core_bind(core, &core->dbg->corebind);
|
|
rz_core_bind(core, &core->io->corebind);
|
|
core->dbg->analysis = core->analysis; // XXX: dupped instance.. can cause lost pointerz
|
|
// rz_debug_use (core->dbg, "native");
|
|
// XXX pushing uninitialized regstate results in trashed reg values
|
|
// rz_reg_arena_push (core->dbg->reg); // create a 2 level register state stack
|
|
// core->dbg->analysis->reg = core->analysis->reg; // XXX: dupped instance.. can cause lost pointerz
|
|
core->io->cb_printf = rz_cons_printf;
|
|
core->dbg->cb_printf = rz_cons_printf;
|
|
core->dbg->bp->cb_printf = rz_cons_printf;
|
|
core->dbg->ev = core->ev;
|
|
// Initialize visual modes after everything else but before config init
|
|
core->visual = rz_core_visual_new();
|
|
// initialize config before any corebind
|
|
rz_core_config_init(core);
|
|
|
|
rz_core_loadlibs_init(core);
|
|
|
|
// TODO: get arch from rz_bin or from native arch
|
|
rz_asm_use(core->rasm, RZ_SYS_ARCH);
|
|
rz_analysis_use(core->analysis, RZ_SYS_ARCH);
|
|
if (RZ_SYS_BITS & RZ_SYS_BITS_64) {
|
|
rz_config_set_i(core->config, "asm.bits", 64);
|
|
} else {
|
|
if (RZ_SYS_BITS & RZ_SYS_BITS_32) {
|
|
rz_config_set_i(core->config, "asm.bits", 32);
|
|
}
|
|
}
|
|
rz_config_set(core->config, "asm.arch", RZ_SYS_ARCH);
|
|
update_sdb(core);
|
|
{
|
|
char *a = rz_path_system(RZ_FLAGS);
|
|
if (a) {
|
|
char *file = rz_file_path_join(a, "tags.rz");
|
|
(void)rz_core_run_script(core, file);
|
|
free(file);
|
|
free(a);
|
|
}
|
|
}
|
|
rz_core_analysis_type_init(core);
|
|
return 0;
|
|
}
|
|
|
|
RZ_API void __cons_cb_fkey(RzCore *core, int fkey) {
|
|
char buf[32];
|
|
snprintf(buf, sizeof(buf), "key.f%d", fkey);
|
|
const char *v = rz_config_get(core->config, buf);
|
|
if (v && *v) {
|
|
rz_cons_printf("%s\n", v);
|
|
rz_core_cmd0(core, v);
|
|
rz_cons_flush();
|
|
}
|
|
}
|
|
|
|
RZ_API void rz_core_bind_cons(RzCore *core) {
|
|
core->cons->num = core->num;
|
|
core->cons->cb_fkey = (RzConsFunctionKey)__cons_cb_fkey;
|
|
core->cons->cb_editor = (RzConsEditorCallback)rz_core_editor;
|
|
core->cons->cb_break = (RzConsBreakCallback)rz_core_break;
|
|
core->cons->cb_sleep_begin = (RzConsSleepBeginCallback)rz_core_sleep_begin;
|
|
core->cons->cb_sleep_end = (RzConsSleepEndCallback)rz_core_sleep_end;
|
|
core->cons->cb_task_oneshot = (RzConsQueueTaskOneshot)rz_core_task_enqueue_oneshot;
|
|
core->cons->user = (void *)core;
|
|
}
|
|
|
|
RZ_API void rz_core_fini(RzCore *c) {
|
|
if (!c) {
|
|
return;
|
|
}
|
|
RZ_FREE_CUSTOM(c->lib, rz_lib_free);
|
|
rz_core_plugin_fini(c);
|
|
rz_core_task_break_all(&c->tasks);
|
|
rz_core_task_join(&c->tasks, NULL, -1);
|
|
rz_core_wait(c);
|
|
// avoid double free
|
|
RZ_FREE_CUSTOM(c->hash, rz_hash_free);
|
|
RZ_FREE_CUSTOM(c->ropchain, rz_list_free);
|
|
RZ_FREE_CUSTOM(c->ev, rz_event_free);
|
|
RZ_FREE(c->cmdlog);
|
|
RZ_FREE(c->lastsearch);
|
|
RZ_FREE(c->cons->pager);
|
|
RZ_FREE(c->cmdqueue);
|
|
RZ_FREE(c->lastcmd);
|
|
RZ_FREE(c->stkcmd);
|
|
RZ_FREE(c->block);
|
|
|
|
RZ_FREE_CUSTOM(c->gadgets, rz_list_free);
|
|
RZ_FREE_CUSTOM(c->num, rz_num_free);
|
|
RZ_FREE(c->table_query);
|
|
RZ_FREE_CUSTOM(c->io, rz_io_free);
|
|
RZ_FREE_CUSTOM(c->files, rz_list_free);
|
|
RZ_FREE_CUSTOM(c->watchers, rz_list_free);
|
|
RZ_FREE_CUSTOM(c->scriptstack, rz_list_free);
|
|
rz_core_task_scheduler_fini(&c->tasks);
|
|
RZ_FREE_CUSTOM(c->rcmd, rz_cmd_free);
|
|
RZ_FREE_CUSTOM(c->cmd_descriptors, rz_list_free);
|
|
RZ_FREE_CUSTOM(c->analysis, rz_analysis_free);
|
|
RZ_FREE_CUSTOM(c->rasm, rz_asm_free);
|
|
RZ_FREE_CUSTOM(c->print, rz_print_free);
|
|
RZ_FREE_CUSTOM(c->bin, rz_bin_free);
|
|
RZ_FREE_CUSTOM(c->lang, rz_lang_free);
|
|
RZ_FREE_CUSTOM(c->dbg, rz_debug_free);
|
|
RZ_FREE_CUSTOM(c->config, rz_config_free);
|
|
/* after rz_config_free, the value of I.teefile is trashed */
|
|
/* rconfig doesnt knows how to deinitialize vars, so we
|
|
should probably need to add a rz_config_free_payload callback */
|
|
rz_cons_free();
|
|
rz_cons_singleton()->teefile = NULL; // HACK
|
|
RZ_FREE_CUSTOM(c->search, rz_search_free);
|
|
RZ_FREE_CUSTOM(c->flags, rz_flag_free);
|
|
RZ_FREE_CUSTOM(c->egg, rz_egg_free);
|
|
RZ_FREE_CUSTOM(c->crypto, rz_crypto_free);
|
|
RZ_FREE_CUSTOM(c->yank_buf, rz_buf_free);
|
|
RZ_FREE_CUSTOM(c->graph, rz_agraph_free);
|
|
RZ_FREE(c->asmqjmps);
|
|
RZ_FREE_CUSTOM(c->sdb, sdb_free);
|
|
RZ_FREE_CUSTOM(c->parser, rz_parse_free);
|
|
RZ_FREE(c->times);
|
|
rz_core_seek_free(c);
|
|
RZ_FREE(c->rtr_host);
|
|
RZ_FREE(c->curtheme);
|
|
RZ_FREE_CUSTOM(c->visual, rz_core_visual_free);
|
|
RZ_FREE_CUSTOM(c->warnings_after, rz_list_free);
|
|
}
|
|
|
|
RZ_API void rz_core_free(RzCore *c) {
|
|
rz_core_fini(c);
|
|
free(c);
|
|
}
|
|
|
|
RZ_API void rz_core_prompt_loop(RzCore *r) {
|
|
int ret;
|
|
do {
|
|
if (rz_config_get_b(r->config, "dbg.status")) {
|
|
rz_core_debug_print_status(r);
|
|
}
|
|
int err = rz_core_prompt(r, false);
|
|
if (err < 1) {
|
|
// handle ^D
|
|
r->num->value = 0; // r.num->value will be read by rz_main_rizin() after calling this fcn
|
|
break;
|
|
}
|
|
/* -1 means invalid command, -2 means quit prompt loop */
|
|
if ((ret = rz_core_prompt_exec(r)) == -2) {
|
|
break;
|
|
}
|
|
} while (ret != RZ_CORE_CMD_EXIT);
|
|
}
|
|
|
|
static bool prompt_add_file(RzCore *core, RzStrBuf *sb, bool add_sep) {
|
|
if (!rz_config_get_b(core->config, "scr.prompt.file") || !core->io->desc) {
|
|
return add_sep;
|
|
}
|
|
if (add_sep) {
|
|
rz_strbuf_append(sb, ":");
|
|
}
|
|
rz_strbuf_append(sb, rz_file_basename(core->io->desc->name));
|
|
return true;
|
|
}
|
|
|
|
static bool prompt_add_section(RzCore *core, RzStrBuf *sb, bool add_sep) {
|
|
if (!rz_config_get_b(core->config, "scr.prompt.sect")) {
|
|
return add_sep;
|
|
}
|
|
const RzBinSection *sec = rz_bin_get_section_at(rz_bin_cur_object(core->bin), core->offset, true);
|
|
if (!sec) {
|
|
return add_sep;
|
|
}
|
|
if (add_sep) {
|
|
rz_strbuf_append(sb, ":");
|
|
}
|
|
rz_strbuf_append(sb, sec->name);
|
|
return true;
|
|
}
|
|
|
|
static bool prompt_add_offset(RzCore *core, RzStrBuf *sb, bool add_sep) {
|
|
if (add_sep) {
|
|
rz_strbuf_append(sb, ":");
|
|
}
|
|
if (rz_config_get_b(core->config, "scr.prompt.flag")) {
|
|
const RzFlagItem *f = rz_flag_get_at(core->flags, core->offset, true);
|
|
if (f) {
|
|
if (f->offset < core->offset) {
|
|
rz_strbuf_appendf(sb, "%s + %" PFMT64u, f->name, core->offset - f->offset);
|
|
} else {
|
|
rz_strbuf_appendf(sb, "%s", f->name);
|
|
}
|
|
if (rz_config_get_b(core->config, "scr.prompt.flag.only")) {
|
|
return true;
|
|
}
|
|
|
|
rz_strbuf_append(sb, ":");
|
|
}
|
|
}
|
|
|
|
if (rz_config_get_b(core->config, "asm.segoff")) {
|
|
ut32 a, b;
|
|
unsigned int seggrn = rz_config_get_i(core->config, "asm.seggrn");
|
|
|
|
a = ((core->offset >> 16) << (16 - seggrn));
|
|
b = (core->offset & 0xffff);
|
|
rz_strbuf_appendf(sb, "%04x:%04x", a, b);
|
|
} else {
|
|
if (core->print->wide_offsets && core->dbg->bits & RZ_SYS_BITS_64) {
|
|
rz_strbuf_appendf(sb, "0x%016" PFMT64x, core->offset);
|
|
} else {
|
|
rz_strbuf_appendf(sb, "0x%08" PFMT64x, core->offset);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void set_prompt(RzCore *r) {
|
|
RzLine *line = r->cons->line;
|
|
const char *cmdprompt = rz_config_get(r->config, "cmd.prompt");
|
|
const char *BEGIN = "";
|
|
const char *END = "";
|
|
const char *remote = "";
|
|
|
|
if (cmdprompt && *cmdprompt) {
|
|
rz_core_cmd(r, cmdprompt, 0);
|
|
}
|
|
|
|
if (r->cmdremote) {
|
|
char *s = rz_core_cmd_str(r, "s");
|
|
r->offset = rz_num_math(NULL, s);
|
|
free(s);
|
|
remote = "R!";
|
|
}
|
|
|
|
if (rz_config_get_i(r->config, "scr.color")) {
|
|
BEGIN = r->cons->context->pal.prompt;
|
|
END = r->cons->context->pal.reset;
|
|
}
|
|
|
|
RzStrBuf prompt_ct;
|
|
rz_strbuf_init(&prompt_ct);
|
|
rz_strbuf_appendf(&prompt_ct, "%s[%s", BEGIN, remote);
|
|
bool added = prompt_add_file(r, &prompt_ct, false);
|
|
added = prompt_add_section(r, &prompt_ct, added);
|
|
added = prompt_add_offset(r, &prompt_ct, added);
|
|
rz_strbuf_appendf(&prompt_ct, "]>%s ", END);
|
|
|
|
char *prompt = rz_strbuf_drain_nofree(&prompt_ct);
|
|
rz_line_set_prompt(line, prompt);
|
|
free(prompt);
|
|
}
|
|
|
|
RZ_API int rz_core_prompt(RzCore *r, int sync) {
|
|
char line[4096];
|
|
|
|
int rnv = r->num->value;
|
|
set_prompt(r);
|
|
int ret = rz_cons_fgets(line, sizeof(line), 0, NULL);
|
|
if (ret == -2) {
|
|
return RZ_CORE_CMD_EXIT; // ^D
|
|
}
|
|
if (ret == -1) {
|
|
return false; // FD READ ERROR
|
|
}
|
|
r->num->value = rnv;
|
|
if (sync) {
|
|
return rz_core_prompt_exec(r);
|
|
}
|
|
free(r->cmdqueue);
|
|
r->cmdqueue = rz_str_dup(line);
|
|
if (r->scr_gadgets && *line && *line != 'q') {
|
|
rz_core_gadget_print(r);
|
|
}
|
|
r->num->value = r->rc;
|
|
return true;
|
|
}
|
|
|
|
RZ_API int rz_core_prompt_exec(RzCore *r) {
|
|
int ret = rz_core_cmd(r, r->cmdqueue, true);
|
|
r->rc = r->num->value;
|
|
// int ret = rz_core_cmd (r, r->cmdqueue, true);
|
|
rz_cons_echo(NULL);
|
|
rz_cons_flush();
|
|
if (r->cons && r->cons->line && r->cons->line->zerosep) {
|
|
rz_cons_zero();
|
|
}
|
|
rz_core_print_warnings_after(r);
|
|
return ret;
|
|
}
|
|
|
|
RZ_API bool rz_core_block_size(RzCore *core, ut32 bsize) {
|
|
ut8 *bump;
|
|
if (bsize == core->blocksize) {
|
|
return true;
|
|
} else if (bsize < 1) {
|
|
bsize = 1;
|
|
} else if (core->blocksize_max && bsize > core->blocksize_max) {
|
|
RZ_LOG_ERROR("block size is bigger than its max 0x%x (check `bm` command)\n", core->blocksize_max);
|
|
return false;
|
|
}
|
|
bump = realloc(core->block, bsize + 1);
|
|
if (!bump) {
|
|
RZ_LOG_ERROR("Oops. cannot allocate that much (%u)\n", bsize);
|
|
return false;
|
|
}
|
|
core->block = bump;
|
|
core->blocksize = bsize;
|
|
memset(core->block, 0xff, core->blocksize);
|
|
rz_core_seek(core, core->offset, true);
|
|
return true;
|
|
}
|
|
|
|
RZ_API char *rz_core_op_str(RzCore *core, ut64 addr) {
|
|
RzAsmOp op = { 0 };
|
|
ut8 buf[64];
|
|
rz_asm_set_pc(core->rasm, addr);
|
|
rz_io_read_at(core->io, addr, buf, sizeof(buf));
|
|
int ret = rz_asm_disassemble(core->rasm, &op, buf, sizeof(buf));
|
|
char *str = (ret > 0) ? rz_str_dup(rz_strbuf_get(&op.buf_asm)) : NULL;
|
|
rz_asm_op_fini(&op);
|
|
return str;
|
|
}
|
|
|
|
RZ_API RzAnalysisOp *rz_core_op_analysis(RzCore *core, ut64 addr, RzAnalysisOpMask mask) {
|
|
ut8 buf[64];
|
|
RzAnalysisOp *op = rz_analysis_op_new();
|
|
rz_io_read_at(core->io, addr, buf, sizeof(buf));
|
|
rz_analysis_op(core->analysis, op, addr, buf, sizeof(buf), mask);
|
|
return op;
|
|
}
|
|
|
|
RZ_API int rz_core_search_cb(RzCore *core, ut64 from, ut64 to, RzCoreSearchCallback cb) {
|
|
int ret, len = core->blocksize;
|
|
ut8 *buf = malloc(len);
|
|
if (!buf) {
|
|
RZ_LOG_ERROR("core: cannot allocate blocksize\n");
|
|
return false;
|
|
}
|
|
while (from < to) {
|
|
ut64 delta = to - from;
|
|
if (delta < len) {
|
|
len = (int)delta;
|
|
}
|
|
if (!rz_io_read_at(core->io, from, buf, len)) {
|
|
RZ_LOG_ERROR("core: cannot read at 0x%" PFMT64x "\n", from);
|
|
break;
|
|
}
|
|
for (ret = 0; ret < len;) {
|
|
int done = cb(core, from, buf + ret, len - ret);
|
|
if (done < 1) { /* interrupted */
|
|
free(buf);
|
|
return false;
|
|
}
|
|
ret += done;
|
|
}
|
|
from += len;
|
|
}
|
|
free(buf);
|
|
return true;
|
|
}
|
|
|
|
RZ_API RZ_OWN char *rz_core_editor(const RzCore *core, RZ_NULLABLE const char *file, RZ_NULLABLE const char *str) {
|
|
const bool interactive = rz_cons_is_interactive();
|
|
if (!interactive) {
|
|
return NULL;
|
|
}
|
|
|
|
const char *editor = rz_config_get(core->config, "cfg.editor");
|
|
if (RZ_STR_ISEMPTY(editor)) {
|
|
RZ_LOG_ERROR("core: please set \"cfg.editor\" to run the editor\n");
|
|
return NULL;
|
|
}
|
|
char *name = NULL, *ret = NULL;
|
|
int fd;
|
|
|
|
bool readonly = false;
|
|
if (file && *file != '*') {
|
|
name = rz_str_dup(file);
|
|
fd = rz_sys_open(file, O_RDWR, 0644);
|
|
if (fd == -1) {
|
|
fd = rz_sys_open(file, O_RDWR | O_CREAT, 0644);
|
|
if (fd == -1) {
|
|
fd = rz_sys_open(file, O_RDONLY, 0644);
|
|
readonly = true;
|
|
}
|
|
}
|
|
} else {
|
|
fd = rz_file_mkstemp(file, &name);
|
|
}
|
|
if (fd == -1) {
|
|
free(name);
|
|
return NULL;
|
|
}
|
|
if (readonly) {
|
|
RZ_LOG_WARN("core: opening in read-only\n");
|
|
} else {
|
|
if (str) {
|
|
const size_t str_len = strlen(str);
|
|
if (write(fd, str, str_len) != str_len) {
|
|
close(fd);
|
|
free(name);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
close(fd);
|
|
|
|
if (name) {
|
|
char *escaped_name = rz_str_escape_sh(name);
|
|
rz_sys_cmdf("%s \"%s\"", editor, escaped_name);
|
|
free(escaped_name);
|
|
}
|
|
size_t len = 0;
|
|
ret = name ? rz_file_slurp(name, &len) : 0;
|
|
if (ret) {
|
|
if (len && ret[len - 1] == '\n') {
|
|
ret[len - 1] = 0; // chop
|
|
}
|
|
if (!file) {
|
|
rz_file_rm(name);
|
|
}
|
|
}
|
|
free(name);
|
|
return ret;
|
|
}
|
|
|
|
/* weak getters */
|
|
RZ_API RzCons *rz_core_get_cons(RzCore *core) {
|
|
return core->cons;
|
|
}
|
|
|
|
RZ_API RzConfig *rz_core_get_config(RzCore *core) {
|
|
return core->config;
|
|
}
|
|
|
|
RZ_API RzBin *rz_core_get_bin(RzCore *core) {
|
|
return core->bin;
|
|
}
|
|
|
|
RZ_API RzBuffer *rz_core_syscallf(RzCore *core, const char *name, const char *fmt, ...) {
|
|
char str[1024];
|
|
RzBuffer *buf;
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
|
|
vsnprintf(str, sizeof(str), fmt, ap);
|
|
buf = rz_core_syscall(core, name, str);
|
|
|
|
va_end(ap);
|
|
return buf;
|
|
}
|
|
|
|
RZ_API RzBuffer *rz_core_syscall(RzCore *core, const char *name, const char *args) {
|
|
char code[1024];
|
|
int num;
|
|
|
|
// arch check
|
|
if (strcmp(core->analysis->cur->arch, "x86")) {
|
|
RZ_LOG_ERROR("architecture '%s' is not yet supported!\n", core->analysis->cur->arch);
|
|
return 0;
|
|
}
|
|
|
|
num = rz_syscall_get_num(core->analysis->syscall, name);
|
|
|
|
// bits check
|
|
switch (core->rasm->bits) {
|
|
case 32:
|
|
if (strcmp(name, "setup") && !num) {
|
|
RZ_LOG_ERROR("core: syscall not found!\n");
|
|
return 0;
|
|
}
|
|
break;
|
|
case 64:
|
|
if (strcmp(name, "read") && !num) {
|
|
RZ_LOG_ERROR("core: syscall not found!\n");
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
RZ_LOG_ERROR("core: syscall not found!\n");
|
|
return 0;
|
|
}
|
|
|
|
snprintf(code, sizeof(code),
|
|
"sc@syscall(%d);\n"
|
|
"main@global(0) { sc(%s);\n"
|
|
":int3\n" /// XXX USE trap
|
|
"}\n",
|
|
num, args);
|
|
rz_egg_reset(core->egg);
|
|
// TODO: setup arch/bits/os?
|
|
rz_egg_load(core->egg, code, 0);
|
|
|
|
if (!rz_egg_compile(core->egg)) {
|
|
RZ_LOG_ERROR("core: cannot compile.\n");
|
|
}
|
|
if (!rz_egg_assemble(core->egg)) {
|
|
RZ_LOG_ERROR("core: rz_egg_assemble: invalid assembly\n");
|
|
}
|
|
return rz_egg_get_bin(core->egg);
|
|
}
|
|
|
|
RZ_API RzTable *rz_core_table(RzCore *core) {
|
|
RzTable *table = rz_table_new();
|
|
if (table) {
|
|
table->cons = core->cons;
|
|
}
|
|
return table;
|
|
}
|
|
|
|
static RzCmdStatus core_core_plugin_print(RzCorePlugin *cp, RzCmdStateOutput *state) {
|
|
const char *name = rz_str_get(cp->name);
|
|
const char *desc = rz_str_get(cp->desc);
|
|
const char *author = rz_str_get(cp->author);
|
|
const char *version = rz_str_get(cp->version);
|
|
const char *license = rz_str_get(cp->license);
|
|
|
|
PJ *pj = state->d.pj;
|
|
switch (state->mode) {
|
|
case RZ_OUTPUT_MODE_TABLE:
|
|
rz_table_add_rowf(state->d.t, "sssss", name, license, author, version, desc);
|
|
break;
|
|
case RZ_OUTPUT_MODE_JSON: {
|
|
pj_o(pj);
|
|
pj_ks(pj, "name", name);
|
|
pj_ks(pj, "description", desc);
|
|
pj_ks(pj, "author", author);
|
|
pj_ks(pj, "version", version);
|
|
pj_ks(pj, "license", license);
|
|
pj_end(pj);
|
|
break;
|
|
}
|
|
case RZ_OUTPUT_MODE_STANDARD: {
|
|
rz_cons_printf("%s: %s (Made by %s, v%s, %s)\n",
|
|
name, desc, author, version, license);
|
|
break;
|
|
}
|
|
case RZ_OUTPUT_MODE_QUIET:
|
|
rz_cons_println(name);
|
|
break;
|
|
default: {
|
|
rz_warn_if_reached();
|
|
return RZ_CMD_STATUS_NONEXISTINGCMD;
|
|
}
|
|
}
|
|
return RZ_CMD_STATUS_OK;
|
|
}
|
|
|
|
RZ_API RzCmdStatus rz_core_core_plugins_print(RzCore *core, RzCmdStateOutput *state) {
|
|
RzIterator *iter = ht_sp_as_iter(core->plugins);
|
|
RzCorePlugin **val;
|
|
RzCmdStatus status;
|
|
if (!core) {
|
|
return RZ_CMD_STATUS_ERROR;
|
|
}
|
|
rz_cmd_state_output_array_start(state);
|
|
rz_cmd_state_output_set_columnsf(state, "sssss", "name", "license", "author", "version", "description");
|
|
rz_iterator_foreach(iter, val) {
|
|
RzCorePlugin *cp = *val;
|
|
status = core_core_plugin_print(cp, state);
|
|
if (status != RZ_CMD_STATUS_OK) {
|
|
return status;
|
|
}
|
|
}
|
|
rz_iterator_free(iter);
|
|
rz_cmd_state_output_array_end(state);
|
|
return RZ_CMD_STATUS_OK;
|
|
}
|