* core/cmd: Adjust math commands * shell: make `?x` commands a parsing failure We have moved the `?x` commands to `%x`, thus now no command should start with `?`. This patch makes the parser fail to parse `?x` strings. * core/tui: use APIs in panels * There's no `%q` anymore, use `%=`
529 lines
14 KiB
C
529 lines
14 KiB
C
// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <string.h>
|
|
|
|
#include <rz_core.h>
|
|
#include <rz_util.h>
|
|
|
|
#include "../core_private.h"
|
|
#include <rz_asm.h>
|
|
#include <rz_util/rz_print.h>
|
|
#include <rz_util/rz_strbuf.h>
|
|
|
|
enum {
|
|
RZ_BYTE_DATA = 1,
|
|
RZ_WORD_DATA = 2,
|
|
RZ_DWORD_DATA = 4,
|
|
RZ_QWORD_DATA = 8
|
|
};
|
|
|
|
// define the data at offset according to the type (byte, word...) n times
|
|
static void define_data_ntimes(RzCore *core, ut64 off, int times, int type) {
|
|
int i = 0;
|
|
rz_meta_del(core->analysis, RZ_META_TYPE_ANY, off, core->blocksize);
|
|
if (times < 0) {
|
|
times = 1;
|
|
}
|
|
for (i = 0; i < times; i++, off += type) {
|
|
rz_meta_set(core->analysis, RZ_META_TYPE_DATA, off, type, "");
|
|
}
|
|
}
|
|
|
|
static bool isDisasmPrint(int mode) {
|
|
return (mode == 1 || mode == 2);
|
|
}
|
|
|
|
static void handleHints(RzCore *core) {
|
|
// TODO extend for more analysis hints
|
|
int i = 0;
|
|
char ch[64] = RZ_EMPTY;
|
|
const char *lines[] = { "[dh]- Define analysis hint:", " b [16,32,64] set bits hint", NULL };
|
|
for (i = 0; lines[i]; i++) {
|
|
rz_cons_fill_line();
|
|
rz_cons_printf("\r%s\n", lines[i]);
|
|
}
|
|
rz_cons_flush();
|
|
rz_line_set_prompt("analysis hint: ");
|
|
if (rz_cons_fgets(ch, sizeof(ch), 0, NULL) > 0) {
|
|
switch (ch[0]) {
|
|
case 'b': {
|
|
char *arg = ch + 1;
|
|
rz_str_trim(arg);
|
|
int bits = atoi(arg);
|
|
if (bits == 8 || bits == 16 || bits == 32 || bits == 64) {
|
|
rz_analysis_hint_set_bits(core->analysis, core->offset, bits);
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_IPI void rz_core_visual_define(RzCore *core, const char *args, int distance) {
|
|
RzCoreVisual *visual = core->visual;
|
|
int plen = core->blocksize;
|
|
ut64 off = core->offset;
|
|
int i, h = 0, n, ch, ntotal = 0;
|
|
ut8 *p = core->block;
|
|
int rep = -1;
|
|
char *name;
|
|
int delta = 0;
|
|
if (core->print->cur_enabled) {
|
|
int cur = core->print->cur;
|
|
if (core->print->ocur != -1) {
|
|
plen = RZ_ABS(core->print->cur - core->print->ocur) + 1;
|
|
if (core->print->ocur < cur) {
|
|
cur = core->print->ocur;
|
|
}
|
|
}
|
|
off += cur;
|
|
p += cur;
|
|
}
|
|
(void)rz_cons_get_size(&h);
|
|
h -= 19;
|
|
if (h < 0) {
|
|
h = 0;
|
|
rz_cons_clear00();
|
|
} else {
|
|
rz_cons_gotoxy(0, 3);
|
|
}
|
|
const char *lines[] = { "", "[Vd]- Define current block as:", " $ define flag size", " 1 edit bits", " a assembly", " b as byte (1 byte)", " B define half word (16 bit, 2 byte size)", " c as code (unset any data / string / format) in here", " C define flag color (fc)", " d set as data", " e end of function", " f analyze function", " F format", " h define hint (for half-word, see 'B')", " i (ahi) immediate base (b(in), o(ct), d(ec), h(ex), s(tr))", " I (ahi1) immediate base (b(in), o(ct), d(ec), h(ex), s(tr))", " j merge down (join this and next functions)", " k merge up (join this and previous function)", " h define analysis hint", " m manpage for current call", " n rename flag used at cursor", " N edit function signature (afs!)", " o opcode string", " r rename function", " R find references /r", " s set string", " S set strings in current block", " t set opcode type via aht hints (call, nop, jump, ...)", " u undefine metadata here", " v rename variable at offset that matches some hex digits", " x find xrefs to current address (./r)", " w set as 32bit word", " W set as 64bit word", " q quit menu", " z zone flag", NULL };
|
|
for (i = 0; lines[i]; i++) {
|
|
rz_cons_fill_line();
|
|
rz_cons_printf("\r%s\n", lines[i]);
|
|
}
|
|
rz_cons_flush();
|
|
int wordsize = 0;
|
|
// get ESC+char, return 'hjkl' char
|
|
repeat:
|
|
if (*args) {
|
|
ch = *args;
|
|
args++;
|
|
} else {
|
|
ch = rz_cons_arrow_to_hjkl(rz_cons_readchar());
|
|
}
|
|
|
|
onemoretime:
|
|
wordsize = 4;
|
|
switch (ch) {
|
|
case 'N':
|
|
rz_core_analysis_function_signature_editor(core, off);
|
|
break;
|
|
case 'F': {
|
|
char cmd[128];
|
|
rz_cons_show_cursor(true);
|
|
rz_core_cmd0(core, "pf?");
|
|
rz_cons_flush();
|
|
rz_line_set_prompt("format: ");
|
|
strcpy(cmd, "Cf 0 ");
|
|
if (rz_cons_fgets(cmd + 5, sizeof(cmd) - 5, 0, NULL) > 0) {
|
|
rz_core_cmdf(core, "%s @ 0x%08" PFMT64x, cmd, off);
|
|
rz_cons_set_raw(1);
|
|
rz_cons_show_cursor(false);
|
|
}
|
|
} break;
|
|
case '1':
|
|
rz_core_visual_bit_editor(core);
|
|
break;
|
|
case 't':
|
|
case 'o': {
|
|
char str[128];
|
|
rz_cons_show_cursor(true);
|
|
rz_line_set_prompt(ch == 't' ? "type: " : "opstr: ");
|
|
if (rz_cons_fgets(str, sizeof(str), 0, NULL) > 0) {
|
|
rz_core_cmdf(core, "ah%c %s @ 0x%" PFMT64x, ch, str, off);
|
|
}
|
|
} break;
|
|
case 'x':
|
|
rz_core_cmd0(core, "/r $$");
|
|
break;
|
|
case 'i': {
|
|
char str[128];
|
|
rz_cons_show_cursor(true);
|
|
rz_line_set_prompt("immbase: ");
|
|
if (rz_cons_fgets(str, sizeof(str), 0, NULL) > 0) {
|
|
int base = rz_num_base_of_string(core->num, str);
|
|
rz_analysis_hint_set_immbase(core->analysis, off, base);
|
|
}
|
|
} break;
|
|
case 'I': {
|
|
char str[128];
|
|
rz_cons_show_cursor(true);
|
|
rz_line_set_prompt("immbase: ");
|
|
if (rz_cons_fgets(str, sizeof(str), 0, NULL) > 0) {
|
|
rz_core_cmdf(core, "ahi1 %s @ 0x%" PFMT64x, str, off);
|
|
}
|
|
} break;
|
|
case 'a':
|
|
rz_core_visual_asm(core, off);
|
|
break;
|
|
case 'b':
|
|
if (plen != core->blocksize) {
|
|
rep = plen / 2;
|
|
}
|
|
define_data_ntimes(core, off, rep, RZ_BYTE_DATA);
|
|
wordsize = 1;
|
|
break;
|
|
case 'B': // "VdB"
|
|
if (plen != core->blocksize) {
|
|
rep = plen / 2;
|
|
}
|
|
define_data_ntimes(core, off, rep, RZ_WORD_DATA);
|
|
wordsize = 2;
|
|
break;
|
|
case 'w':
|
|
if (plen != core->blocksize) {
|
|
rep = plen / 4;
|
|
}
|
|
define_data_ntimes(core, off, rep, RZ_DWORD_DATA);
|
|
wordsize = 4;
|
|
break;
|
|
case 'W':
|
|
if (plen != core->blocksize) {
|
|
rep = plen / 8;
|
|
}
|
|
define_data_ntimes(core, off, rep, RZ_QWORD_DATA);
|
|
wordsize = 8;
|
|
break;
|
|
case 'm': {
|
|
char *man = NULL;
|
|
/* check for manpage */
|
|
RzAnalysisOp *op = rz_core_analysis_op(core, off, RZ_ANALYSIS_OP_MASK_BASIC);
|
|
if (op) {
|
|
if (op->jump != UT64_MAX) {
|
|
RzFlagItem *item = rz_flag_get_i(core->flags, op->jump);
|
|
if (item) {
|
|
const char *ptr = rz_str_lchr(item->name, '.');
|
|
if (ptr) {
|
|
man = strdup(ptr + 1);
|
|
}
|
|
}
|
|
}
|
|
rz_analysis_op_free(op);
|
|
}
|
|
if (man) {
|
|
char *p = strstr(man, "INODE");
|
|
if (p) {
|
|
*p = 0;
|
|
}
|
|
rz_cons_clear();
|
|
rz_cons_flush();
|
|
rz_sys_cmdf("man %s", man);
|
|
free(man);
|
|
}
|
|
rz_cons_any_key(NULL);
|
|
} break;
|
|
case 'n': {
|
|
RzAnalysisOp op;
|
|
ut64 tgt_addr = UT64_MAX;
|
|
if (!isDisasmPrint(visual->printidx)) {
|
|
break;
|
|
}
|
|
// TODO: get the aligned instruction even if the cursor is in the middle of it.
|
|
rz_analysis_op(core->analysis, &op, off,
|
|
core->block + off - core->offset, 32, RZ_ANALYSIS_OP_MASK_BASIC);
|
|
|
|
tgt_addr = op.jump != UT64_MAX ? op.jump : op.ptr;
|
|
RzAnalysisVar *var = rz_analysis_get_used_function_var(core->analysis, op.addr);
|
|
if (var) {
|
|
char *newname = rz_cons_input(sdb_fmt("New variable name for '%s': ", var->name));
|
|
if (newname && *newname) {
|
|
rz_analysis_var_rename(var, newname, true);
|
|
free(newname);
|
|
}
|
|
} else if (tgt_addr != UT64_MAX) {
|
|
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, tgt_addr);
|
|
RzFlagItem *f = rz_flag_get_i(core->flags, tgt_addr);
|
|
if (fcn) {
|
|
char *msg = rz_str_newf("Rename function %s to: ", fcn->name);
|
|
char *newname = rz_cons_input(msg);
|
|
free(msg);
|
|
rz_core_analysis_function_rename(core, tgt_addr, newname);
|
|
free(newname);
|
|
} else if (f) {
|
|
char *msg = rz_str_newf("Rename flag %s to: ", f->name);
|
|
char *newname = rz_cons_input(msg);
|
|
free(msg);
|
|
rz_flag_rename(core->flags, f, newname);
|
|
free(newname);
|
|
} else {
|
|
char *msg = rz_str_newf("Create flag at 0x%" PFMT64x " named: ", tgt_addr);
|
|
char *newname = rz_cons_input(msg);
|
|
free(msg);
|
|
rz_flag_set(core->flags, newname, tgt_addr, 1);
|
|
free(newname);
|
|
}
|
|
}
|
|
|
|
rz_analysis_op_fini(&op);
|
|
break;
|
|
}
|
|
case 'C': {
|
|
RzFlagItem *item = rz_flag_get_i(core->flags, off);
|
|
if (item) {
|
|
char cmd[128];
|
|
rz_cons_show_cursor(true);
|
|
rz_cons_flush();
|
|
rz_line_set_prompt("color: ");
|
|
if (rz_cons_fgets(cmd, sizeof(cmd), 0, NULL) > 0) {
|
|
rz_flag_item_set_color(item, cmd);
|
|
rz_cons_set_raw(1);
|
|
rz_cons_show_cursor(false);
|
|
}
|
|
} else {
|
|
eprintf("Sorry. No flag here\n");
|
|
rz_cons_any_key(NULL);
|
|
}
|
|
} break;
|
|
case '$': {
|
|
RzFlagItem *item = rz_flag_get_i(core->flags, off);
|
|
if (item) {
|
|
char cmd[128];
|
|
rz_cons_printf("Current flag size is: %" PFMT64d "\n", item->size);
|
|
rz_cons_show_cursor(true);
|
|
rz_cons_flush();
|
|
rz_line_set_prompt("new size: ");
|
|
if (rz_cons_fgets(cmd, sizeof(cmd), 0, NULL) > 0) {
|
|
item->size = rz_num_math(core->num, cmd);
|
|
rz_cons_set_raw(1);
|
|
rz_cons_show_cursor(false);
|
|
}
|
|
} else {
|
|
eprintf("Sorry. No flag here\n");
|
|
rz_cons_any_key(NULL);
|
|
}
|
|
} break;
|
|
case 'e':
|
|
// set function size
|
|
{
|
|
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, off, 0);
|
|
if (!fcn) {
|
|
fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
|
|
}
|
|
if (fcn) {
|
|
RzAnalysisOp op;
|
|
ut64 size;
|
|
if (rz_analysis_op(core->analysis, &op, off, core->block + delta,
|
|
core->blocksize - delta, RZ_ANALYSIS_OP_MASK_BASIC) > 0) {
|
|
size = off - fcn->addr + op.size;
|
|
rz_analysis_function_resize(fcn, size);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 'j': {
|
|
ut64 addr = rz_num_math(core->num, "$$+$F");
|
|
rz_core_analysis_fcn_merge(core, off, addr);
|
|
} break;
|
|
case 'k':
|
|
eprintf("TODO: merge up\n");
|
|
rz_cons_any_key(NULL);
|
|
break;
|
|
// very weak and incomplete
|
|
case 'h': // "Vdh"
|
|
handleHints(core);
|
|
break;
|
|
case 'r': // "Vdr"
|
|
rz_core_cmdf(core, "%%i new function name;afn `yp` @ 0x%08" PFMT64x, off);
|
|
break;
|
|
case 'z': // "Vdz"
|
|
rz_core_cmdf(core, "%%i zone name;fz `yp` @ 0x%08" PFMT64x, off);
|
|
break;
|
|
case 'R': // "VdR"
|
|
eprintf("Finding references to 0x%08" PFMT64x " ...\n", off);
|
|
rz_core_cmdf(core, "./r 0x%08" PFMT64x " @ $S", off);
|
|
break;
|
|
case 'S': {
|
|
int i, j;
|
|
bool is_wide = false;
|
|
do {
|
|
n = rz_str_nlen_w((const char *)p + ntotal,
|
|
plen - ntotal) +
|
|
1;
|
|
if (n < 2) {
|
|
break;
|
|
}
|
|
name = malloc(n + 10);
|
|
strcpy(name, "str.");
|
|
for (i = 0, j = 0; i < n; i++, j++) {
|
|
name[4 + i] = p[j + ntotal];
|
|
if (!p[j + ntotal]) {
|
|
break;
|
|
}
|
|
if (!p[j + 1 + ntotal]) {
|
|
// check if is still wide
|
|
if (j + 3 + ntotal < n) {
|
|
if (p[j + 3]) {
|
|
break;
|
|
}
|
|
}
|
|
is_wide = true;
|
|
j++;
|
|
}
|
|
}
|
|
name[4 + n] = '\0';
|
|
if (is_wide) {
|
|
rz_meta_set(core->analysis, RZ_META_TYPE_STRING,
|
|
off + ntotal, (n * 2) + ntotal,
|
|
(const char *)name + 4);
|
|
} else {
|
|
rz_meta_set(core->analysis, RZ_META_TYPE_STRING,
|
|
off + ntotal, n + ntotal,
|
|
(const char *)name + 4);
|
|
}
|
|
rz_name_filter(name, n + 10, true);
|
|
rz_flag_set(core->flags, name, off + ntotal, n);
|
|
free(name);
|
|
if (is_wide) {
|
|
ntotal += n * 2 - 1;
|
|
} else {
|
|
ntotal += n;
|
|
}
|
|
} while (ntotal < plen);
|
|
wordsize = ntotal;
|
|
} break;
|
|
case 's': {
|
|
int i, j;
|
|
bool is_wide = false;
|
|
if (core->print->ocur != -1) {
|
|
n = plen;
|
|
} else {
|
|
n = rz_str_nlen_w((const char *)p, plen) + 1;
|
|
}
|
|
name = malloc(n + 10);
|
|
if (!name) {
|
|
break;
|
|
}
|
|
strcpy(name, "str.");
|
|
for (i = 0, j = 0; i < n; i++, j++) {
|
|
name[4 + i] = p[j];
|
|
if (!p[j + 1]) {
|
|
break;
|
|
}
|
|
if (!p[j + 1]) {
|
|
if (j + 3 < n) {
|
|
if (p[j + 3]) {
|
|
break;
|
|
}
|
|
}
|
|
is_wide = true;
|
|
j++;
|
|
}
|
|
}
|
|
name[4 + n] = '\0';
|
|
// handle wide strings
|
|
// memcpy (name + 4, (const char *)p, n);
|
|
if (is_wide) {
|
|
rz_meta_set(core->analysis, RZ_META_TYPE_STRING, off,
|
|
n * 2, (const char *)name + 4);
|
|
} else {
|
|
rz_meta_set(core->analysis, RZ_META_TYPE_STRING, off,
|
|
n, (const char *)name + 4);
|
|
}
|
|
rz_name_filter(name, n + 10, true);
|
|
rz_flag_set(core->flags, name, off, n);
|
|
wordsize = n;
|
|
free(name);
|
|
} break;
|
|
case 'd': // TODO: check
|
|
rz_meta_del(core->analysis, RZ_META_TYPE_ANY, off, plen);
|
|
rz_meta_set(core->analysis, RZ_META_TYPE_DATA, off, plen, "");
|
|
break;
|
|
case 'c': // TODO: check
|
|
rz_meta_del(core->analysis, RZ_META_TYPE_ANY, off, plen);
|
|
rz_meta_set(core->analysis, RZ_META_TYPE_CODE, off, plen, "");
|
|
break;
|
|
case 'u':
|
|
rz_core_analysis_undefine(core, off);
|
|
break;
|
|
case 'f': {
|
|
RzAnalysisFunction *fcn = rz_analysis_get_fcn_in(core->analysis, core->offset, 0);
|
|
if (fcn) {
|
|
rz_analysis_function_resize(fcn, core->offset - fcn->addr);
|
|
}
|
|
rz_cons_break_push(NULL, NULL);
|
|
// required for thumb autodetection
|
|
rz_core_analysis_function_add(core, NULL, off, false);
|
|
rz_cons_break_pop();
|
|
} break;
|
|
case 'v': {
|
|
ut64 N;
|
|
char *endptr = NULL;
|
|
char *end_off = rz_cons_input("Last hexadecimal digits of instruction: ");
|
|
if (end_off) {
|
|
N = strtoull(end_off, &endptr, 16);
|
|
}
|
|
if (!end_off || end_off == endptr) {
|
|
eprintf("Invalid numeric input\n");
|
|
rz_cons_any_key(NULL);
|
|
free(end_off);
|
|
break;
|
|
}
|
|
free(end_off);
|
|
|
|
ut64 incr = 0x10;
|
|
ut64 tmp_N = N >> 4;
|
|
while (tmp_N > 0) {
|
|
tmp_N = tmp_N >> 4;
|
|
incr = incr << 4;
|
|
}
|
|
ut64 mask = incr - 1;
|
|
|
|
ut64 start_off = (off & ~mask) ^ N;
|
|
if ((off & mask) > N) {
|
|
if (start_off > incr) {
|
|
start_off -= incr;
|
|
} else {
|
|
start_off = N;
|
|
}
|
|
}
|
|
|
|
ut64 try_off;
|
|
RzAnalysisOp *op = NULL;
|
|
RzAnalysisVar *var = NULL;
|
|
for (try_off = start_off; try_off < start_off + incr * 16; try_off += incr) {
|
|
rz_analysis_op_free(op);
|
|
op = rz_core_analysis_op(core, try_off, RZ_ANALYSIS_OP_MASK_ALL);
|
|
if (!op) {
|
|
break;
|
|
}
|
|
var = rz_analysis_get_used_function_var(core->analysis, op->addr);
|
|
if (var) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (var) {
|
|
char *newname = rz_cons_input(sdb_fmt("New variable name for '%s': ", var->name));
|
|
if (newname && *newname) {
|
|
rz_analysis_var_rename(var, newname, true);
|
|
free(newname);
|
|
}
|
|
} else {
|
|
eprintf("Cannot find instruction with a variable\n");
|
|
rz_cons_any_key(NULL);
|
|
}
|
|
|
|
rz_analysis_op_free(op);
|
|
break;
|
|
}
|
|
case 'Q':
|
|
case 'q':
|
|
default:
|
|
if (IS_DIGIT(ch)) {
|
|
if (rep < 0) {
|
|
rep = 0;
|
|
}
|
|
rep = rep * 10 + atoi((char *)&ch);
|
|
goto repeat;
|
|
}
|
|
break;
|
|
}
|
|
if (distance > 0) {
|
|
distance--;
|
|
off += wordsize;
|
|
goto onemoretime;
|
|
}
|
|
}
|