Skip to content

Commit

Permalink
Separate the search collections conceptionally by search space.
Browse files Browse the repository at this point in the history
This adds the ability to have search collections operating
on other search spaces than only bytes.
  • Loading branch information
Rot127 committed Dec 10, 2024
1 parent 36ac49d commit c0abf35
Show file tree
Hide file tree
Showing 11 changed files with 111 additions and 32 deletions.
4 changes: 2 additions & 2 deletions librz/core/csearch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -121,7 +121,7 @@ static RZ_OWN RzList /*<RzSearchHit *>*/ *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");
Expand Down
2 changes: 1 addition & 1 deletion librz/include/rz_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 /*<RzSearchHit *>*/ *rz_search_run(RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzIO *io, RZ_NONNULL RzList /*<RzIOMap *>*/ *search_in);
RZ_IPI RZ_OWN RzList /*<RzSearchHit *>*/ *rz_search_io(RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzIO *io, RZ_NONNULL RzList /*<RzIOMap *>*/ *search_in);

#ifdef __cplusplus
}
Expand Down
2 changes: 1 addition & 1 deletion librz/search/aes_search.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
4 changes: 2 additions & 2 deletions librz/search/bytes_search.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -289,4 +289,4 @@ RZ_API bool rz_search_collection_bytes_add(RZ_NONNULL RzSearchCollection *col, R
return false;
}
return true;
}
}
54 changes: 40 additions & 14 deletions librz/search/collection.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,51 @@
#include <rz_search.h>
#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;
sc->user = user;
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
*
Expand All @@ -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;
}
Expand Down Expand Up @@ -90,13 +111,18 @@ 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;
}

size_t size = rz_th_queue_size(hits);
rz_th_queue_free(hits);
return size > 0;
}
}
2 changes: 1 addition & 1 deletion librz/search/magic_search.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
2 changes: 1 addition & 1 deletion librz/search/pkey_search.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
2 changes: 1 addition & 1 deletion librz/search/regex_search.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
15 changes: 11 additions & 4 deletions librz/search/search.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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 /*<RzSearchHit *>*/ *rz_search_run(RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzIO *io, RZ_NONNULL RzList /*<RzIOMap *>*/ *search_in) {
RZ_IPI RZ_OWN RzList /*<RzSearchHit *>*/ *rz_search_io(RZ_NONNULL RzSearchOpt *opt, RZ_NONNULL RzSearchCollection *col, RZ_NONNULL RzIO *io, RZ_NONNULL RzList /*<RzIOMap *>*/ *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;
Expand Down Expand Up @@ -126,7 +133,7 @@ RZ_API RZ_OWN RzList /*<RzSearchHit *>*/ *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);
Expand Down
54 changes: 50 additions & 4 deletions librz/search/search_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
};
Expand All @@ -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 */
2 changes: 1 addition & 1 deletion librz/search/string_search.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down

0 comments on commit c0abf35

Please sign in to comment.