Skip to content

Commit

Permalink
Unfinished reimplementation of /x.
Browse files Browse the repository at this point in the history
  • Loading branch information
Rot127 committed Dec 11, 2024
1 parent 098c69f commit 3b8b76d
Show file tree
Hide file tree
Showing 13 changed files with 699 additions and 160 deletions.
32 changes: 22 additions & 10 deletions librz/core/cconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,16 @@ static bool cb_str_search_max_threads(void *user, void *data) {
return true;
}

static bool cb_search_max_threads(void *user, void *data) {
RzConfigNode *node = (RzConfigNode *)data;
RzThreadNCores max_threads = rz_th_max_threads(node->i_value);
if (node->value[0] == '?') {
rz_cons_printf("%d\n", max_threads);
return false;
}
return true;
}

static bool cb_str_search_min_length(void *user, void *data) {
RzCore *core = (RzCore *)user;
RzConfigNode *node = (RzConfigNode *)data;
Expand Down Expand Up @@ -3757,14 +3767,7 @@ RZ_API int rz_core_config_init(RzCore *core) {
SETOPTIONS(n, "auto", "rosections", "raw", NULL);

/* search */
SETCB("search.contiguous", "true", &cb_contiguous, "Accept contiguous/adjacent search hits");
SETICB("search.align", 0, &cb_searchalign, "Only catch aligned search hits");
SETI("search.chunk", 0, "Chunk size for /+ (default size is asm.bits/8");
SETI("search.esilcombo", 8, "Stop search after N consecutive hits");
SETI("search.distance", 0, "Search string distance");
SETBPREF("search.flags", "true", "All search results are flagged, otherwise only printed");
SETBPREF("search.overlap", "false", "Look for overlapped search hits");
SETI("search.maxhits", 0, "Maximum number of hits (0: no limit)");
SETICB("search.max_threads", RZ_THREAD_N_CORES_ALL_AVAILABLE, &cb_search_max_threads, "Maximum core number. '0' for all cores. '?' to show available.");
SETI("search.from", 0, "Search start address");
SETI("search.to", UT64_MAX, "Search end address");
n = NODECB("search.in", "io.maps", &cb_search_in);
Expand All @@ -3776,10 +3779,19 @@ RZ_API int rz_core_config_init(RzCore *core) {
"dbg.map", "dbg.maps", "dbg.maps.rwx", "dbg.maps.r", "dbg.maps.rw", "dbg.maps.rx", "dbg.maps.wx", "dbg.maps.x",
"analysis.fcn", "analysis.bb",
NULL);
SETICB("search.kwidx", 0, &cb_search_kwidx, "Store last search index count");
SETPREF("search.prefix", "hit", "Prefix name in search hits label");
SETI("search.maxhits", 0, "Maximum number of hits ('0' means no limit)");
SETBPREF("search.show_progress", "true", "Show the search process.");

SETCB("search.contiguous", "true", &cb_contiguous, "Accept contiguous/adjacent search hits");
SETICB("search.align", 0, &cb_searchalign, "Only catch aligned search hits");
SETI("search.chunk", 0, "Chunk size for /+ (default size is asm.bits/8");
SETI("search.esilcombo", 8, "Stop search after N consecutive hits");
SETI("search.distance", 0, "Search string distance");
SETBPREF("search.flags", "true", "All search results are flagged, otherwise only printed");
SETBPREF("search.overlap", "false", "Look for overlapped search hits");
SETICB("search.kwidx", 0, &cb_search_kwidx, "Store last search index count");
SETBPREF("search.show", "true", "Show search results");
SETI("search.to", -1, "Search end address");
n = NODECB("search.case_sensitive", "smart", &cb_search_case_sensitive);
SETDESC(n, "Set grep(~) as case smart/sensitive/insensitive");
SETOPTIONS(n, "smart", "sensitive", "insensitive", NULL);
Expand Down
51 changes: 50 additions & 1 deletion librz/core/cmd/cmd_search.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <rz_core.h>
#include <rz_io.h>
#include <rz_list.h>
#include <rz_search.h>
#include <rz_types_base.h>
#include "../core_private.h"

Expand Down Expand Up @@ -1726,6 +1727,32 @@ static void __core_cmd_search_asm_byteswap(RzCore *core, int nth) {

RZ_IPI int rz_cmd_search(void *data, const char *input) { return RZ_CMD_STATUS_ERROR; }

// New search

#define CMD_SEARCH_BEGIN() \
if (core->in_search) { \
RZ_LOG_ERROR("core: recursive search is forbidden.\n"); \
return RZ_CMD_STATUS_ERROR; \
} \
RzSearchOpt *search_opts = rz_search_opt_new(); \
bool opt_applid = rz_search_opt_set_max_hits(search_opts, rz_config_get_i(core->config, "search.maxhits")); \
opt_applid &= rz_search_opt_set_max_threads(search_opts, rz_th_max_threads(rz_config_get_i(core->config, "search.max_threads"))); \
core->in_search = true;

#define CMD_SEARCH_END() \
do { \
rz_search_opt_free(search_opts); \
core->in_search = false; \
} while (0)

static bool cmd_search_progress_cancel(void *user, size_t n_hits, RzSearchCancelReason invoke_reason) {
if (user) {
// we have RzCmdStateOutput state
rz_cons_printf("Searching... hits: %" PFMTSZu "\r", n_hits);
}
return rz_cons_is_breaked();
}

// "/a"
RZ_IPI RzCmdStatus rz_cmd_search_assemble_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
return RZ_CMD_STATUS_NONEXISTINGCMD;
Expand Down Expand Up @@ -1803,7 +1830,29 @@ RZ_IPI RzCmdStatus rz_cmd_search_value_64_handler(RzCore *core, int argc, const

// "/x"
RZ_IPI RzCmdStatus rz_cmd_search_hex_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
return RZ_CMD_STATUS_NONEXISTINGCMD;
CMD_SEARCH_BEGIN();

ut32 buf_size = 0;
ut8 *bytes = NULL;
ut8 *mask = NULL;

bool progress = rz_config_get_b(core->config, "search.show_progress");
opt_applid &= rz_search_opt_set_inverse_match(search_opts, false);
opt_applid &= rz_search_opt_set_buffer_size(search_opts, buf_size);
opt_applid &= rz_search_opt_set_cancel_cb(search_opts, cmd_search_progress_cancel, progress ? state : NULL);
if (!opt_applid) {
RZ_LOG_ERROR("code: Failed to setup default search options.\n");
CMD_SEARCH_END();
return RZ_CMD_STATUS_ERROR;
}
RzList *hits = rz_core_search_bytes(core, search_opts, bytes, mask, buf_size);
if (!hits) {
RZ_LOG_ERROR("Failed to perform search.\n");
CMD_SEARCH_END();
return RZ_CMD_STATUS_ERROR;
}
CMD_SEARCH_END();
return RZ_CMD_STATUS_OK;
}

// "/z"
Expand Down
13 changes: 7 additions & 6 deletions librz/core/cmd_descs/cmd_search.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -301,18 +301,19 @@ commands:
- RZ_OUTPUT_MODE_QUIET
- RZ_OUTPUT_MODE_TABLE
args:
- name: bytes
- name: pattern
type: RZ_CMD_ARG_TYPE_STRING
details:
- name: Usage example
entries:
- text: "Hexadecimal search of the exact bytes"
- text: "Hexadecimal search for the exact bytes 'ffcc33'."
comment: "/x ffcc33"
- text: "Hexadecimal search of the bytes with ignored nibbles"
comment: "/x ff..33"
- text: "Hexadecimal search of the bytes with bytes mask"
- text: "Hexadecimal search for the byte pattern 'ff..33.0.'. The '.' is a wildcard for 4bits."
comment: "/x ff..33.0"
- text: "Hexadecimal search of the bytes with mask. Pattern: '<mask>:<resulting bytes>'"
comment: "/x ff43:ffd0"

- text: "Hexadecimal search of the bytes with bytes mask and wildcard. Pattern: '<mask>:<resulting bytes>'."
comment: "/x ff43:f..0"
- name: "/z"
summary: String search.
subcommands:
Expand Down
128 changes: 128 additions & 0 deletions librz/core/csearch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-FileCopyrightText: 2024 deroad <[email protected]>
// SPDX-FileCopyrightText: 2024 Rot127 <[email protected]>
// SPDX-License-Identifier: LGPL-3.0-only

#include "rz_util/rz_log.h"
#include <rz_core.h>
#include <rz_search.h>
#include <rz_util/rz_assert.h>
#include <rz_util/rz_str_search.h>

/**
* \brief Sets up the search parameters according to the core IO layer and config.
*
* \param core The core to get the IO maps, settings and other relevant information from.
* \param search_opts Search options to set up. Only fields to search behavior will be set (max_threads, max hits). Can be NULL.
*
* \return The boundaries to search in. Or NULL in case of failure.
*/
RZ_API RZ_OWN RzList /*<RzIOMap *>*/ *rz_core_setup_io_search_parameters(RzCore *core, RZ_NULLABLE RZ_OUT RzSearchOpt *search_opts) {
rz_return_val_if_fail(core && core->io && core->config, NULL);
RzList *boundaries = NULL;
const char *search_prefix = NULL;

if (!core->io) {
RZ_LOG_ERROR("core: RzIO is not available.\n");
return NULL;
}
search_prefix = rz_config_get(core->config, "search.prefix");
if (RZ_STR_ISEMPTY(search_prefix)) {
// ensure thre prefix is always set.
search_prefix = "hit";
}

boundaries = rz_core_get_boundaries_select(core, "search.from", "search.to", "search.in");
if (!boundaries || rz_list_empty(boundaries)) {
ut64 from = rz_config_get_i(core->config, "search.from");
ut64 to = rz_config_get_i(core->config, "search.to");
RZ_LOG_ERROR("core: Failed to get search boundaries within [0x%" PFMT64x ", 0x%" PFMT64x "].\n", from, to);
goto fail;
}

if (search_opts) {
// Set search options known by core.
ut32 max_threads = rz_th_max_threads(rz_config_get_i(core->config, "search.max_threads"));
if (!rz_search_opt_set_max_threads(search_opts, max_threads)) {
RZ_LOG_ERROR("core: Failed to set 'max_threads' search option.\n");
goto fail;
}

ut32 max_hits = rz_config_get_i(core->config, "search.maxhits");
if (!rz_search_opt_set_max_hits(search_opts, max_hits)) {
RZ_LOG_ERROR("core: Failed to set 'max_hits' search option.\n");
goto fail;
}
}

return boundaries;
fail:
rz_list_free(boundaries);
return NULL;
}

static bool default_search_no_cancel(void *user, size_t n_hits, RzSearchCancelReason invoke_reason) {
return rz_cons_is_breaked();
}

/**
* \brief Finds a byte array in the IO layer of the given core and core configuration.
*
* \param core The RzCore core.
* \param opt The search options to apply. If it is NULL a default set of options is used.
* \param[in] bytes The bytes to search.
* \param[in] mask The mask to apply to the bytes before comparison (can be NULL for exact match).
* \param[in] size Size of array \p bytes and \p mask (if not NULL).
*
* \return On success returns a valid pointer, otherwise NULL
*/
RZ_API RZ_OWN RzList /*<RzSearchHit *>*/ *rz_core_search_bytes(RZ_NONNULL RzCore *core, RZ_BORROW RZ_NULLABLE RzSearchOpt *user_opts, RZ_NONNULL const ut8 *bytes, RZ_NULLABLE const ut8 *mask, size_t size) {
rz_return_val_if_fail(core && core->config && bytes, NULL);
if (size < 1) {
RZ_LOG_ERROR("core: Cannot search for bytes if 'size' < 1.\n");
return NULL;
}

RzList *hits = NULL;
RzList *boundaries = NULL;
RzSearchOpt *search_opts = NULL;

RzSearchCollection *collection = rz_search_collection_bytes();
if (!collection ||
!rz_search_collection_bytes_add(collection, "bytes", bytes, mask, size)) {
RZ_LOG_ERROR("core: Failed to initialize search collection.\n");
goto quit;
}

if (!user_opts) {
// Use default search options for byte search.
search_opts = rz_search_opt_new();
bool opt_applid = rz_search_opt_set_inverse_match(search_opts, false);
opt_applid &= rz_search_opt_set_buffer_size(search_opts, size);
opt_applid &= rz_search_opt_set_cancel_cb(search_opts, default_search_no_cancel, NULL);
if (!opt_applid) {
RZ_LOG_ERROR("code: Failed to setup default search options.\n");
goto quit;
}
}

// Don't pass the search options.
// They were set up by the user and we respect them.
boundaries = rz_core_setup_io_search_parameters(core, user_opts ? NULL : search_opts);
if (!boundaries) {
RZ_LOG_ERROR("core: Setting up search from core failed.\n");
goto quit;
}

hits = rz_search_on_io(user_opts ? user_opts : 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");
RZ_LOG_ERROR("core: Failed to search within [0x%" PFMT64x ", 0x%" PFMT64x "].\n", from, to);
}

quit:
rz_list_free(boundaries);
rz_search_opt_free(search_opts);
rz_search_collection_free(collection);
return hits;
}
5 changes: 4 additions & 1 deletion librz/include/rz_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,6 @@ struct rz_core_t {
RzFlag *flags;
char *lastsearch; ///< Legacy search. Will be removed
RzSearch *search; ///< Legacy search. Will be removed
RzSearchOpt *search_opts;
RzEgg *egg;
RzCrypto *crypto;
RzAGraph *graph;
Expand Down Expand Up @@ -1349,6 +1348,10 @@ RZ_API void rz_core_analysis_bytes_il(RZ_NONNULL RzCore *core, ut64 len, ut64 nu
RZ_API bool rz_core_disasm_until_ret(RZ_NONNULL RzCore *core, ut64 addr, int limit, RzOutputMode mode,
bool ret_val, RZ_NULLABLE RZ_OUT RzStrBuf *buf);

RZ_API RZ_OWN RzList /*<RzIOMap *>*/ *rz_core_setup_io_search_parameters(RzCore *core, RZ_OUT RzSearchOpt *search_opts);

RZ_API RZ_OWN RzList /*<RzSearchHit *>*/ *rz_core_search_bytes(RZ_NONNULL RzCore *core, RZ_BORROW RZ_NULLABLE RzSearchOpt *opt, RZ_NONNULL const ut8 *bytes, RZ_NULLABLE const ut8 *mask, size_t size);

#endif

#ifdef __cplusplus
Expand Down
5 changes: 4 additions & 1 deletion librz/include/rz_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ RZ_LIB_VERSION_HEADER(rz_search);
#define RZ_SEARCH_MIN_BUFFER_SIZE 512u
#define RZ_SEARCH_CANCEL_CHECK_INTERVAL_USEC 1000 * 1000

/**
* \brief Private search options for the search module. Use the rz_search_opt_*() functions to edit it.
*/
typedef struct rz_search_opt_t RzSearchOpt;

typedef struct rz_search_collection_t RzSearchCollection;
Expand Down Expand Up @@ -188,7 +191,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_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_IPI RZ_OWN RzList /*<RzSearchHit *>*/ *rz_search_on_io(RZ_BORROW RZ_NONNULL RzSearchOpt *opt, RZ_BORROW RZ_NONNULL RzSearchCollection *col, RZ_BORROW RZ_NONNULL RzIO *io, RZ_BORROW RZ_NONNULL RzList /*<RzIOMap *>*/ *search_in);

#ifdef __cplusplus
}
Expand Down
Loading

0 comments on commit 3b8b76d

Please sign in to comment.