From 9b1253f9a96f367700b903f0231945c689b78f86 Mon Sep 17 00:00:00 2001 From: wargio Date: Tue, 26 Nov 2024 19:41:57 +0800 Subject: [PATCH] Add rz_search_collection_strings --- librz/include/rz_search.h | 3 + librz/include/rz_util/rz_str_search.h | 6 +- librz/main/rz-find.c | 40 ++++--- librz/meson.build | 2 +- librz/search/aes_search.c | 44 ++++---- librz/search/bytes_search.c | 74 +++++++------ librz/search/collection.c | 39 +++---- librz/search/magic_search.c | 62 +++-------- librz/search/meson.build | 5 +- librz/search/pkey_search.c | 80 +++++++------- librz/search/regex_search.c | 20 +++- librz/search/search.c | 2 +- librz/search/search_internal.h | 15 +-- librz/search/string_search.c | 149 ++++++++++++++++++++++++++ 14 files changed, 346 insertions(+), 195 deletions(-) create mode 100644 librz/search/string_search.c diff --git a/librz/include/rz_search.h b/librz/include/rz_search.h index d443056bfc2..4f435bce3b1 100644 --- a/librz/include/rz_search.h +++ b/librz/include/rz_search.h @@ -42,6 +42,9 @@ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_bytes(); RZ_API bool rz_search_collection_bytes_add(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL const char *metadata, RZ_NONNULL const ut8 *bytes, RZ_NULLABLE const ut8 *mask, size_t length); RZ_API bool rz_search_collection_bytes_add_pattern(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL const char *hex_pattern); +RZ_API RZ_OWN RzSearchCollection *rz_search_collection_strings(RZ_NONNULL RzUtilStrScanOptions *opts, RzStrEnc expected, bool caseless); +RZ_API bool rz_search_collection_string_add(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL const char *string); + RZ_API RZ_OWN RzSearchCollection *rz_search_collection_magic(RZ_NONNULL const char *magic_dir); RZ_API bool rz_search_collection_match_any(RZ_NULLABLE RzSearchCollection *sc, RZ_NONNULL const ut8 *buffer, size_t length); diff --git a/librz/include/rz_util/rz_str_search.h b/librz/include/rz_util/rz_str_search.h index 75c0ccc715d..33901e052f6 100644 --- a/librz/include/rz_util/rz_str_search.h +++ b/librz/include/rz_util/rz_str_search.h @@ -14,10 +14,10 @@ extern "C" { * Represent a detected string. */ typedef struct { - char *string; ///< Pointer to the string + char *string; ///< Pointer to the decoded utf-8 string ut64 addr; ///< Address of the string in the RzBuffer - ut32 size; ///< Size of buffer containing the string in bytes - ut32 length; ///< Length of string in chars + ut32 size; ///< Raw size of buffer containing the string in bytes + ut32 length; ///< Decoded length of string in utf-8 chars RzStrEnc type; ///< String type } RzDetectedString; diff --git a/librz/main/rz-find.c b/librz/main/rz-find.c index c9008b1dcba..febb72c5e96 100644 --- a/librz/main/rz-find.c +++ b/librz/main/rz-find.c @@ -15,6 +15,18 @@ #include #include +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; + typedef struct { bool showstr; bool rad; @@ -26,7 +38,7 @@ typedef struct { bool widestr; bool nonstop; bool json; - int mode; + search_mode mode; int align; ut8 *buf; ut64 bsize; @@ -48,7 +60,7 @@ static void rzfind_options_fini(RzfindOptions *ro) { static void rzfind_options_init(RzfindOptions *ro) { memset(ro, 0, sizeof(RzfindOptions)); - ro->mode = RZ_SEARCH_MODE_STRING; + ro->mode = SEARCH_MODE_STRING; ro->bsize = 4096; ro->to = UT64_MAX; ro->keywords = rz_list_newf(NULL); @@ -370,7 +382,7 @@ static int rzfind_open_file(RzfindOptions *ro, const char *file, const ut8 *data io->cb_printf = printf; RzBinFile *bf = rz_bin_open(bin, file, &opt); - if (ro->mode == RZ_SEARCH_MODE_STRING) { + if (ro->mode == SEARCH_MODE_STRING) { PJ *pj = NULL; if (ro->json) { pj = pj_new(); @@ -402,7 +414,7 @@ static int rzfind_open_file(RzfindOptions *ro, const char *file, const ut8 *data goto done; } - if (ro->mode == RZ_SEARCH_MODE_MAGIC) { + 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" @@ -414,14 +426,14 @@ static int rzfind_open_file(RzfindOptions *ro, const char *file, const ut8 *data free(tostr); goto done; } - if (ro->mode == RZ_SEARCH_MODE_ESIL) { + if (ro->mode == SEARCH_MODE_ESIL) { /* TODO: implement using api */ rz_list_foreach (ro->keywords, iter, kw) { rz_sys_cmdf("rizin -qc \"/E %s\" \"%s\"", kw, efile); } goto done; } - if (ro->mode == RZ_SEARCH_MODE_KEYWORD) { + if (ro->mode == SEARCH_MODE_KEYWORD) { rz_list_foreach (ro->keywords, iter, kw) { if (ro->hexstr) { if (ro->mask) { @@ -435,7 +447,7 @@ static int rzfind_open_file(RzfindOptions *ro, const char *file, const ut8 *data rz_search_kw_add(rs, rz_search_keyword_new_str(kw, ro->mask, NULL, 0)); } } - } else if (ro->mode == RZ_SEARCH_MODE_STRING) { + } else if (ro->mode == SEARCH_MODE_STRING) { rz_search_kw_add(rs, rz_search_keyword_new_hexmask("00", NULL)); // XXX } @@ -543,10 +555,10 @@ RZ_API int rz_main_rz_find(int argc, const char **argv) { ro.nonstop = 1; break; case 'm': - ro.mode = RZ_SEARCH_MODE_MAGIC; + ro.mode = SEARCH_MODE_MAGIC; break; case 'e': - ro.mode = RZ_SEARCH_MODE_REGEXP; + ro.mode = SEARCH_MODE_REGEXP; ro.hexstr = 0; rz_list_append(ro.keywords, (void *)opt.arg); break; @@ -555,13 +567,13 @@ RZ_API int rz_main_rz_find(int argc, const char **argv) { ro.exec_command = opt.arg; break; case 's': - ro.mode = RZ_SEARCH_MODE_KEYWORD; + ro.mode = SEARCH_MODE_KEYWORD; ro.hexstr = false; ro.widestr = false; rz_list_append(ro.keywords, (void *)opt.arg); break; case 'w': - ro.mode = RZ_SEARCH_MODE_KEYWORD; + ro.mode = SEARCH_MODE_KEYWORD; ro.hexstr = false; ro.widestr = true; rz_list_append(ro.keywords, (void *)opt.arg); @@ -593,7 +605,7 @@ RZ_API int rz_main_rz_find(int argc, const char **argv) { } char *hexdata = rz_hex_bin2strdup((ut8 *)data, data_size); if (hexdata) { - ro.mode = RZ_SEARCH_MODE_KEYWORD; + ro.mode = SEARCH_MODE_KEYWORD; ro.hexstr = true; ro.widestr = false; rz_list_append(ro.keywords, (void *)hexdata); @@ -604,7 +616,7 @@ RZ_API int rz_main_rz_find(int argc, const char **argv) { ro.to = rz_num_math(NULL, opt.arg); break; case 'x': - ro.mode = RZ_SEARCH_MODE_KEYWORD; + ro.mode = SEARCH_MODE_KEYWORD; ro.hexstr = 1; ro.widestr = 0; rz_list_append(ro.keywords, (void *)opt.arg); @@ -620,7 +632,7 @@ RZ_API int rz_main_rz_find(int argc, const char **argv) { case 'h': return show_help(argv[0], 0); case 'z': - ro.mode = RZ_SEARCH_MODE_STRING; + ro.mode = SEARCH_MODE_STRING; break; case 'Z': ro.showstr = true; diff --git a/librz/meson.build b/librz/meson.build index bc57eb27e58..bf29e954587 100644 --- a/librz/meson.build +++ b/librz/meson.build @@ -14,8 +14,8 @@ subdir('diff') subdir('io') subdir('bp') subdir('syscall') -subdir('search') subdir('magic') +subdir('search') subdir('flag') subdir('reg') subdir('type') diff --git a/librz/search/aes_search.c b/librz/search/aes_search.c index 33f8742a57e..8036dcd5ced 100644 --- a/librz/search/aes_search.c +++ b/librz/search/aes_search.c @@ -46,9 +46,9 @@ static bool aes128_key_test(const ut8 *buf) { return word1 && word2; } -#define SEARCH_OVER_AES_KEY_FCN(name) search_over_aes_##name -#define SEARCH_OVER_AES_KEY(bits) \ - static bool SEARCH_OVER_AES_KEY_FCN(bits)(ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { \ +#define AES_KEY_FIND_FCN(name) aes##name##_key_find_in_buffer +#define AES_KEY_FIND(bits) \ + static bool AES_KEY_FIND_FCN(bits)(ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { \ for (size_t offset = 0; offset < size; offset += AES##bits##_SEARCH_LENGTH) { \ if (aes##bits##_key_test(buffer + offset)) { \ RzSearchHit *hit = rz_search_hit_new("aes", address + offset, AES##bits##_KEY_LENGTH); \ @@ -61,37 +61,31 @@ static bool aes128_key_test(const ut8 *buf) { return true; \ } -SEARCH_OVER_AES_KEY(128) -SEARCH_OVER_AES_KEY(192) -SEARCH_OVER_AES_KEY(256) +AES_KEY_FIND(128) +AES_KEY_FIND(192) +AES_KEY_FIND(256) -bool search_over_aes(RzPVector /**/ *collection, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { - void **it; - rz_pvector_foreach (collection, it) { - SearchAesKey search_key = (SearchAesKey)*it; - if (!search_key(address, buffer, size, hits)) { - return false; - } +static bool aes_keys_find(void *user, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { + if (!AES_KEY_FIND_FCN(128)(address, buffer, size, hits)) { + return false; + } else if (!AES_KEY_FIND_FCN(192)(address, buffer, size, hits)) { + return false; + } else if (!AES_KEY_FIND_FCN(256)(address, buffer, size, hits)) { + return false; } return true; } +static bool aes_keys_is_empty(void *user) { + // we always return false. + return false; +} + /** * \brief Allocates and initialize an AES RzSearchCollection * * \return On success returns a valid pointer, otherwise NULL */ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_aes_keys() { - RzSearchCollection *sc = rz_search_collection_new(search_over_aes, NULL); - if (!sc) { - return NULL; - } - if (!rz_pvector_push(sc->collection, SEARCH_OVER_AES_KEY_FCN(128)) || - !rz_pvector_push(sc->collection, SEARCH_OVER_AES_KEY_FCN(192)) || - !rz_pvector_push(sc->collection, SEARCH_OVER_AES_KEY_FCN(256))) { - RZ_LOG_ERROR("search: failed to initialize AES search collection\n"); - rz_search_collection_free(sc); - return NULL; - } - return sc; + return rz_search_collection_new(aes_keys_find, aes_keys_is_empty, NULL, NULL); } diff --git a/librz/search/bytes_search.c b/librz/search/bytes_search.c index f467bfc1e27..c79933c8775 100644 --- a/librz/search/bytes_search.c +++ b/librz/search/bytes_search.c @@ -9,14 +9,14 @@ #define IS_PATBYTE(c) (IS_HEXCHAR(c) || (c) == '.') -typedef struct hex_pattern { +typedef struct bytes_pattern { const char *metadata; ///< Pattern metadata ut8 *bytes; ///< Pattern bytes ut8 *mask; ///< Pattern mask (when NULL full match) ut32 length; ///< Pattern & mask length -} HexPattern; +} BytesPattern; -static void bytes_pattern_free(HexPattern *hp) { +static void bytes_pattern_free(BytesPattern *hp) { if (!hp) { return; } @@ -54,10 +54,10 @@ static inline ut8 decode_mask(char high, char low) { } // copy from given bytes -static HexPattern *hex_pattern_copy(const char *metadata, const ut8 *bytes, const ut8 *mask, size_t length) { - HexPattern *hp = RZ_NEW0(HexPattern); +static BytesPattern *bytes_pattern_copy(const char *metadata, const ut8 *bytes, const ut8 *mask, size_t length) { + BytesPattern *hp = RZ_NEW0(BytesPattern); if (!hp) { - RZ_LOG_ERROR("search: cannot allocate HexPattern struct\n"); + RZ_LOG_ERROR("search: cannot allocate BytesPattern struct\n"); return NULL; } @@ -88,14 +88,14 @@ static HexPattern *hex_pattern_copy(const char *metadata, const ut8 *bytes, cons return hp; } -static HexPattern *hex_pattern_parse(const char *hex_pattern) { - HexPattern *hp = RZ_NEW0(HexPattern); +static BytesPattern *bytes_pattern_parse(const char *bytes_pattern) { + BytesPattern *hp = RZ_NEW0(BytesPattern); if (!hp) { - RZ_LOG_ERROR("search: cannot allocate HexPattern struct\n"); + RZ_LOG_ERROR("search: cannot allocate BytesPattern struct\n"); return NULL; } - size_t length = strlen(hex_pattern); + size_t length = strlen(bytes_pattern); if (length & 1) { RZ_LOG_ERROR("search: hex pattern length is a multiple of 2\n"); return NULL; @@ -125,8 +125,8 @@ static HexPattern *hex_pattern_parse(const char *hex_pattern) { for (ut32 i = 0; i < hp->length; ++i) { ut32 p = i << 1; - char high = hex_pattern[p]; - char low = hex_pattern[p + 1]; + char high = bytes_pattern[p]; + char low = bytes_pattern[p + 1]; if (!IS_PATBYTE(high)) { RZ_LOG_ERROR("search: invalid hex '%c' at char %u\n", high, p); bytes_pattern_free(hp); @@ -153,7 +153,7 @@ static HexPattern *hex_pattern_parse(const char *hex_pattern) { return hp; } -static bool hex_pattern_compare(HexPattern *hp, const ut8 *buffer, size_t buffer_size) { +static bool bytes_pattern_compare(BytesPattern *hp, const ut8 *buffer, size_t buffer_size) { size_t i = 0; if (buffer_size < hp->length) { return false; @@ -181,17 +181,18 @@ static bool hex_pattern_compare(HexPattern *hp, const ut8 *buffer, size_t buffer return true; } -static bool search_over_bytes(RzPVector /**/ *collection, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { +static bool bytes_find(void *user, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { + RzPVector /**/ *patterns = (RzPVector *)user; void **it = NULL; - HexPattern *hp = NULL; + BytesPattern *hp = NULL; - rz_pvector_foreach (collection, it) { - hp = (HexPattern *)*it; + rz_pvector_foreach (patterns, it) { + hp = (BytesPattern *)*it; for (size_t offset = 0; offset < size;) { size_t leftovers = size - offset; if (hp->length > leftovers) { break; - } else if (!hex_pattern_compare(hp, buffer + offset, leftovers)) { + } else if (!bytes_pattern_compare(hp, buffer + offset, leftovers)) { offset++; continue; } @@ -206,39 +207,48 @@ static bool search_over_bytes(RzPVector /**/ *collection, ut64 add return true; } +static bool bytes_is_empty(void *user) { + return rz_pvector_empty((RzPVector *)user); +} + /** * \brief Allocates and initialize a bytes RzSearchCollection * * \return On success returns a valid pointer, otherwise NULL */ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_bytes() { - return rz_search_collection_new(search_over_bytes, (RzPVectorFree)bytes_pattern_free); + RzPVector /**/ *patterns = rz_pvector_new((RzPVectorFree)bytes_pattern_free); + if (!patterns) { + RZ_LOG_ERROR("search: failed to initialize bytes collection\n"); + return NULL; + } + return rz_search_collection_new(bytes_find, bytes_is_empty, (RzSearchFreeCallback)rz_pvector_free, patterns); } /** * \brief Parses and adds a hex pattern into a bytes RzSearchCollection * * \param[in] col The RzSearchCollection to use - * \param[in] hex_pattern The hexadecimal pattern to add + * \param[in] bytes_pattern The hexadecimal pattern to add * * \return On success returns true, otherwise false */ -RZ_API bool rz_search_collection_bytes_add_pattern(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL const char *hex_pattern) { - rz_return_val_if_fail(col && hex_pattern, false); +RZ_API bool rz_search_collection_bytes_add_pattern(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL const char *bytes_pattern) { + rz_return_val_if_fail(col && bytes_pattern, false); - if (!rz_search_collection_has_callback(col, search_over_bytes)) { - RZ_LOG_ERROR("search: cannot add hex to non-hex collection\n"); + if (!rz_search_collection_has_find_callback(col, bytes_find)) { + RZ_LOG_ERROR("search: cannot add hex to non-bytes collection\n"); return false; - } else if (RZ_STR_ISEMPTY(hex_pattern)) { - RZ_LOG_ERROR("search: cannot add an empty string to a hex collection\n"); + } else if (RZ_STR_ISEMPTY(bytes_pattern)) { + RZ_LOG_ERROR("search: cannot parse an empty string as bytes pattern\n"); return false; } - HexPattern *hp = hex_pattern_parse(hex_pattern); + BytesPattern *hp = bytes_pattern_parse(bytes_pattern); if (!hp) { return false; - } else if (!rz_pvector_push(col->collection, hp)) { - RZ_LOG_ERROR("search: cannot add '%s' hex pattern.\n", hex_pattern); + } else if (!rz_pvector_push((RzPVector *)col->user, hp)) { + RZ_LOG_ERROR("search: cannot add '%s' hex pattern.\n", bytes_pattern); bytes_pattern_free(hp); return false; } @@ -259,7 +269,7 @@ RZ_API bool rz_search_collection_bytes_add_pattern(RZ_NONNULL RzSearchCollection RZ_API bool rz_search_collection_bytes_add(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL const char *metadata, RZ_NONNULL const ut8 *bytes, RZ_NULLABLE const ut8 *mask, size_t length) { rz_return_val_if_fail(col && metadata && bytes, false); - if (!rz_search_collection_has_callback(col, search_over_bytes)) { + if (!rz_search_collection_has_find_callback(col, bytes_find)) { RZ_LOG_ERROR("search: cannot add bytes to non-bytes collection\n"); return false; } else if (length < 1) { @@ -270,10 +280,10 @@ RZ_API bool rz_search_collection_bytes_add(RZ_NONNULL RzSearchCollection *col, R return false; } - HexPattern *hp = hex_pattern_copy(metadata, bytes, mask, length); + BytesPattern *hp = bytes_pattern_copy(metadata, bytes, mask, length); if (!hp) { return false; - } else if (!rz_pvector_push(col->collection, hp)) { + } else if (!rz_pvector_push((RzPVector *)col->user, hp)) { RZ_LOG_ERROR("search: cannot add bytes pattern.\n"); bytes_pattern_free(hp); return false; diff --git a/librz/search/collection.c b/librz/search/collection.c index d2821a8d492..329350a4d4f 100644 --- a/librz/search/collection.c +++ b/librz/search/collection.c @@ -8,25 +8,24 @@ /** * \brief Initialize a new RzSearchCollection * - * \param[in] search_over The search over callback to set - * \param[in] free The free function to set + * \param[in] find The find callback to set + * \param[in] is_empty The callback to use to check if collection is empty + * \param[in] free The callback to use to free the context + * \param user The additional context needed. * * \return On success returns a valid pointer, otherwise NULL. */ -RZ_IPI RZ_OWN RzSearchCollection *rz_search_collection_new(RZ_NONNULL RzSearchOverCallback search_over, RZ_NULLABLE RzPVectorFree free) { - rz_return_val_if_fail(search_over, NULL); +RZ_IPI RZ_OWN RzSearchCollection *rz_search_collection_new(RZ_NONNULL RzSearchFindCallback find, RZ_NONNULL RzSearchIsEmptyCallback is_empty, RZ_NULLABLE RzSearchFreeCallback free, RZ_NULLABLE void *user) { + rz_return_val_if_fail(find && is_empty, NULL); RzSearchCollection *sc = RZ_NEW0(RzSearchCollection); if (!sc) { RZ_LOG_ERROR("search: failed to allocate RzSearchCollection\n"); return NULL; } - sc->search_over = search_over; - sc->collection = rz_pvector_new(free); - if (!sc->collection) { - RZ_LOG_ERROR("search: failed to allocate pvec for RzSearchCollection\n"); - free(sc); - return NULL; - } + sc->find = find; + sc->is_empty = is_empty; + sc->free = free; + sc->user = user; return sc; } @@ -39,21 +38,23 @@ RZ_API void rz_search_collection_free(RZ_NULLABLE RzSearchCollection *sc) { if (!sc) { return; } - rz_pvector_free(sc->collection); + if (sc->free) { + sc->free(sc->user); + } free(sc); } /** - * \brief Checks if a given RzSearchCollection has an expected callback + * \brief Checks if a given RzSearchCollection has an expected find callback * * \param col The RzSearchCollection to test - * \param[in] expected The expected callback + * \param[in] expected The expected find callback * * \return Returns true when the RzSearchCollection callback matches the expected one. */ -RZ_IPI bool rz_search_collection_has_callback(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzSearchOverCallback expected) { +RZ_IPI bool rz_search_collection_has_find_callback(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzSearchFindCallback expected) { rz_return_val_if_fail(col && expected, false); - return col->search_over == expected; + return col->find == expected; } /** @@ -64,8 +65,8 @@ RZ_IPI bool rz_search_collection_has_callback(RZ_NONNULL RzSearchCollection *col * \return Returns true when the RzSearchCollection is empty. */ RZ_IPI bool rz_search_collection_is_empty(RZ_NONNULL RzSearchCollection *col) { - rz_return_val_if_fail(col, false); - return rz_pvector_empty(col->collection); + rz_return_val_if_fail(col && col->is_empty, false); + return col->is_empty(col->user); } /** @@ -90,7 +91,7 @@ RZ_API bool rz_search_collection_match_any(RZ_NULLABLE RzSearchCollection *sc, R return false; } - if (!sc->search_over(sc->collection, 0, buffer, length, hits)) { + if (!sc->find(sc->user, 0, buffer, length, hits)) { RZ_LOG_ERROR("search: failed to run search over collection\n"); return false; } diff --git a/librz/search/magic_search.c b/librz/search/magic_search.c index eb4c5c9cd67..f40b53513c8 100644 --- a/librz/search/magic_search.c +++ b/librz/search/magic_search.c @@ -7,56 +7,27 @@ #include "search_internal.h" -static char *magic_metadata_new(const char *match) { - char *meta = rz_str_newf("magic.%s", match); - if (!meta) { - return NULL; - } - - // filter any non-alphanum - for (size_t i = 0; meta[i]; i++) { - if (!IS_ALPHANUM(meta[i])) { - meta[i] = '.'; - } - } - return meta; +static bool magic_is_empty(void *user) { + // always return false. + return false; } -static bool search_over_magic(RzPVector /**/ *collection, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { - void **it = NULL; - RzMagic *magic = rz_magic_new(0); - if (!magic) { - RZ_LOG_ERROR("search: cannot initialize RzMagic\n"); - return false; - } - - rz_pvector_foreach (collection, it) { - const char *magic_dir = (const char *)*it; - rz_magic_load(magic, magic_dir); - } +static bool magic_find(void *user, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { + RzMagic *magic = (RzMagic *)user; - // there are no-single byte signatures + // there are no single-byte signatures for (size_t i = 0; i < size; i += 2) { const char *match = rz_magic_buffer(magic, buffer + i, size - i); if (!match) { continue; } - char *meta = magic_metadata_new(match); - if (!meta) { - RZ_LOG_ERROR("search: cannot allocate magic metadata string for %s\n", match); - rz_magic_free(magic); - return false; - } - RzSearchHit *hit = rz_search_hit_new(meta, address + i, RZ_SEARCH_MAGIC_BLOCK_ALIGN); - free(meta); + + RzSearchHit *hit = rz_search_hit_new("magic", address + i, 0); if (!hit || !rz_th_queue_push(hits, hit, true)) { rz_search_hit_free(hit); - rz_magic_free(magic); return false; } } - - rz_magic_free(magic); return true; } @@ -68,16 +39,17 @@ static bool search_over_magic(RzPVector /**/ *collection, ut64 address, * \return On success returns a valid pointer, otherwise NULL */ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_magic(RZ_NONNULL const char *magic_dir) { - RzSearchCollection *sc = rz_search_collection_new(search_over_magic, free); - if (!sc) { + if (RZ_STR_ISEMPTY(magic_dir)) { + RZ_LOG_ERROR("search: cannot initialize RzMagic without a valid magic dir\n"); return NULL; } - char *copy = rz_str_dup(magic_dir); - if (!copy || !rz_pvector_push(sc->collection, copy)) { - RZ_LOG_ERROR("search: failed to initialize magic search collection\n"); - free(copy); - rz_search_collection_free(sc); + + RzMagic *magic = rz_magic_new(0); + if (!magic || !rz_magic_load(magic, magic_dir)) { + RZ_LOG_ERROR("search: cannot initialize RzMagic\n"); + rz_magic_free(magic); return NULL; } - return sc; + + return rz_search_collection_new(magic_find, magic_is_empty, (RzSearchFreeCallback)rz_magic_free, magic); } diff --git a/librz/search/meson.build b/librz/search/meson.build index cf4e645d6e3..3563f03894b 100644 --- a/librz/search/meson.build +++ b/librz/search/meson.build @@ -7,11 +7,12 @@ rz_search_sources = [ 'pkey_search.c', 'regex_search.c', 'search.c', + 'string_search.c', ] rz_search = library('rz_search', rz_search_sources, include_directories: [platform_inc], - dependencies: [rz_util_dep, rz_io_dep], + dependencies: [rz_util_dep, rz_io_dep, rz_magic_dep], install: true, implicit_include_directories: false, install_rpath: rpath_lib, @@ -27,5 +28,5 @@ meson.override_dependency('rz_search', rz_search_dep) modules += { 'rz_search': { 'target': rz_search, - 'dependencies': ['rz_util', 'rz_io'] + 'dependencies': ['rz_util', 'rz_io', 'rz_magic'] }} diff --git a/librz/search/pkey_search.c b/librz/search/pkey_search.c index 74b7c9eb511..93d7bd8ddce 100644 --- a/librz/search/pkey_search.c +++ b/librz/search/pkey_search.c @@ -91,65 +91,61 @@ static int check_fields(const ut8 *start) { } // Finds and return index of a private key based on the asn1 bytes -static bool search_over_pkeys(RzPVector /**/ *collection, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { - - void **it; +static bool pkeys_find(void *user, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { + const char *metadata = NULL; // always skip the first 2 bytes since they are the sequence identifier + size for (size_t offset = 2; offset < (size - PRIVKEY_SEARCH_MIN_LENGTH); offset++) { - rz_pvector_foreach (collection, it) { - PKeyMarker *version = (PKeyMarker *)*it; - if (memcmp(buffer + offset, version->marker, sizeof(version->marker))) { - continue; - } + if (!memcmp(buffer + offset, rsa_version.marker, sizeof(rsa_version.marker))) { + metadata = rsa_version.metadata; + } else if (!memcmp(buffer + offset, ecc_version.marker, sizeof(ecc_version.marker))) { + metadata = ecc_version.metadata; + } else if (!memcmp(buffer + offset, safecurves_version.marker, sizeof(safecurves_version.marker))) { + metadata = safecurves_version.metadata; + } else { + continue; + } - size_t key_len = 0; - size_t max = 5; - ssize_t index = -1; - // Going backward maximum up to 5 characters. - if (offset < 5) { - max = offset; - } - for (size_t k = offset - 2; k >= offset - max; k--) { - if (buffer[k] == 0x30) { // The asn1 sequence identifier is 0x30 - index = k; - break; - } + size_t key_len = 0; + size_t max = 5; + ssize_t index = -1; + // Going backward maximum up to 5 characters. + if (offset < 5) { + max = offset; + } + for (size_t k = offset - 2; k >= offset - max; k--) { + if (buffer[k] == 0x30) { // The asn1 sequence identifier is 0x30 + index = k; + break; } + } - if (index == -1) { - continue; - } + if (index == -1) { + continue; + } - if (check_fields(buffer + index)) { - parse_next_field(buffer + index, &key_len); + if (check_fields(buffer + index)) { + parse_next_field(buffer + index, &key_len); - RzSearchHit *hit = rz_search_hit_new(version->metadata, address + index, key_len); - if (!hit || !rz_th_queue_push(hits, hit, true)) { - rz_search_hit_free(hit); - return false; - } + RzSearchHit *hit = rz_search_hit_new(metadata, address + index, key_len); + if (!hit || !rz_th_queue_push(hits, hit, true)) { + rz_search_hit_free(hit); + return false; } } } return true; } +static bool pkeys_is_empty(void *user) { + // we always return false. + return false; +} + /** * \brief Allocates and initialize a pkey RzSearchCollection * * \return On success returns a valid pointer, otherwise NULL */ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_private_keys() { - RzSearchCollection *sc = rz_search_collection_new(search_over_pkeys, NULL); - if (!sc) { - return NULL; - } - if (!rz_pvector_push(sc->collection, &rsa_version) || - !rz_pvector_push(sc->collection, &ecc_version) || - !rz_pvector_push(sc->collection, &safecurves_version)) { - RZ_LOG_ERROR("search: failed to initialize pkey search collection\n"); - rz_search_collection_free(sc); - return NULL; - } - return sc; + return rz_search_collection_new(pkeys_find, pkeys_is_empty, NULL, NULL); } diff --git a/librz/search/regex_search.c b/librz/search/regex_search.c index 2fac39122aa..d36bcf1f4d6 100644 --- a/librz/search/regex_search.c +++ b/librz/search/regex_search.c @@ -7,12 +7,13 @@ #include "search_internal.h" -static bool search_over_regex(RzPVector /**/ *collection, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { +static bool regex_find(void *user, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { void **it_c = NULL; + RzPVector *pvec = (RzPVector *)user; RzPVector *matches = NULL; RzRegex *compiled = NULL; - rz_pvector_foreach (collection, it_c) { + rz_pvector_foreach (pvec, it_c) { compiled = (RzRegex *)*it_c; matches = rz_regex_match_all_not_grouped(compiled, (char *)buffer, size, 0, RZ_REGEX_DEFAULT); void **it_m = NULL; @@ -30,13 +31,22 @@ static bool search_over_regex(RzPVector /**/ *collection, ut64 addres return true; } +static bool regex_is_empty(void *user) { + return rz_pvector_empty((RzPVector *)user); +} + /** * \brief Allocates and initialize a regex RzSearchCollection * * \return On success returns a valid pointer, otherwise NULL */ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_regex() { - return rz_search_collection_new(search_over_regex, rz_regex_free); + RzPVector /**/ *pvec = rz_pvector_new((RzPVectorFree)rz_regex_free); + if (!pvec) { + RZ_LOG_ERROR("search: failed to initialize regex collection\n"); + return NULL; + } + return rz_search_collection_new(regex_find, regex_is_empty, (RzSearchFreeCallback)rz_pvector_free, pvec); } /** @@ -51,7 +61,7 @@ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_regex() { RZ_API bool rz_search_collection_regex_add(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL const char *regex, bool caseless) { rz_return_val_if_fail(col && regex, false); - if (!rz_search_collection_has_callback(col, search_over_regex)) { + if (!rz_search_collection_has_find_callback(col, regex_find)) { RZ_LOG_ERROR("search: cannot add regex to non-regex collection\n"); return false; } else if (RZ_STR_ISEMPTY(regex)) { @@ -68,7 +78,7 @@ RZ_API bool rz_search_collection_regex_add(RZ_NONNULL RzSearchCollection *col, R if (!compiled) { RZ_LOG_ERROR("search: cannot compile '%s' regexp.\n", regex); return false; - } else if (!rz_pvector_push(col->collection, compiled)) { + } else if (!rz_pvector_push((RzPVector *)col->user, compiled)) { RZ_LOG_ERROR("search: cannot add '%s' regexp.\n", regex); rz_regex_free(compiled); return false; diff --git a/librz/search/search.c b/librz/search/search.c index ade6af3ce53..1b5def9de71 100644 --- a/librz/search/search.c +++ b/librz/search/search.c @@ -63,7 +63,7 @@ static bool search_iterator_cb(void *element, void *user) { if (!rz_io_read_at(ctx->io, at, buffer, size)) { RZ_LOG_ERROR("search: failed to read at 0x%08" PFMT64x " (%" PFMTSZu " bytes)\n", at, size); break; - } else if (!col->search_over(col->collection, at, buffer, size, ctx->hits)) { + } else if (!col->find(col->user, at, buffer, size, ctx->hits)) { RZ_LOG_ERROR("search: failed search at 0x%08" PFMT64x "\n", at); break; } diff --git a/librz/search/search_internal.h b/librz/search/search_internal.h index 2d16984ff00..e282c0770f6 100644 --- a/librz/search/search_internal.h +++ b/librz/search/search_internal.h @@ -14,12 +14,15 @@ #define RZ_SEARCH_MIN_BUFFER_SIZE 512u #define RZ_SEARCH_MAX_HEX_PATTERN UT16_MAX -typedef void (*RzSearchFiniCallback)(void *user); -typedef bool (*RzSearchOverCallback)(RzPVector /**/ *collection, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits); +typedef void (*RzSearchFreeCallback)(void *user); +typedef bool (*RzSearchIsEmptyCallback)(void *user); +typedef bool (*RzSearchFindCallback)(void *user, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits); struct rz_search_collection_t { - RzPVector /**/ *collection; ///< Collection of elements to search in a buffer - RzSearchOverCallback search_over; ///< Collection search over the collection callback + void *user; ///< Context defined by the various collections + RzSearchFindCallback find; ///< Callback used for finding the data requested by the user + RzSearchIsEmptyCallback is_empty; ///< Callback used to check if the collection is empty. + RzSearchFreeCallback free; ///< Callback used to free the collection. }; struct rz_search_opt_t { @@ -36,8 +39,8 @@ struct rz_search_opt_t { RZ_IPI RZ_OWN RzSearchHit *rz_search_hit_new(const char *metadata, ut64 address, size_t size); -RZ_IPI RZ_OWN RzSearchCollection *rz_search_collection_new(RZ_NONNULL RzSearchOverCallback search_over, RZ_NULLABLE RzPVectorFree free); -RZ_IPI bool rz_search_collection_has_callback(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzSearchOverCallback expected); +RZ_IPI RZ_OWN RzSearchCollection *rz_search_collection_new(RZ_NONNULL RzSearchFindCallback find, RZ_NONNULL RzSearchIsEmptyCallback is_empty, RZ_NULLABLE RzSearchFreeCallback free, RZ_NULLABLE void *user); +RZ_IPI bool rz_search_collection_has_find_callback(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzSearchFindCallback expected); RZ_IPI bool rz_search_collection_is_empty(RZ_NONNULL RzSearchCollection *col); #endif /* RZ_SEARCH_INTERNAL_H */ diff --git a/librz/search/string_search.c b/librz/search/string_search.c new file mode 100644 index 00000000000..88cab2e0e13 --- /dev/null +++ b/librz/search/string_search.c @@ -0,0 +1,149 @@ +// SPDX-FileCopyrightText: 2024 RizinOrg +// SPDX-FileCopyrightText: 2024 deroad +// SPDX-License-Identifier: LGPL-3.0-only + +#include +#include + +#include "search_internal.h" + +typedef struct string_search { + RzUtilStrScanOptions options; ///< String scan options + RzStrEnc expected; ///< Expected encoding + bool caseless; + RzPVector /**/ *strings; ///< UTF-8 strings to search +} StringSearch; + +static bool string_find(void *user, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits) { + StringSearch *ss = (StringSearch *)user; + RzDetectedString *detected = NULL; + RzListIter *it_s = NULL; + + RzList *found = rz_list_newf((RzListFree)rz_detected_string_free); + if (!found) { + RZ_LOG_ERROR("search: failed to allocate found list for strings collection\n"); + return false; + } + + const ut64 end = address + size; + if (rz_scan_strings_raw(buffer, found, &ss->options, address, end, ss->expected) <= 0) { + rz_list_free(found); + return true; + } + + rz_list_foreach (found, it_s, detected) { + void **it_m = NULL; + rz_pvector_foreach (ss->strings, it_m) { + RzDetectedString *find = *it_m; + if (detected->length < find->length) { + // ignore strings that are smaller than the one we are looking for. + continue; + } + size_t len = RZ_MIN(detected->length, find->length); + if ((ss->caseless && rz_str_ncasecmp(detected->string, find->string, len)) || + (!ss->caseless && strncmp(detected->string, find->string, len))) { + // ignore strings that are not matching till len. + continue; + } + + RzSearchHit *hit = rz_search_hit_new("string", detected->addr, detected->size); + if (!hit || !rz_th_queue_push(hits, hit, true)) { + rz_search_hit_free(hit); + rz_list_free(found); + return false; + } + } + } + + rz_list_free(found); + return true; +} + +static bool string_is_empty(void *user) { + StringSearch *ss = (StringSearch *)user; + return rz_pvector_empty(ss->strings); +} + +static void string_free(void *user) { + if (!user) { + return; + } + StringSearch *ss = (StringSearch *)user; + rz_pvector_free(ss->strings); + free(ss); +} + +/** + * \brief Allocates and initialize a string RzSearchCollection + * + * \param opts The RzUtilStrScanOptions options to use + * \param[in] expected The expected encoding + * \param[in] caseless When true performs a caseless compare + * + * \return On success returns a valid pointer, otherwise NULL + */ +RZ_API RZ_OWN RzSearchCollection *rz_search_collection_strings(RZ_NONNULL RzUtilStrScanOptions *opts, RzStrEnc expected, bool caseless) { + rz_return_val_if_fail(opts, NULL); + + StringSearch *ss = RZ_NEW0(StringSearch); + if (!ss) { + RZ_LOG_ERROR("search: failed to initialize strings collection\n"); + return NULL; + } + + ss->strings = rz_pvector_new((RzPVectorFree)rz_detected_string_free); + if (!ss->strings) { + RZ_LOG_ERROR("search: failed to initialize string collection\n"); + string_free(ss); + return NULL; + } + + ss->options = *opts; // copy the values + ss->expected = expected; + ss->caseless = caseless; + + return rz_search_collection_new(string_find, string_is_empty, string_free, ss); +} + +static RzDetectedString *string_copy(const char *string) { + char *copy = rz_str_dup(string); + if (!copy) { + return NULL; + } + RzDetectedString *ds = RZ_NEW0(RzDetectedString); + if (!ds) { + free(copy); + return NULL; + } + ds->length = strlen(copy); + return ds; +} + +/** + * \brief Adds a new string into a string RzSearchCollection + * + * \param[in] col The RzSearchCollection to use + * \param[in] string The regular expression to add + * + * \return On success returns true, otherwise false + */ +RZ_API bool rz_search_collection_string_add(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL const char *string) { + rz_return_val_if_fail(col && string, false); + + if (!rz_search_collection_has_find_callback(col, string_find)) { + RZ_LOG_ERROR("search: cannot add string to non-string collection\n"); + return false; + } else if (RZ_STR_ISEMPTY(string)) { + RZ_LOG_ERROR("search: cannot add an empty string to a string collection\n"); + return false; + } + StringSearch *ss = (StringSearch *)col->user; + + RzDetectedString *s = string_copy(string); + if (!s || !rz_pvector_push(ss->strings, s)) { + RZ_LOG_ERROR("search: cannot add the string '%s'.\n", string); + rz_detected_string_free(s); + return false; + } + return true; +}