diff --git a/librz/core/csearch.c b/librz/core/csearch.c index 6dcb5fa419b..0e68720ce0c 100644 --- a/librz/core/csearch.c +++ b/librz/core/csearch.c @@ -75,7 +75,7 @@ RZ_API bool rz_core_search_preludes(RZ_NONNULL RzCore *core, bool log) { goto fail; } - hits = rz_search_run(core->search_opts, preludes, core->io, boundaries); + hits = rz_search_io(core->search_opts, preludes, core->io, boundaries); if (!hits) { ut64 from = rz_config_get_i(core->config, "search.from"); ut64 to = rz_config_get_i(core->config, "search.to"); @@ -121,7 +121,7 @@ static RZ_OWN RzList /**/ *core_run_search(RzCore *core, RzSearch goto fail; } - hits = rz_search_run(search_opts, collection, core->io, boundaries); + hits = rz_search_io(search_opts, collection, core->io, boundaries); if (!hits) { ut64 from = rz_config_get_i(core->config, "search.from"); ut64 to = rz_config_get_i(core->config, "search.to"); diff --git a/librz/include/rz_search.h b/librz/include/rz_search.h index 6ca8f8c337a..197492cce51 100644 --- a/librz/include/rz_search.h +++ b/librz/include/rz_search.h @@ -71,7 +71,7 @@ RZ_API bool rz_search_collection_match_any(RZ_NULLABLE RzSearchCollection *sc, R RZ_API void rz_search_collection_free(RZ_NULLABLE RzSearchCollection *sc); RZ_API void rz_search_hit_free(RZ_NULLABLE RzSearchHit *hit); -RZ_API RZ_OWN RzList /**/ *rz_search_run(RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzIO *io, RZ_NONNULL RzList /**/ *search_in); +RZ_IPI RZ_OWN RzList /**/ *rz_search_io(RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzIO *io, RZ_NONNULL RzList /**/ *search_in); #ifdef __cplusplus } diff --git a/librz/search/aes_search.c b/librz/search/aes_search.c index 8036dcd5ced..860b9898f30 100644 --- a/librz/search/aes_search.c +++ b/librz/search/aes_search.c @@ -87,5 +87,5 @@ static bool aes_keys_is_empty(void *user) { * \return On success returns a valid pointer, otherwise NULL */ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_aes_keys() { - return rz_search_collection_new(aes_keys_find, aes_keys_is_empty, NULL, NULL); + return rz_search_collection_new_bytes(aes_keys_find, aes_keys_is_empty, NULL, NULL); } diff --git a/librz/search/bytes_search.c b/librz/search/bytes_search.c index c79933c8775..51829170283 100644 --- a/librz/search/bytes_search.c +++ b/librz/search/bytes_search.c @@ -222,7 +222,7 @@ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_bytes() { 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); + return rz_search_collection_new_bytes(bytes_find, bytes_is_empty, (RzSearchFreeCallback)rz_pvector_free, patterns); } /** @@ -289,4 +289,4 @@ RZ_API bool rz_search_collection_bytes_add(RZ_NONNULL RzSearchCollection *col, R return false; } return true; -} \ No newline at end of file +} diff --git a/librz/search/collection.c b/librz/search/collection.c index 329350a4d4f..1cbc7092414 100644 --- a/librz/search/collection.c +++ b/librz/search/collection.c @@ -5,23 +5,14 @@ #include #include "search_internal.h" -/** - * \brief Initialize a new RzSearchCollection - * - * \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 RzSearchFindCallback find, RZ_NONNULL RzSearchIsEmptyCallback is_empty, RZ_NULLABLE RzSearchFreeCallback free, RZ_NULLABLE void *user) { +static RZ_OWN RzSearchCollection *rz_search_collection_new(RzSearchSpace space, RZ_NONNULL void *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->space = space; sc->find = find; sc->is_empty = is_empty; sc->free = free; @@ -29,6 +20,36 @@ RZ_IPI RZ_OWN RzSearchCollection *rz_search_collection_new(RZ_NONNULL RzSearchFi return sc; } +/** + * \brief Initialize a new RzSearchCollection over a graph. + * + * \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_graph(RZ_NONNULL RzSearchFindGraphCallback find, RZ_NONNULL RzSearchIsEmptyCallback is_empty, RZ_NULLABLE RzSearchFreeCallback free, RZ_NULLABLE void *user) { + rz_return_val_if_fail(find && is_empty, NULL); + return rz_search_collection_new(RZ_SEARCH_SPACE_GRAPH, find, is_empty, free, user); +} + +/** + * \brief Initialize a new RzSearchCollection over bytes. + * + * \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_bytes(RZ_NONNULL RzSearchFindBytesCallback find, RZ_NONNULL RzSearchIsEmptyCallback is_empty, RZ_NULLABLE RzSearchFreeCallback free, RZ_NULLABLE void *user) { + rz_return_val_if_fail(find && is_empty, NULL); + return rz_search_collection_new(RZ_SEARCH_SPACE_BYTES, find, is_empty, free, user); +} + /** * \brief Frees a RzSearchCollection structure * @@ -52,7 +73,7 @@ RZ_API void rz_search_collection_free(RZ_NULLABLE RzSearchCollection *sc) { * * \return Returns true when the RzSearchCollection callback matches the expected one. */ -RZ_IPI bool rz_search_collection_has_find_callback(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzSearchFindCallback expected) { +RZ_IPI bool rz_search_collection_has_find_callback(RZ_NONNULL RzSearchCollection *col, RZ_NONNULL void *expected) { rz_return_val_if_fail(col && expected, false); return col->find == expected; } @@ -90,8 +111,13 @@ RZ_API bool rz_search_collection_match_any(RZ_NULLABLE RzSearchCollection *sc, R RZ_LOG_ERROR("search: cannot allocate RzSearchHit queue.\n"); return false; } + if (!rz_search_collection_on_bytes_space(sc)) { + RZ_LOG_ERROR("search: Search collection is not initialized for bytes.\n"); + return false; + } - if (!sc->find(sc->user, 0, buffer, length, hits)) { + RzSearchFindBytesCallback find = sc->find; + if (!find(sc->user, 0, buffer, length, hits)) { RZ_LOG_ERROR("search: failed to run search over collection\n"); return false; } @@ -99,4 +125,4 @@ RZ_API bool rz_search_collection_match_any(RZ_NULLABLE RzSearchCollection *sc, R size_t size = rz_th_queue_size(hits); rz_th_queue_free(hits); return size > 0; -} \ No newline at end of file +} diff --git a/librz/search/magic_search.c b/librz/search/magic_search.c index b71f310df8d..601bdc7c74b 100644 --- a/librz/search/magic_search.c +++ b/librz/search/magic_search.c @@ -52,5 +52,5 @@ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_magic(RZ_NONNULL const ch return NULL; } - return rz_search_collection_new(magic_find, magic_is_empty, (RzSearchFreeCallback)rz_magic_free, magic); + return rz_search_collection_new_bytes(magic_find, magic_is_empty, (RzSearchFreeCallback)rz_magic_free, magic); } diff --git a/librz/search/pkey_search.c b/librz/search/pkey_search.c index 93d7bd8ddce..92abe9c59e0 100644 --- a/librz/search/pkey_search.c +++ b/librz/search/pkey_search.c @@ -147,5 +147,5 @@ static bool pkeys_is_empty(void *user) { * \return On success returns a valid pointer, otherwise NULL */ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_private_keys() { - return rz_search_collection_new(pkeys_find, pkeys_is_empty, NULL, NULL); + return rz_search_collection_new_bytes(pkeys_find, pkeys_is_empty, NULL, NULL); } diff --git a/librz/search/regex_search.c b/librz/search/regex_search.c index d36bcf1f4d6..e23a80b7ca9 100644 --- a/librz/search/regex_search.c +++ b/librz/search/regex_search.c @@ -46,7 +46,7 @@ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_regex() { 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); + return rz_search_collection_new_bytes(regex_find, regex_is_empty, (RzSearchFreeCallback)rz_pvector_free, pvec); } /** diff --git a/librz/search/search.c b/librz/search/search.c index 31c3585cb41..060504a0da0 100644 --- a/librz/search/search.c +++ b/librz/search/search.c @@ -31,7 +31,7 @@ static void *search_cancel_th(void *user) { return NULL; } -static bool search_iterator_cb(void *element, void *user) { +static bool search_iterator_bytes_cb(void *element, void *user) { search_ctx_t *ctx = (search_ctx_t *)user; RzIOMap *map = (RzIOMap *)element; if (!map) { @@ -63,7 +63,9 @@ 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->find(col->user, at, buffer, size, ctx->hits)) { + } + RzSearchFindBytesCallback find = col->find; + if (!find(col->user, at, buffer, size, ctx->hits)) { RZ_LOG_ERROR("search: failed search at 0x%08" PFMT64x "\n", at); break; } @@ -83,13 +85,18 @@ static bool search_iterator_cb(void *element, void *user) { * * \return On success returns all the hits. */ -RZ_API RZ_OWN RzList /**/ *rz_search_run(RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzIO *io, RZ_NONNULL RzList /**/ *search_in) { +RZ_IPI RZ_OWN RzList /**/ *rz_search_io(RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzIO *io, RZ_NONNULL RzList /**/ *search_in) { rz_return_val_if_fail(opt && col && io && search_in, NULL); search_ctx_t ctx = { 0 }; RzList *results = NULL; RzThreadQueue *hits = NULL; RzThread *cancel_th = NULL; + if (!rz_search_collection_on_bytes_space(col)) { + RZ_LOG_ERROR("search: The search collection is not initialized for bytes.\n"); + return NULL; + } + if (opt->buffer_size < RZ_SEARCH_MIN_BUFFER_SIZE) { RZ_LOG_ERROR("search: cannot search when buffer size is less than %u bytes.\n", RZ_SEARCH_MIN_BUFFER_SIZE); return NULL; @@ -126,7 +133,7 @@ RZ_API RZ_OWN RzList /**/ *rz_search_run(RZ_NONNULL RzSearchOpt * } } - if (!rz_th_iterate_list(search_in, search_iterator_cb, opt->max_threads, &ctx)) { + if (!rz_th_iterate_list(search_in, search_iterator_bytes_cb, opt->max_threads, &ctx)) { RZ_LOG_ERROR("search: cannot iterate over list.\n"); } else { results = rz_th_queue_pop_all(hits); diff --git a/librz/search/search_internal.h b/librz/search/search_internal.h index b7042d2e6d4..df3a6cc6f91 100644 --- a/librz/search/search_internal.h +++ b/librz/search/search_internal.h @@ -13,13 +13,56 @@ #define RZ_SEARCH_PRIVATE_KEY_LENGTH 11 #define RZ_SEARCH_MAX_HEX_PATTERN UT16_MAX +/** + * \brief The callback to free the private user data in the RzSearchCollection. + * + * \param user The private user data to free. + */ typedef void (*RzSearchFreeCallback)(void *user); + +/** + * \brief The callback to check if the search collection is considered empty. + * + * \param user The private user data. + */ typedef bool (*RzSearchIsEmptyCallback)(void *user); -typedef bool (*RzSearchFindCallback)(void *user, ut64 address, const ut8 *buffer, size_t size, RzThreadQueue *hits); + +/** + * \brief A callback checking a chunk of bytes if it matches the search criteria. + * + * \param user The private user data. + * \param address The address associated with the given bytes. + * \param buffer The bytes buffer. + * \param size The buffer size in bytes. + * \param The queue to push new hits onto. + * + * \return True, if a match was found. + * \return False otherwise. + */ +typedef bool (*RzSearchFindBytesCallback)(void *user, ut64 address, const ut8 *buffer, size_t size, RZ_OUT RzThreadQueue *hits); + +/** + * \brief A callback to search a graph for a pattern. + * + * \param user The private user data. + * \param graph The graph to search in. + * \param The queue to push new hits onto. + * + * \return True, if a match was found. + * \return False otherwise. + */ +typedef bool (*RzSearchFindGraphCallback)(void *user, const RzGraph *graph, RZ_OUT RzThreadQueue *hits); + +typedef enum { + RZ_SEARCH_SPACE_BYTES = 0, ///< The search is performed on bytes. + RZ_SEARCH_SPACE_GRAPH, ///< The search is performed on a graph. + RZ_SEARCH_SPACE_KB, ///< The search is performed on the knowledge base. +} RzSearchSpace; struct rz_search_collection_t { void *user; ///< Context defined by the various collections - RzSearchFindCallback find; ///< Callback used for finding the data requested by the user + RzSearchSpace space; ///< The search space of this collection. + void *find; ///< Callback to do the search in a given chunk. The callback type depends on \ref rz_search_collection_t.space. RzSearchIsEmptyCallback is_empty; ///< Callback used to check if the collection is empty. RzSearchFreeCallback free; ///< Callback used to free the collection. }; @@ -37,8 +80,11 @@ 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 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 RZ_OWN RzSearchCollection *rz_search_collection_new_bytes(RZ_NONNULL RzSearchFindBytesCallback find, RZ_NONNULL RzSearchIsEmptyCallback is_empty, RZ_NULLABLE RzSearchFreeCallback free, RZ_NULLABLE void *user); +RZ_IPI RZ_OWN RzSearchCollection *rz_search_collection_new_graph(RZ_NONNULL RzSearchFindGraphCallback 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 void *expected); RZ_IPI bool rz_search_collection_is_empty(RZ_NONNULL RzSearchCollection *col); +RZ_IPI static inline bool rz_search_collection_on_bytes_space(RZ_NONNULL RzSearchCollection *col) { return col->space == RZ_SEARCH_SPACE_BYTES; }; +RZ_IPI static inline bool rz_search_collection_on_graph_space(RZ_NONNULL RzSearchCollection *col) { return col->space == RZ_SEARCH_SPACE_GRAPH; }; #endif /* RZ_SEARCH_INTERNAL_H */ diff --git a/librz/search/string_search.c b/librz/search/string_search.c index f96426aad5a..bc21e5720cb 100644 --- a/librz/search/string_search.c +++ b/librz/search/string_search.c @@ -102,7 +102,7 @@ RZ_API RZ_OWN RzSearchCollection *rz_search_collection_strings(RZ_NONNULL RzUtil ss->expected = expected; ss->caseless = caseless; - return rz_search_collection_new(string_find, string_is_empty, string_free, ss); + return rz_search_collection_new_bytes(string_find, string_is_empty, string_free, ss); } static RzDetectedString *string_copy(const char *string) {