If a function calls 'rz_cons_break_push()' but never calls 'rz_cons_break_pop()' before return, the stack count of 'RzConsContext->break_stack' contains too many elements (each time one too much). This in turn will lead to not resetting 'RzConsContext->breaked' flag. Because the flag is only set to false, if 'rz_stack_is_empty(context->break_stack) == true' (in 'rz_cons_context_break_push()'). This wasn't a problem so far, because 'RzConsContext->breaked' is simply never set to true (exceptions are some timeout cases as far as I can see). Also these cases when 'rz_cons_break_pop()' was forgetten to be called, were edge error cases. So not often hit. But if Rizin is usd by Cutter 'RzConsContext->breaked' is set to 'true', if an `AnalysisTask` interrupt is handled (in 'AnalysisTask::interrupt()'). This interrupt is triggered for example, when the introduction dialog is closed and the main Cutter window opens (after the optional 'aaa'). Now, if the binary file was analysed with 'aaa', and a lot of error cases were hit, those error cases sometimes never called 'rz_cons_break_pop()' before returning from their function. Although, of course, they should have to the `RzConsContext->break_stack` is in a proper state. This means, when the main Cutter window opens binary files which trigger many error edge cases, the `RzConsContext->break_stack` is not empty (because of the not executed 'rz_cons_break_pop()'). This also means, that the last thing done, was setting 'RzConsContext->breaked = true' (by 'AnalysisTask::interrupt()'). If Cutter wants to show some disassembly, it calls 'rz_core_print_disasm()' which checks 'RzConsContext == false' via 'rz_cons_is_breaked()'. This condition is never true, because the flag was not reset to `false` because the stack was never empty. So it returns before anything was disassembled. Hence Cutter gets no disassembly text. Fixes https://github.com/rizinorg/cutter/issues/2552 Fixes https://github.com/rizinorg/cutter/issues/3275
3860 lines
109 KiB
C
3860 lines
109 KiB
C
// SPDX-FileCopyrightText: 2010-2021 pancake <pancake@nopcode.org>
|
||
// SPDX-License-Identifier: LGPL-3.0-only
|
||
|
||
#include <rz_util/ht_uu.h>
|
||
#include <rz_asm.h>
|
||
#include <rz_core.h>
|
||
#include <rz_io.h>
|
||
#include <rz_list.h>
|
||
#include <rz_util/rz_regex.h>
|
||
#include <rz_types_base.h>
|
||
#include "../core_private.h"
|
||
|
||
#include "cmd_search_rop.c"
|
||
|
||
#define USE_EMULATION 0
|
||
|
||
#define AES_SEARCH_LENGTH 40
|
||
#define PRIVATE_KEY_SEARCH_LENGTH 11
|
||
|
||
static const char *help_msg_search_esil[] = {
|
||
"/E", " [esil-expr]", "search offsets matching a specific esil expression",
|
||
"/Ej", " [esil-expr]", "same as above but using the given magic file",
|
||
"/E?", " ", "show this help",
|
||
"\nExamples:", "", "",
|
||
"", "/E $$,0x100001060,-,!", "hit when address is 0x100001060",
|
||
NULL
|
||
};
|
||
|
||
static const char *help_msg_slash_m[] = {
|
||
"/m", "", "search for known magic patterns",
|
||
"/m", " [file]", "same as above but using the given magic file",
|
||
"/mb", "", "search recognized RzBin headers",
|
||
NULL
|
||
};
|
||
|
||
static const char *help_msg_slash[] = {
|
||
"Usage:", "/[!bf] [arg]", "Search stuff (see 'e??search' for options)\n"
|
||
"|Use io.va for searching in non virtual addressing spaces",
|
||
"/", " foo\\x00", "search for string 'foo\\0'",
|
||
"/j", " foo\\x00", "search for string 'foo\\0' (json output)",
|
||
"/!", " ff", "search for first occurrence not matching, command modifier",
|
||
"/!x", " 00", "inverse hexa search (find first byte != 0x00)",
|
||
"/+", " /bin/sh", "construct the string with chunks",
|
||
"//", "", "repeat last search",
|
||
"/a", "[?][1aoditfmsltf] jmp eax", "assemble opcode and search its bytes",
|
||
"/b", "", "search backwards, command modifier, followed by other command",
|
||
"/c", "[?][adr]", "search for crypto materials",
|
||
"/d", " 101112", "search for a deltified sequence of bytes",
|
||
"/e", " /E.F/i", "match regular expression",
|
||
"/E", " esil-expr", "offset matching given esil expressions $$ = here",
|
||
"/f", "", "search forwards, (command modifier)",
|
||
"/F", " file [off] [sz]", "search contents of file with offset and size",
|
||
// TODO: add subcommands to find paths between functions and filter only function names instead of offsets, etc
|
||
"/g", "[g] [from]", "find all graph paths A to B (/gg follow jumps, see search.count and analysis.depth)",
|
||
"/h", "[t] [hash] [len]", "find block matching this hash. See ph",
|
||
"/i", " foo", "search for string 'foo' ignoring case",
|
||
"/m", "[?][ebm] magicfile", "search for magic, filesystems or binary headers",
|
||
"/o", " [n]", "show offset of n instructions backward",
|
||
"/O", " [n]", "same as /o, but with a different fallback if analysis cannot be used",
|
||
"/p", " patternsize", "search for pattern of given size",
|
||
"/P", " patternsize", "search similar blocks",
|
||
"/s", "[*] [threshold]", "find sections by grouping blocks with similar entropy",
|
||
"/r[rwx]", "[?] sym.printf", "analyze opcode reference an offset",
|
||
"/R", " [grepopcode]", "search for matching ROP gadgets, semicolon-separated",
|
||
// moved into /as "/s", "", "search for all syscalls in a region (EXPERIMENTAL)",
|
||
"/v", "[1248] value", "look for an `cfg.bigendian` 32bit value",
|
||
"/V", "[1248] min max", "look for an `cfg.bigendian` 32bit value in range",
|
||
"/w", " foo", "search for wide string 'f\\0o\\0o\\0'",
|
||
"/wi", " foo", "search for wide string ignoring case 'f\\0o\\0o\\0'",
|
||
"/x", " ff..33", "search for hex string ignoring some nibbles",
|
||
"/x", " ff0033", "search for hex string",
|
||
"/x", " ff43:ffd0", "search for hexpair with mask",
|
||
"/z", " min max", "search for strings of given size",
|
||
"/*", " [comment string]", "add multiline comment, end it with '*/'",
|
||
#if 0
|
||
"\nConfiguration:", "", " (type `e??search.` for a complete list)",
|
||
"e", " cmd.hit = x", "command to execute on every search hit",
|
||
"e", " search.in = ?", "specify where to search stuff (depends on .from/.to)",
|
||
"e", " search.align = 4", "only catch aligned search hits",
|
||
"e", " search.from = 0", "start address",
|
||
"e", " search.to = 0", "end address",
|
||
"e", " search.flags = true", "if enabled store flags on keyword hits",
|
||
#endif
|
||
NULL
|
||
};
|
||
|
||
static const char *help_msg_slash_a[] = {
|
||
"Usage:", "/a[?] [arg]", "Search for assembly instructions matching given properties",
|
||
"/a", " push rbp", "Assemble given instruction and search the bytes",
|
||
"/a1", " [number]", "Find valid assembly generated by changing only the nth byte",
|
||
"/aI", "", "Search for infinite loop instructions (jmp $$)",
|
||
"/aa", " mov eax", "Linearly find aproximated assembly (case insensitive strstr)",
|
||
"/ac", " mov eax", "Same as /aa, but case-sensitive",
|
||
"/ad", "[/*j] push;mov", "Match ins1 followed by ins2 in linear disasm",
|
||
"/ad/", " ins1;ins2", "Search for regex instruction 'ins1' followed by regex 'ins2'",
|
||
"/ad/a", " instr", "Search for every byte instruction that matches regexp 'instr'",
|
||
"/ae", " esil", "Search for esil expressions matching substring",
|
||
"/af", "[l] family", "Search for instruction of specific family (afl=list",
|
||
"/ai", "[j] 0x300 [0x500]", "Find all the instructions using that immediate (in range)",
|
||
"/al", "", "Same as aoml, list all opcodes",
|
||
"/am", " opcode", "Search for specific instructions of specific mnemonic",
|
||
"/ao", " instr", "Search for instruction 'instr' (in all offsets)",
|
||
"/as", "[l] ([type])", "Search for syscalls (See /at swi and /af priv)",
|
||
"/at", "[l] ([type])", "Search for instructions of given type",
|
||
NULL
|
||
};
|
||
|
||
static const char *help_msg_slash_c[] = {
|
||
"Usage: /c", "", "Search for crypto materials",
|
||
"/ca", "", "Search for AES keys expanded in memory",
|
||
"/cc", "[algo] [digest]", "Find collisions (bruteforce block length values until given checksum is found)",
|
||
"/cd", "", "Search for ASN1/DER certificates",
|
||
"/cr", "", "Search for ASN1/DER private keys (RSA and ECC)",
|
||
NULL
|
||
};
|
||
|
||
static const char *help_msg_slash_r[] = {
|
||
"Usage:", "/r[acerwx] [address]", " search references to this specific address",
|
||
"/r", " [addr]", "search references to this specific address",
|
||
"/ra", "", "search all references",
|
||
"/rc", "", "search for call references",
|
||
"/rr", "", "Find read references",
|
||
"/rw", "", "Find write references",
|
||
"/rx", "", "Find exec references",
|
||
NULL
|
||
};
|
||
|
||
static const char *help_msg_slash_R[] = {
|
||
"Usage: /R", "", "Search for ROP gadgets",
|
||
"/R", " [filter-by-string]", "Show gadgets",
|
||
"/R/", " [filter-by-regexp]", "Show gadgets [regular expression]",
|
||
"/R/j", " [filter-by-regexp]", "JSON output [regular expression]",
|
||
"/R/q", " [filter-by-regexp]", "Show gadgets in a quiet manner [regular expression]",
|
||
"/Rj", " [filter-by-string]", "JSON output",
|
||
"/Rk", " [select-by-class]", "Query stored ROP gadgets",
|
||
"/Rq", " [filter-by-string]", "Show gadgets in a quiet manner",
|
||
NULL
|
||
};
|
||
|
||
static const char *help_msg_slash_Rk[] = {
|
||
"Usage: /Rk", "", "Query stored ROP gadgets",
|
||
"/Rk", " [nop|mov|const|arithm|arithm_ct]", "Show gadgets",
|
||
"/Rkj", "", "JSON output",
|
||
"/Rkq", "", "List Gadgets offsets",
|
||
NULL
|
||
};
|
||
|
||
static const char *help_msg_slash_x[] = {
|
||
"Usage:", "/x [hexpairs]:[binmask]", "Search in memory",
|
||
"/x ", "9090cd80", "search for those bytes",
|
||
"/x ", "9090cd80:ffff7ff0", "search with binary mask",
|
||
NULL
|
||
};
|
||
|
||
static int preludecnt = 0;
|
||
static int searchflags = 0;
|
||
static int searchshow = 0;
|
||
static const char *searchprefix = NULL;
|
||
|
||
struct search_parameters {
|
||
RzCore *core;
|
||
RzList /*<RzIOMap *>*/ *boundaries;
|
||
const char *mode;
|
||
const char *cmd_hit;
|
||
PJ *pj;
|
||
int outmode; // 0 or RZ_MODE_RIZINCMD or RZ_MODE_JSON
|
||
bool inverse;
|
||
bool aes_search;
|
||
bool privkey_search;
|
||
};
|
||
|
||
struct endlist_pair {
|
||
int instr_offset;
|
||
int delay_size;
|
||
};
|
||
|
||
static int search_hash(RzCore *core, const char *hashname, const char *hashstr, ut32 minlen, ut32 maxlen, struct search_parameters *param) {
|
||
RzIOMap *map;
|
||
ut8 *buf;
|
||
int i, j;
|
||
RzListIter *iter;
|
||
|
||
if (!minlen || minlen == UT32_MAX) {
|
||
minlen = core->blocksize;
|
||
}
|
||
if (!maxlen || maxlen == UT32_MAX) {
|
||
maxlen = minlen;
|
||
}
|
||
|
||
rz_cons_break_push(NULL, NULL);
|
||
for (j = minlen; j <= maxlen; j++) {
|
||
ut32 len = j;
|
||
eprintf("Searching %s for %d byte length.\n", hashname, j);
|
||
rz_list_foreach (param->boundaries, iter, map) {
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
ut64 from = map->itv.addr, to = rz_itv_end(map->itv);
|
||
st64 bufsz;
|
||
bufsz = to - from;
|
||
if (len > bufsz) {
|
||
RZ_LOG_ERROR("core: Hash length is bigger than range 0x%" PFMT64x "\n", from);
|
||
continue;
|
||
}
|
||
buf = malloc(bufsz);
|
||
if (!buf) {
|
||
RZ_LOG_ERROR("core: Cannot allocate %" PFMT64d " bytes\n", bufsz);
|
||
goto fail;
|
||
}
|
||
eprintf("Search in range 0x%08" PFMT64x " and 0x%08" PFMT64x "\n", from, to);
|
||
int blocks = (int)(to - from - len);
|
||
eprintf("Carving %d blocks...\n", blocks);
|
||
(void)rz_io_read_at(core->io, from, buf, bufsz);
|
||
for (i = 0; (from + i + len) < to; i++) {
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
char *s = rz_hash_cfg_calculate_small_block_string(core->hash, hashname, buf + i, len, NULL, false);
|
||
if (!s) {
|
||
RZ_LOG_ERROR("core: Hash fail\n");
|
||
break;
|
||
}
|
||
if (!(i % 5)) {
|
||
eprintf("%d\r", i);
|
||
}
|
||
// eprintf ("0x%08"PFMT64x" %s\n", from+i, s);
|
||
if (!strcmp(s, hashstr)) {
|
||
eprintf("Found at 0x%" PFMT64x "\n", from + i);
|
||
rz_cons_printf("f hash.%s.%s @ 0x%" PFMT64x "\n",
|
||
hashname, hashstr, from + i);
|
||
free(s);
|
||
free(buf);
|
||
rz_cons_break_pop();
|
||
return 1;
|
||
}
|
||
free(s);
|
||
}
|
||
free(buf);
|
||
}
|
||
}
|
||
rz_cons_break_pop();
|
||
eprintf("No hashes found\n");
|
||
return 0;
|
||
fail:
|
||
rz_cons_break_pop();
|
||
return -1;
|
||
}
|
||
|
||
static void cmd_search_bin(RzCore *core, RzInterval itv) {
|
||
ut64 from = itv.addr, to = rz_itv_end(itv);
|
||
int size; // , sz = sizeof (buf);
|
||
|
||
int fd = core->file->fd;
|
||
RzBuffer *b = rz_buf_new_with_io_fd(&core->analysis->iob, fd);
|
||
rz_cons_break_push(NULL, NULL);
|
||
while (from < to) {
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
RzBuffer *ref = rz_buf_new_slice(b, from, to);
|
||
RzBinPlugin *plug = rz_bin_get_binplugin_by_buffer(core->bin, ref);
|
||
if (plug) {
|
||
rz_cons_printf("0x%08" PFMT64x " %s\n", from, plug->name);
|
||
if (plug->size) {
|
||
RzBinOptions opt = {
|
||
.pluginname = plug->name,
|
||
.obj_opts = { 0 },
|
||
.sz = 4096,
|
||
.xtr_idx = 0,
|
||
.fd = fd,
|
||
};
|
||
rz_bin_open_io(core->bin, &opt);
|
||
size = plug->size(core->bin->cur);
|
||
if (size > 0) {
|
||
rz_cons_printf("size %d\n", size);
|
||
}
|
||
}
|
||
}
|
||
rz_buf_free(ref);
|
||
from++;
|
||
}
|
||
rz_buf_free(b);
|
||
rz_cons_break_pop();
|
||
}
|
||
|
||
static int __prelude_cb_hit(RzSearchKeyword *kw, void *user, ut64 addr) {
|
||
RzCore *core = (RzCore *)user;
|
||
int depth = rz_config_get_i(core->config, "analysis.depth");
|
||
// eprintf ("ap: Found function prelude %d at 0x%08"PFMT64x"\n", preludecnt, addr);
|
||
rz_core_analysis_fcn(core, addr, -1, RZ_ANALYSIS_XREF_TYPE_NULL, depth);
|
||
preludecnt++;
|
||
return 1;
|
||
}
|
||
|
||
RZ_API int rz_core_search_prelude(RzCore *core, ut64 from, ut64 to, const ut8 *buf, int blen, const ut8 *mask, int mlen) {
|
||
ut64 at;
|
||
ut8 *b = (ut8 *)malloc(core->blocksize);
|
||
if (!b) {
|
||
return 0;
|
||
}
|
||
// TODO: handle sections ?
|
||
if (from >= to) {
|
||
RZ_LOG_ERROR("core: Invalid search range 0x%08" PFMT64x " - 0x%08" PFMT64x "\n", from, to);
|
||
free(b);
|
||
return 0;
|
||
}
|
||
rz_search_reset(core->search, RZ_SEARCH_KEYWORD);
|
||
rz_search_kw_add(core->search, rz_search_keyword_new(buf, blen, mask, mlen, NULL));
|
||
rz_search_begin(core->search);
|
||
rz_search_set_callback(core->search, &__prelude_cb_hit, core);
|
||
preludecnt = 0;
|
||
for (at = from; at < to; at += core->blocksize) {
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
if (!rz_io_is_valid_offset(core->io, at, 0)) {
|
||
break;
|
||
}
|
||
(void)rz_io_read_at(core->io, at, b, core->blocksize);
|
||
if (rz_search_update(core->search, at, b, core->blocksize) == -1) {
|
||
RZ_LOG_ERROR("core: update read error at 0x%08" PFMT64x "\n", at);
|
||
break;
|
||
}
|
||
}
|
||
// rz_search_reset might also benifet from having an if(s->data) RZ_FREE(s->data), but im not sure.
|
||
// add a commit that puts it in there to this PR if it wouldn't break anything. (don't have to worry about this happening again, since all searches start by resetting core->search)
|
||
// For now we will just use rz_search_kw_reset
|
||
rz_search_kw_reset(core->search);
|
||
free(b);
|
||
return preludecnt;
|
||
}
|
||
|
||
RZ_API int rz_core_search_preludes(RzCore *core, bool log) {
|
||
int ret = -1;
|
||
ut64 from = UT64_MAX;
|
||
ut64 to = UT64_MAX;
|
||
int keyword_length = 0;
|
||
ut8 *keyword = NULL;
|
||
const char *prelude = rz_config_get(core->config, "analysis.prelude");
|
||
const char *where = rz_config_get(core->config, "analysis.in");
|
||
ut64 limit = rz_config_get_i(core->config, "analysis.prelude.limit");
|
||
|
||
RzList *list = rz_core_get_boundaries_prot(core, RZ_PERM_X, where, "search");
|
||
RzList *arch_preludes = NULL;
|
||
RzListIter *iter = NULL, *iter2 = NULL;
|
||
RzIOMap *p = NULL;
|
||
RzSearchKeyword *kw = NULL;
|
||
|
||
if (!list) {
|
||
return -1;
|
||
}
|
||
|
||
if (RZ_STR_ISNOTEMPTY(prelude)) {
|
||
keyword = malloc(strlen(prelude) + 1);
|
||
if (!keyword) {
|
||
RZ_LOG_ERROR("aap: cannot allocate 'analysis.prelude' buffer\n");
|
||
rz_list_free(list);
|
||
return -1;
|
||
}
|
||
keyword_length = rz_hex_str2bin(prelude, keyword);
|
||
} else {
|
||
arch_preludes = rz_analysis_preludes(core->analysis);
|
||
if (!arch_preludes) {
|
||
rz_list_free(list);
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
rz_list_foreach (list, iter, p) {
|
||
if (!(p->perm & RZ_PERM_X)) {
|
||
continue;
|
||
}
|
||
from = p->itv.addr;
|
||
to = rz_itv_end(p->itv);
|
||
if ((to - from) >= limit) {
|
||
RZ_LOG_WARN("aap: search interval (from 0x%" PFMT64x
|
||
" to 0x%" PFMT64x ") exeeds analysis.prelude.limit (0x%" PFMT64x "), skipping it.\n",
|
||
from, to, limit);
|
||
continue;
|
||
}
|
||
if (keyword && keyword_length > 0) {
|
||
ret = rz_core_search_prelude(core, from, to, keyword, keyword_length, NULL, 0);
|
||
} else {
|
||
rz_list_foreach (arch_preludes, iter2, kw) {
|
||
ret = rz_core_search_prelude(core, from, to,
|
||
kw->bin_keyword, kw->keyword_length,
|
||
kw->bin_binmask, kw->binmask_length);
|
||
}
|
||
}
|
||
}
|
||
free(keyword);
|
||
rz_list_free(list);
|
||
rz_list_free(arch_preludes);
|
||
return ret;
|
||
}
|
||
|
||
/* TODO: maybe move into util/str */
|
||
static char *getstring(char *b, int l) {
|
||
char *r, *res = malloc(l + 1);
|
||
int i;
|
||
if (!res) {
|
||
return NULL;
|
||
}
|
||
for (i = 0, r = res; i < l; b++, i++) {
|
||
if (IS_PRINTABLE(*b)) {
|
||
*r++ = *b;
|
||
}
|
||
}
|
||
*r = 0;
|
||
return res;
|
||
}
|
||
|
||
static int _cb_hit(RzSearchKeyword *kw, void *user, ut64 addr) {
|
||
struct search_parameters *param = user;
|
||
RzCore *core = param->core;
|
||
const RzSearch *search = core->search;
|
||
ut64 base_addr = 0;
|
||
bool use_color = core->print->flags & RZ_PRINT_FLAGS_COLOR;
|
||
int keyword_len = kw ? kw->keyword_length + (search->mode == RZ_SEARCH_DELTAKEY) : 0;
|
||
char tmpbuf[128];
|
||
|
||
if (searchshow && kw && kw->keyword_length > 0) {
|
||
int len, i, extra, mallocsize;
|
||
char *s = NULL, *str = NULL, *p = NULL;
|
||
extra = (param->outmode == RZ_MODE_JSON) ? 3 : 1;
|
||
const char *type = "hexpair";
|
||
ut8 *buf = malloc(keyword_len);
|
||
if (!buf) {
|
||
return 0;
|
||
}
|
||
switch (kw->type) {
|
||
case RZ_SEARCH_KEYWORD_TYPE_STRING: {
|
||
const int ctx = 16;
|
||
const int prectx = addr > 16 ? ctx : addr;
|
||
char *pre, *pos, *wrd;
|
||
const int len = keyword_len;
|
||
char *buf = calloc(1, len + 32 + ctx * 2);
|
||
type = "string";
|
||
rz_io_read_at(core->io, addr - prectx, (ut8 *)buf, len + (ctx * 2));
|
||
pre = getstring(buf, prectx);
|
||
pos = getstring(buf + prectx + len, ctx);
|
||
if (!pos) {
|
||
pos = strdup("");
|
||
}
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
wrd = getstring(buf + prectx, len);
|
||
s = rz_str_newf("%s%s%s", pre, wrd, pos);
|
||
} else {
|
||
wrd = rz_str_utf16_encode(buf + prectx, len);
|
||
s = rz_str_newf(use_color ? ".%s" Color_YELLOW "%s" Color_RESET "%s."
|
||
: "\"%s%s%s\"",
|
||
pre, wrd, pos);
|
||
}
|
||
free(buf);
|
||
free(pre);
|
||
free(wrd);
|
||
free(pos);
|
||
}
|
||
free(p);
|
||
break;
|
||
default:
|
||
len = keyword_len; // 8 byte context
|
||
mallocsize = (len * 2) + extra;
|
||
str = (len > 0xffff) ? NULL : malloc(mallocsize);
|
||
if (str) {
|
||
p = str;
|
||
memset(str, 0, len);
|
||
rz_io_read_at(core->io, base_addr + addr, buf, keyword_len);
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
p = str;
|
||
}
|
||
const int bytes = (len > 40) ? 40 : len;
|
||
for (i = 0; i < bytes; i++) {
|
||
sprintf(p, "%02x", buf[i]);
|
||
p += 2;
|
||
}
|
||
if (bytes != len) {
|
||
strcpy(p, "...");
|
||
p += 3;
|
||
}
|
||
*p = 0;
|
||
} else {
|
||
RZ_LOG_ERROR("core: Cannot allocate %d\n", mallocsize);
|
||
}
|
||
s = str;
|
||
str = NULL;
|
||
break;
|
||
}
|
||
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_o(param->pj);
|
||
pj_kn(param->pj, "offset", base_addr + addr);
|
||
pj_ks(param->pj, "type", type);
|
||
pj_ks(param->pj, "data", s);
|
||
pj_end(param->pj);
|
||
} else {
|
||
rz_cons_printf("0x%08" PFMT64x " %s%d_%d %s\n",
|
||
base_addr + addr, searchprefix, kw->kwidx, kw->count, s);
|
||
}
|
||
free(s);
|
||
free(buf);
|
||
free(str);
|
||
} else if (kw) {
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_o(param->pj);
|
||
pj_kn(param->pj, "offset", base_addr + addr);
|
||
pj_ki(param->pj, "len", keyword_len);
|
||
pj_end(param->pj);
|
||
} else {
|
||
if (searchflags) {
|
||
rz_cons_printf("%s%d_%d\n", searchprefix, kw->kwidx, kw->count);
|
||
} else {
|
||
rz_cons_printf("f %s%d_%d %d @ 0x%08" PFMT64x "\n", searchprefix,
|
||
kw->kwidx, kw->count, keyword_len, base_addr + addr);
|
||
}
|
||
}
|
||
}
|
||
if (searchflags && kw) {
|
||
const char *flag = rz_strf(tmpbuf, "%s%d_%d", searchprefix, kw->kwidx, kw->count);
|
||
rz_flag_set(core->flags, flag, base_addr + addr, keyword_len);
|
||
}
|
||
if (*param->cmd_hit) {
|
||
ut64 here = core->offset;
|
||
rz_core_seek(core, base_addr + addr, true);
|
||
rz_core_cmd(core, param->cmd_hit, 0);
|
||
rz_core_seek(core, here, true);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
static int c = 0;
|
||
|
||
static inline void print_search_progress(ut64 at, ut64 to, int n, struct search_parameters *param) {
|
||
if ((++c % 64) || (param->outmode == RZ_MODE_JSON)) {
|
||
return;
|
||
}
|
||
if (rz_cons_singleton()->columns < 50) {
|
||
eprintf("\r[ ] 0x%08" PFMT64x " hits = %d \r%s",
|
||
at, n, (c % 2) ? "[ #]" : "[# ]");
|
||
} else {
|
||
eprintf("\r[ ] 0x%08" PFMT64x " < 0x%08" PFMT64x " hits = %d \r%s",
|
||
at, to, n, (c % 2) ? "[ #]" : "[# ]");
|
||
}
|
||
}
|
||
|
||
static void append_bound(RzList /*<RzIOMap *>*/ *list, RzIO *io, RzInterval search_itv, ut64 from, ut64 size, int perms) {
|
||
RzIOMap *map = RZ_NEW0(RzIOMap);
|
||
if (!map) {
|
||
return;
|
||
}
|
||
if (io && io->desc) {
|
||
map->fd = rz_io_fd_get_current(io);
|
||
}
|
||
|
||
map->perm = perms;
|
||
RzInterval itv = { from, size };
|
||
if (size == -1) {
|
||
RZ_LOG_ERROR("core: Invalid range. Use different search.in=? or analysis.in=dbg.maps.x\n");
|
||
free(map);
|
||
return;
|
||
}
|
||
// TODO UT64_MAX is a valid address. search.from and search.to are not specified
|
||
if (search_itv.addr == UT64_MAX && !search_itv.size) {
|
||
map->itv = itv;
|
||
rz_list_append(list, map);
|
||
} else if (rz_itv_overlap(itv, search_itv)) {
|
||
map->itv = rz_itv_intersect(itv, search_itv);
|
||
if (map->itv.size) {
|
||
rz_list_append(list, map);
|
||
} else {
|
||
free(map);
|
||
}
|
||
} else {
|
||
free(map);
|
||
}
|
||
}
|
||
|
||
static bool maskMatches(int perm, int mask, bool only) {
|
||
if (mask) {
|
||
if (only) {
|
||
return ((perm & 7) != mask);
|
||
}
|
||
return (perm & mask) != mask;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
RZ_API RZ_OWN RzList /*<RzIOMap *>*/ *rz_core_get_boundaries_prot(RzCore *core, int perm, const char *mode, const char *prefix) {
|
||
rz_return_val_if_fail(core, NULL);
|
||
|
||
RzList *list = rz_list_newf(free); // XXX rz_io_map_free);
|
||
if (!list) {
|
||
return NULL;
|
||
}
|
||
|
||
char bound_in[32];
|
||
char bound_from[32];
|
||
char bound_to[32];
|
||
snprintf(bound_in, sizeof(bound_in), "%s.%s", prefix, "in");
|
||
snprintf(bound_from, sizeof(bound_from), "%s.%s", prefix, "from");
|
||
snprintf(bound_to, sizeof(bound_to), "%s.%s", prefix, "to");
|
||
const ut64 search_from = rz_config_get_i(core->config, bound_from),
|
||
search_to = rz_config_get_i(core->config, bound_to);
|
||
const RzInterval search_itv = { search_from, search_to - search_from };
|
||
if (!mode) {
|
||
mode = rz_config_get(core->config, bound_in);
|
||
}
|
||
if (!rz_config_get_b(core->config, "cfg.debug") && !core->io->va) {
|
||
append_bound(list, core->io, search_itv, 0, rz_io_size(core->io), 7);
|
||
} else if (!strcmp(mode, "file")) {
|
||
append_bound(list, core->io, search_itv, 0, rz_io_size(core->io), 7);
|
||
} else if (!strcmp(mode, "block")) {
|
||
append_bound(list, core->io, search_itv, core->offset, core->blocksize, 7);
|
||
} else if (!strcmp(mode, "io.map")) {
|
||
RzIOMap *m = rz_io_map_get(core->io, core->offset);
|
||
if (m) {
|
||
append_bound(list, core->io, search_itv, m->itv.addr, m->itv.size, m->perm);
|
||
}
|
||
} else if (!strcmp(mode, "io.maps")) { // Non-overlapping RzIOMap parts not overridden by others (skyline)
|
||
ut64 begin = UT64_MAX;
|
||
ut64 end = UT64_MAX;
|
||
#define USE_SKYLINE 0
|
||
#if USE_SKYLINE
|
||
const RzPVector *skyline = &core->io->map_skyline;
|
||
size_t i;
|
||
for (i = 0; i < rz_pvector_len(skyline); i++) {
|
||
const RzIOMapSkyline *part = rz_pvector_at(skyline, i);
|
||
ut64 from = rz_itv_begin(part->itv);
|
||
ut64 to = rz_itv_end(part->itv);
|
||
// XXX skyline's fake map perms are wrong
|
||
RzIOMap *m = rz_io_map_get(core->io, from);
|
||
int rwx = m ? m->perm : part->map->perm;
|
||
#else
|
||
void **it;
|
||
RzPVector *maps = rz_io_maps(core->io);
|
||
rz_pvector_foreach (maps, it) {
|
||
RzIOMap *map = *it;
|
||
ut64 from = rz_itv_begin(map->itv);
|
||
ut64 to = rz_itv_end(map->itv);
|
||
int rwx = map->perm;
|
||
#endif
|
||
// eprintf ("--------- %llx %llx (%llx %llx)\n", from, to, begin, end);
|
||
if (begin == UT64_MAX) {
|
||
begin = from;
|
||
}
|
||
if (end == UT64_MAX) {
|
||
end = to;
|
||
} else {
|
||
if (end == from) {
|
||
end = to;
|
||
} else {
|
||
append_bound(list, NULL, search_itv,
|
||
begin, end - begin, rwx);
|
||
begin = from;
|
||
end = to;
|
||
}
|
||
}
|
||
}
|
||
if (end != UT64_MAX) {
|
||
append_bound(list, NULL, search_itv, begin, end - begin, 7);
|
||
}
|
||
} else if (rz_str_startswith(mode, "io.maps.")) {
|
||
int len = strlen("io.maps.");
|
||
int mask = (mode[len - 1] == '.') ? rz_str_rwx(mode + len) : 0;
|
||
// bool only = (bool)(size_t)strstr (mode, ".only");
|
||
|
||
void **it;
|
||
RzPVector *maps = rz_io_maps(core->io);
|
||
rz_pvector_foreach (maps, it) {
|
||
RzIOMap *map = *it;
|
||
ut64 from = rz_itv_begin(map->itv);
|
||
// ut64 to = rz_itv_end (map->itv);
|
||
int rwx = map->perm;
|
||
if ((rwx & mask) != mask) {
|
||
continue;
|
||
}
|
||
append_bound(list, core->io, search_itv, from, rz_itv_size(map->itv), rwx);
|
||
}
|
||
} else if (rz_str_startswith(mode, "io.sky.")) {
|
||
int len = strlen("io.sky.");
|
||
int mask = (mode[len - 1] == '.') ? rz_str_rwx(mode + len) : 0;
|
||
bool only = (bool)(size_t)strstr(mode, ".only");
|
||
RzVector *skyline = &core->io->map_skyline.v;
|
||
ut64 begin = UT64_MAX;
|
||
ut64 end = UT64_MAX;
|
||
size_t i;
|
||
for (i = 0; i < rz_vector_len(skyline); i++) {
|
||
const RzSkylineItem *part = rz_vector_index_ptr(skyline, i);
|
||
ut64 from = part->itv.addr;
|
||
ut64 to = part->itv.addr + part->itv.size;
|
||
int perm = ((RzIOMap *)part->user)->perm;
|
||
if (maskMatches(perm, mask, only)) {
|
||
continue;
|
||
}
|
||
// eprintf ("--------- %llx %llx (%llx %llx)\n", from, to, begin, end);
|
||
if (begin == UT64_MAX) {
|
||
begin = from;
|
||
}
|
||
if (end == UT64_MAX) {
|
||
end = to;
|
||
} else {
|
||
if (end == from) {
|
||
end = to;
|
||
} else {
|
||
// eprintf ("[%llx - %llx]\n", begin, end);
|
||
append_bound(list, NULL, search_itv, begin, end - begin, perm);
|
||
begin = from;
|
||
end = to;
|
||
}
|
||
}
|
||
}
|
||
if (end != UT64_MAX) {
|
||
append_bound(list, NULL, search_itv, begin, end - begin, 7);
|
||
}
|
||
} else if (rz_str_startswith(mode, "bin.segments")) {
|
||
int len = strlen("bin.segments.");
|
||
int mask = (mode[len - 1] == '.') ? rz_str_rwx(mode + len) : 0;
|
||
bool only = (bool)(size_t)strstr(mode, ".only");
|
||
RzBinObject *obj = rz_bin_cur_object(core->bin);
|
||
if (obj) {
|
||
RzBinSection *s;
|
||
void **iter;
|
||
rz_pvector_foreach (obj->sections, iter) {
|
||
s = *iter;
|
||
if (!s->is_segment) {
|
||
continue;
|
||
}
|
||
if (maskMatches(s->perm, mask, only)) {
|
||
continue;
|
||
}
|
||
ut64 addr = core->io->va ? s->vaddr : s->paddr;
|
||
ut64 size = core->io->va ? s->vsize : s->size;
|
||
append_bound(list, core->io, search_itv, addr, size, s->perm);
|
||
}
|
||
}
|
||
} else if (rz_str_startswith(mode, "code")) {
|
||
RzBinObject *obj = rz_bin_cur_object(core->bin);
|
||
if (obj) {
|
||
ut64 from = UT64_MAX;
|
||
ut64 to = 0;
|
||
RzBinSection *s;
|
||
void **iter;
|
||
rz_pvector_foreach (obj->sections, iter) {
|
||
s = *iter;
|
||
if (s->is_segment) {
|
||
continue;
|
||
}
|
||
if (maskMatches(s->perm, 1, false)) {
|
||
continue;
|
||
}
|
||
ut64 addr = core->io->va ? s->vaddr : s->paddr;
|
||
ut64 size = core->io->va ? s->vsize : s->size;
|
||
from = RZ_MIN(addr, from);
|
||
to = RZ_MAX(to, addr + size);
|
||
}
|
||
if (from == UT64_MAX) {
|
||
int mask = 1;
|
||
void **it;
|
||
RzPVector *maps = rz_io_maps(core->io);
|
||
rz_pvector_foreach (maps, it) {
|
||
RzIOMap *map = *it;
|
||
ut64 from = rz_itv_begin(map->itv);
|
||
ut64 size = rz_itv_size(map->itv);
|
||
int rwx = map->perm;
|
||
if ((rwx & mask) != mask) {
|
||
continue;
|
||
}
|
||
append_bound(list, core->io, search_itv, from, size, rwx);
|
||
}
|
||
}
|
||
append_bound(list, core->io, search_itv, from, to - from, 1);
|
||
}
|
||
} else if (rz_str_startswith(mode, "bin.sections")) {
|
||
int len = strlen("bin.sections.");
|
||
int mask = (mode[len - 1] == '.') ? rz_str_rwx(mode + len) : 0;
|
||
bool only = (bool)(size_t)strstr(mode, ".only");
|
||
RzBinObject *obj = rz_bin_cur_object(core->bin);
|
||
if (obj) {
|
||
RzBinSection *s;
|
||
void **iter;
|
||
rz_pvector_foreach (obj->sections, iter) {
|
||
s = *iter;
|
||
if (s->is_segment) {
|
||
continue;
|
||
}
|
||
if (maskMatches(s->perm, mask, only)) {
|
||
continue;
|
||
}
|
||
ut64 addr = core->io->va ? s->vaddr : s->paddr;
|
||
ut64 size = core->io->va ? s->vsize : s->size;
|
||
append_bound(list, core->io, search_itv, addr, size, s->perm);
|
||
}
|
||
}
|
||
} else if (!strcmp(mode, "bin.segment")) {
|
||
RzBinObject *obj = rz_bin_cur_object(core->bin);
|
||
if (obj) {
|
||
RzBinSection *s;
|
||
void **iter;
|
||
rz_pvector_foreach (obj->sections, iter) {
|
||
s = *iter;
|
||
if (!s->is_segment) {
|
||
continue;
|
||
}
|
||
ut64 addr = core->io->va ? s->vaddr : s->paddr;
|
||
ut64 size = core->io->va ? s->vsize : s->size;
|
||
if (RZ_BETWEEN(addr, core->offset, addr + size)) {
|
||
append_bound(list, core->io, search_itv, addr, size, s->perm);
|
||
}
|
||
}
|
||
}
|
||
} else if (!strcmp(mode, "bin.section")) {
|
||
RzBinObject *obj = rz_bin_cur_object(core->bin);
|
||
if (obj) {
|
||
RzBinSection *s;
|
||
void **iter;
|
||
rz_pvector_foreach (obj->sections, iter) {
|
||
s = *iter;
|
||
if (s->is_segment) {
|
||
continue;
|
||
}
|
||
ut64 addr = core->io->va ? s->vaddr : s->paddr;
|
||
ut64 size = core->io->va ? s->vsize : s->size;
|
||
if (RZ_BETWEEN(addr, core->offset, addr + size)) {
|
||
append_bound(list, core->io, search_itv, addr, size, s->perm);
|
||
}
|
||
}
|
||
}
|
||
} else if (!strcmp(mode, "analysis.fcn") || !strcmp(mode, "analysis.bb")) {
|
||
RzAnalysisFunction *f = rz_analysis_get_fcn_in(core->analysis, core->offset,
|
||
RZ_ANALYSIS_FCN_TYPE_FCN | RZ_ANALYSIS_FCN_TYPE_SYM);
|
||
if (f) {
|
||
ut64 from = f->addr, size = rz_analysis_function_size_from_entry(f);
|
||
|
||
/* Search only inside the basic block */
|
||
if (!strcmp(mode, "analysis.bb")) {
|
||
RzListIter *iter;
|
||
RzAnalysisBlock *bb;
|
||
|
||
rz_list_foreach (f->bbs, iter, bb) {
|
||
ut64 at = core->offset;
|
||
if ((at >= bb->addr) && (at < (bb->addr + bb->size))) {
|
||
from = bb->addr;
|
||
size = bb->size;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
append_bound(list, core->io, search_itv, from, size, 5);
|
||
} else {
|
||
RZ_LOG_WARN("core: search.in = ( analysis.bb | analysis.fcn )"
|
||
"requires to seek into a valid function\n");
|
||
append_bound(list, core->io, search_itv, core->offset, 1, 5);
|
||
}
|
||
} else if (!strncmp(mode, "dbg.", 4)) {
|
||
if (core->bin->is_debugger) {
|
||
int mask = 0;
|
||
int add = 0;
|
||
bool heap = false;
|
||
bool stack = false;
|
||
bool all = false;
|
||
bool first = false;
|
||
RzListIter *iter;
|
||
RzDebugMap *map;
|
||
|
||
rz_debug_map_sync(core->dbg);
|
||
|
||
if (!strcmp(mode, "dbg.map")) {
|
||
int perm = 0;
|
||
ut64 from = core->offset;
|
||
ut64 to = core->offset;
|
||
rz_list_foreach (core->dbg->maps, iter, map) {
|
||
if (from >= map->addr && from < map->addr_end) {
|
||
from = map->addr;
|
||
to = map->addr_end;
|
||
perm = map->perm;
|
||
break;
|
||
}
|
||
}
|
||
if (perm) {
|
||
RzIOMap *nmap = RZ_NEW0(RzIOMap);
|
||
if (nmap) {
|
||
// nmap->fd = core->io->desc->fd;
|
||
nmap->itv.addr = from;
|
||
nmap->itv.size = to - from;
|
||
nmap->perm = perm;
|
||
nmap->delta = 0;
|
||
rz_list_append(list, nmap);
|
||
}
|
||
}
|
||
} else {
|
||
bool only = false;
|
||
mask = 0;
|
||
if (!strcmp(mode, "dbg.program")) {
|
||
first = true;
|
||
mask = RZ_PERM_X;
|
||
} else if (!strcmp(mode, "dbg.maps")) {
|
||
all = true;
|
||
} else if (rz_str_startswith(mode, "dbg.maps.")) {
|
||
mask = rz_str_rwx(mode + 9);
|
||
only = (bool)(size_t)strstr(mode, ".only");
|
||
} else if (!strcmp(mode, "dbg.heap")) {
|
||
heap = true;
|
||
} else if (!strcmp(mode, "dbg.stack")) {
|
||
stack = true;
|
||
}
|
||
|
||
ut64 from = UT64_MAX;
|
||
ut64 to = 0;
|
||
rz_list_foreach (core->dbg->maps, iter, map) {
|
||
if (!all && maskMatches(map->perm, mask, only)) {
|
||
continue;
|
||
}
|
||
add = (stack && strstr(map->name, "stack")) ? 1 : 0;
|
||
if (!add && (heap && (map->perm & RZ_PERM_W)) && strstr(map->name, "heap")) {
|
||
add = 1;
|
||
}
|
||
if ((mask && (map->perm & mask)) || add || all) {
|
||
if (!list) {
|
||
list = rz_list_newf(free);
|
||
}
|
||
RzIOMap *nmap = RZ_NEW0(RzIOMap);
|
||
if (!nmap) {
|
||
break;
|
||
}
|
||
nmap->itv.addr = map->addr;
|
||
nmap->itv.size = map->addr_end - map->addr;
|
||
if (nmap->itv.addr) {
|
||
from = RZ_MIN(from, nmap->itv.addr);
|
||
to = RZ_MAX(to - 1, rz_itv_end(nmap->itv) - 1) + 1;
|
||
}
|
||
nmap->perm = map->perm;
|
||
nmap->delta = 0;
|
||
rz_list_append(list, nmap);
|
||
if (first) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
/* obey temporary seek if defined '/x 8080 @ addr:len' */
|
||
if (core->tmpseek) {
|
||
append_bound(list, core->io, search_itv, core->offset, core->blocksize, 5);
|
||
} else {
|
||
// TODO: repeat last search doesnt works for /a
|
||
ut64 from = rz_config_get_i(core->config, bound_from);
|
||
if (from == UT64_MAX) {
|
||
from = core->offset;
|
||
}
|
||
ut64 to = rz_config_get_i(core->config, bound_to);
|
||
if (to == UT64_MAX) {
|
||
if (core->io->va) {
|
||
/* TODO: section size? */
|
||
} else {
|
||
if (core->file) {
|
||
to = rz_io_fd_size(core->io, core->file->fd);
|
||
}
|
||
}
|
||
}
|
||
append_bound(list, core->io, search_itv, from, to - from, 5);
|
||
}
|
||
}
|
||
return list;
|
||
}
|
||
|
||
static bool is_end_gadget(const RzAnalysisOp *aop, const ut8 crop) {
|
||
if (aop->family == RZ_ANALYSIS_OP_FAMILY_SECURITY) {
|
||
return false;
|
||
}
|
||
switch (aop->type) {
|
||
case RZ_ANALYSIS_OP_TYPE_TRAP:
|
||
case RZ_ANALYSIS_OP_TYPE_RET:
|
||
case RZ_ANALYSIS_OP_TYPE_UCALL:
|
||
case RZ_ANALYSIS_OP_TYPE_RCALL:
|
||
case RZ_ANALYSIS_OP_TYPE_ICALL:
|
||
case RZ_ANALYSIS_OP_TYPE_IRCALL:
|
||
case RZ_ANALYSIS_OP_TYPE_UJMP:
|
||
case RZ_ANALYSIS_OP_TYPE_RJMP:
|
||
case RZ_ANALYSIS_OP_TYPE_IJMP:
|
||
case RZ_ANALYSIS_OP_TYPE_IRJMP:
|
||
case RZ_ANALYSIS_OP_TYPE_JMP:
|
||
case RZ_ANALYSIS_OP_TYPE_CALL:
|
||
return true;
|
||
}
|
||
if (crop) { // if conditional jumps, calls and returns should be used for the gadget-search too
|
||
switch (aop->type) {
|
||
case RZ_ANALYSIS_OP_TYPE_CJMP:
|
||
case RZ_ANALYSIS_OP_TYPE_UCJMP:
|
||
case RZ_ANALYSIS_OP_TYPE_CCALL:
|
||
case RZ_ANALYSIS_OP_TYPE_UCCALL:
|
||
case RZ_ANALYSIS_OP_TYPE_CRET:
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static bool insert_into(void *user, const ut64 k, const ut64 v) {
|
||
HtUU *ht = (HtUU *)user;
|
||
ht_uu_insert(ht, k, v);
|
||
return true;
|
||
}
|
||
|
||
// TODO: follow unconditional jumps
|
||
static RzList /*<RzCoreAsmHit *>*/ *construct_rop_gadget(RzCore *core, ut64 addr, ut8 *buf, int buflen, int idx, const char *grep, int regex, RzList /*<char *>*/ *rx_list, struct endlist_pair *end_gadget, HtUU *badstart) {
|
||
int endaddr = end_gadget->instr_offset;
|
||
int branch_delay = end_gadget->delay_size;
|
||
RzAnalysisOp aop = { 0 };
|
||
const char *start = NULL, *end = NULL;
|
||
char *grep_str = NULL;
|
||
RzCoreAsmHit *hit = NULL;
|
||
RzList *hitlist = rz_core_asm_hit_list_new();
|
||
ut8 nb_instr = 0;
|
||
const ut8 max_instr = rz_config_get_i(core->config, "rop.len");
|
||
bool valid = false;
|
||
int grep_find;
|
||
int search_hit;
|
||
char *rx = NULL;
|
||
HtUUOptions opt = { 0 };
|
||
HtUU *localbadstart = ht_uu_new_opt(&opt);
|
||
int count = 0;
|
||
|
||
if (grep) {
|
||
start = grep;
|
||
end = strchr(grep, ';');
|
||
if (!end) { // We filter on a single opcode, so no ";"
|
||
end = start + strlen(grep);
|
||
}
|
||
grep_str = calloc(1, end - start + 1);
|
||
strncpy(grep_str, start, end - start);
|
||
if (regex) {
|
||
// get the first regexp.
|
||
if (rz_list_length(rx_list) > 0) {
|
||
rx = rz_list_get_n(rx_list, count++);
|
||
}
|
||
}
|
||
}
|
||
|
||
bool found;
|
||
ht_uu_find(badstart, idx, &found);
|
||
if (found) {
|
||
valid = false;
|
||
goto ret;
|
||
}
|
||
while (nb_instr < max_instr) {
|
||
ht_uu_insert(localbadstart, idx, 1);
|
||
|
||
int error = rz_analysis_op(core->analysis, &aop, addr, buf + idx, buflen - idx, RZ_ANALYSIS_OP_MASK_DISASM);
|
||
if (error < 0 || (nb_instr == 0 && (is_end_gadget(&aop, 0) || aop.type == RZ_ANALYSIS_OP_TYPE_NOP))) {
|
||
valid = false;
|
||
goto ret;
|
||
}
|
||
|
||
const int opsz = aop.size;
|
||
// opsz = rz_strbuf_length (asmop.buf);
|
||
char *opst = aop.mnemonic;
|
||
if (!opst) {
|
||
RZ_LOG_WARN("Analysis plugin %s did not return disassembly\n", core->analysis->cur->name);
|
||
RzAsmOp asmop;
|
||
rz_asm_set_pc(core->rasm, addr);
|
||
if (rz_asm_disassemble(core->rasm, &asmop, buf + idx, buflen - idx) < 0) {
|
||
valid = false;
|
||
goto ret;
|
||
}
|
||
opst = strdup(rz_asm_op_get_asm(&asmop));
|
||
rz_asm_op_fini(&asmop);
|
||
}
|
||
if (!rz_str_ncasecmp(opst, "invalid", strlen("invalid")) ||
|
||
!rz_str_ncasecmp(opst, ".byte", strlen(".byte"))) {
|
||
valid = false;
|
||
goto ret;
|
||
}
|
||
|
||
hit = rz_core_asm_hit_new();
|
||
if (hit) {
|
||
hit->addr = addr;
|
||
hit->len = opsz;
|
||
rz_list_append(hitlist, hit);
|
||
}
|
||
|
||
// Move on to the next instruction
|
||
idx += opsz;
|
||
addr += opsz;
|
||
if (rx) {
|
||
grep_find = rz_regex_contains(rx, opst, RZ_REGEX_ZERO_TERMINATED, RZ_REGEX_EXTENDED, RZ_REGEX_DEFAULT);
|
||
search_hit = (end && grep && grep_find);
|
||
} else {
|
||
search_hit = (end && grep && strstr(opst, grep_str));
|
||
}
|
||
|
||
// Handle (possible) grep
|
||
if (search_hit) {
|
||
if (end[0] == ';') { // fields are semicolon-separated
|
||
start = end + 1; // skip the ;
|
||
end = strchr(start, ';');
|
||
end = end ? end : start + strlen(start); // latest field?
|
||
free(grep_str);
|
||
grep_str = calloc(1, end - start + 1);
|
||
if (grep_str) {
|
||
strncpy(grep_str, start, end - start);
|
||
}
|
||
} else {
|
||
end = NULL;
|
||
}
|
||
if (regex) {
|
||
rx = rz_list_get_n(rx_list, count++);
|
||
}
|
||
}
|
||
if (endaddr <= (idx - opsz)) {
|
||
valid = (endaddr == idx - opsz);
|
||
goto ret;
|
||
}
|
||
rz_analysis_op_fini(&aop);
|
||
nb_instr++;
|
||
}
|
||
ret:
|
||
rz_analysis_op_fini(&aop);
|
||
free(grep_str);
|
||
if (regex && rx) {
|
||
rz_list_free(hitlist);
|
||
ht_uu_free(localbadstart);
|
||
return NULL;
|
||
}
|
||
if (!valid || (grep && end)) {
|
||
rz_list_free(hitlist);
|
||
ht_uu_free(localbadstart);
|
||
return NULL;
|
||
}
|
||
ht_uu_foreach(localbadstart, insert_into, badstart);
|
||
ht_uu_free(localbadstart);
|
||
// If our arch has bds then we better be including them
|
||
if (branch_delay && rz_list_length(hitlist) < (1 + branch_delay)) {
|
||
rz_list_free(hitlist);
|
||
return NULL;
|
||
}
|
||
return hitlist;
|
||
}
|
||
|
||
static void print_rop(RzCore *core, RzList /*<RzCoreAsmHit *>*/ *hitlist, PJ *pj, int mode) {
|
||
RzCoreAsmHit *hit = NULL;
|
||
RzListIter *iter;
|
||
RzList *ropList = NULL;
|
||
unsigned int size = 0;
|
||
RzAnalysisOp aop = RZ_EMPTY;
|
||
RzAsmOp asmop;
|
||
Sdb *db = NULL;
|
||
const bool colorize = rz_config_get_i(core->config, "scr.color");
|
||
const bool rop_comments = rz_config_get_i(core->config, "rop.comments");
|
||
const bool esil = rz_config_get_i(core->config, "asm.esil");
|
||
const bool rop_db = rz_config_get_i(core->config, "rop.db");
|
||
char tmpbuf[16];
|
||
|
||
if (rop_db) {
|
||
db = sdb_ns(core->sdb, "rop", true);
|
||
ropList = rz_list_newf(free);
|
||
if (!db) {
|
||
RZ_LOG_ERROR("core: Could not create SDB 'rop' namespace\n");
|
||
rz_list_free(ropList);
|
||
return;
|
||
}
|
||
}
|
||
|
||
switch (mode) {
|
||
case 'j':
|
||
pj_o(pj);
|
||
pj_ka(pj, "opcodes");
|
||
rz_list_foreach (hitlist, iter, hit) {
|
||
ut8 *buf = malloc(hit->len);
|
||
if (!buf) {
|
||
return;
|
||
}
|
||
rz_io_read_at(core->io, hit->addr, buf, hit->len);
|
||
rz_asm_set_pc(core->rasm, hit->addr);
|
||
rz_asm_disassemble(core->rasm, &asmop, buf, hit->len);
|
||
rz_analysis_op(core->analysis, &aop, hit->addr, buf, hit->len, RZ_ANALYSIS_OP_MASK_ESIL);
|
||
size += hit->len;
|
||
if (aop.type != RZ_ANALYSIS_OP_TYPE_RET) {
|
||
char *opstr_n = rz_str_newf(" %s", RZ_STRBUF_SAFEGET(&aop.esil));
|
||
rz_list_append(ropList, (void *)opstr_n);
|
||
}
|
||
pj_o(pj);
|
||
pj_kn(pj, "offset", hit->addr);
|
||
pj_ki(pj, "size", hit->len);
|
||
pj_ks(pj, "opcode", rz_asm_op_get_asm(&asmop));
|
||
pj_ks(pj, "type", rz_analysis_optype_to_string(aop.type));
|
||
pj_end(pj);
|
||
free(buf);
|
||
rz_analysis_op_fini(&aop);
|
||
}
|
||
pj_end(pj);
|
||
if (db && hit) {
|
||
const ut64 addr = ((RzCoreAsmHit *)rz_list_get_head_data(hitlist))->addr;
|
||
// rz_cons_printf ("Gadget size: %d\n", (int)size);
|
||
const char *key = rz_strf(tmpbuf, "0x%08" PFMT64x, addr);
|
||
rop_classify(core, db, ropList, key, size);
|
||
}
|
||
if (hit) {
|
||
pj_kn(pj, "retaddr", hit->addr);
|
||
pj_ki(pj, "size", size);
|
||
}
|
||
pj_end(pj);
|
||
break;
|
||
case 'q':
|
||
// Print gadgets in a 'linear manner', each sequence
|
||
// on one line.
|
||
rz_cons_printf("0x%08" PFMT64x ":",
|
||
((RzCoreAsmHit *)rz_list_get_head_data(hitlist))->addr);
|
||
rz_list_foreach (hitlist, iter, hit) {
|
||
ut8 *buf = malloc(hit->len);
|
||
rz_io_read_at(core->io, hit->addr, buf, hit->len);
|
||
rz_asm_set_pc(core->rasm, hit->addr);
|
||
rz_asm_disassemble(core->rasm, &asmop, buf, hit->len);
|
||
rz_analysis_op(core->analysis, &aop, hit->addr, buf, hit->len, RZ_ANALYSIS_OP_MASK_BASIC);
|
||
size += hit->len;
|
||
const char *opstr = RZ_STRBUF_SAFEGET(&aop.esil);
|
||
if (aop.type != RZ_ANALYSIS_OP_TYPE_RET) {
|
||
rz_list_append(ropList, rz_str_newf(" %s", opstr));
|
||
}
|
||
if (esil) {
|
||
rz_cons_printf("%s\n", opstr);
|
||
} else if (colorize) {
|
||
RzStrBuf *colored_asm, *bw_str = rz_strbuf_new(rz_asm_op_get_asm(&asmop));
|
||
colored_asm = rz_asm_colorize_asm_str(bw_str, core->print, rz_asm_get_parse_param(core->analysis->reg, aop.type), asmop.asm_toks);
|
||
rz_cons_printf(" %s%s;", colored_asm ? rz_strbuf_get(colored_asm) : "", Color_RESET);
|
||
rz_strbuf_free(colored_asm);
|
||
} else {
|
||
rz_cons_printf(" %s;", rz_asm_op_get_asm(&asmop));
|
||
}
|
||
free(buf);
|
||
rz_analysis_op_fini(&aop);
|
||
}
|
||
if (db && hit) {
|
||
const ut64 addr = ((RzCoreAsmHit *)rz_list_get_head_data(hitlist))->addr;
|
||
// rz_cons_printf ("Gadget size: %d\n", (int)size);
|
||
const char *key = rz_strf(tmpbuf, "0x%08" PFMT64x, addr);
|
||
rop_classify(core, db, ropList, key, size);
|
||
}
|
||
break;
|
||
default:
|
||
// Print gadgets with new instruction on a new line.
|
||
rz_list_foreach (hitlist, iter, hit) {
|
||
const char *comment = rop_comments ? rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, hit->addr) : NULL;
|
||
if (hit->len < 0) {
|
||
RZ_LOG_ERROR("core: Invalid hit length here\n");
|
||
continue;
|
||
}
|
||
ut8 *buf = malloc(1 + hit->len);
|
||
if (!buf) {
|
||
break;
|
||
}
|
||
buf[hit->len] = 0;
|
||
rz_io_read_at(core->io, hit->addr, buf, hit->len);
|
||
rz_asm_set_pc(core->rasm, hit->addr);
|
||
rz_asm_disassemble(core->rasm, &asmop, buf, hit->len);
|
||
rz_analysis_op(core->analysis, &aop, hit->addr, buf, hit->len, RZ_ANALYSIS_OP_MASK_ESIL);
|
||
size += hit->len;
|
||
if (aop.type != RZ_ANALYSIS_OP_TYPE_RET) {
|
||
char *opstr_n = rz_str_newf(" %s", RZ_STRBUF_SAFEGET(&aop.esil));
|
||
rz_list_append(ropList, (void *)opstr_n);
|
||
}
|
||
char *asm_op_hex = rz_asm_op_get_hex(&asmop);
|
||
if (colorize) {
|
||
RzStrBuf *colored_asm, *bw_str = rz_strbuf_new(rz_asm_op_get_asm(&asmop));
|
||
colored_asm = rz_asm_colorize_asm_str(bw_str, core->print, rz_asm_get_parse_param(core->analysis->reg, aop.type), asmop.asm_toks);
|
||
if (comment) {
|
||
rz_cons_printf(" 0x%08" PFMT64x " %18s %s%s ; %s\n",
|
||
hit->addr, asm_op_hex, colored_asm ? rz_strbuf_get(colored_asm) : "", Color_RESET, comment);
|
||
} else {
|
||
rz_cons_printf(" 0x%08" PFMT64x " %18s %s%s\n",
|
||
hit->addr, asm_op_hex, colored_asm ? rz_strbuf_get(colored_asm) : "", Color_RESET);
|
||
}
|
||
rz_strbuf_free(colored_asm);
|
||
} else {
|
||
if (comment) {
|
||
rz_cons_printf(" 0x%08" PFMT64x " %18s %s ; %s\n",
|
||
hit->addr, asm_op_hex, rz_asm_op_get_asm(&asmop), comment);
|
||
} else {
|
||
rz_cons_printf(" 0x%08" PFMT64x " %18s %s\n",
|
||
hit->addr, asm_op_hex, rz_asm_op_get_asm(&asmop));
|
||
}
|
||
}
|
||
free(asm_op_hex);
|
||
free(buf);
|
||
rz_analysis_op_fini(&aop);
|
||
}
|
||
if (db && hit) {
|
||
const ut64 addr = ((RzCoreAsmHit *)rz_list_get_head_data(hitlist))->addr;
|
||
// rz_cons_printf ("Gadget size: %d\n", (int)size);
|
||
const char *key = rz_strf(tmpbuf, "0x%08" PFMT64x, addr);
|
||
rop_classify(core, db, ropList, key, size);
|
||
}
|
||
}
|
||
if (mode != 'j') {
|
||
rz_cons_newline();
|
||
}
|
||
rz_list_free(ropList);
|
||
}
|
||
|
||
static int rz_core_search_rop(RzCore *core, RzInterval search_itv, int opt, const char *grep, int regexp, struct search_parameters *param) {
|
||
const ut8 crop = rz_config_get_i(core->config, "rop.conditional"); // decide if cjmp, cret, and ccall should be used too for the gadget-search
|
||
const ut8 subchain = rz_config_get_i(core->config, "rop.subchains");
|
||
const ut8 max_instr = rz_config_get_i(core->config, "rop.len");
|
||
const char *arch = rz_config_get(core->config, "asm.arch");
|
||
int max_count = rz_config_get_i(core->config, "search.maxhits");
|
||
int i = 0, end = 0, mode = 0, increment = 1, ret, result = true;
|
||
RzList /*<endlist_pair>*/ *end_list = rz_list_newf(free);
|
||
RzList /*<char *>*/ *rx_list = NULL;
|
||
int align = core->search->align;
|
||
RzListIter *itermap = NULL;
|
||
char *tok, *gregexp = NULL;
|
||
char *grep_arg = NULL;
|
||
char *rx = NULL;
|
||
int delta = 0;
|
||
ut8 *buf;
|
||
RzIOMap *map;
|
||
RzAsmOp asmop;
|
||
|
||
Sdb *gadgetSdb = NULL;
|
||
if (rz_config_get_i(core->config, "rop.sdb")) {
|
||
if (!(gadgetSdb = sdb_ns(core->sdb, "gadget_sdb", false))) {
|
||
gadgetSdb = sdb_ns(core->sdb, "gadget_sdb", true);
|
||
}
|
||
}
|
||
if (max_count == 0) {
|
||
max_count = -1;
|
||
}
|
||
if (max_instr <= 1) {
|
||
rz_list_free(end_list);
|
||
RZ_LOG_ERROR("core: ROP length (rop.len) must be greater than 1.\n");
|
||
if (max_instr == 1) {
|
||
RZ_LOG_ERROR("core: For rop.len = 1, use /c to search for single "
|
||
"instructions. See /c? for help.\n");
|
||
}
|
||
return false;
|
||
}
|
||
|
||
if (!strcmp(arch, "mips")) { // MIPS has no jump-in-the-middle
|
||
increment = 4;
|
||
} else if (!strcmp(arch, "arm")) { // ARM has no jump-in-the-middle
|
||
increment = rz_config_get_i(core->config, "asm.bits") == 16 ? 2 : 4;
|
||
} else if (!strcmp(arch, "avr")) { // AVR is halfword aligned.
|
||
increment = 2;
|
||
}
|
||
|
||
// Options, like JSON, linear, ...
|
||
grep_arg = strchr(grep, ' ');
|
||
if (*grep) {
|
||
if (grep_arg) {
|
||
mode = *(grep_arg - 1);
|
||
} else {
|
||
mode = *grep;
|
||
++grep;
|
||
}
|
||
}
|
||
if (grep_arg) {
|
||
grep_arg = strdup(grep_arg);
|
||
grep_arg = rz_str_replace(grep_arg, ",,", ";", true);
|
||
grep = grep_arg;
|
||
}
|
||
|
||
if (*grep == ' ') { // grep mode
|
||
for (++grep; *grep == ' '; grep++) {
|
||
;
|
||
}
|
||
} else {
|
||
grep = NULL;
|
||
}
|
||
|
||
// Deal with the grep guy.
|
||
if (grep && regexp) {
|
||
if (!rx_list) {
|
||
rx_list = rz_list_newf(free);
|
||
}
|
||
gregexp = strdup(grep);
|
||
tok = strtok(gregexp, ";");
|
||
while (tok) {
|
||
rx = strdup(tok);
|
||
rz_list_append(rx_list, rx);
|
||
tok = strtok(NULL, ";");
|
||
}
|
||
}
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_a(param->pj);
|
||
}
|
||
rz_cons_break_push(NULL, NULL);
|
||
|
||
rz_list_foreach (param->boundaries, itermap, map) {
|
||
HtUUOptions opt = { 0 };
|
||
HtUU *badstart = ht_uu_new_opt(&opt);
|
||
if (!rz_itv_overlap(search_itv, map->itv)) {
|
||
continue;
|
||
}
|
||
RzInterval itv = rz_itv_intersect(search_itv, map->itv);
|
||
ut64 from = itv.addr, to = rz_itv_end(itv);
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
delta = to - from;
|
||
buf = calloc(1, delta);
|
||
if (!buf) {
|
||
result = false;
|
||
goto bad;
|
||
}
|
||
(void)rz_io_read_at(core->io, from, buf, delta);
|
||
|
||
// Find the end gadgets.
|
||
for (i = 0; i + 32 < delta; i += increment) {
|
||
RzAnalysisOp end_gadget = RZ_EMPTY;
|
||
// Disassemble one.
|
||
if (rz_analysis_op(core->analysis, &end_gadget, from + i, buf + i,
|
||
delta - i, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
|
||
rz_analysis_op_fini(&end_gadget);
|
||
continue;
|
||
}
|
||
if (is_end_gadget(&end_gadget, crop)) {
|
||
#if 0
|
||
if (search->maxhits && rz_list_length (end_list) >= search->maxhits) {
|
||
// limit number of high level rop gadget results
|
||
rz_analysis_op_fini (&end_gadget);
|
||
break;
|
||
}
|
||
#endif
|
||
struct endlist_pair *epair = RZ_NEW0(struct endlist_pair);
|
||
if (epair) {
|
||
// If this arch has branch delay slots, add the next instr as well
|
||
if (end_gadget.delay) {
|
||
epair->instr_offset = i + increment;
|
||
epair->delay_size = end_gadget.delay;
|
||
} else {
|
||
epair->instr_offset = (intptr_t)i;
|
||
epair->delay_size = end_gadget.delay;
|
||
}
|
||
rz_list_append(end_list, (void *)(intptr_t)epair);
|
||
}
|
||
}
|
||
rz_analysis_op_fini(&end_gadget);
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
// Right now we have a list of all of the end/stop gadgets.
|
||
// We can just construct gadgets from a little bit before them.
|
||
}
|
||
rz_list_reverse(end_list);
|
||
// If we have no end gadgets, just skip all of this search nonsense.
|
||
if (!rz_list_empty(end_list)) {
|
||
int prev, next, ropdepth;
|
||
const int max_inst_size_x86 = 15;
|
||
// Get the depth of rop search, should just be max_instr
|
||
// instructions, x86 and friends are weird length instructions, so
|
||
// we'll just assume 15 byte instructions.
|
||
ropdepth = increment == 1 ? max_instr * max_inst_size_x86 /* wow, x86 is long */ : max_instr * increment;
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
struct endlist_pair *end_gadget = (struct endlist_pair *)rz_list_pop(end_list);
|
||
next = end_gadget->instr_offset;
|
||
prev = 0;
|
||
// Start at just before the first end gadget.
|
||
for (i = next - ropdepth; i < (delta - max_inst_size_x86) && max_count; i += increment) {
|
||
if (increment == 1) {
|
||
// give in-boundary instructions a shot
|
||
if (i < prev - max_inst_size_x86) {
|
||
i = prev - max_inst_size_x86;
|
||
}
|
||
} else {
|
||
if (i < prev) {
|
||
i = prev;
|
||
}
|
||
}
|
||
if (i < 0) {
|
||
i = 0;
|
||
}
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
if (i >= next) {
|
||
// We've exhausted the first end-gadget section,
|
||
// move to the next one.
|
||
free(end_gadget);
|
||
if (rz_list_get_n(end_list, 0)) {
|
||
prev = i;
|
||
end_gadget = (struct endlist_pair *)rz_list_pop(end_list);
|
||
next = end_gadget->instr_offset;
|
||
i = next - ropdepth;
|
||
if (i < 0) {
|
||
i = 0;
|
||
}
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
if (i >= end) { // read by chunk of 4k
|
||
rz_io_read_at(core->io, from + i, buf + i,
|
||
RZ_MIN((delta - i), 4096));
|
||
end = i + 2048;
|
||
}
|
||
ret = rz_asm_disassemble(core->rasm, &asmop, buf + i, delta - i);
|
||
if (ret) {
|
||
rz_asm_set_pc(core->rasm, from + i);
|
||
RzList *hitlist = construct_rop_gadget(core,
|
||
from + i, buf, delta, i, grep, regexp,
|
||
rx_list, end_gadget, badstart);
|
||
if (!hitlist) {
|
||
continue;
|
||
}
|
||
if (align && (0 != ((from + i) % align))) {
|
||
continue;
|
||
}
|
||
if (gadgetSdb) {
|
||
RzListIter *iter;
|
||
|
||
RzCoreAsmHit *hit = (RzCoreAsmHit *)rz_list_get_head_data(hitlist);
|
||
char *headAddr = rz_str_newf("%" PFMT64x, hit->addr);
|
||
if (!headAddr) {
|
||
result = false;
|
||
goto bad;
|
||
}
|
||
|
||
rz_list_foreach (hitlist, iter, hit) {
|
||
char *addr = rz_str_newf("%" PFMT64x "(%" PFMT32d ")", hit->addr, hit->len);
|
||
if (!addr) {
|
||
free(headAddr);
|
||
result = false;
|
||
goto bad;
|
||
}
|
||
sdb_concat(gadgetSdb, headAddr, addr, 0);
|
||
free(addr);
|
||
}
|
||
free(headAddr);
|
||
}
|
||
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
mode = 'j';
|
||
}
|
||
if ((mode == 'q') && subchain) {
|
||
do {
|
||
print_rop(core, hitlist, NULL, mode);
|
||
hitlist->head = hitlist->head->next;
|
||
} while (hitlist->head->next);
|
||
} else {
|
||
print_rop(core, hitlist, param->pj, mode);
|
||
}
|
||
rz_list_free(hitlist);
|
||
if (max_count > 0) {
|
||
max_count--;
|
||
if (max_count < 1) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (increment != 1) {
|
||
i = next;
|
||
}
|
||
}
|
||
}
|
||
free(buf);
|
||
}
|
||
if (rz_cons_is_breaked()) {
|
||
eprintf("\n");
|
||
}
|
||
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_end(param->pj);
|
||
}
|
||
bad:
|
||
rz_cons_break_pop();
|
||
rz_list_free(rx_list);
|
||
rz_list_free(end_list);
|
||
free(grep_arg);
|
||
free(gregexp);
|
||
return result;
|
||
}
|
||
|
||
static bool esil_addrinfo(RzAnalysisEsil *esil) {
|
||
RzCore *core = (RzCore *)esil->cb.user;
|
||
ut64 num = 0;
|
||
char *src = rz_analysis_esil_pop(esil);
|
||
if (src && *src && rz_analysis_esil_get_parm(esil, src, &num)) {
|
||
num = rz_core_analysis_address(core, num);
|
||
rz_analysis_esil_pushnum(esil, num);
|
||
} else {
|
||
// error. empty stack?
|
||
return false;
|
||
}
|
||
free(src);
|
||
return true;
|
||
}
|
||
|
||
static void do_esil_search(RzCore *core, struct search_parameters *param, const char *input) {
|
||
const int hit_combo_limit = rz_config_get_i(core->config, "search.esilcombo");
|
||
const bool cfgDebug = rz_config_get_b(core->config, "cfg.debug");
|
||
RzSearch *search = core->search;
|
||
RzSearchKeyword kw = RZ_EMPTY;
|
||
if (input[0] != 'E') {
|
||
return;
|
||
}
|
||
if (input[1] == 'j') { // "/Ej"
|
||
pj_a(param->pj);
|
||
param->outmode = RZ_MODE_JSON;
|
||
input++;
|
||
}
|
||
if (input[1] != ' ') { // "/E?"
|
||
rz_core_cmd_help(core, help_msg_search_esil);
|
||
return;
|
||
}
|
||
if (!core->analysis->esil) {
|
||
// initialize esil vm
|
||
rz_core_analysis_esil_reinit(core);
|
||
if (!core->analysis->esil) {
|
||
RZ_LOG_ERROR("core: Cannot initialize the ESIL vm\n");
|
||
return;
|
||
}
|
||
}
|
||
RzIOMap *map;
|
||
RzListIter *iter;
|
||
rz_list_foreach (param->boundaries, iter, map) {
|
||
const int iotrap = rz_config_get_i(core->config, "esil.iotrap");
|
||
const int stacksize = rz_config_get_i(core->config, "esil.stacksize");
|
||
int nonull = rz_config_get_i(core->config, "esil.nonull");
|
||
bool hit_happens = false;
|
||
size_t hit_combo = 0;
|
||
char *res;
|
||
ut64 nres, addr;
|
||
ut64 from = map->itv.addr;
|
||
ut64 to = rz_itv_end(map->itv);
|
||
unsigned int addrsize = rz_config_get_i(core->config, "esil.addr.size");
|
||
if (!core->analysis->esil) {
|
||
core->analysis->esil = rz_analysis_esil_new(stacksize, iotrap, addrsize);
|
||
}
|
||
/* hook addrinfo */
|
||
core->analysis->esil->cb.user = core;
|
||
rz_analysis_esil_set_op(core->analysis->esil, "AddrInfo", esil_addrinfo, 1, 1, RZ_ANALYSIS_ESIL_OP_TYPE_UNKNOWN);
|
||
/* hook addrinfo */
|
||
rz_analysis_esil_setup(core->analysis->esil, core->analysis, 1, 0, nonull);
|
||
rz_analysis_esil_stack_free(core->analysis->esil);
|
||
core->analysis->esil->verbose = 0;
|
||
|
||
rz_cons_break_push(NULL, NULL);
|
||
for (addr = from; addr < to; addr++) {
|
||
if (core->search->align) {
|
||
if ((addr % core->search->align)) {
|
||
continue;
|
||
}
|
||
}
|
||
#if 0
|
||
// we need a way to retrieve info from a speicif address, and make it accessible from the esil search
|
||
// maybe we can just do it like this: 0x804840,AddressType,3,&, ... bitmask
|
||
// executable = 1
|
||
// writable = 2
|
||
// inprogram
|
||
// instack
|
||
// inlibrary
|
||
// inheap
|
||
rz_analysis_esil_set_op (core->analysis->esil, "AddressInfo", esil_search_address_info);
|
||
#endif
|
||
if (rz_cons_is_breaked()) {
|
||
RZ_LOG_WARN("core: Breaked at 0x%08" PFMT64x "\n", addr);
|
||
break;
|
||
}
|
||
rz_analysis_esil_set_pc(core->analysis->esil, addr);
|
||
if (!rz_analysis_esil_parse(core->analysis->esil, input + 2)) {
|
||
// XXX: return value doesnt seems to be correct here
|
||
RZ_LOG_ERROR("core: Cannot parse esil (%s)\n", input + 2);
|
||
break;
|
||
}
|
||
hit_happens = false;
|
||
res = rz_analysis_esil_pop(core->analysis->esil);
|
||
if (rz_analysis_esil_get_parm(core->analysis->esil, res, &nres)) {
|
||
if (cfgDebug) {
|
||
eprintf("RES 0x%08" PFMT64x " %" PFMT64d "\n", addr, nres);
|
||
}
|
||
if (nres) {
|
||
eprintf("hits: %d\r", kw.count);
|
||
hit_happens = true;
|
||
if (param->outmode != RZ_MODE_JSON) {
|
||
if (!_cb_hit(&kw, param, addr)) {
|
||
free(res);
|
||
break;
|
||
}
|
||
// eprintf (" HIT AT 0x%"PFMT64x"\n", addr);
|
||
kw.type = 0; // RZ_SEARCH_TYPE_ESIL;
|
||
kw.kwidx = search->n_kws;
|
||
kw.count++;
|
||
kw.keyword_length = 0;
|
||
}
|
||
}
|
||
} else {
|
||
RZ_LOG_ERROR("core: Cannot parse esil (%s)\n", input + 2);
|
||
rz_analysis_esil_stack_free(core->analysis->esil);
|
||
free(res);
|
||
break;
|
||
}
|
||
rz_analysis_esil_stack_free(core->analysis->esil);
|
||
free(res);
|
||
|
||
if (hit_happens) {
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_o(param->pj);
|
||
pj_kn(param->pj, "addr", addr);
|
||
pj_kn(param->pj, "value", nres);
|
||
pj_end(param->pj);
|
||
}
|
||
hit_combo++;
|
||
if (hit_combo > hit_combo_limit) {
|
||
RZ_LOG_WARN("core: Hit search.esilcombo reached (%d). Stopping search. Use f-\n", hit_combo_limit);
|
||
break;
|
||
}
|
||
} else {
|
||
hit_combo = 0;
|
||
}
|
||
}
|
||
rz_config_set_i(core->config, "search.kwidx", search->n_kws); // TODO remove
|
||
rz_cons_break_pop();
|
||
}
|
||
rz_cons_clear_line(1);
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_end(param->pj);
|
||
}
|
||
}
|
||
|
||
#define MAXINSTR 8
|
||
#define SUMARRAY(arr, size, res) \
|
||
do \
|
||
(res) += (arr)[--(size)]; \
|
||
while ((size))
|
||
|
||
#if USE_EMULATION
|
||
// IMHO This code must be deleted
|
||
static int emulateSyscallPrelude(RzCore *core, ut64 at, ut64 curpc) {
|
||
int i, inslen, bsize = RZ_MIN(64, core->blocksize);
|
||
ut8 *arr;
|
||
RzAnalysisOp aop;
|
||
const int mininstrsz = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
|
||
const int minopcode = RZ_MAX(1, mininstrsz);
|
||
const char *a0 = rz_reg_get_name(core->analysis->reg, RZ_REG_NAME_SN);
|
||
const char *pc = rz_reg_get_name(core->dbg->reg, RZ_REG_NAME_PC);
|
||
RzRegItem *r = rz_reg_get(core->dbg->reg, pc, -1);
|
||
RzRegItem *reg_a0 = rz_reg_get(core->dbg->reg, a0, -1);
|
||
|
||
arr = malloc(bsize);
|
||
if (!arr) {
|
||
RZ_LOG_ERROR("core: Cannot allocate %d byte(s)\n", bsize);
|
||
free(arr);
|
||
return -1;
|
||
}
|
||
rz_reg_set_value(core->dbg->reg, r, curpc);
|
||
for (i = 0; curpc < at; curpc++, i++) {
|
||
if (i >= (bsize - 32)) {
|
||
i = 0;
|
||
}
|
||
if (!i) {
|
||
rz_io_read_at(core->io, curpc, arr, bsize);
|
||
}
|
||
inslen = rz_analysis_op(core->analysis, &aop, curpc, arr + i, bsize - i, RZ_ANALYSIS_OP_MASK_BASIC);
|
||
if (inslen > 0) {
|
||
int incr = (core->search->align > 0) ? core->search->align - 1 : inslen - 1;
|
||
if (incr < 0) {
|
||
incr = minopcode;
|
||
}
|
||
i += incr;
|
||
curpc += incr;
|
||
if (rz_analysis_op_nonlinear(aop.type)) { // skip the instr
|
||
rz_reg_set_value(core->dbg->reg, r, curpc + 1);
|
||
} else { // step instr
|
||
rz_core_esil_step(core, UT64_MAX, NULL, NULL);
|
||
}
|
||
}
|
||
}
|
||
free(arr);
|
||
int sysno = rz_debug_reg_get(core->dbg, a0);
|
||
rz_reg_set_value(core->dbg->reg, reg_a0, -2); // clearing register A0
|
||
return sysno;
|
||
}
|
||
#endif
|
||
|
||
static void do_syscall_search(RzCore *core, struct search_parameters *param) {
|
||
RzSearch *search = core->search;
|
||
ut64 at;
|
||
#if USE_EMULATION
|
||
ut64 curpc;
|
||
#endif
|
||
ut8 *buf;
|
||
int curpos, idx = 0, count = 0;
|
||
RzAnalysisOp aop = { 0 };
|
||
int i, ret, bsize = RZ_MAX(64, core->blocksize);
|
||
int kwidx = core->search->n_kws;
|
||
RzIOMap *map;
|
||
RzListIter *iter;
|
||
const int mininstrsz = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
|
||
const int minopcode = RZ_MAX(1, mininstrsz);
|
||
RzAnalysisEsil *esil;
|
||
int align = core->search->align;
|
||
int stacksize = rz_config_get_i(core->config, "esil.stack.depth");
|
||
int iotrap = rz_config_get_i(core->config, "esil.iotrap");
|
||
unsigned int addrsize = rz_config_get_i(core->config, "esil.addr.size");
|
||
|
||
if (!(esil = rz_analysis_esil_new(stacksize, iotrap, addrsize))) {
|
||
return;
|
||
}
|
||
int *previnstr = calloc(MAXINSTR + 1, sizeof(int));
|
||
if (!previnstr) {
|
||
rz_analysis_esil_free(esil);
|
||
return;
|
||
}
|
||
buf = malloc(bsize);
|
||
if (!buf) {
|
||
RZ_LOG_ERROR("core: Cannot allocate %d byte(s)\n", bsize);
|
||
rz_analysis_esil_free(esil);
|
||
free(previnstr);
|
||
return;
|
||
}
|
||
ut64 oldoff = core->offset;
|
||
int syscallNumber = 0;
|
||
rz_cons_break_push(NULL, NULL);
|
||
const char *a0 = rz_reg_get_name(core->analysis->reg, RZ_REG_NAME_SN);
|
||
char *esp = rz_str_newf("%s,=", a0);
|
||
char *esp32 = NULL;
|
||
if (core->analysis->bits == 64) {
|
||
const char *reg = rz_reg_64_to_32(core->analysis->reg, a0);
|
||
if (reg) {
|
||
esp32 = rz_str_newf("%s,=", reg);
|
||
}
|
||
}
|
||
rz_list_foreach (param->boundaries, iter, map) {
|
||
ut64 from = map->itv.addr;
|
||
ut64 to = rz_itv_end(map->itv);
|
||
if (from >= to) {
|
||
RZ_LOG_ERROR("core: `from` value must be lower than `to` value\n");
|
||
goto beach;
|
||
}
|
||
if (to == UT64_MAX) {
|
||
RZ_LOG_ERROR("core: Invalid destination boundary\n");
|
||
goto beach;
|
||
}
|
||
for (i = 0, at = from; at < to; at++, i++) {
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
if (i >= (bsize - 32)) {
|
||
i = 0;
|
||
}
|
||
if (align && (at % align)) {
|
||
continue;
|
||
}
|
||
if (!i) {
|
||
rz_io_read_at(core->io, at, buf, bsize);
|
||
}
|
||
ret = rz_analysis_op(core->analysis, &aop, at, buf + i, bsize - i, RZ_ANALYSIS_OP_MASK_ESIL);
|
||
curpos = idx++ % (MAXINSTR + 1);
|
||
previnstr[curpos] = ret; // This array holds prev n instr size + cur instr size
|
||
if (aop.type == RZ_ANALYSIS_OP_TYPE_MOV) {
|
||
const char *es = RZ_STRBUF_SAFEGET(&aop.esil);
|
||
if (strstr(es, esp)) {
|
||
if (aop.val != -1) {
|
||
syscallNumber = aop.val;
|
||
}
|
||
} else if (esp32 && strstr(es, esp32)) {
|
||
if (aop.val != -1) {
|
||
syscallNumber = aop.val;
|
||
}
|
||
}
|
||
}
|
||
if ((aop.type == RZ_ANALYSIS_OP_TYPE_SWI) && ret) { // && (aop.val > 10)) {
|
||
int scVector = -1; // int 0x80, svc 0x70, ...
|
||
int scNumber = 0; // r0/eax/...
|
||
#if USE_EMULATION
|
||
// This for calculating no of bytes to be subtracted , to get n instr above syscall
|
||
int nbytes = 0;
|
||
int nb_opcodes = MAXINSTR;
|
||
SUMARRAY(previnstr, nb_opcodes, nbytes);
|
||
curpc = at - (nbytes - previnstr[curpos]);
|
||
scNumber = emulateSyscallPrelude(core, at, curpc);
|
||
#else
|
||
scNumber = syscallNumber;
|
||
#endif
|
||
scVector = (aop.val > 0) ? aop.val : -1; // int 0x80 (aop.val = 0x80)
|
||
RzSyscallItem *item = rz_syscall_get(core->analysis->syscall, scNumber, scVector);
|
||
if (item) {
|
||
rz_cons_printf("0x%08" PFMT64x " %s\n", at, item->name);
|
||
}
|
||
memset(previnstr, 0, (MAXINSTR + 1) * sizeof(*previnstr)); // clearing the buffer
|
||
if (searchflags) {
|
||
char *flag = rz_str_newf("%s%d_%d.%s", searchprefix, kwidx, count, item ? item->name : "syscall");
|
||
rz_flag_set(core->flags, flag, at, ret);
|
||
free(flag);
|
||
}
|
||
rz_syscall_item_free(item);
|
||
if (*param->cmd_hit) {
|
||
ut64 here = core->offset;
|
||
rz_core_seek(core, at, true);
|
||
rz_core_cmd(core, param->cmd_hit, 0);
|
||
rz_core_seek(core, here, true);
|
||
}
|
||
count++;
|
||
if (search->maxhits > 0 && count >= search->maxhits) {
|
||
rz_analysis_op_fini(&aop);
|
||
break;
|
||
}
|
||
syscallNumber = 0;
|
||
}
|
||
int inc = (core->search->align > 0) ? core->search->align - 1 : ret - 1;
|
||
if (inc < 0) {
|
||
inc = minopcode;
|
||
}
|
||
i += inc;
|
||
at += inc;
|
||
rz_analysis_op_fini(&aop);
|
||
}
|
||
}
|
||
beach:
|
||
rz_core_seek(core, oldoff, true);
|
||
rz_analysis_esil_free(esil);
|
||
rz_cons_break_pop();
|
||
free(buf);
|
||
free(esp32);
|
||
free(esp);
|
||
free(previnstr);
|
||
}
|
||
|
||
static void do_ref_search(RzCore *core, ut64 addr, ut64 from, ut64 to, struct search_parameters *param) {
|
||
const int size = 12;
|
||
char str[512];
|
||
RzAnalysisFunction *fcn;
|
||
RzAnalysisXRef *xref;
|
||
RzListIter *iter;
|
||
ut8 buf[12];
|
||
RzAsmOp asmop;
|
||
RzList *list = rz_analysis_xrefs_get_to(core->analysis, addr);
|
||
if (list) {
|
||
rz_list_foreach (list, iter, xref) {
|
||
rz_io_read_at(core->io, xref->from, buf, size);
|
||
rz_asm_set_pc(core->rasm, xref->from);
|
||
rz_asm_disassemble(core->rasm, &asmop, buf, size);
|
||
fcn = rz_analysis_get_fcn_in(core->analysis, xref->from, 0);
|
||
RzAnalysisHint *hint = rz_analysis_hint_get(core->analysis, xref->from);
|
||
rz_parse_filter(core->parser, xref->from, core->flags, hint, rz_strbuf_get(&asmop.buf_asm),
|
||
str, sizeof(str), core->print->big_endian);
|
||
rz_analysis_hint_free(hint);
|
||
const char *comment = rz_meta_get_string(core->analysis, RZ_META_TYPE_COMMENT, xref->from);
|
||
char *print_comment = NULL;
|
||
const char *nl = comment ? strchr(comment, '\n') : NULL;
|
||
if (nl) { // display only until the first newline
|
||
comment = print_comment = rz_str_ndup(comment, nl - comment);
|
||
}
|
||
char *buf_fcn = comment
|
||
? rz_str_newf("%s; %s", fcn ? fcn->name : "(nofunc)", comment)
|
||
: rz_str_newf("%s", fcn ? fcn->name : "(nofunc)");
|
||
free(print_comment);
|
||
if (from <= xref->from && to >= xref->from) {
|
||
rz_cons_printf("%s 0x%" PFMT64x " [%s] %s\n",
|
||
buf_fcn, xref->from, rz_analysis_xrefs_type_tostring(xref->type), str);
|
||
if (*param->cmd_hit) {
|
||
ut64 here = core->offset;
|
||
rz_core_seek(core, xref->from, true);
|
||
rz_core_cmd(core, param->cmd_hit, 0);
|
||
rz_core_seek(core, here, true);
|
||
}
|
||
}
|
||
free(buf_fcn);
|
||
}
|
||
}
|
||
rz_list_free(list);
|
||
}
|
||
|
||
static bool do_analysis_search(RzCore *core, struct search_parameters *param, const char *input) {
|
||
RzSearch *search = core->search;
|
||
ut64 at;
|
||
RzAnalysisOp aop;
|
||
int type = 0;
|
||
int mode = 0;
|
||
int kwidx = core->search->n_kws;
|
||
int i, ret, count = 0;
|
||
|
||
while (*input && *input != ' ') {
|
||
switch (*input) {
|
||
case 'j':
|
||
case 'q':
|
||
mode = *input;
|
||
break;
|
||
case 'l': // "/alt" "/alf"
|
||
switch (type) {
|
||
case 't': // "/alt"
|
||
case 'f': // "/alf"
|
||
for (i = 0; i < 64; i++) {
|
||
const char *str = type == 'f'
|
||
? rz_analysis_op_family_to_string(i)
|
||
: rz_analysis_optype_to_string(i);
|
||
if (!str || !*str) {
|
||
break;
|
||
}
|
||
if (!strcmp(str, "undefined")) {
|
||
continue;
|
||
}
|
||
rz_cons_println(str);
|
||
}
|
||
break;
|
||
case 's': { // "/als"
|
||
RzListIter *iter;
|
||
RzSyscallItem *si;
|
||
RzList *list = rz_syscall_list(core->analysis->syscall);
|
||
rz_list_foreach (list, iter, si) {
|
||
if (si->num > SYSCALL_HEX_LIMIT) {
|
||
rz_cons_printf("%s = 0x%02x.%x\n", si->name, si->swi, si->num);
|
||
} else {
|
||
rz_cons_printf("%s = 0x%02x.%d\n", si->name, si->swi, si->num);
|
||
}
|
||
}
|
||
rz_list_free(list);
|
||
break;
|
||
}
|
||
case 0:
|
||
rz_core_cmd0(core, "aoml");
|
||
break;
|
||
default:
|
||
RZ_LOG_ERROR("/al%c - unknown command\n", type);
|
||
break;
|
||
}
|
||
return false;
|
||
case 'f': // "/af"
|
||
case 's': // "/as"
|
||
case 't': // "/at"
|
||
case 'm': // "/am"
|
||
case ' ':
|
||
type = *input;
|
||
break;
|
||
case 0:
|
||
case '?':
|
||
default:
|
||
rz_core_cmd_help(core, help_msg_slash_a);
|
||
return false;
|
||
}
|
||
input++;
|
||
}
|
||
if (type == 's') {
|
||
rz_warn_if_reached();
|
||
return true;
|
||
}
|
||
if (mode == 'j') {
|
||
pj_a(param->pj);
|
||
}
|
||
input = rz_str_trim_head_ro(input);
|
||
rz_cons_break_push(NULL, NULL);
|
||
RzIOMap *map;
|
||
RzListIter *iter;
|
||
rz_list_foreach (param->boundaries, iter, map) {
|
||
ut64 from = map->itv.addr;
|
||
ut64 to = rz_itv_end(map->itv);
|
||
for (i = 0, at = from; at < to; i++, at++) {
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
at = from + i;
|
||
ut8 bufop[32];
|
||
rz_io_read_at(core->io, at, bufop, sizeof(bufop));
|
||
ret = rz_analysis_op(core->analysis, &aop, at, bufop, sizeof(bufop), RZ_ANALYSIS_OP_MASK_BASIC | RZ_ANALYSIS_OP_MASK_DISASM);
|
||
if (ret > 0) {
|
||
bool match = false;
|
||
if (type == 'm') {
|
||
const char *fam = aop.mnemonic;
|
||
if (fam && (!*input || rz_str_startswith(fam, input))) {
|
||
match = true;
|
||
}
|
||
} else if (type == 'f') {
|
||
const char *fam = rz_analysis_op_family_to_string(aop.family);
|
||
if (fam && (!*input || !strcmp(input, fam))) {
|
||
match = true;
|
||
}
|
||
} else {
|
||
const char *type = rz_analysis_optype_to_string(aop.type);
|
||
if (type) {
|
||
bool isCandidate = !*input;
|
||
if (!strcmp(input, "cswi")) {
|
||
if (*input && !strcmp(input + 1, type)) {
|
||
isCandidate = true;
|
||
}
|
||
} else {
|
||
if (!strcmp(input, type)) {
|
||
isCandidate = true;
|
||
}
|
||
}
|
||
if (isCandidate) {
|
||
if (strstr(input, "swi")) {
|
||
if (*input == 'c') {
|
||
match = true; // aop.cond;
|
||
} else {
|
||
match = aop.cond == RZ_TYPE_COND_AL;
|
||
}
|
||
} else {
|
||
match = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (match) {
|
||
// char *opstr = rz_core_disassemble_instr (core, at, 1);
|
||
char *opstr = rz_core_op_str(core, at);
|
||
switch (mode) {
|
||
case 'j':
|
||
pj_o(param->pj);
|
||
pj_kn(param->pj, "addr", at);
|
||
pj_ki(param->pj, "size", ret);
|
||
pj_ks(param->pj, "opstr", opstr);
|
||
pj_end(param->pj);
|
||
break;
|
||
case 'q':
|
||
rz_cons_printf("0x%08" PFMT64x "\n", at);
|
||
break;
|
||
default:
|
||
if (type == 'f') {
|
||
const char *fam = rz_analysis_op_family_to_string(aop.family);
|
||
rz_cons_printf("0x%08" PFMT64x " %d %s %s\n", at, ret, fam, opstr);
|
||
} else {
|
||
rz_cons_printf("0x%08" PFMT64x " %d %s\n", at, ret, opstr);
|
||
}
|
||
break;
|
||
}
|
||
RZ_FREE(opstr);
|
||
if (*input && searchflags) {
|
||
char flag[64];
|
||
snprintf(flag, sizeof(flag), "%s%d_%d",
|
||
searchprefix, kwidx, count);
|
||
rz_flag_set(core->flags, flag, at, ret);
|
||
}
|
||
if (*param->cmd_hit) {
|
||
ut64 here = core->offset;
|
||
rz_core_seek(core, at, true);
|
||
rz_core_cmd(core, param->cmd_hit, 0);
|
||
rz_core_seek(core, here, true);
|
||
}
|
||
count++;
|
||
if (search->maxhits && count >= search->maxhits) {
|
||
goto done;
|
||
}
|
||
}
|
||
int inc = (core->search->align > 0) ? core->search->align - 1 : ret - 1;
|
||
if (inc < 0) {
|
||
inc = 0;
|
||
}
|
||
i += inc;
|
||
at += inc;
|
||
}
|
||
}
|
||
}
|
||
done:
|
||
if (mode == 'j') {
|
||
pj_end(param->pj);
|
||
}
|
||
rz_cons_break_pop();
|
||
return false;
|
||
}
|
||
|
||
static void do_section_search(RzCore *core, struct search_parameters *param, const char *input) {
|
||
double threshold = 1;
|
||
bool r2mode = false;
|
||
if (input && *input) {
|
||
if (*input == '*') {
|
||
r2mode = true;
|
||
}
|
||
sscanf(input, "%lf", &threshold);
|
||
if (threshold < 1) {
|
||
threshold = 1;
|
||
}
|
||
}
|
||
int buf_size = core->blocksize;
|
||
ut8 *buf = malloc(buf_size);
|
||
if (!buf) {
|
||
return;
|
||
}
|
||
double oe = 0;
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
ut64 begin = UT64_MAX;
|
||
ut64 at, end = 0;
|
||
int index = 0;
|
||
bool lastBlock = true;
|
||
rz_cons_break_push(NULL, NULL);
|
||
rz_list_foreach (param->boundaries, iter, map) {
|
||
ut64 from = map->itv.addr;
|
||
ut64 to = rz_itv_end(map->itv);
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
for (at = from; at < to; at += buf_size) {
|
||
if (begin == UT64_MAX) {
|
||
begin = at;
|
||
}
|
||
rz_io_read_at(core->io, at, buf, buf_size);
|
||
double e = rz_hash_entropy(buf, buf_size);
|
||
double diff = oe - e;
|
||
diff = RZ_ABS(diff);
|
||
end = at + buf_size;
|
||
if (diff > threshold) {
|
||
if (r2mode) {
|
||
rz_cons_printf("f entropy_section_%d 0x%08" PFMT64x " @ 0x%08" PFMT64x "\n", index, end - begin, begin);
|
||
} else {
|
||
rz_cons_printf("0x%08" PFMT64x " - 0x%08" PFMT64x " ~ %lf\n", begin, end, e);
|
||
}
|
||
begin = UT64_MAX;
|
||
index++;
|
||
lastBlock = false;
|
||
} else {
|
||
lastBlock = true;
|
||
}
|
||
oe = e;
|
||
}
|
||
begin = UT64_MAX;
|
||
}
|
||
if (begin != UT64_MAX && lastBlock) {
|
||
if (r2mode) {
|
||
rz_cons_printf("f entropy_section_%d 0x%08" PFMT64x " @ 0x%08" PFMT64x "\n", index, end - begin, begin);
|
||
} else {
|
||
rz_cons_printf("0x%08" PFMT64x " - 0x%08" PFMT64x " ~ %d .. last\n", begin, end, 0);
|
||
}
|
||
index++;
|
||
}
|
||
rz_cons_break_pop();
|
||
free(buf);
|
||
}
|
||
|
||
static void do_asm_search(RzCore *core, struct search_parameters *param, const char *input, int mode, RzInterval search_itv) {
|
||
RzCoreAsmHit *hit;
|
||
RzListIter *iter, *itermap;
|
||
int count = 0, maxhits = 0, filter = 0;
|
||
int kwidx = core->search->n_kws; // (int)rz_config_get_i (core->config, "search.kwidx")-1;
|
||
RzList *hits;
|
||
RzIOMap *map;
|
||
bool regexp = input[0] == '/'; // "/c/"
|
||
bool everyByte = regexp && input[1] == 'a';
|
||
char tmpbuf[128];
|
||
char *end_cmd = strchr(input, ' ');
|
||
switch ((end_cmd ? *(end_cmd - 1) : input[0])) {
|
||
case 'j':
|
||
param->outmode = RZ_MODE_JSON;
|
||
break;
|
||
case '*':
|
||
param->outmode = RZ_MODE_RIZINCMD;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
if (mode == 'o') {
|
||
everyByte = true;
|
||
}
|
||
|
||
maxhits = (int)rz_config_get_i(core->config, "search.maxhits");
|
||
filter = (int)rz_config_get_i(core->config, "asm.sub.names");
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_a(param->pj);
|
||
}
|
||
rz_cons_break_push(NULL, NULL);
|
||
if (everyByte) {
|
||
input++;
|
||
}
|
||
rz_list_foreach (param->boundaries, itermap, map) {
|
||
if (!rz_itv_overlap(search_itv, map->itv)) {
|
||
continue;
|
||
}
|
||
ut64 from = map->itv.addr;
|
||
ut64 to = rz_itv_end(map->itv);
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
if (maxhits && count >= maxhits) {
|
||
break;
|
||
}
|
||
hits = rz_core_asm_strsearch(core, end_cmd,
|
||
from, to, maxhits, regexp, everyByte, mode);
|
||
if (hits) {
|
||
const char *cmdhit = rz_config_get(core->config, "cmd.hit");
|
||
rz_list_foreach (hits, iter, hit) {
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
if (cmdhit && *cmdhit) {
|
||
rz_core_cmdf(core, "%s @ 0x%" PFMT64x, cmdhit, hit->addr);
|
||
}
|
||
switch (param->outmode) {
|
||
case RZ_MODE_JSON:
|
||
pj_o(param->pj);
|
||
pj_kn(param->pj, "offset", hit->addr);
|
||
pj_ki(param->pj, "len", hit->len);
|
||
pj_ks(param->pj, "code", hit->code);
|
||
pj_end(param->pj);
|
||
break;
|
||
case RZ_MODE_RIZINCMD:
|
||
rz_cons_printf("f %s%d_%i @ 0x%08" PFMT64x "\n",
|
||
searchprefix, kwidx, count, hit->addr);
|
||
break;
|
||
default:
|
||
if (filter) {
|
||
char tmp[128] = {
|
||
0
|
||
};
|
||
RzAnalysisHint *hint = rz_analysis_hint_get(core->analysis, hit->addr);
|
||
rz_parse_filter(core->parser, hit->addr, core->flags, hint, hit->code, tmp, sizeof(tmp),
|
||
core->print->big_endian);
|
||
rz_analysis_hint_free(hint);
|
||
rz_cons_printf("0x%08" PFMT64x " # %i: %s\n",
|
||
hit->addr, hit->len, tmp);
|
||
} else {
|
||
rz_cons_printf("0x%08" PFMT64x " # %i: %s\n",
|
||
hit->addr, hit->len, hit->code);
|
||
}
|
||
break;
|
||
}
|
||
if (searchflags) {
|
||
const char *flagname = rz_strf(tmpbuf, "%s%d_%d", searchprefix, kwidx, count);
|
||
if (flagname) {
|
||
rz_flag_set(core->flags, flagname, hit->addr, hit->len);
|
||
}
|
||
}
|
||
count++;
|
||
}
|
||
rz_list_purge(hits);
|
||
free(hits);
|
||
}
|
||
}
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_end(param->pj);
|
||
}
|
||
rz_cons_break_pop();
|
||
}
|
||
|
||
static void do_string_search(RzCore *core, RzInterval search_itv, struct search_parameters *param) {
|
||
ut64 at;
|
||
ut8 *buf;
|
||
RzSearch *search = core->search;
|
||
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_a(param->pj);
|
||
}
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
if (!searchflags && param->outmode != RZ_MODE_JSON) {
|
||
rz_cons_printf("fs hits\n");
|
||
}
|
||
core->search->inverse = param->inverse;
|
||
// TODO Bad but is to be compatible with the legacy behavior
|
||
if (param->inverse) {
|
||
core->search->maxhits = 1;
|
||
}
|
||
if (core->search->n_kws > 0) {
|
||
/* set callback */
|
||
/* TODO: handle last block of data */
|
||
/* TODO: handle ^C */
|
||
/* TODO: launch search in background support */
|
||
// REMOVE OLD FLAGS rz_core_cmdf (core, "f-%s*", rz_config_get (core->config, "search.prefix"));
|
||
rz_search_set_callback(core->search, &_cb_hit, param);
|
||
if (!(buf = malloc(core->blocksize))) {
|
||
return;
|
||
}
|
||
if (search->bckwrds) {
|
||
rz_search_string_prepare_backward(search);
|
||
}
|
||
rz_cons_break_push(NULL, NULL);
|
||
// TODO search cross boundary
|
||
rz_list_foreach (param->boundaries, iter, map) {
|
||
if (!rz_itv_overlap(search_itv, map->itv)) {
|
||
continue;
|
||
}
|
||
const ut64 saved_nhits = search->nhits;
|
||
RzInterval itv = rz_itv_intersect(search_itv, map->itv);
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
if (param->outmode != RZ_MODE_JSON) {
|
||
RzSearchKeyword *kw = rz_list_first(core->search->kws);
|
||
int lenstr = kw ? kw->keyword_length : 0;
|
||
const char *bytestr = lenstr > 1 ? "bytes" : "byte";
|
||
eprintf("Searching %d %s in [0x%" PFMT64x "-0x%" PFMT64x "]\n",
|
||
kw ? kw->keyword_length : 0, bytestr, itv.addr, rz_itv_end(itv));
|
||
}
|
||
if (!core->search->bckwrds) {
|
||
RzListIter *it;
|
||
RzSearchKeyword *kw;
|
||
rz_list_foreach (core->search->kws, it, kw) {
|
||
kw->last = 0;
|
||
}
|
||
}
|
||
|
||
const ut64 from = itv.addr, to = rz_itv_end(itv),
|
||
from1 = search->bckwrds ? to : from,
|
||
to1 = search->bckwrds ? from : to;
|
||
ut64 len;
|
||
for (at = from1; at != to1; at = search->bckwrds ? at - len : at + len) {
|
||
print_search_progress(at, to1, search->nhits, param);
|
||
if (rz_cons_is_breaked()) {
|
||
eprintf("\n\n");
|
||
break;
|
||
}
|
||
if (search->bckwrds) {
|
||
len = RZ_MIN(core->blocksize, at - from);
|
||
// TODO prefix_read_at
|
||
if (!rz_io_is_valid_offset(core->io, at - len, 0)) {
|
||
break;
|
||
}
|
||
(void)rz_io_read_at(core->io, at - len, buf, len);
|
||
} else {
|
||
len = RZ_MIN(core->blocksize, to - at);
|
||
if (!rz_io_is_valid_offset(core->io, at, 0)) {
|
||
break;
|
||
}
|
||
(void)rz_io_read_at(core->io, at, buf, len);
|
||
}
|
||
rz_search_update(core->search, at, buf, len);
|
||
if (param->aes_search) {
|
||
// Adjust length to search between blocks.
|
||
if (len == core->blocksize) {
|
||
len -= AES_SEARCH_LENGTH - 1;
|
||
}
|
||
} else if (param->privkey_search) {
|
||
// Adjust length to search between blocks.
|
||
if (len == core->blocksize) {
|
||
len -= PRIVATE_KEY_SEARCH_LENGTH - 1;
|
||
}
|
||
}
|
||
if (core->search->maxhits > 0 && core->search->nhits >= core->search->maxhits) {
|
||
goto done;
|
||
}
|
||
}
|
||
print_search_progress(at, to1, search->nhits, param);
|
||
rz_cons_clear_line(1);
|
||
core->num->value = search->nhits;
|
||
if (param->outmode != RZ_MODE_JSON) {
|
||
eprintf("hits: %" PFMT64d "\n", search->nhits - saved_nhits);
|
||
}
|
||
}
|
||
done:
|
||
rz_cons_break_pop();
|
||
free(buf);
|
||
} else {
|
||
RZ_LOG_ERROR("core: No keywords defined\n");
|
||
}
|
||
|
||
if (param->outmode == RZ_MODE_JSON) {
|
||
pj_end(param->pj);
|
||
}
|
||
}
|
||
|
||
static void rop_kuery(void *data, const char *input, PJ *pj) {
|
||
RzCore *core = (RzCore *)data;
|
||
Sdb *db_rop = sdb_ns(core->sdb, "rop", false);
|
||
SdbListIter *sdb_iter, *it;
|
||
SdbList *sdb_list;
|
||
SdbNs *ns;
|
||
SdbKv *kv;
|
||
char *out;
|
||
|
||
if (!db_rop) {
|
||
RZ_LOG_ERROR("core: could not find SDB 'rop' namespace\n");
|
||
return;
|
||
}
|
||
|
||
switch (*input) {
|
||
case 'q':
|
||
ls_foreach (db_rop->ns, it, ns) {
|
||
sdb_list = sdb_foreach_list(ns->sdb, false);
|
||
ls_foreach (sdb_list, sdb_iter, kv) {
|
||
rz_cons_printf("%s ", sdbkv_key(kv));
|
||
}
|
||
}
|
||
break;
|
||
case 'j':
|
||
pj_o(pj);
|
||
pj_ka(pj, "gadgets");
|
||
ls_foreach (db_rop->ns, it, ns) {
|
||
sdb_list = sdb_foreach_list(ns->sdb, false);
|
||
ls_foreach (sdb_list, sdb_iter, kv) {
|
||
char *dup = strdup(sdbkv_value(kv));
|
||
bool flag = false; // to free tok when doing strdup
|
||
char *size = strtok(dup, " ");
|
||
char *tok = strtok(NULL, "{}");
|
||
if (!tok) {
|
||
tok = strdup("NOP");
|
||
flag = true;
|
||
}
|
||
pj_o(pj);
|
||
pj_ks(pj, "address", sdbkv_key(kv));
|
||
pj_ks(pj, "size", size);
|
||
pj_ks(pj, "type", ns->name);
|
||
pj_ks(pj, "effect", tok);
|
||
pj_end(pj);
|
||
free(dup);
|
||
if (flag) {
|
||
free(tok);
|
||
}
|
||
}
|
||
}
|
||
pj_end(pj);
|
||
pj_end(pj);
|
||
break;
|
||
case ' ':
|
||
if (!strcmp(input + 1, "nop")) {
|
||
out = sdb_querys(core->sdb, NULL, 0, "rop/nop/*");
|
||
if (out) {
|
||
rz_cons_println(out);
|
||
free(out);
|
||
}
|
||
} else if (!strcmp(input + 1, "mov")) {
|
||
out = sdb_querys(core->sdb, NULL, 0, "rop/mov/*");
|
||
if (out) {
|
||
rz_cons_println(out);
|
||
free(out);
|
||
}
|
||
} else if (!strcmp(input + 1, "const")) {
|
||
out = sdb_querys(core->sdb, NULL, 0, "rop/const/*");
|
||
if (out) {
|
||
rz_cons_println(out);
|
||
free(out);
|
||
}
|
||
} else if (!strcmp(input + 1, "arithm")) {
|
||
out = sdb_querys(core->sdb, NULL, 0, "rop/arithm/*");
|
||
if (out) {
|
||
rz_cons_println(out);
|
||
free(out);
|
||
}
|
||
} else if (!strcmp(input + 1, "arithm_ct")) {
|
||
out = sdb_querys(core->sdb, NULL, 0, "rop/arithm_ct/*");
|
||
if (out) {
|
||
rz_cons_println(out);
|
||
free(out);
|
||
}
|
||
} else {
|
||
RZ_LOG_ERROR("core: Invalid ROP class\n");
|
||
}
|
||
break;
|
||
default:
|
||
out = sdb_querys(core->sdb, NULL, 0, "rop/***");
|
||
if (out) {
|
||
rz_cons_println(out);
|
||
free(out);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static int memcmpdiff(const ut8 *a, const ut8 *b, int len) {
|
||
int i, diff = 0;
|
||
for (i = 0; i < len; i++) {
|
||
if (a[i] == b[i] && a[i] == 0x00) {
|
||
/* ignore nulls */
|
||
} else if (a[i] != b[i]) {
|
||
diff++;
|
||
}
|
||
}
|
||
return diff;
|
||
}
|
||
|
||
static void search_similar_pattern_in(RzCore *core, int count, ut64 from, ut64 to) {
|
||
ut64 addr = from;
|
||
ut8 *block = calloc(core->blocksize, 1);
|
||
if (!block) {
|
||
return;
|
||
}
|
||
while (addr < to) {
|
||
(void)rz_io_read_at(core->io, addr, block, core->blocksize);
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
int diff = memcmpdiff(core->block, block, core->blocksize);
|
||
int equal = core->blocksize - diff;
|
||
if (equal >= count) {
|
||
int pc = (equal * 100) / core->blocksize;
|
||
rz_cons_printf("0x%08" PFMT64x " %4d/%d %3d%% ", addr, equal, core->blocksize, pc);
|
||
ut8 ptr[2] = {
|
||
(ut8)(pc * 2.5), 0
|
||
};
|
||
RzHistogramOptions opts = {
|
||
.unicode = rz_config_get_b(core->config, "scr.utf8"),
|
||
.thinline = !rz_config_get_b(core->config, "scr.hist.block"),
|
||
.legend = false,
|
||
.offset = rz_config_get_b(core->config, "hex.offset"),
|
||
.offpos = UT64_MAX,
|
||
.cursor = false,
|
||
.curpos = 0,
|
||
.color = rz_config_get_i(core->config, "scr.color"),
|
||
.pal = &core->cons->context->pal
|
||
};
|
||
RzStrBuf *strbuf = rz_histogram_vertical(&opts, ptr, 1, core->blocksize);
|
||
if (!strbuf) {
|
||
RZ_LOG_ERROR("Cannot generate vertical histogram\n");
|
||
} else {
|
||
rz_cons_print(rz_strbuf_drain(strbuf));
|
||
}
|
||
}
|
||
addr += core->blocksize;
|
||
}
|
||
free(block);
|
||
}
|
||
|
||
static void search_similar_pattern(RzCore *core, int count, struct search_parameters *param) {
|
||
RzIOMap *p;
|
||
RzListIter *iter;
|
||
|
||
rz_cons_break_push(NULL, NULL);
|
||
rz_list_foreach (param->boundaries, iter, p) {
|
||
search_similar_pattern_in(core, count, p->itv.addr, rz_itv_end(p->itv));
|
||
}
|
||
rz_cons_break_pop();
|
||
}
|
||
|
||
static bool isArm(RzCore *core) {
|
||
RzAsm *as = core ? core->rasm : NULL;
|
||
if (as && as->cur && as->cur->arch) {
|
||
if (rz_str_startswith(as->cur->arch, "arm")) {
|
||
if (as->cur->bits < 64) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void _CbInRangeSearchV(RzCore *core, ut64 from, ut64 to, int vsize, void *user) {
|
||
struct search_parameters *param = user;
|
||
bool isarm = isArm(core);
|
||
// this is expensive operation that could be cached but is a callback
|
||
// and for not messing adding a new param
|
||
const char *prefix = rz_config_get(core->config, "search.prefix");
|
||
if (isarm) {
|
||
if (to & 1) {
|
||
to--;
|
||
}
|
||
}
|
||
if (param->outmode != RZ_MODE_JSON) {
|
||
rz_cons_printf("0x%" PFMT64x ": 0x%" PFMT64x "\n", from, to);
|
||
} else {
|
||
pj_o(param->pj);
|
||
pj_kn(param->pj, "offset", from);
|
||
pj_kn(param->pj, "value", to);
|
||
pj_end(param->pj);
|
||
}
|
||
rz_core_cmdf(core, "f %s.value.0x%08" PFMT64x " %d @ 0x%08" PFMT64x " \n", prefix, to, vsize, to); // flag at value of hit
|
||
rz_core_cmdf(core, "f %s.offset.0x%08" PFMT64x " %d @ 0x%08" PFMT64x " \n", prefix, from, vsize, from); // flag at offset of hit
|
||
const char *cmdHit = rz_config_get(core->config, "cmd.hit");
|
||
if (cmdHit && *cmdHit) {
|
||
ut64 addr = core->offset;
|
||
rz_core_seek(core, from, true);
|
||
rz_core_cmd(core, cmdHit, 0);
|
||
rz_core_seek(core, addr, true);
|
||
}
|
||
}
|
||
|
||
static ut8 *v_writebuf(RzCore *core, RzList /*<char *>*/ *nums, int len, char ch, int bsize) {
|
||
ut8 *ptr;
|
||
ut64 n64;
|
||
ut32 n32;
|
||
ut16 n16;
|
||
ut8 n8;
|
||
int i = 0;
|
||
ut8 *buf = calloc(1, bsize);
|
||
if (!buf) {
|
||
RZ_LOG_ERROR("core: Cannot allocate %d byte(s)\n", bsize);
|
||
free(buf);
|
||
return NULL;
|
||
}
|
||
ptr = buf;
|
||
for (i = 0; i < len; i++) {
|
||
switch (ch) {
|
||
case '1':
|
||
n8 = rz_num_math(core->num, rz_list_pop_head(nums));
|
||
rz_write_le8(ptr, n8);
|
||
ptr = (ut8 *)ptr + sizeof(ut8);
|
||
break;
|
||
case '2':
|
||
n16 = rz_num_math(core->num, rz_list_pop_head(nums));
|
||
rz_write_le16(ptr, n16);
|
||
ptr = (ut8 *)ptr + sizeof(ut16);
|
||
break;
|
||
case '4':
|
||
n32 = (ut32)rz_num_math(core->num, rz_list_pop_head(nums));
|
||
rz_write_le32(ptr, n32);
|
||
ptr = (ut8 *)ptr + sizeof(ut32);
|
||
break;
|
||
default:
|
||
case '8':
|
||
n64 = rz_num_math(core->num, rz_list_pop_head(nums));
|
||
rz_write_le64(ptr, n64);
|
||
ptr = (ut8 *)ptr + sizeof(ut64);
|
||
break;
|
||
}
|
||
if (ptr > ptr + bsize) {
|
||
free(buf);
|
||
return NULL;
|
||
}
|
||
}
|
||
return buf;
|
||
}
|
||
|
||
// maybe useful as in util/big.c .?
|
||
static void incBuffer(ut8 *buf, int bufsz) {
|
||
int i = 0;
|
||
while (i < bufsz) {
|
||
buf[i]++;
|
||
if (!buf[i]) {
|
||
i++;
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
// may overflow/hang/end/stop/whatever here
|
||
}
|
||
|
||
static void incPrintBuffer(ut8 *buf, int bufsz) {
|
||
int i = 0;
|
||
while (i < bufsz) {
|
||
buf[i]++;
|
||
if (!buf[i]) {
|
||
i++;
|
||
continue;
|
||
}
|
||
if (IS_PRINTABLE(buf[i])) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void incLowerBuffer(ut8 *buf, int bufsz) {
|
||
int i = 0;
|
||
while (i < bufsz) {
|
||
buf[i]++;
|
||
if (buf[i] && isalpha(buf[i]) && islower(buf[i])) {
|
||
break;
|
||
}
|
||
if (!buf[i]) {
|
||
i++;
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void incUpperBuffer(ut8 *buf, int bufsz) {
|
||
int i = 0;
|
||
while (i < bufsz) {
|
||
buf[i]++;
|
||
if (buf[i] && isalpha(buf[i]) && isupper(buf[i])) {
|
||
break;
|
||
}
|
||
if (!buf[i]) {
|
||
i++;
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void incAlphaBuffer(ut8 *buf, int bufsz) {
|
||
int i = 0;
|
||
while (i < bufsz) {
|
||
buf[i]++;
|
||
if (buf[i] && isalpha(buf[i])) {
|
||
break;
|
||
}
|
||
if (!buf[i]) {
|
||
i++;
|
||
continue;
|
||
}
|
||
}
|
||
// may overflow/hang/end/stop/whatever here
|
||
}
|
||
|
||
static void incDigitBuffer(ut8 *buf, int bufsz) {
|
||
int i = 0;
|
||
while (i < bufsz) {
|
||
buf[i]++;
|
||
if (buf[i] && isdigit(buf[i])) {
|
||
break;
|
||
}
|
||
if (!buf[i]) {
|
||
i++;
|
||
continue;
|
||
}
|
||
}
|
||
// may overflow/hang/end/stop/whatever here
|
||
}
|
||
|
||
static void search_collisions(RzCore *core, const char *hashName, const ut8 *hashValue, int hashLength, int mode) {
|
||
ut8 RZ_ALIGNED(8) cmphash[128];
|
||
const RzHashPlugin *crc32 = rz_hash_plugin_by_name(core->hash, "crc32");
|
||
ut8 *digest = NULL;
|
||
|
||
int i = 0;
|
||
int bufsz = core->blocksize;
|
||
ut8 *buf = calloc(1, bufsz);
|
||
if (!buf) {
|
||
return;
|
||
}
|
||
memcpy(buf, core->block, bufsz);
|
||
if (hashLength > sizeof(cmphash)) {
|
||
RZ_LOG_WARN("core: Hashlength mismatch %d %d\n", hashLength, (int)sizeof(cmphash));
|
||
free(buf);
|
||
return;
|
||
}
|
||
memcpy(cmphash, hashValue, hashLength);
|
||
|
||
if (hashLength != 4) {
|
||
RZ_LOG_ERROR("core: Invalid hash size %d (expected 4)\n", hashLength);
|
||
free(buf);
|
||
return;
|
||
}
|
||
|
||
rz_cons_break_push(NULL, NULL);
|
||
ut64 prev = rz_time_now_mono();
|
||
ut64 inc = 0;
|
||
int amount = 0;
|
||
int mount = 0;
|
||
while (!rz_cons_is_breaked()) {
|
||
ut64 now = rz_time_now_mono();
|
||
if (now < (prev + 1000000)) {
|
||
amount++;
|
||
} else {
|
||
mount += amount;
|
||
mount /= 2;
|
||
amount = 0;
|
||
prev = now;
|
||
}
|
||
switch (mode) {
|
||
case 'p': // digits+alpha
|
||
incPrintBuffer(buf, bufsz);
|
||
break;
|
||
case 'a': // lowercase alpha
|
||
incLowerBuffer(buf, bufsz);
|
||
break;
|
||
case 'A': // uppercase alpha
|
||
incUpperBuffer(buf, bufsz);
|
||
break;
|
||
case 'l': // letters
|
||
incAlphaBuffer(buf, bufsz);
|
||
break;
|
||
case 'd': // digits
|
||
incDigitBuffer(buf, bufsz);
|
||
break;
|
||
default: // binary
|
||
incBuffer(buf, bufsz);
|
||
break;
|
||
}
|
||
|
||
rz_cons_printf("0x%08" PFMT64x " input:", inc);
|
||
for (i = 0; i < bufsz; i++) {
|
||
rz_cons_printf("%02x", buf[i]);
|
||
}
|
||
if (mode) {
|
||
rz_cons_printf(" \"%s\"", buf);
|
||
}
|
||
|
||
crc32->small_block(buf, bufsz, &digest, NULL);
|
||
|
||
rz_cons_printf(" digest:");
|
||
for (i = 0; i < hashLength; i++) {
|
||
rz_cons_printf("%02x", digest[i]);
|
||
}
|
||
rz_cons_printf(" (%d h/s) \r", mount);
|
||
if (!memcmp(hashValue, digest, hashLength)) {
|
||
rz_cons_printf("\nCOLLISION FOUND!\n");
|
||
rz_core_print_hexdump(core, core->offset, buf, bufsz, 0, 16, 0);
|
||
}
|
||
rz_cons_flush();
|
||
RZ_FREE(digest);
|
||
inc++;
|
||
}
|
||
rz_cons_break_pop();
|
||
free(buf);
|
||
}
|
||
|
||
static void __core_cmd_search_asm_infinite(RzCore *core, const char *arg) {
|
||
const char *search_in = rz_config_get(core->config, "search.in");
|
||
RzList *boundaries = rz_core_get_boundaries_prot(core, -1, search_in, "search");
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
RzAnalysisOp aop;
|
||
ut64 at;
|
||
rz_list_foreach (boundaries, iter, map) {
|
||
ut64 map_begin = map->itv.addr;
|
||
ut64 map_size = map->itv.size;
|
||
ut64 map_end = map_begin + map_size;
|
||
ut8 *buf = calloc(map_end - map_begin, 1);
|
||
if (!buf) {
|
||
continue;
|
||
}
|
||
(void)rz_io_read_at(core->io, map_begin, buf, map_size);
|
||
for (at = map->itv.addr; at + 24 < map_end; at += 1) {
|
||
rz_analysis_op(core->analysis, &aop, at, buf + (at - map_begin), 24, RZ_ANALYSIS_OP_MASK_HINT);
|
||
if (at == aop.jump) {
|
||
rz_cons_printf("0x%08" PFMT64x "\n", at);
|
||
}
|
||
at += aop.size;
|
||
rz_analysis_op_fini(&aop);
|
||
}
|
||
free(buf);
|
||
}
|
||
}
|
||
|
||
static void __core_cmd_search_asm_byteswap(RzCore *core, int nth) {
|
||
RzAsmOp asmop;
|
||
ut8 buf[32];
|
||
int i;
|
||
rz_io_read_at(core->io, 0, buf, sizeof(buf));
|
||
if (nth < 0 || nth >= sizeof(buf) - 1) {
|
||
return;
|
||
}
|
||
for (i = 0; i <= 0xff; i++) {
|
||
buf[nth] = i;
|
||
if (rz_asm_disassemble(core->rasm, &asmop, buf, sizeof(buf)) > 0) {
|
||
const char *asmstr = rz_strbuf_get(&asmop.buf_asm);
|
||
if (!strstr(asmstr, "invalid") && !strstr(asmstr, "unaligned")) {
|
||
rz_cons_printf("%02x %s\n", i, asmstr);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
RZ_IPI int rz_cmd_search(void *data, const char *input) {
|
||
bool dosearch = false;
|
||
int ret = true;
|
||
RzCore *core = (RzCore *)data;
|
||
struct search_parameters param = {
|
||
.core = core,
|
||
.cmd_hit = rz_config_get(core->config, "cmd.hit"),
|
||
.outmode = 0,
|
||
.inverse = false,
|
||
.aes_search = false,
|
||
.privkey_search = false,
|
||
};
|
||
if (!param.cmd_hit) {
|
||
param.cmd_hit = "";
|
||
}
|
||
RzSearch *search = core->search;
|
||
int ignorecase = false;
|
||
int param_offset = 2;
|
||
char *inp;
|
||
if (!core || !core->io) {
|
||
RZ_LOG_ERROR("core: Can't search if we don't have an open file.\n");
|
||
return false;
|
||
}
|
||
if (core->in_search) {
|
||
RZ_LOG_ERROR("core: Can't search from within a search.\n");
|
||
return false;
|
||
}
|
||
if (input[0] == '/') {
|
||
if (core->lastsearch) {
|
||
input = core->lastsearch;
|
||
} else {
|
||
RZ_LOG_ERROR("core: No previous search done\n");
|
||
return false;
|
||
}
|
||
} else {
|
||
free(core->lastsearch);
|
||
core->lastsearch = strdup(input);
|
||
}
|
||
|
||
core->in_search = true;
|
||
rz_flag_space_push(core->flags, "search");
|
||
const ut64 search_from = rz_config_get_i(core->config, "search.from"),
|
||
search_to = rz_config_get_i(core->config, "search.to");
|
||
if (search_from > search_to && search_to) {
|
||
RZ_LOG_ERROR("core: search.from > search.to is not supported\n");
|
||
ret = false;
|
||
goto beach;
|
||
}
|
||
// {.addr = UT64_MAX, .size = 0} means search range is unspecified
|
||
RzInterval search_itv = { search_from, search_to - search_from };
|
||
bool empty_search_itv = search_from == search_to && search_from != UT64_MAX;
|
||
if (empty_search_itv) {
|
||
RZ_LOG_ERROR("core: `from` address is equal `to`\n");
|
||
ret = false;
|
||
goto beach;
|
||
}
|
||
// TODO full address cannot be represented, shrink 1 byte to [0, UT64_MAX)
|
||
if (search_from == UT64_MAX && search_to == UT64_MAX) {
|
||
search_itv.addr = 0;
|
||
search_itv.size = UT64_MAX;
|
||
}
|
||
|
||
c = 0;
|
||
|
||
searchshow = rz_config_get_i(core->config, "search.show");
|
||
param.mode = rz_config_get(core->config, "search.in");
|
||
param.boundaries = rz_core_get_boundaries_prot(core, -1, param.mode, "search");
|
||
|
||
/*
|
||
this introduces a bug until we implement backwards search
|
||
for all search types
|
||
if (__to < __from) {
|
||
eprintf ("Invalid search range. Check 'e search.{from|to}'\n");
|
||
return false;
|
||
}
|
||
since the backward search will be implemented soon I'm not gonna stick
|
||
checks for every case in switch // jjdredd
|
||
remove when everything is done
|
||
*/
|
||
|
||
core->search->align = rz_config_get_i(core->config, "search.align");
|
||
searchflags = rz_config_get_i(core->config, "search.flags");
|
||
core->search->maxhits = rz_config_get_i(core->config, "search.maxhits");
|
||
searchprefix = rz_config_get(core->config, "search.prefix");
|
||
core->search->overlap = rz_config_get_i(core->config, "search.overlap");
|
||
core->search->bckwrds = false;
|
||
|
||
/* Quick & dirty check for json output */
|
||
if (input[0] && (input[1] == 'j') && (input[0] != ' ')) {
|
||
param.outmode = RZ_MODE_JSON;
|
||
param_offset++;
|
||
}
|
||
param.pj = pj_new();
|
||
|
||
reread:
|
||
switch (*input) {
|
||
case '!':
|
||
input++;
|
||
param.inverse = true;
|
||
goto reread;
|
||
case 'b': // "/b" backward search
|
||
if (*(++input) == '?') {
|
||
RZ_LOG_ERROR("core: Usage: /b<command> [value] backward search, see '/?'\n");
|
||
goto beach;
|
||
}
|
||
search->bckwrds = true;
|
||
if (core->offset) {
|
||
RzInterval itv = { 0, core->offset };
|
||
if (!rz_itv_overlap(search_itv, itv)) {
|
||
ret = false;
|
||
goto beach;
|
||
} else {
|
||
search_itv = rz_itv_intersect(search_itv, itv);
|
||
}
|
||
}
|
||
goto reread;
|
||
case 'o': { // "/o" print the offset of the Previous opcode
|
||
ut64 addr, n = input[param_offset - 1] ? rz_num_math(core->num, input + param_offset) : 1;
|
||
n = RZ_ABS((st64)n);
|
||
if (((st64)n) < 1) {
|
||
n = 1;
|
||
}
|
||
if (!rz_core_prevop_addr(core, core->offset, n, &addr)) {
|
||
addr = UT64_MAX;
|
||
(void)rz_core_asm_bwdis_len(core, NULL, &addr, n);
|
||
}
|
||
if (param.outmode == RZ_MODE_JSON) {
|
||
rz_cons_printf("[%" PFMT64u "]", addr);
|
||
} else {
|
||
rz_cons_printf("0x%08" PFMT64x "\n", addr);
|
||
}
|
||
break;
|
||
}
|
||
case 'O': { // "/O" alternative to "/o"
|
||
ut64 addr, n = input[param_offset - 1] ? rz_num_math(core->num, input + param_offset) : 1;
|
||
if (!n) {
|
||
n = 1;
|
||
}
|
||
addr = rz_core_prevop_addr_force(core, core->offset, n);
|
||
if (param.outmode == RZ_MODE_JSON) {
|
||
rz_cons_printf("[%" PFMT64u "]", addr);
|
||
} else {
|
||
rz_cons_printf("0x%08" PFMT64x "\n", addr);
|
||
}
|
||
break;
|
||
}
|
||
case 'R': // "/R"
|
||
if (input[1] == '?') {
|
||
rz_core_cmd_help(core, help_msg_slash_R);
|
||
} else if (input[1] == '/') {
|
||
rz_core_search_rop(core, search_itv, 0, input + 1, 1, ¶m);
|
||
} else if (input[1] == 'k') {
|
||
if (input[2] == '?') {
|
||
rz_core_cmd_help(core, help_msg_slash_Rk);
|
||
} else {
|
||
rop_kuery(core, input + 2, param.pj);
|
||
}
|
||
} else {
|
||
Sdb *gadgetSdb = sdb_ns(core->sdb, "gadget_sdb", false);
|
||
|
||
if (!gadgetSdb) {
|
||
rz_core_search_rop(core, search_itv, 0, input + 1, 0, ¶m);
|
||
} else {
|
||
SdbKv *kv;
|
||
SdbListIter *sdb_iter;
|
||
SdbList *sdb_list = sdb_foreach_list(gadgetSdb, true);
|
||
|
||
ls_foreach (sdb_list, sdb_iter, kv) {
|
||
RzList *hitlist = rz_core_asm_hit_list_new();
|
||
if (!hitlist) {
|
||
goto beach;
|
||
}
|
||
|
||
char *s = sdbkv_value(kv);
|
||
ut64 addr;
|
||
int opsz;
|
||
int mode = 0;
|
||
|
||
// Options, like JSON, linear, ...
|
||
if (*(input + 1)) {
|
||
mode = *(input + 1);
|
||
}
|
||
|
||
do {
|
||
RzCoreAsmHit *hit = rz_core_asm_hit_new();
|
||
if (!hit) {
|
||
rz_list_free(hitlist);
|
||
goto beach;
|
||
}
|
||
sscanf(s, "%" PFMT64x "(%" PFMT32d ")", &addr, &opsz);
|
||
hit->addr = addr;
|
||
hit->len = opsz;
|
||
rz_list_append(hitlist, hit);
|
||
} while (*(s = strchr(s, ')') + 1) != '\0');
|
||
|
||
print_rop(core, hitlist, param.pj, mode);
|
||
rz_list_free(hitlist);
|
||
}
|
||
}
|
||
}
|
||
goto beach;
|
||
case 'r': // "/r"
|
||
{
|
||
ut64 n = (input[1] == ' ' || (input[1] && input[2] == ' '))
|
||
? rz_num_math(core->num, input + 2)
|
||
: UT64_MAX;
|
||
if (n == 0LL) {
|
||
RZ_LOG_ERROR("core: Cannot find null references.\n");
|
||
break;
|
||
}
|
||
switch (input[1]) {
|
||
case 'c': // "/rc"
|
||
{
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
rz_list_foreach (param.boundaries, iter, map) {
|
||
eprintf("-- 0x%" PFMT64x " 0x%" PFMT64x "\n", map->itv.addr, rz_itv_end(map->itv));
|
||
rz_core_analysis_search(core, map->itv.addr, rz_itv_end(map->itv), n, 'c');
|
||
}
|
||
} break;
|
||
case 'a': // "/ra"
|
||
{
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
rz_list_foreach (param.boundaries, iter, map) {
|
||
eprintf("-- 0x%" PFMT64x " 0x%" PFMT64x "\n", map->itv.addr, rz_itv_end(map->itv));
|
||
rz_core_analysis_search(core, map->itv.addr, rz_itv_end(map->itv), n, 0);
|
||
}
|
||
} break;
|
||
case 'r': // "/rr" - read refs
|
||
{
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
rz_list_foreach (param.boundaries, iter, map) {
|
||
eprintf("-- 0x%" PFMT64x " 0x%" PFMT64x "\n", map->itv.addr, rz_itv_end(map->itv));
|
||
rz_core_analysis_search(core, map->itv.addr, rz_itv_end(map->itv), n, 'r');
|
||
}
|
||
} break;
|
||
case 'w': // "/rw" - write refs
|
||
{
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
rz_list_foreach (param.boundaries, iter, map) {
|
||
eprintf("-- 0x%" PFMT64x " 0x%" PFMT64x "\n", map->itv.addr, rz_itv_end(map->itv));
|
||
rz_core_analysis_search(core, map->itv.addr, rz_itv_end(map->itv), n, 'w');
|
||
}
|
||
} break;
|
||
case ' ': // "/r $$"
|
||
case 0: // "/r"
|
||
{
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
rz_list_foreach (param.boundaries, iter, map) {
|
||
ut64 from = map->itv.addr;
|
||
ut64 to = rz_itv_end(map->itv);
|
||
if (input[param_offset - 1] == ' ') {
|
||
rz_core_analysis_search(core, from, to,
|
||
rz_num_math(core->num, input + 2), 0);
|
||
do_ref_search(core, rz_num_math(core->num, input + 2), from, to, ¶m);
|
||
} else {
|
||
rz_core_analysis_search(core, from, to, core->offset, 0);
|
||
do_ref_search(core, core->offset, from, to, ¶m);
|
||
}
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
}
|
||
} break;
|
||
case '?':
|
||
rz_core_cmd_help(core, help_msg_slash_r);
|
||
break;
|
||
}
|
||
} break;
|
||
case 'a': // "/a"
|
||
if (input[1] == '?') {
|
||
rz_core_cmd_help(core, help_msg_slash_a);
|
||
} else if (input[1] == 'd') { // "ad"
|
||
dosearch = 0;
|
||
do_asm_search(core, ¶m, input + 2, 0, search_itv);
|
||
} else if (input[1] == 'e') { // "ae"
|
||
dosearch = 0;
|
||
do_asm_search(core, ¶m, input + 2, 'e', search_itv);
|
||
} else if (input[1] == 'c') { // "/ac"
|
||
dosearch = 0;
|
||
do_asm_search(core, ¶m, input + 2, 'c', search_itv);
|
||
} else if (input[1] == 'o') { // "/ao"
|
||
dosearch = 0;
|
||
do_asm_search(core, ¶m, input + 2, 'o', search_itv);
|
||
} else if (input[1] == 'a') { // "/aa"
|
||
dosearch = 0;
|
||
do_asm_search(core, ¶m, input + 2, 'a', search_itv);
|
||
} else if (input[1] == 'i') { // "/ai"
|
||
do_asm_search(core, ¶m, input + 2, 'i', search_itv);
|
||
} else if (input[1] == '1') { // "a1"
|
||
__core_cmd_search_asm_byteswap(core, (int)rz_num_math(core->num, input + 2));
|
||
} else if (input[1] == 'I') { // "/aI" - infinite
|
||
__core_cmd_search_asm_infinite(core, rz_str_trim_head_ro(input + 1));
|
||
} else if (input[1] == ' ') {
|
||
if (input[param_offset - 1]) {
|
||
char *kwd = rz_core_asm_search(core, input + param_offset);
|
||
if (!kwd) {
|
||
ret = false;
|
||
goto beach;
|
||
}
|
||
dosearch = true;
|
||
rz_search_reset(core->search, RZ_SEARCH_KEYWORD);
|
||
rz_search_set_distance(core->search, (int)rz_config_get_i(core->config, "search.distance"));
|
||
rz_search_kw_add(core->search,
|
||
rz_search_keyword_new_hexmask(kwd, NULL));
|
||
free(kwd);
|
||
}
|
||
} else if (input[1] == 's') {
|
||
if (input[2] == 'l') { // "asl"
|
||
rz_core_cmd0(core, "asl");
|
||
} else { // "as"
|
||
do_syscall_search(core, ¶m);
|
||
}
|
||
dosearch = false;
|
||
} else {
|
||
dosearch = do_analysis_search(core, ¶m, input + 1);
|
||
}
|
||
break;
|
||
case 'c': { // "/c"
|
||
dosearch = true;
|
||
switch (input[1]) {
|
||
case 'c': // "/cc"
|
||
{
|
||
ret = false;
|
||
char *space = strchr(input, ' ');
|
||
const char *arg = space ? rz_str_trim_head_ro(space + 1) : NULL;
|
||
if (!arg || input[2] == '?') {
|
||
RZ_LOG_ERROR("core: Usage: /cc[aAdlpb] [hashname] [hexpairhashvalue]\n");
|
||
RZ_LOG_ERROR("core: /cca - lowercase alphabet chars only\n");
|
||
RZ_LOG_ERROR("core: /ccA - uppercase alphabet chars only\n");
|
||
RZ_LOG_ERROR("core: /ccl - letters (lower + upper alphabet chars)\n");
|
||
RZ_LOG_ERROR("core: /ccd - digits (only numbers)\n");
|
||
RZ_LOG_ERROR("core: /ccp - printable (alpha + digit)\n");
|
||
RZ_LOG_ERROR("core: /ccb - binary (any number is valid)\n");
|
||
goto beach;
|
||
}
|
||
char *s = strdup(arg);
|
||
char *sp = strchr(s, ' ');
|
||
int mode = input[2];
|
||
if (sp) {
|
||
*sp = 0;
|
||
sp++;
|
||
char *hashName = s;
|
||
ut8 *hashValue = (ut8 *)strdup(sp);
|
||
if (hashValue) {
|
||
if (!rz_str_startswith((const char *)hashValue, "0x")) {
|
||
// TODO: support bigger hashes
|
||
int hashLength = 4;
|
||
ut32 n = (ut32)rz_num_get(NULL, (const char *)hashValue);
|
||
memcpy(hashValue, (const ut8 *)&n, sizeof(ut32));
|
||
search_collisions(core, hashName, hashValue, hashLength, mode);
|
||
} else {
|
||
int hashLength = rz_hex_str2bin(sp, hashValue);
|
||
if (hashLength > 0) {
|
||
search_collisions(core, hashName, hashValue, hashLength, mode);
|
||
} else {
|
||
RZ_LOG_ERROR("core: Invalid expected hash hexpairs.\n");
|
||
}
|
||
}
|
||
free(hashValue);
|
||
} else {
|
||
RZ_LOG_ERROR("core: Cannot allocate memory.\n");
|
||
}
|
||
ret = true;
|
||
} else {
|
||
RZ_LOG_ERROR("core: Usage: /cc [hashname] [hexpairhashvalue]\n");
|
||
RZ_LOG_ERROR("core: Usage: /CC to search ascii collisions\n");
|
||
}
|
||
free(s);
|
||
goto beach;
|
||
} break;
|
||
case 'd': // "cd"
|
||
{
|
||
RzSearchKeyword *kw;
|
||
kw = rz_search_keyword_new_hex("308200003082", "ffff0000ffff", NULL);
|
||
rz_search_reset(core->search, RZ_SEARCH_KEYWORD);
|
||
if (kw) {
|
||
rz_search_kw_add(core->search, kw);
|
||
// eprintf ("Searching %d byte(s)...\n", kw->keyword_length);
|
||
rz_search_begin(core->search);
|
||
} else {
|
||
RZ_LOG_ERROR("core: null pointer on search keyword\n");
|
||
dosearch = false;
|
||
}
|
||
} break;
|
||
case 'a': // "ca"
|
||
{
|
||
RzSearchKeyword *kw;
|
||
kw = rz_search_keyword_new_hexmask("00", NULL);
|
||
// AES search is done over 40 bytes
|
||
kw->keyword_length = AES_SEARCH_LENGTH;
|
||
rz_search_reset(core->search, RZ_SEARCH_AES);
|
||
rz_search_kw_add(search, kw);
|
||
rz_search_begin(core->search);
|
||
param.aes_search = true;
|
||
break;
|
||
}
|
||
case 'r': // "cr"
|
||
{
|
||
RzSearchKeyword *kw;
|
||
kw = rz_search_keyword_new_hexmask("00", NULL);
|
||
// Private key search is at least 11 bytes
|
||
kw->keyword_length = PRIVATE_KEY_SEARCH_LENGTH;
|
||
rz_search_reset(core->search, RZ_SEARCH_PRIV_KEY);
|
||
rz_search_kw_add(search, kw);
|
||
rz_search_begin(core->search);
|
||
param.privkey_search = true;
|
||
break;
|
||
}
|
||
default: {
|
||
dosearch = false;
|
||
rz_core_cmd_help(core, help_msg_slash_c);
|
||
}
|
||
}
|
||
} break;
|
||
case 'm': // "/m"
|
||
dosearch = false;
|
||
if (input[1] == '?') { // "/m?"
|
||
rz_core_cmd_help(core, help_msg_slash_m);
|
||
} else if (input[1] == 'b') { // "/mb"
|
||
bool bin_verbose = rz_config_get_i(core->config, "bin.verbose");
|
||
rz_config_set_i(core->config, "bin.verbose", false);
|
||
// TODO : iter maps?
|
||
cmd_search_bin(core, search_itv);
|
||
rz_config_set_i(core->config, "bin.verbose", bin_verbose);
|
||
} else if (input[1] == ' ' || input[1] == '\0' || param.outmode == RZ_MODE_JSON) {
|
||
int ret;
|
||
const char *file = input[param_offset - 1] ? input + param_offset : NULL;
|
||
ut64 addr = search_itv.addr;
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
if (param.outmode == RZ_MODE_JSON) {
|
||
pj_a(param.pj);
|
||
}
|
||
rz_core_magic_reset(core);
|
||
int maxHits = rz_config_get_i(core->config, "search.maxhits");
|
||
int hits = 0;
|
||
rz_list_foreach (param.boundaries, iter, map) {
|
||
if (param.outmode != RZ_MODE_JSON) {
|
||
eprintf("-- %llx %llx\n", map->itv.addr, rz_itv_end(map->itv));
|
||
}
|
||
rz_cons_break_push(NULL, NULL);
|
||
for (addr = map->itv.addr; addr < rz_itv_end(map->itv); addr++) {
|
||
if (rz_cons_is_breaked()) {
|
||
break;
|
||
}
|
||
ret = rz_core_magic_at(core, file, addr, 99, false, param.outmode == RZ_MODE_JSON ? param.pj : NULL, &hits);
|
||
if (ret == -1) {
|
||
// something went terribly wrong.
|
||
break;
|
||
}
|
||
if (maxHits && hits >= maxHits) {
|
||
break;
|
||
}
|
||
addr += ret - 1;
|
||
}
|
||
rz_cons_clear_line(1);
|
||
rz_cons_break_pop();
|
||
}
|
||
if (param.outmode == RZ_MODE_JSON) {
|
||
pj_end(param.pj);
|
||
}
|
||
} else {
|
||
RZ_LOG_ERROR("core: Usage: /m [file]\n");
|
||
}
|
||
rz_cons_clear_line(1);
|
||
break;
|
||
case 'p': // "/p"
|
||
{
|
||
if (input[param_offset - 1]) {
|
||
int ps = atoi(input + param_offset);
|
||
if (ps > 1) {
|
||
RzListIter *iter;
|
||
RzIOMap *map;
|
||
rz_list_foreach (param.boundaries, iter, map) {
|
||
eprintf("-- %llx %llx\n", map->itv.addr, rz_itv_end(map->itv));
|
||
rz_cons_break_push(NULL, NULL);
|
||
rz_search_pattern_size(core->search, ps);
|
||
rz_search_pattern(core->search, map->itv.addr, rz_itv_end(map->itv));
|
||
rz_cons_break_pop();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
RZ_LOG_ERROR("core: Invalid pattern size (must be > 0)\n");
|
||
} break;
|
||
case 'P': // "/P"
|
||
search_similar_pattern(core, atoi(input + 1), ¶m);
|
||
break;
|
||
case 'V': // "/V"
|
||
{
|
||
if (input[2] == 'j') {
|
||
param.outmode = RZ_MODE_JSON;
|
||
param_offset++;
|
||
} else if (strchr(input + 1, '*')) {
|
||
param.outmode = RZ_MODE_RIZINCMD;
|
||
}
|
||
int err = 1, vsize = atoi(input + 1);
|
||
const char *num_str = input + param_offset + 1;
|
||
if (vsize && input[2] && num_str) {
|
||
if (param.outmode == RZ_MODE_JSON) {
|
||
pj_a(param.pj);
|
||
}
|
||
char *w = strchr(num_str, ' ');
|
||
if (w) {
|
||
*w++ = 0;
|
||
ut64 vmin = rz_num_math(core->num, num_str);
|
||
ut64 vmax = rz_num_math(core->num, w);
|
||
if (vsize > 0) {
|
||
RzIOMap *map;
|
||
RzListIter *iter;
|
||
rz_list_foreach (param.boundaries, iter, map) {
|
||
err = 0;
|
||
int hits = rz_core_search_value_in_range(core, map->itv,
|
||
vmin, vmax, vsize,
|
||
_CbInRangeSearchV, ¶m);
|
||
if (param.outmode != RZ_MODE_JSON) {
|
||
eprintf("hits: %d\n", hits);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (param.outmode == RZ_MODE_JSON) {
|
||
pj_end(param.pj);
|
||
}
|
||
}
|
||
if (err) {
|
||
RZ_LOG_ERROR("core: Usage: /V[1|2|4|8] [minval] [maxval]\n");
|
||
}
|
||
}
|
||
dosearch = false;
|
||
break;
|
||
case 'v': // "/v"
|
||
if (input[1]) {
|
||
if (input[1] == '?') {
|
||
rz_cons_print("Usage: /v[1|2|4|8] [value]\n");
|
||
break;
|
||
}
|
||
if (input[2] == 'j') {
|
||
param.outmode = RZ_MODE_JSON;
|
||
param_offset++;
|
||
}
|
||
}
|
||
rz_search_reset(core->search, RZ_SEARCH_KEYWORD);
|
||
rz_search_set_distance(core->search, (int)rz_config_get_i(core->config, "search.distance"));
|
||
char *v_str = (char *)rz_str_trim_head_ro(input + param_offset);
|
||
RzList *nums = rz_num_str_split_list(v_str);
|
||
int len = rz_list_length(nums);
|
||
int bsize = 0;
|
||
ut8 *v_buf = NULL;
|
||
switch (input[1]) {
|
||
case '8':
|
||
if (input[param_offset]) {
|
||
bsize = sizeof(ut64) * len;
|
||
v_buf = v_writebuf(core, nums, len, '8', bsize);
|
||
} else {
|
||
RZ_LOG_ERROR("core: Usage: /v8 value\n");
|
||
}
|
||
break;
|
||
case '1':
|
||
if (input[param_offset]) {
|
||
bsize = sizeof(ut8) * len;
|
||
v_buf = v_writebuf(core, nums, len, '1', bsize);
|
||
} else {
|
||
RZ_LOG_ERROR("core: Usage: /v1 value\n");
|
||
}
|
||
break;
|
||
case '2':
|
||
if (input[param_offset]) {
|
||
bsize = sizeof(ut16) * len;
|
||
v_buf = v_writebuf(core, nums, len, '2', bsize);
|
||
} else {
|
||
RZ_LOG_ERROR("core: Usage: /v2 value\n");
|
||
}
|
||
break;
|
||
default: // default size
|
||
case '4':
|
||
if (input[param_offset - 1]) {
|
||
if (input[param_offset]) {
|
||
bsize = sizeof(ut32) * len;
|
||
v_buf = v_writebuf(core, nums, len, '4', bsize);
|
||
}
|
||
} else {
|
||
RZ_LOG_ERROR("core: Usage: /v4 value\n");
|
||
}
|
||
break;
|
||
}
|
||
if (v_buf) {
|
||
rz_search_kw_add(core->search,
|
||
rz_search_keyword_new((const ut8 *)v_buf, bsize, NULL, 0, NULL));
|
||
free(v_buf);
|
||
}
|
||
rz_search_begin(core->search);
|
||
dosearch = true;
|
||
break;
|
||
case 'w': // "/w" search wide string, includes ignorecase search functionality (/wi cmd)!
|
||
if (input[2]) {
|
||
if (input[1] == 'j' || input[2] == 'j') {
|
||
param.outmode = RZ_MODE_JSON;
|
||
}
|
||
if (input[1] == 'i' || input[2] == 'i') {
|
||
ignorecase = true;
|
||
}
|
||
} else {
|
||
param.outmode = RZ_MODE_RIZINCMD;
|
||
}
|
||
|
||
size_t shift = 1 + ignorecase;
|
||
if (param.outmode == RZ_MODE_JSON) {
|
||
shift++;
|
||
}
|
||
size_t strstart;
|
||
const char *p2;
|
||
char *p;
|
||
strstart = shift + 1;
|
||
len = strlen(input + strstart);
|
||
inp = calloc((len + 1), 2);
|
||
for (p2 = input + strstart, p = inp; *p2; p += 2, p2++) {
|
||
if (ignorecase) {
|
||
p[0] = tolower((const ut8)*p2);
|
||
} else {
|
||
p[0] = *p2;
|
||
}
|
||
p[1] = 0;
|
||
}
|
||
rz_search_reset(core->search, RZ_SEARCH_KEYWORD);
|
||
rz_search_set_distance(core->search, (int)rz_config_get_i(core->config, "search.distance"));
|
||
RzSearchKeyword *skw;
|
||
skw = rz_search_keyword_new((const ut8 *)inp, len * 2, NULL, 0, NULL);
|
||
free(inp);
|
||
if (skw) {
|
||
skw->icase = ignorecase;
|
||
rz_search_kw_add(core->search, skw);
|
||
rz_search_begin(core->search);
|
||
dosearch = true;
|
||
} else {
|
||
RZ_LOG_ERROR("core: Invalid keyword\n");
|
||
break;
|
||
}
|
||
break;
|
||
case 'i': // "/i"
|
||
if (input[param_offset - 1] != ' ') {
|
||
RZ_LOG_ERROR("core: Missing ' ' after /i\n");
|
||
ret = false;
|
||
goto beach;
|
||
}
|
||
ignorecase = true;
|
||
// fallthrough
|
||
case 'j': // "/j"
|
||
if (input[0] == 'j' && input[1] == ' ') {
|
||
param.outmode = RZ_MODE_JSON;
|
||
}
|
||
// fallthrough
|
||
case ' ': // "/ " search string
|
||
inp = strdup(input + 1 + ignorecase + (param.outmode == RZ_MODE_JSON ? 1 : 0));
|
||
len = rz_str_unescape(inp);
|
||
rz_search_reset(core->search, RZ_SEARCH_KEYWORD);
|
||
rz_search_set_distance(core->search, (int)rz_config_get_i(core->config, "search.distance"));
|
||
{
|
||
RzSearchKeyword *skw;
|
||
skw = rz_search_keyword_new((const ut8 *)inp, len, NULL, 0, NULL);
|
||
free(inp);
|
||
if (skw) {
|
||
skw->icase = ignorecase;
|
||
skw->type = RZ_SEARCH_KEYWORD_TYPE_STRING;
|
||
rz_search_kw_add(core->search, skw);
|
||
} else {
|
||
RZ_LOG_ERROR("core: Invalid keyword\n");
|
||
break;
|
||
}
|
||
}
|
||
rz_search_begin(core->search);
|
||
dosearch = true;
|
||
break;
|
||
case 'e': // "/e" match regexp
|
||
if (input[1] == '?') {
|
||
RZ_LOG_ERROR("core: Usage: /e /foo/i or /e/foo/i\n");
|
||
} else if (input[1]) {
|
||
RzSearchKeyword *kw;
|
||
kw = rz_search_keyword_new_regexp(input + 1, NULL);
|
||
if (!kw) {
|
||
RZ_LOG_ERROR("core: Invalid regexp specified\n");
|
||
break;
|
||
}
|
||
rz_search_reset(core->search, RZ_SEARCH_REGEXP);
|
||
// TODO distance is unused
|
||
rz_search_set_distance(core->search, (int)rz_config_get_i(core->config, "search.distance"));
|
||
rz_search_kw_add(core->search, kw);
|
||
rz_search_begin(core->search);
|
||
dosearch = true;
|
||
} else {
|
||
RZ_LOG_ERROR("core: Missing regex\n");
|
||
}
|
||
break;
|
||
case 'E': // "/E"
|
||
if (core->bin && core->bin->is_debugger) {
|
||
rz_debug_map_sync(core->dbg);
|
||
}
|
||
do_esil_search(core, ¶m, input);
|
||
goto beach;
|
||
case 'd': // "/d" search delta key
|
||
if (input[1]) {
|
||
rz_search_reset(core->search, RZ_SEARCH_DELTAKEY);
|
||
rz_search_kw_add(core->search,
|
||
rz_search_keyword_new_hexmask(input + param_offset, NULL));
|
||
rz_search_begin(core->search);
|
||
dosearch = true;
|
||
} else {
|
||
RZ_LOG_ERROR("core: Missing delta\n");
|
||
}
|
||
break;
|
||
case 'h': // "/h"
|
||
{
|
||
char *p, *arg = rz_str_trim_dup(input + 1);
|
||
p = strchr(arg, ' ');
|
||
if (p) {
|
||
*p++ = 0;
|
||
if (*arg == '?') {
|
||
RZ_LOG_ERROR("core: Usage: /h md5 [hash] [datalen]\n");
|
||
} else {
|
||
ut32 min = UT32_MAX;
|
||
ut32 max = UT32_MAX;
|
||
char *pmax, *pmin = strchr(p, ' ');
|
||
if (pmin) {
|
||
*pmin++ = 0;
|
||
pmax = strchr(pmin, ' ');
|
||
if (pmax) {
|
||
*pmax++ = 0;
|
||
max = rz_num_math(core->num, pmax);
|
||
}
|
||
min = rz_num_math(core->num, pmin);
|
||
}
|
||
search_hash(core, arg, p, min, max, ¶m);
|
||
}
|
||
} else {
|
||
RZ_LOG_ERROR("core: Missing hash. See ph?\n");
|
||
}
|
||
free(arg);
|
||
} break;
|
||
case 'f': // "/f" forward search
|
||
if (core->offset) {
|
||
RzInterval itv = { core->offset, -core->offset };
|
||
if (!rz_itv_overlap(search_itv, itv)) {
|
||
ret = false;
|
||
goto beach;
|
||
} else {
|
||
search_itv = rz_itv_intersect(search_itv, itv);
|
||
}
|
||
}
|
||
break;
|
||
case 'g': // "/g" graph search
|
||
if (input[1] == '?') {
|
||
rz_cons_printf("Usage: /g[g] [fromaddr] @ [toaddr]\n");
|
||
rz_cons_printf("(find all graph paths A to B (/gg follow jumps, see search.count and analysis.depth)");
|
||
} else {
|
||
ut64 addr = UT64_MAX;
|
||
if (input[1]) {
|
||
addr = rz_num_math(core->num, input + 2);
|
||
} else {
|
||
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, addr);
|
||
if (fcn) {
|
||
addr = fcn->addr;
|
||
} else {
|
||
addr = core->offset;
|
||
}
|
||
}
|
||
const int depth = rz_config_get_i(core->config, "analysis.depth");
|
||
// Va;ifate input length
|
||
if (input[1] != '\0') {
|
||
rz_core_analysis_paths(core, addr, core->offset, input[1] == 'g', depth, (input[1] == 'j' || input[2] == 'j'));
|
||
}
|
||
}
|
||
break;
|
||
case 'F': // "/F" search file /F [file] ([offset] ([sz]))
|
||
if (input[param_offset - 1] == ' ') {
|
||
int n_args;
|
||
char **args = rz_str_argv(input + param_offset, &n_args);
|
||
ut8 *buf = NULL;
|
||
ut64 offset = 0;
|
||
size_t size;
|
||
buf = (ut8 *)rz_file_slurp(args[0], &size);
|
||
if (!buf) {
|
||
RZ_LOG_ERROR("core: Cannot open '%s'\n", args[0]);
|
||
rz_str_argv_free(args);
|
||
break;
|
||
}
|
||
if (n_args > 1) {
|
||
offset = rz_num_math(core->num, args[1]);
|
||
if (size <= offset) {
|
||
RZ_LOG_ERROR("core: size <= offset\n");
|
||
rz_str_argv_free(args);
|
||
free(buf);
|
||
break;
|
||
}
|
||
}
|
||
if (n_args > 2) {
|
||
len = rz_num_math(core->num, args[2]);
|
||
if (len > size - offset) {
|
||
RZ_LOG_ERROR("core: len too large\n");
|
||
rz_str_argv_free(args);
|
||
free(buf);
|
||
break;
|
||
}
|
||
} else {
|
||
len = size - offset;
|
||
}
|
||
RzSearchKeyword *kw;
|
||
rz_search_reset(core->search, RZ_SEARCH_KEYWORD);
|
||
rz_search_set_distance(core->search, (int)rz_config_get_i(core->config, "search.distance"));
|
||
kw = rz_search_keyword_new(buf + offset, len, NULL, 0, NULL);
|
||
if (kw) {
|
||
rz_search_kw_add(core->search, kw);
|
||
// eprintf ("Searching %d byte(s)...\n", kw->keyword_length);
|
||
rz_search_begin(core->search);
|
||
dosearch = true;
|
||
} else {
|
||
RZ_LOG_ERROR("core: no keyword\n");
|
||
}
|
||
|
||
rz_str_argv_free(args);
|
||
free(buf);
|
||
} else {
|
||
RZ_LOG_ERROR("core: Usage: /F[j] [file] ([offset] ([sz]))\n");
|
||
}
|
||
break;
|
||
case 'x': // "/x" search hex
|
||
if (input[1] == '?') {
|
||
rz_core_cmd_help(core, help_msg_slash_x);
|
||
} else {
|
||
RzSearchKeyword *kw;
|
||
char *s, *p = strdup(input + param_offset);
|
||
rz_search_reset(core->search, RZ_SEARCH_KEYWORD);
|
||
rz_search_set_distance(core->search, (int)rz_config_get_i(core->config, "search.distance"));
|
||
s = strchr(p, ':');
|
||
if (s) {
|
||
*s++ = 0;
|
||
kw = rz_search_keyword_new_hex(p, s, NULL);
|
||
} else {
|
||
kw = rz_search_keyword_new_hexmask(p, NULL);
|
||
}
|
||
if (kw) {
|
||
rz_search_kw_add(core->search, kw);
|
||
// eprintf ("Searching %d byte(s)...\n", kw->keyword_length);
|
||
rz_search_begin(core->search);
|
||
dosearch = true;
|
||
} else {
|
||
RZ_LOG_ERROR("core: no keyword\n");
|
||
}
|
||
free(p);
|
||
}
|
||
break;
|
||
case 's': // "/s"
|
||
do_section_search(core, ¶m, input + 1);
|
||
break;
|
||
case '+': // "/+"
|
||
if (input[1] == ' ') {
|
||
// TODO: support /+j
|
||
char *buf = malloc(strlen(input) * 2);
|
||
char *str = strdup(input + 2);
|
||
int ochunksize;
|
||
int i, len, chunksize = rz_config_get_i(core->config, "search.chunk");
|
||
if (chunksize < 1) {
|
||
chunksize = core->rasm->bits / 8;
|
||
}
|
||
len = rz_str_unescape(str);
|
||
ochunksize = chunksize = RZ_MIN(len, chunksize);
|
||
RZ_LOG_ERROR("core: Using chunksize: %d\n", chunksize);
|
||
core->in_search = false;
|
||
for (i = 0; i < len; i += chunksize) {
|
||
chunksize = ochunksize;
|
||
again:
|
||
rz_hex_bin2str((ut8 *)str + i, RZ_MIN(chunksize, len - i), buf);
|
||
RZ_LOG_ERROR("core: /x %s\n", buf);
|
||
rz_core_cmdf(core, "/x %s", buf);
|
||
if (core->num->value == 0) {
|
||
chunksize--;
|
||
if (chunksize < 1) {
|
||
RZ_LOG_ERROR("core: Oops\n");
|
||
free(buf);
|
||
free(str);
|
||
goto beach;
|
||
}
|
||
RZ_LOG_ERROR("core: Repeat with chunk size %d\n", chunksize);
|
||
goto again;
|
||
}
|
||
}
|
||
free(str);
|
||
free(buf);
|
||
} else {
|
||
RZ_LOG_ERROR("core: Usage: /+ [string]\n");
|
||
}
|
||
break;
|
||
case 'z': // "/z" search strings of min-max range
|
||
{
|
||
char *p;
|
||
ut32 min, max;
|
||
if (!input[1]) {
|
||
RZ_LOG_ERROR("core: Usage: /z min max\n");
|
||
break;
|
||
}
|
||
if ((p = strchr(input + 2, ' '))) {
|
||
*p = 0;
|
||
max = rz_num_math(core->num, p + 1);
|
||
} else {
|
||
RZ_LOG_ERROR("core: Usage: /z min max\n");
|
||
break;
|
||
}
|
||
min = rz_num_math(core->num, input + 2);
|
||
if (!rz_search_set_string_limits(core->search, min, max)) {
|
||
RZ_LOG_ERROR("core: min must be lower than max\n");
|
||
break;
|
||
}
|
||
rz_search_reset(core->search, RZ_SEARCH_STRING);
|
||
rz_search_set_distance(core->search, (int)rz_config_get_i(core->config, "search.distance"));
|
||
{
|
||
RzSearchKeyword *kw = rz_search_keyword_new_hexmask("00", NULL);
|
||
kw->type = RZ_SEARCH_KEYWORD_TYPE_STRING;
|
||
rz_search_kw_add(search, kw);
|
||
}
|
||
rz_search_begin(search);
|
||
dosearch = true;
|
||
} break;
|
||
case '?': // "/?"
|
||
rz_core_cmd_help(core, help_msg_slash);
|
||
break;
|
||
default:
|
||
RZ_LOG_ERROR("core: See /? for help.\n");
|
||
break;
|
||
}
|
||
rz_config_set_i(core->config, "search.kwidx", search->n_kws);
|
||
if (dosearch) {
|
||
do_string_search(core, search_itv, ¶m);
|
||
}
|
||
beach:
|
||
core->num->value = search->nhits;
|
||
core->in_search = false;
|
||
rz_flag_space_pop(core->flags);
|
||
if (param.outmode == RZ_MODE_JSON) {
|
||
rz_cons_println(pj_string(param.pj));
|
||
}
|
||
pj_free(param.pj);
|
||
rz_list_free(param.boundaries);
|
||
rz_search_kw_reset(search);
|
||
return ret;
|
||
}
|