diff --git a/librz/core/cconfig.c b/librz/core/cconfig.c index a752bf26619..c52b920169c 100644 --- a/librz/core/cconfig.c +++ b/librz/core/cconfig.c @@ -2415,13 +2415,6 @@ static bool cb_print_align(void *user, void *data) { return true; } -static bool cb_search_align(void *user, void *data) { - RzCore *core = (RzCore *)user; - RzConfigNode *node = (RzConfigNode *)data; - core->print->addrmod = node->i_value; - return rz_search_opt_set_buffer_size(core->search_opts, node->i_value); -} - static bool cb_search_inverse_match(void *user, void *data) { RzCore *core = (RzCore *)user; RzConfigNode *node = (RzConfigNode *)data; diff --git a/librz/core/cmd/cmd.c b/librz/core/cmd/cmd.c index 479620e36a2..baca8c7282f 100644 --- a/librz/core/cmd/cmd.c +++ b/librz/core/cmd/cmd.c @@ -5218,7 +5218,6 @@ RZ_API void rz_core_cmd_init(RzCore *core) { const char *description; RzCmdCb cb; } cmds[] = { - { "/", "search kw, pattern aes", rz_cmd_search }, { "p", "print current block", rz_cmd_print }, { "x", "alias for px", rz_cmd_hexdump }, }; diff --git a/librz/core/csearch.c b/librz/core/csearch.c index 30255c49290..6dcb5fa419b 100644 --- a/librz/core/csearch.c +++ b/librz/core/csearch.c @@ -155,11 +155,15 @@ RZ_API RZ_OWN RzList /**/ *rz_core_search_string(RZ_NONNULL RzCor } // Copy RzUtilStrScanOptions from RzBin - RzUtilStrScanOptions str_opts; - memcpy(&str_opts, &core->bin->str_search_cfg, sizeof(str_opts)); - str_opts.buf_size = rz_config_get_i(core->config, "search.buffer_size"); - - RzSearchCollection *collection = rz_search_collection_strings(&str_opts, expected, caseless); + RzUtilStrScanOptions scan_opt = { + .buf_size = rz_config_get_i(core->config, "search.buffer_size"), + .max_uni_blocks = core->bin->str_search_cfg.max_uni_blocks, + .min_str_length = core->bin->str_search_cfg.min_length, + .prefer_big_endian = core->analysis->big_endian, + .check_ascii_freq = core->bin->str_search_cfg.check_ascii_freq, + }; + + RzSearchCollection *collection = rz_search_collection_strings(&scan_opt, expected, caseless); if (!collection || !rz_search_collection_string_add(collection, string)) { rz_search_collection_free(collection); @@ -315,3 +319,18 @@ RZ_API RZ_OWN RzList /**/ *rz_core_search_hex_pattern(RZ_NONNULL return core_run_search(core, opt, collection); } + +/** + * \brief Uses the given RzSearchCollection to find matches within the search.in boundaries + * + * \param core The RzCore core + * \param opt The search options to apply + * \param[in] collection The RzSearchCollection to use + * + * \return On success returns a valid pointer, otherwise NULL + */ +RZ_API RZ_OWN RzList /**/ *rz_core_search_collection(RZ_NONNULL RzCore *core, RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *collection) { + rz_return_val_if_fail(core && opt && collection, NULL); + + return core_run_search(core, opt, collection); +} diff --git a/librz/include/rz_core.h b/librz/include/rz_core.h index b3bf818df3e..7566ecc61b0 100644 --- a/librz/include/rz_core.h +++ b/librz/include/rz_core.h @@ -1088,6 +1088,7 @@ RZ_API RZ_OWN RzList /**/ *rz_core_search_magic(RZ_NONNULL RzCore RZ_API RZ_OWN RzList /**/ *rz_core_search_regex(RZ_NONNULL RzCore *core, RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL const char *regex, bool caseless); RZ_API RZ_OWN RzList /**/ *rz_core_search_bytes(RZ_NONNULL RzCore *core, RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL const ut8 *bytes, RZ_NULLABLE const ut8 *mask, size_t size); RZ_API RZ_OWN RzList /**/ *rz_core_search_hex_pattern(RZ_NONNULL RzCore *core, RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL const char *hex_pattern); +RZ_API RZ_OWN RzList /**/ *rz_core_search_collection(RZ_NONNULL RzCore *core, RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *collection); #define RZ_CORE_BOUNDARIES_PERMS_ANY 0 #define RZ_CORE_BOUNDARIES_MASK_NONE 0 diff --git a/librz/include/rz_util/rz_print.h b/librz/include/rz_util/rz_print.h index 96b0ae0e17d..d63fcf93840 100644 --- a/librz/include/rz_util/rz_print.h +++ b/librz/include/rz_util/rz_print.h @@ -235,7 +235,7 @@ RZ_API RZ_OWN char *rz_print_json_indent(RZ_NULLABLE const char *s, bool color, RZ_API char *rz_print_json_human(const char *s); RZ_API RZ_OWN RzStrBuf *rz_print_colorize_asm_str(RZ_BORROW RzPrint *p, const RzAsmTokenString *toks); -RZ_API void rz_print_colored_help_option(const char *option, const char *arg, const char *description, size_t maxOptionAndArgLength); +RZ_API void rz_print_colored_help_option(const char *option, const char *arg, const char *description, size_t max_length); #endif #ifdef __cplusplus diff --git a/librz/main/rz-find.c b/librz/main/rz-find.c index 921eed7f68f..057249649af 100644 --- a/librz/main/rz-find.c +++ b/librz/main/rz-find.c @@ -14,675 +14,840 @@ #include #include #include +#include + +#define DEFAULT_BUFFER_SIZE 4096 + +typedef enum { + RZ_FIND_OUTPUT_STANDARD = 0, + RZ_FIND_OUTPUT_JSON, + RZ_FIND_OUTPUT_QUIET, + RZ_FIND_OUTPUT_HEXDUMP, + RZ_FIND_OUTPUT_COMMAND, +} RzFindOutput; typedef enum { - SEARCH_MODE_BYTES = 0, - SEARCH_MODE_REGEXP, - SEARCH_MODE_STRING, - SEARCH_MODE_AES, - SEARCH_MODE_PRIVATE_KEY, - SEARCH_MODE_MAGIC, - SEARCH_MODE_XREFS, - /* enum size */ - SEARCH_MODE_LAST -} search_mode; + RZ_FIND_MODE_NOT_SET = 0, + RZ_FIND_MODE_MAGIC, + RZ_FIND_MODE_AES_KEYS, + RZ_FIND_MODE_PRIVATE_KEYS, + RZ_FIND_MODE_STRINGS, + RZ_FIND_MODE_REGEXES, + RZ_FIND_MODE_HEX_PATTERNS, + RZ_FIND_MODE_IMPORTS, + RZ_FIND_MODE_SYMBOLS, +} RzFindMode; typedef struct rz_find_options { - bool showstr; - bool identify; - bool import; /* search within import table */ - bool symbol; /* search within symbol table */ - bool quiet; - bool hexstr; - bool widestr; - bool nonstop; - bool json; - search_mode mode; - int align; - ut8 *buf; - ut64 bsize; - ut64 from; - ut64 to; - ut64 cur; - RzPrint *pr; + PJ *pj; + RzSearchCollection *collection; RzList /**/ *keywords; - const char *mask; - const char *curfile; - const char *comma; const char *exec_command; + const char *exec_command_at; + size_t buffer_size; + ut64 from; + ut64 to; + RzFindOutput output; + RzFindMode mode; + RzStrEnc encoding; + bool ignore_read_errors; + bool caseless; } RzFindOptions; -static void rzfind_options_fini(RzFindOptions *ro) { - free(ro->buf); - ro->cur = 0; +typedef bool (*RzFindIsValidInput)(const char *input, int line); + +static void rz_find_options_fini(RzFindOptions *ro) { + rz_search_collection_free(ro->collection); + rz_list_free(ro->keywords); + pj_free(ro->pj); } -static void rzfind_options_init(RzFindOptions *ro) { +static void rz_find_options_init(RzFindOptions *ro) { memset(ro, 0, sizeof(RzFindOptions)); - ro->mode = SEARCH_MODE_STRING; - ro->bsize = 4096; + ro->keywords = rz_list_new(); + ro->buffer_size = DEFAULT_BUFFER_SIZE; ro->to = UT64_MAX; - ro->keywords = rz_list_newf(NULL); - ro->exec_command = NULL; + ro->output = RZ_FIND_OUTPUT_STANDARD; + ro->mode = RZ_FIND_MODE_NOT_SET; + ro->encoding = RZ_STRING_ENC_GUESS; } -static int rzfind_open(RzFindOptions *ro, const char *file); - -static bool hit(RzFindOptions *ro, RzSearchHit *hit) { - ut64 addr = hit->address; - int delta = addr - ro->cur; - if (ro->cur > addr && (ro->cur - addr == hit->size - 1)) { - // This case occurs when there is hit in search left over - delta = ro->cur - addr; +static bool rz_find_get_buffer_at(RzCore *core, RzSearchHit *hit, ut8 **buffer, size_t *size) { + size_t buffer_size = hit->size; + if (buffer_size < 1 || buffer_size & 0xf) { + // align buffer. + buffer_size = (buffer_size + 0x10) & 0xf; } - if (delta < 0 || delta >= ro->bsize) { - eprintf("Invalid delta\n"); + + ut8 *b = malloc(buffer_size); + if (!b) { return false; } - char _str[128]; - char *str = _str; - *_str = 0; - if (ro->showstr) { - if (ro->widestr) { - str = _str; - int i, j = 0; - for (i = delta; ro->buf[i] && i < sizeof(_str); i++) { - char ch = ro->buf[i]; - if (ch == '"' || ch == '\\') { - ch = '\''; - } - if (!IS_PRINTABLE(ch)) { - break; - } - str[j++] = ch; - i++; - if (j > 80) { - strcpy(str + j, "..."); - j += 3; - break; - } - if (ro->buf[i]) { - break; - } - } - str[j] = 0; - } else { - size_t i; - for (i = 0; i < sizeof(_str) - 1; i++) { - char ch = ro->buf[delta + i]; - if (ch == '"' || ch == '\\') { - ch = '\''; - } - if (!ch || !IS_PRINTABLE(ch)) { - break; - } - str[i] = ch; - } - str[i] = 0; - } - } else { - size_t i; - for (i = 0; i < sizeof(_str) - 1; i++) { - char ch = ro->buf[delta + i]; - if (ch == '"' || ch == '\\') { - ch = '\''; - } - if (!ch || !IS_PRINTABLE(ch)) { - break; - } - str[i] = ch; - } - str[i] = 0; + + rz_io_read_at(core->io, hit->address, b, buffer_size); + *buffer = b; + *size = buffer_size; + return true; +} + +static void rz_find_output_hit_as_hexdump(RzFindOptions *ro, RzSearchHit *hit, RzCore *core) { + ut8 *buffer = NULL; + size_t size = 0; + + if (!rz_find_get_buffer_at(core, hit, &buffer, &size)) { + eprintf("Error: failed to read at 0x%" PFMT64x "\n", hit->address); + return; } - if (ro->json) { - const char *type = "string"; - printf("%s{\"offset\":%" PFMT64d ",\"type\":\"%s\",\"data\":\"%s\"}", - ro->comma, addr, type, str); - ro->comma = ","; - } else { - if (ro->showstr) { - printf("0x%" PFMT64x " %s\n", addr, str); - } else { - printf("0x%" PFMT64x "\n", addr); - if (ro->pr) { - char *dump = rz_print_hexdump_str(ro->pr, addr, (ut8 *)ro->buf + delta, 78, 16, 1, 1); - printf("%s", dump); - free(dump); - } - } + + char *output = rz_print_hexdump_str(core->print, hit->address, buffer, size, 16, 1, 1); + free(buffer); + if (!output) { + eprintf("Error: failed to hexdump at 0x%" PFMT64x "\n", hit->address); + return; } - if (ro->exec_command) { - char *command = rz_str_newf("%s %s", ro->exec_command, ro->curfile); - int status = rz_sys_system(command); - if (status == -1) { - RZ_LOG_ERROR("Failed to execute command: %s\n", command); + printf(output); + free(output); +} + +static void rz_find_output_string(RzFindOptions *ro, RzSearchHit *hit, RzCore *core) { + switch (ro->mode) { + case RZ_FIND_OUTPUT_HEXDUMP: + rz_find_output_hit_as_hexdump(ro, hit, core); + return; + case RZ_FIND_OUTPUT_QUIET: + printf("0x%" PFMT64x "\n", hit->address); + return; + default: /* RZ_FIND_OUTPUT_STANDARD */ + break; + } + + RzBinSection *s = NULL; + RzBinObject *bo = NULL; + RzDetectedString *ds = NULL; + const char *encoding = ""; + const char *section_name = ""; + ut8 *buffer = NULL; + size_t size = 0; + + if (!rz_find_get_buffer_at(core, hit, &buffer, &size)) { + eprintf("Error: failed to read at 0x%" PFMT64x "\n", hit->address); + return; + } + + RzUtilStrScanOptions opts; + opts.buf_size = size; + opts.max_uni_blocks = core->bin->str_search_cfg.max_uni_blocks; + opts.min_str_length = core->bin->str_search_cfg.min_length; + opts.prefer_big_endian = core->analysis->big_endian; + opts.check_ascii_freq = core->bin->str_search_cfg.check_ascii_freq; + + if (!rz_scan_strings_single_raw(buffer, size, &opts, ro->encoding, &ds)) { + eprintf("Error: failed to decode string at 0x%" PFMT64x "\n", hit->address); + free(buffer); + return; + } + free(buffer); + + if (ds) { + bo = rz_bin_cur_object(core->bin); + if (bo) { + s = rz_bin_get_section_at(bo, hit->address, false); + if (s) { + section_name = s->name; + } } - free(command); + encoding = rz_str_enc_as_string(ds->type); + } + + switch (ro->mode) { + case RZ_FIND_OUTPUT_JSON: + pj_o(ro->pj); + pj_kn(ro->pj, "address", hit->address); + pj_kn(ro->pj, "size", ds->size); + pj_kn(ro->pj, "length", ds->length); + pj_ks(ro->pj, "section", section_name); + pj_ks(ro->pj, "type", encoding); + pj_ks(ro->pj, "string", ds->string); + pj_end(ro->pj); + break; + default: /* RZ_FIND_OUTPUT_STANDARD */ + printf("0x%" PFMT64x " %" PFMTSZu "%s\n", hit->address, hit->size, ds->string); + break; } - return true; } -static void rz_find_run_search(RzFindOptions *ro, RzList *hits) { - RzListIter *it = NULL; - RzSearchHit *hit = NULL; - rz_list_foreach (hits, it, hit) { - if (!hit(ro, hit) && !ro.nonstop) { - break; - } +static void rz_find_output_hit(RzFindOptions *ro, RzSearchHit *hit, RzCore *core) { + switch (ro->mode) { + case RZ_FIND_OUTPUT_JSON: + pj_o(ro->pj); + pj_kn(ro->pj, "address", hit->address); + pj_kn(ro->pj, "size", hit->size); + pj_ks(ro->pj, "type", hit->metadata); + pj_end(ro->pj); + break; + case RZ_FIND_OUTPUT_STANDARD: + printf("0x%" PFMT64x " %" PFMTSZu " %s\n", hit->address, hit->size, hit->metadata); + break; + case RZ_FIND_OUTPUT_HEXDUMP: + rz_find_output_hit_as_hexdump(ro, hit, core); + break; + default: + printf("0x%" PFMT64x "\n", hit->address); + break; } +} +static void rz_find_output_import(RzFindOptions *ro, RzBinImport *import) { + switch (ro->mode) { + case RZ_FIND_OUTPUT_JSON: + pj_o(ro->pj); + pj_ks(ro->pj, "name", rz_str_get(import->name)); + pj_ks(ro->pj, "dname", rz_str_get(import->dname)); + pj_ks(ro->pj, "libname", rz_str_get(import->libname)); + pj_ks(ro->pj, "bind", rz_str_get(import->bind)); + pj_ks(ro->pj, "type", rz_str_get(import->type)); + pj_ks(ro->pj, "classname", rz_str_get(import->classname)); + pj_ks(ro->pj, "descriptor", rz_str_get(import->descriptor)); + pj_kn(ro->pj, "ordinal", import->ordinal); + pj_kn(ro->pj, "visibility", import->visibility); + pj_end(ro->pj); + break; + default: + printf("%s\n", import->name); + break; + } } -static void print_bin_string(RzBinFile *bf, RzBinString *string, PJ *pj) { - rz_return_if_fail(bf && string); - - RzBinSection *s = rz_bin_get_section_at(bf->o, string->paddr, false); - if (s) { - string->vaddr = s->vaddr + (string->paddr - s->paddr); - } - string->vaddr = bf->o ? rz_bin_object_get_vaddr(bf->o, string->paddr, string->vaddr) : UT64_MAX; - - if (pj) { - const char *section_name = s ? s->name : ""; - const char *type_string = rz_str_enc_as_string(string->type); - pj_o(pj); - pj_kn(pj, "vaddr", string->vaddr); - pj_kn(pj, "paddr", string->paddr); - pj_kn(pj, "ordinal", string->ordinal); - pj_kn(pj, "size", string->size); - pj_kn(pj, "length", string->length); - pj_ks(pj, "section", section_name); - pj_ks(pj, "type", type_string); - pj_ks(pj, "string", string->string); - pj_end(pj); - } else { - printf("%s\n", string->string); +static void rz_find_output_symbol(RzFindOptions *ro, RzBinSymbol *symbol) { + switch (ro->mode) { + case RZ_FIND_OUTPUT_JSON: + pj_o(ro->pj); + pj_ks(ro->pj, "name", rz_str_get(symbol->name)); + pj_ks(ro->pj, "dname", rz_str_get(symbol->dname)); + pj_ks(ro->pj, "libname", rz_str_get(symbol->libname)); + pj_ks(ro->pj, "class", rz_str_get(symbol->classname)); + pj_ks(ro->pj, "visibility", rz_str_get(symbol->visibility_str)); + pj_kn(ro->pj, "vaddr", symbol->vaddr); + pj_kn(ro->pj, "paddr", symbol->paddr); + pj_kn(ro->pj, "size", symbol->size); + pj_end(ro->pj); + break; + case RZ_FIND_OUTPUT_STANDARD: + printf("0x%" PFMT64x " %u %s\n", symbol->vaddr, symbol->size, symbol->name); + break; + default: + printf("%s\n", symbol->name); + break; } } -static int show_help(const char *argv0, int line) { - printf("%s%s%s", Color_CYAN, "Usage: ", Color_RESET); - printf("%s [-mXnzZhqv] [-a align] [-b sz] [-f/t from/to] [-[e|s|w|S|I] str] [-x hex] -|file|dir ..\n", argv0); - if (line) { - return 0; +static void rz_find_output_command(RzFindOptions *ro, const char *filename) { + char *command = NULL; + if (ro->exec_command_at) { + command = rz_str_dup(ro->exec_command); + command = rz_str_replace(command, ro->exec_command_at, filename, 1); + } else { + command = rz_str_newf("%s %s", ro->exec_command, filename); } - const char *options[] = { - // clang-format off - "-b", "[size]", "Set block size", - "-e", "[regex]", "Search for regex matches (can be used multiple times)", - "-E", "[cmd]", "Execute command for each file found", - "-f", "[from]", "Start searching from address 'from'", - "-F", "[file]", "Read the contents of the file and use it as keyword", - "-h", "", "Show this help", - "-i", "", "Identify filetype (rizin -nqcpm file)", - "-j", "", "Output in JSON", - "-m", "", "Magic search, file-type carver", - "-M", "[str]", "Set a binary mask to be applied on keywords", - "-n", "", "Do not stop on read errors", - "-s", "[str]", "Search for a specific string (can be used multiple times)", - "-w", "[enc]", "Forces a specific string encoding.", - "-I", "[str]", "Search for an entry in import table.", - "-S", "[str]", "Search for a symbol in symbol table.", - "-t", "[to]", "Stop search at address 'to'", - "-q", "", "Quiet - do not show headings (filenames) above matching contents (default for searching a single file)", - "-v", "", "Show version information", - "-x", "[hex]", "Search for hexpair string (909090) (can be used multiple times)", - "-X", "", "Show hexdump of search results", - "-z", "", "Search for zero-terminated strings", - "-Z", "", "Show string found on each search hit", - // clang-format on - }; - size_t max_arg_len = 0; - for (size_t i = 0; i < RZ_ARRAY_SIZE(options); i += 3) { - size_t optionLength = strlen(options[i]); - size_t argLength = strlen(options[i + 1]); - size_t totalLength = optionLength + argLength; - if (totalLength > max_arg_len) { - max_arg_len = totalLength; - } + int status = rz_sys_system(command); + if (status == -1) { + eprintf("Error: Failed to execute command: %s\n", command); } - for (size_t i = 0; i < RZ_ARRAY_SIZE(options); i += 3) { - rz_print_colored_help_option(options[i], options[i + 1], options[i + 2], max_arg_len); + free(command); +} + +static inline RzBinFile *core_get_file(RzCoreFile *cfile) { + return rz_pvector_at(&cfile->binfiles, 0); +} + +static bool rz_find_search_progress_cancel(void *user, size_t n_hits) { + return rz_cons_is_breaked(); +} + +static bool rz_find_match_string(const char *string, size_t slen, const char *find, bool caseless) { + size_t flen = strlen(find); + if (slen < flen) { + // ignore strings that are smaller than the one we are looking for. + return false; } - printf("Supported encodings (-w option):\n"); - const char *encodings[] = { - // clang-format off - "ascii", "ASCII from 0 to 0x7f" - "mutf8", "modified utf8" - "utf8", "utf-8" - "utf16le", "utf-16 little endian" - "utf32le", "utf-32 little endian" - "utf16be", "utf-16 big endian" - "utf32be", "utf-32 big endian" - "ibm037", "ibm037" - "ibm290", "ibm290" - "ebcdices", "ebcdic ES" - "ebcdicuk", "ebcdic UK" - "ebcdicus", "ebcdic US" - // clang-format on + size_t len = RZ_MIN(slen, flen); + if ((caseless && rz_str_ncasecmp(string, find, len)) || + (!caseless && strncmp(string, find, len))) { + return false; } - max_arg_len = 0; - for (size_t i = 0; i < RZ_ARRAY_SIZE(encodings); i += 2) { - size_t encoding = strlen(encodings[i]); - if (encoding > max_arg_len) { - max_arg_len = encoding; + return true; +} + +static void rz_find_over_imports(RzFindOptions *ro, RzCore *core) { + void **vit; + RzListIter *it = NULL; + const char *find; + RzBinImport *import; + RzBinObject *bo = rz_bin_cur_object(core->bin); + const RzPVector *imports = bo ? rz_bin_object_get_imports(bo) : NULL; + rz_pvector_foreach (imports, vit) { + import = *vit; + size_t slen = strlen(import->name); + + rz_list_foreach (ro->keywords, it, find) { + if (rz_find_match_string(import->name, slen, find, ro->caseless)) { + rz_find_output_import(ro, import); + } } } - for (size_t i = 0; i < RZ_ARRAY_SIZE(encodings); i += 2) { - rz_print_colored_help_option(encodings[i], "", encodings[i + 1], max_arg_len); - } - return 0; } -static int rzfind_open_file(RzFindOptions *ro, const char *file, const ut8 *data, int datalen) { - RzListIter *iter; - RzSearchOpt *rs = NULL; - const char *kw; - bool last = false; - int ret, result = 0; - - ro->buf = NULL; - if (!ro->quiet) { - printf("File: %s\n", file); - } - - char *efile = rz_str_escape_sh(file); - - if (ro->identify) { - char *cmd = rz_str_newf("rizin -e search.show=false -e search.maxhits=1 -nqcpm \"%s\"", efile); - rz_sys_xsystem(cmd); - free(cmd); - free(efile); - return 0; - } - - if (ro->import || ro->symbol) { - RzBinFile *bf; - const RzPVector *symbols; - const RzPVector *imports; - RzListIter *iter; - void **it; - void **vec_it; - RzBinSymbol *symbol; - RzBinImport *import; - RzBin *bin = rz_bin_new(); - RzIO *rio = rz_io_new(); - RzBinOptions opt = { 0 }; - - if (!bin || !rio) { - result = 1; - goto sym_end; +static void rz_find_over_symbols(RzFindOptions *ro, RzCore *core) { + void **vit; + const char *find; + RzListIter *it = NULL; + RzBinSymbol *symbol; + RzBinObject *bo = rz_bin_cur_object(core->bin); + const RzPVector *symbols = bo ? rz_bin_object_get_symbols(bo) : NULL; + rz_pvector_foreach (symbols, vit) { + symbol = *vit; + size_t slen = strlen(symbol->name); + + rz_list_foreach (ro->keywords, it, find) { + if (rz_find_match_string(symbol->name, slen, find, ro->caseless)) { + rz_find_output_symbol(ro, symbol); + } } + } +} - rz_io_bind(rio, &bin->iob); - rz_bin_options_init(&opt, 0, 0, 0, false); - - bf = rz_bin_open(bin, file, &opt); - if (!bf) { - result = 1; - goto sym_end; +static bool rz_find_init_collection(RzFindOptions *ro) { + RzSearchHit *hit = NULL; + const char *find; + + switch(ro->mode) { + case RZ_FIND_MODE_MAGIC: { + char *sys_magic = rz_path_system(RZ_SDB_MAGIC); + ro->collection = rz_search_collection_magic(sys_magic); + free(sys_magic); + return ro->collection != NULL; + } + case RZ_FIND_MODE_AES_KEYS: + ro->collection = rz_search_collection_aes_keys(); + return ro->collection != NULL; + case RZ_FIND_MODE_PRIVATE_KEYS: + ro->collection = rz_search_collection_private_keys(); + return ro->collection != NULL; + case RZ_FIND_MODE_STRINGS: + + ro->collection = rz_search_collection_strings(RZ_NONNULL RzUtilStrScanOptions *opts, RzStrEnc expected, bool caseless); + if (ro->collection == NULL){ + return false; } + break; + case RZ_FIND_MODE_REGEXES: + ro->collection = rz_search_collection_private_keys(); + if (ro->collection == NULL){ + return false; + } + break; + case RZ_FIND_MODE_HEX_PATTERNS: + ro->collection = rz_search_collection_private_keys(); + if (ro->collection == NULL){ + return false; + } + break; + default: + // nothing to initialize + return true; + } - if (ro->import) { - imports = rz_bin_object_get_imports(bf->o); - rz_list_foreach (ro->keywords, iter, kw) { - if (!kw) { - continue; - } - rz_pvector_foreach (imports, vec_it) { - import = *vec_it; - if (!strcmp(import->name, kw)) { - printf("ordinal: %d %s\n", import->ordinal, kw); - } - } - } + rz_list_foreach (ro->keywords, it, find) { + if (rz_find_match_string(symbol->name, slen, find, ro->caseless)) { + rz_find_output_symbol(ro, symbol); } + } +} + +static void rz_find_over_collection(RzFindOptions *ro, RzCore *core) { + // use RzSearch + RzListIter *it = NULL; + RzList *hits = NULL; + RzSearchHit *hit = NULL; + + rz_search_opt_set_buffer_size(core->search_opts, ro->buffer_size); + rz_search_opt_set_cancel_cb(core->search_opts, rz_find_search_progress_cancel, NULL); - if (ro->symbol) { - symbols = rz_bin_object_get_symbols(bf->o); - rz_list_foreach (ro->keywords, iter, kw) { - if (!kw) { - continue; - } - rz_pvector_foreach (symbols, it) { - symbol = *it; - if (!symbol->name) { - continue; - } - - if (!strcmp(symbol->name, kw)) { - printf("paddr: 0x%08" PFMT64x " vaddr: 0x%08" PFMT64x " type: %s %s\n", symbol->paddr, symbol->vaddr, symbol->type, symbol->name); - } - } + hits = rz_core_search_collection(core, core->search_opts, ro->collection); + if (hits && !rz_cons_is_breaked()) { + rz_list_foreach (hits, it, hit) { + if (ro->mode == RZ_FIND_MODE_STRINGS) { + rz_find_output_string(ro, hit, core); + } else { + rz_find_output_hit(ro, hit, core); } } + } + + rz_list_free(hits); +} - result = 0; +static bool rz_find_search_in_file(RzFindOptions *ro, const char *filename) { + RzCore *core = NULL; + RzCoreFile *cfile = NULL; + RzBinFile *bfile = NULL; + bool res = false; - sym_end: - rz_bin_free(bin); - rz_io_free(rio); - free(efile); - return result; + core = rz_core_new(); + if (!core) { + eprintf("rzfind: Cannot allocate RzCore\n"); + goto rz_diff_load_file_with_core_fail; } + rz_core_loadlibs(core, RZ_CORE_LOADLIBS_ALL); - RzIO *io = rz_io_new(); - if (!io) { - free(efile); - return 1; + rz_config_set_i(core->config, "scr.color", true); + rz_config_set_b(core->config, "scr.interactive", false); + rz_config_set_b(core->config, "cfg.debug", false); + rz_config_set_b(core->config, "scr.prompt", false); + core->print->scr_prompt = false; + cfile = rz_core_file_open(core, filename, 0, 0); + if (!cfile) { + eprintf("rzfind: Cannot open file '%s'\n", filename); + goto rz_diff_load_file_with_core_fail; } - if (!rz_io_open_nomap(io, file, RZ_PERM_R, 0)) { - eprintf("Cannot open file '%s'\n", file); - result = 1; - goto err; + if (!rz_core_bin_load(core, NULL, UT64_MAX)) { + eprintf("rzfind: Cannot load file '%s'\n", filename); + goto rz_diff_load_file_with_core_fail; } - if (data) { - rz_io_write_at(io, 0, data, datalen); + if (!rz_core_bin_update_arch_bits(core)) { + eprintf("rzfind: Cannot set architecture with bits\n"); + goto rz_diff_load_file_with_core_fail; } - rs = rz_search_opt_new(); - if (!rs) { - result = 1; - goto err; + bfile = core_get_file(cfile); + if (!bfile) { + eprintf("rzfind: Cannot get RzBinFile\n"); + goto rz_diff_load_file_with_core_fail; } - ro->buf = calloc(1, ro->bsize); - if (!ro->buf) { - eprintf("Cannot allocate %" PFMT64d " bytes\n", ro->bsize); - result = 1; - goto err; + if (rz_pvector_empty(bfile->o->maps)) { + // if there are no maps, then must be loaded as raw + rz_config_set_b(core->config, "io.va", false); } - rz_search_set_callback(rs, &hit, ro); - ut64 to = ro->to; - if (to == -1) { - to = rz_io_size(io); + if (ro->output == RZ_FIND_OUTPUT_COMMAND) { + // stop after 1 hit + rz_search_opt_set_max_hits(core->search_opts, 1); + } else if (ro->output == RZ_FIND_OUTPUT_JSON) { + pj_o(ro->pj); // { + pj_ks(ro->pj, "file", filename); + pj_ka(ro->pj, "hits"); } - if (!rz_cons_new()) { - result = 1; - goto err; + if (ro->output == RZ_FIND_OUTPUT_COMMAND) { + rz_find_output_command(ro, filename); + } else if (ro->mode == RZ_FIND_MODE_IMPORTS) { + // loop over imports + rz_find_over_imports(ro, core); + } else if (ro->mode == RZ_FIND_MODE_SYMBOLS) { + // loop over symbols + rz_find_over_symbols(ro, core); + } else { + rz_find_over_collection(ro, core); } - RzBinOptions opt; - rz_bin_options_init(&opt, 0, 0, 0, false); - RzBin *bin = rz_bin_new(); - rz_io_bind(io, &bin->iob); - io->cb_printf = printf; - RzBinFile *bf = rz_bin_open(bin, file, &opt); + if (ro->output == RZ_FIND_OUTPUT_JSON) { + pj_end(ro->pj); // ] + pj_end(ro->pj); // } + } - if (ro->mode == SEARCH_MODE_STRING) { - PJ *pj = NULL; - if (ro->json) { - pj = pj_new(); - if (!pj) { - eprintf("rz-bin: Cannot allocate buffer for json array\n"); - result = 1; - goto err; - } - pj_a(pj); - } + res = true; +rz_diff_load_file_with_core_fail: + rz_core_free(core); + return res; +} - RzBinStringSearchOpt opt = bin->str_search_cfg; - // enforce raw binary search - opt.mode = RZ_BIN_STRING_SEARCH_MODE_RAW_BINARY; +static void rz_find_open(RzFindOptions *ro, const char *path); - RzPVector *vec = rz_bin_file_strings(bf, &opt); - void **it; - RzBinString *string; - rz_pvector_foreach (vec, it) { - string = *it; - print_bin_string(bf, string, pj); - } - rz_pvector_free(vec); - if (pj) { - pj_end(pj); - printf("%s", pj_string(pj)); - pj_free(pj); - } - goto done; - } - - if (ro->mode == SEARCH_MODE_MAGIC) { - /* TODO: implement using api */ - char *tostr = (to && to != UT64_MAX) ? rz_str_newf("-e search.to=%" PFMT64d, to) : rz_str_dup(""); - rz_sys_cmdf("rizin" - " -e search.in=range" - " -e search.align=%d" - " -e search.from=%" PFMT64d - " %s -qnc/m%s \"%s\"", - ro->align, ro->from, tostr, ro->json ? "j" : "", efile); - free(tostr); - goto done; - } - if (ro->mode == SEARCH_MODE_KEYWORD) { - rz_list_foreach (ro->keywords, iter, kw) { - if (ro->hexstr) { - if (ro->mask) { - rz_search_kw_add(rs, rz_search_keyword_new_hex(kw, ro->mask, NULL)); - } else { - rz_search_kw_add(rs, rz_search_keyword_new_hexmask(kw, NULL)); - } - } else if (ro->widestr) { - rz_search_kw_add(rs, rz_search_keyword_new_wide(kw, ro->mask, NULL, 0)); - } else { - rz_search_kw_add(rs, rz_search_keyword_new_str(kw, ro->mask, NULL, 0)); - } - } - } else if (ro->mode == SEARCH_MODE_STRING) { - rz_search_kw_add(rs, rz_search_keyword_new_hexmask("00", NULL)); // XXX - } - - ro->curfile = file; - rz_search_begin(rs); - (void)rz_io_seek(io, ro->from, RZ_IO_SEEK_SET); - result = 0; - ut64 bsize = ro->bsize; - for (ro->cur = ro->from; !last && ro->cur < to; ro->cur += bsize) { - if ((ro->cur + bsize) > to) { - bsize = to - ro->cur; - last = true; - } - ret = rz_io_pread_at(io, ro->cur, ro->buf, bsize); - if (ret == 0) { - if (ro->nonstop) { - continue; - } - result = 1; - break; - } - if (ret != bsize && ret > 0) { - bsize = ret; - } +static void rz_find_open_dir(RzFindOptions *ro, const char *dir) { + RzListIter *iter; + char *fullpath; + char *path = NULL; + + RzList *files = rz_sys_dir(dir); - if (rz_search_update(rs, ro->cur, ro->buf, ret) == -1) { - eprintf("search: update read error at 0x%08" PFMT64x "\n", ro->cur); + if (!files) { + return; + } + + rz_list_foreach (files, iter, path) { + /* filter-out unwanted entries */ + if (*path == '.') { + continue; } + fullpath = rz_file_path_join(dir, path); + rz_find_open_dir(ro, fullpath); + free(fullpath); } -done: - rz_cons_free(); - rz_bin_free(bin); -err: - free(efile); - rz_search_free(rs); - rz_io_free(io); - rzfind_options_fini(ro); - return result; + rz_list_free(files); } -static int rzfind_open_dir(RzFindOptions *ro, const char *dir) { - RzListIter *iter; - char *fullpath; - char *fname = NULL; +static void rz_find_open(RzFindOptions *ro, const char *path) { + if (rz_file_is_directory(path)) { + rz_find_open_dir(ro, path); + } else { + rz_find_search_in_file(ro, path); + } +} - RzList *files = rz_sys_dir(dir); +static bool rz_find_is_valid_regex(const char *input, int line) { + RzRegex *compiled = rz_regex_new(input, RZ_REGEX_EXTENDED, 0); + if (compiled) { + rz_regex_free(compiled); + return true; + } + if (line < 1) { + eprintf("rzfind: cannot compile '%s' regex.\n", input); + } else { + eprintf("rzfind: cannot compile '%s' regex at line %d.\n", input, line); + } + return false; +} - if (files) { - rz_list_foreach (files, iter, fname) { - /* Filter-out unwanted entries */ - if (*fname == '.') { - continue; - } - fullpath = rz_file_path_join(dir, fname); - (void)rzfind_open(ro, fullpath); - free(fullpath); +static bool rz_find_is_valid_string(const char *input, int line) { + if (RZ_STR_ISNOTEMPTY(input)) { + return true; + } + if (line < 1) { + eprintf("rzfind: '%s' is not a valid string.\n", input); + } else { + eprintf("rzfind: '%s' is not a valid string at line %d.\n", input, line); + } + return false; +} + +static bool rz_find_is_valid_hex_pattern(const char *input, int line) { + if (RZ_STR_ISNOTEMPTY(input) || !(strlen(input) & 1)) { + // must be a len mod 2 == 0 string + return true; + } + if (line < 1) { + eprintf("rzfind: '%s' is not a valid hex pattern.\n", input); + } else { + eprintf("rzfind: '%s' is not a valid hex pattern at line %d.\n", input, line); + } + return false; +} + +static bool rz_find_read_keywords_from_file(const char *file, RzList /**/ *keywords, RzFindIsValidInput validator) { + RzListIter *it; + char *line = NULL; + char *buffer = rz_file_slurp(file, NULL); + if (!buffer) { + eprintf("rzfind: failed to read '%s'.\n", file); + return false; + } + // free(buffer) must be called at the end since rz_str_split_list does NOT dup strings. + RzList *lines = rz_str_split_list(buffer, "\n", 0); + + int n_line = 1; + rz_list_foreach (lines, it, line) { + if (!validator(line, n_line)) { + free(buffer); + return false; } - rz_list_free(files); + rz_list_append(keywords, line); + n_line++; } - return 0; + + free(buffer); + return true; +} + +static bool rz_find_add_keyword(const char *input, RzList /**/ *keywords, RzFindIsValidInput validator) { + if (!validator(input, 0)) { + return false; + } + rz_list_append(keywords, (void *)input); + return true; } -static int rzfind_open(RzFindOptions *ro, const char *file) { - if (!strcmp(file, "-")) { - int sz = 0; - ut8 *buf = (ut8 *)rz_stdin_slurp(&sz); - if (!buf) { - return 0; +static int show_help(const char *argv0, bool only_usage) { + printf("%s%s%s", Color_CYAN, "Usage: ", Color_RESET); + printf("%s -[bcfthjnCEmAPzrxisZRXISqvX] [file|directory]\n", argv0); + if (only_usage) { + return 1; + } + const char *options[] = { + // clang-format off + "-b", "[size]", "Set buffer size (default: " RZ_STR(DEFAULT_BUFFER_SIZE) ")", + "-c", "[cmd]", "Execute command for each file found (filename is passed as argument)", + "-k", "[key]", "Replace a certain keyword with the filename with the command (requires -c)", + "-f", "[from]", "Start search offset (default 0)", + "-t", "[to]", "Stop search offset (default file size)", + "-h", "", "Show this help", + "-d", "", "Show this help", + "-n", "", "Ignore read errors", + "-C", "", "Ignore case (only used by -s -r -I -S)", + "-E", "[enc]", "Forces a specific string encoding (see -h output).", + "-m", "", "Search for magic signatures in the whole file", + "-A", "", "Search for AES keys in the whole file", + "-P", "", "Search for private RSA/ECC/EdDSA keys in the whole file", + "-z", "[str]", "Search for one or multiple strings in the whole file", + "-r", "[regex]", "Search via one or multiple regexes in the whole file", + "-x", "[hex]", "Search for one or multiple hex patterns in the whole file", + "-i", "[import]", "Search for one or multiple strings in the import table.", + "-s", "[symbol]", "Search for one or multiple strings in the symbol table.", + "-Z", "[file]", "Search for strings in the whole file (use file lines as input)", + "-R", "[file]", "Search via regex (use file lines as input)", + "-X", "[file]", "Search for hex patterns (use file lines as input)", + "-I", "[file]", "Search for strings in the import table (use file lines as input).", + "-S", "[file]", "Search for strings in the symbol table (use file lines as input).", + "-j", "", "JSON output (outputs the matching filenames or offsets)", + "-q", "", "Quiet output (outputs the matching filenames or offsets)", + "-D", "", "Hexdump output of the matching region.", + "-v", "", "Show version information", + // clang-format on + }; + size_t max_arg_len = 0; + for (size_t i = 0; i < RZ_ARRAY_SIZE(options); i += 3) { + size_t flag = strlen(options[i]); + size_t arg = strlen(options[i + 1]); + size_t sum = flag + arg; + if (sum > max_arg_len) { + max_arg_len = sum; } - char *ff = rz_str_newf("malloc://%d", sz); - int res = rzfind_open_file(ro, ff, buf, sz); - free(ff); - free(buf); - return res; - } - return rz_file_is_directory(file) - ? rzfind_open_dir(ro, file) - : rzfind_open_file(ro, file, NULL, -1); + } + for (size_t i = 0; i < RZ_ARRAY_SIZE(options); i += 3) { + rz_print_colored_help_option(options[i], options[i + 1], options[i + 2], max_arg_len); + } + + printf("Supported encodings (-E option):\n"); + const char *encodings[] = { + // clang-format off + "ascii", "ASCII from 0 to 0x7f" + "mutf8", "Modified UTF-8" + "utf8", "UTF-8" + "utf16le", "UTF-16 little endian" + "utf32le", "UTF-32 little endian" + "utf16be", "UTF-16 big endian" + "utf32be", "UTF-32 big endian" + "ibm037", "IBM037" + "ibm290", "IBM290" + "ebcdices", "ebcdic ES" + "ebcdicuk", "ebcdic UK" + "ebcdicus", "ebcdic US" + // clang-format on + }; + max_arg_len = 0; + for (size_t i = 0; i < RZ_ARRAY_SIZE(encodings) && i + 1 < RZ_ARRAY_SIZE(encodings); i += 2) { + size_t encoding = strlen(encodings[i]); + if (encoding > max_arg_len) { + max_arg_len = encoding; + } + } + for (size_t i = 0; i < RZ_ARRAY_SIZE(encodings) && i + 1 < RZ_ARRAY_SIZE(encodings); i += 2) { + rz_print_colored_help_option(encodings[i], "", encodings[i + 1], max_arg_len); + } + return 0; } +#define ERROR_USAGE_ONLY_ONCE(test) \ + do { \ + if (test) { \ + eprintf("Error: -%c can be set only once.\n", c); \ + return 1; \ + } \ + } while (0) + +#define ERROR_ON_NOT_MODE(umode, bad_flags) \ + ERROR_USAGE_INCOMPATIBLE_WITH((ro.mode == RZ_FIND_MODE_NOT_SET || ro.mode == umode), bad_flags) + +#define ERROR_USAGE_INCOMPATIBLE_WITH(test, bad_flags) \ + do { \ + if (test) { \ + eprintf("Error: -%c be set only once and is incompatible with " bad_flags ".\n", c); \ + return 1; \ + } \ + } while (0) + RZ_API int rz_main_rz_find(int argc, const char **argv) { RzFindOptions ro; - rzfind_options_init(&ro); + rz_find_options_init(&ro); int c; - const char *file = NULL; - RzGetopt opt; - rz_getopt_init(&opt, argc, argv, "a:ie:b:jmM:s:w:S:I:x:Xzf:F:t:E:rqnhvZ"); + rz_getopt_init(&opt, argc, argv, "c:k:f:t:E:z:r:x:i:s:Z:R:X:I:S:C:njqDvhb"); while ((c = rz_getopt_next(&opt)) != -1) { switch (c) { - case 'i': - ro.identify = true; + case 'b': // Set buffer size + ERROR_USAGE_ONLY_ONCE(ro.buffer_size != DEFAULT_BUFFER_SIZE); + ro.buffer_size = rz_num_math(NULL, opt.arg); + if (ro.buffer_size < 1) { + eprintf("Error: invalid buffer size: %" PFMTSZu "\n", ro.buffer_size); + return 1; + } break; - case 'j': - ro.json = true; + case 'c': // Execute command for each file found (filename is passed as argument) + ERROR_USAGE_ONLY_ONCE(ro.exec_command); + ro.output = RZ_FIND_OUTPUT_COMMAND; + ro.exec_command = opt.arg; break; - case 'n': - ro.nonstop = 1; + case 'k': // Replace a certain keyword with the filename with the command (requires -c) + ERROR_USAGE_ONLY_ONCE(ro.exec_command_at); + ro.exec_command_at = opt.arg; break; - case 'm': - ro.mode = SEARCH_MODE_MAGIC; + case 'f': // Start search offset + ERROR_USAGE_ONLY_ONCE(ro.from != 0); + ro.from = rz_num_math(NULL, opt.arg); break; - case 'e': - ro.mode = SEARCH_MODE_REGEXP; - ro.hexstr = 0; - rz_list_append(ro.keywords, (void *)opt.arg); + case 't': // Stop search offset + ERROR_USAGE_ONLY_ONCE(ro.from != UT64_MAX); + ro.to = rz_num_math(NULL, opt.arg); break; - case 'E': - ro.quiet = true; - ro.exec_command = opt.arg; + case 'n': // Ignore read errors + ERROR_USAGE_ONLY_ONCE(ro.ignore_read_errors); + ro.ignore_read_errors = true; break; - case 's': - ro.mode = SEARCH_MODE_KEYWORD; - ro.hexstr = false; - ro.widestr = false; - rz_list_append(ro.keywords, (void *)opt.arg); + case 'C': // Ignore case (only used by -s -r -I -S) + ERROR_USAGE_ONLY_ONCE(ro.caseless); + ro.caseless = true; break; - case 'w': - ro.mode = SEARCH_MODE_KEYWORD; - ro.hexstr = false; - ro.widestr = true; - rz_list_append(ro.keywords, (void *)opt.arg); + case 'E': // Forces a specific string encoding (see -h output). + ERROR_USAGE_ONLY_ONCE(ro.encoding != RZ_STRING_ENC_GUESS); + if (!strcmp(opt.arg, "base64") || !strncmp(opt.arg, "guess", 5)) { + // forbid base64 and guess encoding + eprintf("Error: encoding '%s' is unsupported.\n", opt.arg); + return 1; + } + ro.encoding = rz_str_enc_string_as_type(opt.arg); + if (ro.encoding == RZ_STRING_ENC_GUESS) { + eprintf("Error: invalid '%s' encoding.\n", opt.arg); + return 1; + } break; - case 'I': - ro.import = true; - rz_list_append(ro.keywords, (void *)opt.arg); + case 'm': // Search for magic signatures in the whole file", + ERROR_USAGE_INCOMPATIBLE_WITH(ro.mode != RZ_FIND_MODE_NOT_SET, "-A -P -z -r -x -i -s -Z -R -X -I -S"); + ro.mode = RZ_FIND_MODE_MAGIC; break; - case 'S': - ro.symbol = true; - rz_list_append(ro.keywords, (void *)opt.arg); + case 'A': // Search for AES keys in the whole file", + ERROR_USAGE_INCOMPATIBLE_WITH(ro.mode != RZ_FIND_MODE_NOT_SET, "-m -P -z -r -x -i -s -Z -R -X -I -S"); + ro.mode = RZ_FIND_MODE_AES_KEYS; break; - case 'b': - ro.bsize = rz_num_math(NULL, opt.arg); + case 'P': // Search for private RSA/ECC/EdDSA keys in the whole file + ERROR_USAGE_INCOMPATIBLE_WITH(ro.mode != RZ_FIND_MODE_NOT_SET, "-m -A -z -r -x -i -s -Z -R -X -I -S"); + ro.mode = RZ_FIND_MODE_PRIVATE_KEYS; break; - case 'M': - // XXX should be from hexbin - ro.mask = opt.arg; + case 'z': // Search for one or multiple strings in the whole file + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -r -x -i -s -R -X -I -S"); + ro.mode = RZ_FIND_MODE_STRINGS; + if (!rz_find_add_keyword(opt.arg, ro.keywords, rz_find_is_valid_string)) { + return 1; + } break; - case 'f': - ro.from = rz_num_math(NULL, opt.arg); + case 'r': // Search via one or multiple regexes in the whole file + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -z -x -i -s -Z -X -I -S"); + ro.mode = RZ_FIND_MODE_REGEXES; + if (!rz_find_add_keyword(opt.arg, ro.keywords, rz_find_is_valid_regex)) { + return 1; + } break; - case 'F': { - size_t data_size; - char *data = rz_file_slurp(opt.arg, &data_size); - if (!data) { - eprintf("Cannot slurp '%s'\n", opt.arg); + case 'x': // Search for one or multiple hex patterns in the whole file + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -z -r -i -s -Z -R -I -S"); + ro.mode = RZ_FIND_MODE_HEX_PATTERNS; + if (!rz_find_add_keyword(opt.arg, ro.keywords, rz_find_is_valid_hex_pattern)) { return 1; } - char *hexdata = rz_hex_bin2strdup((ut8 *)data, data_size); - if (hexdata) { - ro.mode = SEARCH_MODE_KEYWORD; - ro.hexstr = true; - ro.widestr = false; - rz_list_append(ro.keywords, (void *)hexdata); + break; + case 'i': // Search for one or multiple strings in the import table. + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -z -r -x -s -Z -R -X -S"); + ro.mode = RZ_FIND_MODE_IMPORTS; + if (!rz_find_add_keyword(opt.arg, ro.keywords, rz_find_is_valid_string)) { + return 1; } - free(data); - } break; - case 't': - ro.to = rz_num_math(NULL, opt.arg); break; - case 'x': - ro.mode = SEARCH_MODE_KEYWORD; - ro.hexstr = 1; - ro.widestr = 0; - rz_list_append(ro.keywords, (void *)opt.arg); + case 's': // Search for one or multiple strings in the symbol table. + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -z -r -x -i -Z -R -X -I"); + ro.mode = RZ_FIND_MODE_SYMBOLS; + if (!rz_find_add_keyword(opt.arg, ro.keywords, rz_find_is_valid_string)) { + return 1; + } break; - case 'X': - ro.pr = rz_print_new(); + case 'Z': // Search for strings in the whole file (use file lines as input) + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -r -x -i -s -R -X -I -S"); + ro.mode = RZ_FIND_MODE_STRINGS; + if (rz_find_read_keywords_from_file(opt.arg, ro.keywords, rz_find_is_valid_string)) { + return 1; + } break; - case 'q': - ro.quiet = true; + case 'R': // Search via regex (use file lines as input) + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -z -x -i -s -Z -X -I -S"); + ro.mode = RZ_FIND_MODE_REGEXES; + if (rz_find_read_keywords_from_file(opt.arg, ro.keywords, rz_find_is_valid_regex)) { + return 1; + } break; - case 'v': - return rz_main_version_print("rz-find"); - case 'h': - return show_help(argv[0], 0); - case 'z': - ro.mode = SEARCH_MODE_STRING; + case 'X': // Search for hex patterns (use file lines as input) + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -z -r -i -s -Z -R -I -S"); + ro.mode = RZ_FIND_MODE_HEX_PATTERNS; + if (rz_find_read_keywords_from_file(opt.arg, ro.keywords, rz_find_is_valid_hex_pattern)) { + return 1; + } break; - case 'Z': - ro.showstr = true; + case 'I': // Search for strings in the import table (use file lines as input). + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -z -r -x -s -Z -R -X -S"); + ro.mode = RZ_FIND_MODE_IMPORTS; + if (rz_find_read_keywords_from_file(opt.arg, ro.keywords, rz_find_is_valid_string)) { + return 1; + } break; + case 'S': // Search for strings in the symbol table (use file lines as input). + ERROR_ON_NOT_MODE(RZ_FIND_MODE_NOT_SET, "-m -A -P -z -r -x -i -Z -R -X -I"); + ro.mode = RZ_FIND_MODE_SYMBOLS; + if (rz_find_read_keywords_from_file(opt.arg, ro.keywords, rz_find_is_valid_string)) { + return 1; + } + break; + case 'j': // JSON output (outputs the matching filenames or offsets) + ERROR_USAGE_INCOMPATIBLE_WITH(ro.output != RZ_FIND_OUTPUT_STANDARD, "-q -D"); + ro.output = RZ_FIND_OUTPUT_JSON; + break; + case 'q': // Quiet output (outputs the matching filenames or offsets) + ERROR_USAGE_INCOMPATIBLE_WITH(ro.output != RZ_FIND_OUTPUT_STANDARD, "-j -D"); + ro.output = RZ_FIND_OUTPUT_QUIET; + break; + case 'D': // Hexdump output of the matching region. + ERROR_USAGE_INCOMPATIBLE_WITH(ro.output != RZ_FIND_OUTPUT_STANDARD, "-j -q"); + ro.output = RZ_FIND_OUTPUT_HEXDUMP; + break; + case 'v': // Show version information + return rz_main_version_print("rz-find"); + case 'h': // Show this help + /* fall-thru */ default: - return show_help(argv[0], 1); + return show_help(argv[0], false); } } if (opt.ind == argc) { - return show_help(argv[0], 1); + return show_help(argv[0], true); } - /* Enable quiet mode if searching just a single file */ - if (opt.ind + 1 == argc && RZ_STR_ISNOTEMPTY(argv[opt.ind]) && !rz_file_is_directory(argv[opt.ind])) { - ro.quiet = true; - } - if (ro.json) { - printf("["); + + if (ro.output == RZ_FIND_OUTPUT_JSON) { + ro.pj = pj_new(); + pj_a(ro.pj); // [ } - for (; opt.ind < argc; opt.ind++) { - file = argv[opt.ind]; + for (; opt.ind < argc; opt.ind++) { + const char *file = argv[opt.ind]; if (RZ_STR_ISEMPTY(file)) { - eprintf("Cannot open empty path\n"); - rz_list_free(ro.keywords); - return 1; + continue; } - rzfind_open(&ro, file); + rz_find_open(&ro, file); } - rz_list_free(ro.keywords); - if (ro.json) { - printf("]\n"); + + if (ro.output == RZ_FIND_OUTPUT_JSON) { + pj_end(ro.pj); // ] + printf("%s\n", pj_string(ro.pj)); } + + rz_find_options_fini(&ro); return 0; } diff --git a/librz/util/print.c b/librz/util/print.c index e077000ea5c..79e25c9b15f 100644 --- a/librz/util/print.c +++ b/librz/util/print.c @@ -1457,15 +1457,15 @@ RZ_API RZ_OWN RzStrBuf *rz_print_colorize_asm_str(RZ_BORROW RzPrint *p, const Rz } // Prints a help option with the option/arg strings colorized and aligned to a max length. -RZ_API void rz_print_colored_help_option(const char *option, const char *arg, const char *description, size_t maxOptionAndArgLength) { - size_t optionWidth = strlen(option); - size_t maxSpaces = maxOptionAndArgLength + 2; - printf(Color_GREEN " %-.*s" Color_RESET, (int)optionWidth, option); - size_t remainingSpaces = maxSpaces - optionWidth; +RZ_API void rz_print_colored_help_option(const char *option, const char *arg, const char *description, size_t max_length) { + size_t option_width = strlen(option); + size_t max_spaces = max_length + 2; + printf(Color_GREEN " %-.*s" Color_RESET, (int)option_width, option); + size_t remaining_spaces = max_spaces - option_width; if (RZ_STR_ISNOTEMPTY(arg)) { printf(Color_YELLOW " %-s " Color_RESET, arg); - remainingSpaces -= strlen(arg) + 2; + remaining_spaces -= strlen(arg) + 2; } - printf("%-*.*s", (int)remainingSpaces, (int)remainingSpaces, ""); + printf("%-*.*s", (int)remaining_spaces, (int)remaining_spaces, ""); printf(Color_RESET "%s\n", description); }