RzAnalysis: Generalize ROP core search to Gadget for future JOP/COP support (#6130)

This commit is contained in:
MrQuantum1915 2026-04-14 10:53:08 +05:30 committed by GitHub
parent 02f3ae94d2
commit 4e5a8e4826
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 972 additions and 721 deletions

View file

@ -164,8 +164,8 @@ RZ_API RzAnalysis *rz_analysis_new(RZ_NULLABLE const char *sdb_types_path) {
}
}
analysis->ht_global_var = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_analysis_var_global_free);
analysis->ht_rop_semantics = NULL;
analysis->ht_rop = NULL;
analysis->ht_gadget_semantics = NULL;
analysis->ht_gadget = NULL;
analysis->global_var_tree = NULL;
analysis->il_vm = NULL;
analysis->hash = rz_hash_new();
@ -220,7 +220,7 @@ RZ_API void rz_analysis_free(RZ_NULLABLE RzAnalysis *a) {
rz_list_free(a->imports);
rz_str_constpool_fini(&a->constpool);
ht_sp_free(a->ht_global_var);
ht_up_free(a->ht_rop_semantics);
ht_up_free(a->ht_gadget_semantics);
ht_sp_free(a->plugins);
rz_analysis_debug_info_free(a->debug_info);
ht_sp_free(a->ht_virtual_xrefs);
@ -513,14 +513,14 @@ RZ_API void rz_analysis_set_xrefs_to(RZ_NONNULL RzAnalysis *analysis, HtUP *xref
analysis->ht_xrefs_to = xrefs_to;
}
RZ_API RZ_BORROW HtUP *rz_analysis_get_rop_semantics(RZ_NONNULL RzAnalysis *analysis) {
RZ_API RZ_BORROW HtUP *rz_analysis_get_gadget_semantics(RZ_NONNULL RzAnalysis *analysis) {
rz_return_val_if_fail(analysis, NULL);
return analysis->ht_rop_semantics;
return analysis->ht_gadget_semantics;
}
RZ_API void rz_analysis_set_rop_semantics(RZ_NONNULL RzAnalysis *analysis, HtUP *rop_semantics) {
RZ_API void rz_analysis_set_gadget_semantics(RZ_NONNULL RzAnalysis *analysis, HtUP *gadget_semantics) {
rz_return_if_fail(analysis);
analysis->ht_rop_semantics = rop_semantics;
analysis->ht_gadget_semantics = gadget_semantics;
}
RZ_API RZ_BORROW RzAnalysisCallbacks *rz_analysis_get_callbacks(RZ_NONNULL RzAnalysis *analysis) {

View file

@ -87,8 +87,8 @@ struct rz_analysis_t {
RzPlatformTarget *arch_target;
RzPlatformTargetIndex *platform_target;
HtSP *ht_global_var; // global variables
HtUP *ht_rop_semantics; ///< cache rop gadget semantic information
HtUP *ht_rop; ///< cache rop gadget address list
HtUP *ht_gadget_semantics; ///< cache gadget semantic information
HtUP *ht_gadget; ///< cache gadget address list
RBTree global_var_tree; // global variables by address. must not overlap
RzHash *hash;
RzAnalysisDebugInfo *debug_info; ///< store all debug info parsed from DWARF, etc..

View file

@ -3632,12 +3632,12 @@ RZ_API int rz_core_config_init(RzCore *core) {
SETDESC(n, "Set grep(~) as case smart/sensitive/insensitive");
SETOPTIONS(n, "smart", "sensitive", "insensitive", NULL);
/* rop */
SETI("rop.len", 5, "Maximum ROP gadget length");
SETBPREF("rop.cache", "false", "Cache rop gadget results(experimental)");
SETBPREF("rop.subchains", "false", "Display every length gadget from rop.len=X to 2 in /Rl");
SETBPREF("rop.conditional", "false", "Include conditional jump, calls and returns in ropsearch");
SETBPREF("rop.comments", "false", "Display comments in rop search output");
/* gadget */
SETI("gadget.len", 5, "Maximum number of instructions per gadget");
SETBPREF("gadget.cache", "false", "Cache gadget results(experimental)");
SETBPREF("gadget.subchains", "false", "Display every length gadget from gadget.len=X to 2");
SETBPREF("gadget.conditional", "false", "Include conditional jump, calls and returns in gadget search");
SETBPREF("gadget.comments", "false", "Display comments in gadget search output");
/* io */
SETCB("io.cache", "false", &cb_io_cache, "Change both of io.cache.{read,write}");

View file

@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2026 MrQuantum1915 <darshanpatelgdh@gmail.com>
// SPDX-FileCopyrightText: 2010-2021 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
@ -9,7 +10,7 @@
#include <rz_search.h>
#include <rz_types_base.h>
#include "cmd_search_rop.c"
#include "cmd_search_gadget.c"
#include "rz_cons.h"
#include <rz_config.h>
#include <rz_flag.h>
@ -44,19 +45,34 @@ struct search_parameters {
bool regex_search;
};
static RzGadgetType gadget_type_from_cmd(const char *cmd_name) {
rz_return_val_if_fail(cmd_name && cmd_name[0] && cmd_name[1], RZ_GADGET_TYPE_ROP);
switch (cmd_name[1]) {
case 'J':
return RZ_GADGET_TYPE_JOP;
case 'C':
return RZ_GADGET_TYPE_COP;
case 'R':
default:
return RZ_GADGET_TYPE_ROP;
}
}
RZ_IPI RzCmdStatus rz_cmd_info_gadget_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
const char *input = argc > 1 ? argv[1] : "";
if (!input) {
return RZ_CMD_STATUS_ERROR;
}
RzRopSearchContext *context = rz_core_rop_search_context_new(core, argv[1], false, RZ_ROP_GADGET_PRINT, RZ_ROP_DETAIL_SEARCH_NON, state);
RzCmdStatus status = rz_core_rop_gadget_info(core, context);
RzGadgetType gadget_type = gadget_type_from_cmd(argv[0]);
RzGadgetSearchContext *context = rz_core_gadget_search_context_new(core, gadget_type, input, false, RZ_GADGET_PRINT, RZ_GADGET_DETAIL_SEARCH_NON, state);
RzCmdStatus status = rz_core_gadget_info(core, context);
return status;
}
RZ_IPI RzCmdStatus rz_cmd_query_gadget_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzPVector /*<RzRopConstraint *>*/ *constraints = rz_core_rop_constraint_map_parse(core, argc, argv);
RzPVector /*<RzGadgetConstraint *>*/ *constraints = rz_core_gadget_constraint_map_parse(core, argc, argv);
if (!constraints) {
return RZ_CMD_STATUS_ERROR;
}
@ -65,15 +81,17 @@ RZ_IPI RzCmdStatus rz_cmd_query_gadget_handler(RzCore *core, int argc, const cha
return RZ_CMD_STATUS_INVALID;
}
RzRopSearchContext *context = rz_core_rop_search_context_new(core, NULL, false,
RZ_ROP_GADGET_PRINT | RZ_ROP_GADGET_ANALYZE, RZ_ROP_DETAIL_SEARCH_NON, state);
RzGadgetType gadget_type = gadget_type_from_cmd(argv[0]);
RzGadgetSearchContext *context = rz_core_gadget_search_context_new(core, gadget_type, NULL, false,
RZ_GADGET_PRINT | RZ_GADGET_ANALYZE, RZ_GADGET_DETAIL_SEARCH_NON, state);
if (!context) {
rz_pvector_free(constraints);
return RZ_CMD_STATUS_ERROR;
}
context->constraints = constraints;
const RzCmdStatus cmd_status = rz_core_rop_search(core, context);
rz_core_rop_search_context_free(context);
const RzCmdStatus cmd_status = rz_core_gadget_search(core, context);
rz_core_gadget_search_context_free(context);
return cmd_status;
}
@ -82,29 +100,39 @@ RZ_IPI RzCmdStatus rz_cmd_search_gadget_handler(RzCore *core, int argc, const ch
if (!input) {
return RZ_CMD_STATUS_ERROR;
}
RzRopSearchContext *context = rz_core_rop_search_context_new(core, input, true, RZ_ROP_GADGET_PRINT, RZ_ROP_DETAIL_SEARCH_NON, state);
RzCmdStatus status = rz_core_rop_search(core, context);
rz_core_rop_search_context_free(context);
RzGadgetType gadget_type = gadget_type_from_cmd(argv[0]);
RzGadgetSearchContext *context = rz_core_gadget_search_context_new(core, gadget_type, input, true, RZ_GADGET_PRINT, RZ_GADGET_DETAIL_SEARCH_NON, state);
RzCmdStatus status = rz_core_gadget_search(core, context);
rz_core_gadget_search_context_free(context);
return status;
}
RZ_IPI RzCmdStatus rz_cmd_detail_gadget_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
const char *input = argc > 1 ? argv[1] : "";
RzRopSearchContext *context = rz_core_rop_search_context_new(core, input, false, RZ_ROP_GADGET_PRINT_DETAIL | RZ_ROP_GADGET_ANALYZE, RZ_ROP_DETAIL_SEARCH_NON, state);
RzCmdStatus status = rz_core_rop_search(core, context);
rz_core_rop_search_context_free(context);
RzGadgetType gadget_type = gadget_type_from_cmd(argv[0]);
RzGadgetSearchContext *context = rz_core_gadget_search_context_new(core, gadget_type, input, false, RZ_GADGET_PRINT_DETAIL | RZ_GADGET_ANALYZE, RZ_GADGET_DETAIL_SEARCH_NON, state);
RzCmdStatus status = rz_core_gadget_search(core, context);
rz_core_gadget_search_context_free(context);
return status;
}
RZ_IPI RzCmdStatus rz_cmd_rop_search_stack_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzRopSearchContext *context = rz_core_rop_search_context_new(core, argv[1], false, RZ_ROP_GADGET_PRINT_DETAIL | RZ_ROP_GADGET_ANALYZE, RZ_ROP_DETAIL_SEARCH_STACK, state);
RzCmdStatus status = rz_core_rop_gadget_info(core, context);
RzGadgetType gadget_type = RZ_GADGET_TYPE_ROP;
RzGadgetSearchContext *context = rz_core_gadget_search_context_new(core, gadget_type, argv[1], false, RZ_GADGET_PRINT_DETAIL | RZ_GADGET_ANALYZE, RZ_GADGET_DETAIL_SEARCH_STACK, state);
RzCmdStatus status = rz_core_gadget_info(core, context);
return status;
}
RZ_IPI RzCmdStatus rz_cmd_rop_search_size_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzRopSearchContext *context = rz_core_rop_search_context_new(core, argv[1], false, RZ_ROP_GADGET_PRINT_DETAIL | RZ_ROP_GADGET_ANALYZE, RZ_ROP_DETAIL_SEARCH_SIZE, state);
RzCmdStatus status = rz_core_rop_gadget_info(core, context);
RZ_IPI RzCmdStatus rz_cmd_gadget_search_size_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state) {
RzGadgetType gadget_type = gadget_type_from_cmd(argv[0]);
RzGadgetSearchContext *context = rz_core_gadget_search_context_new(core, gadget_type, argv[1], false, RZ_GADGET_PRINT_DETAIL | RZ_GADGET_ANALYZE, RZ_GADGET_DETAIL_SEARCH_SIZE, state);
RzCmdStatus status = rz_core_gadget_info(core, context);
return status;
}

View file

@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2026 MrQuantum1915 <darshanpatelgdh@gmail.com>
// SPDX-FileCopyrightText: 2024 z3phyr <giridh1337@gmail.com>
// SPDX-FileCopyrightText: 2009-2016 Alexandru Caciulescu <alex.darredevil@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
@ -7,7 +8,7 @@
#include "rz_core.h"
#include "rz_list.h"
#include "rz_types_base.h"
#include "rz_rop.h"
#include "rz_gadget.h"
static void skip_whitespace(const char *str, ut64 *idx) {
if (*idx >= strlen(str)) {
@ -194,8 +195,8 @@ static bool parse_il_op(const char *str, ut64 *idx, bool *is_compound_op, RzILOp
return false;
}
static bool rop_constraint_set_regs(RzRopConstraint *rc,
RzRopILInstructionType il_type,
static bool gadget_constraint_set_regs(RzGadgetConstraint *rc,
RzGadgetILInstructionType il_type,
RZ_NONNULL const RzRegItem *dst,
RZ_NONNULL const RzRegItem *src0,
RZ_NULLABLE const RzRegItem *src1) {
@ -213,7 +214,7 @@ static bool rop_constraint_set_regs(RzRopConstraint *rc,
return true;
}
static bool rop_constraint_set_op(RzRopConstraint *rc, RzILOpPureCode op) {
static bool gadget_constraint_set_op(RzGadgetConstraint *rc, RzILOpPureCode op) {
if (op >= RZ_IL_OP_PURE_MAX) {
return false;
}
@ -226,8 +227,8 @@ static bool rop_constraint_set_op(RzRopConstraint *rc, RzILOpPureCode op) {
return true;
}
static bool rop_constraint_set_const(RzRopConstraint *rc,
RzRopILInstructionType il_type,
static bool gadget_constraint_set_const(RzGadgetConstraint *rc,
RzGadgetILInstructionType il_type,
RZ_NONNULL const RzRegItem *dst,
RZ_NULLABLE const RzRegItem *src0,
ut64 const_value) {
@ -245,7 +246,7 @@ static bool rop_constraint_set_const(RzRopConstraint *rc,
return true;
}
static bool parse_compound_op(const RzCore *core, const char *str, RzRopConstraint *rc) {
static bool parse_compound_op(const RzCore *core, const char *str, RzGadgetConstraint *gadget_constraint) {
ut64 idx = 0;
ut64 const_value = 0;
bool inc_dec = false;
@ -284,14 +285,14 @@ static bool parse_compound_op(const RzCore *core, const char *str, RzRopConstrai
if (constant_status && is_compound_op) {
// dst = dst (math op) num
return rop_constraint_set_const(rc, MOV_OP_CONST, dst_reg, dst_reg, const_value) &&
rop_constraint_set_op(rc, op);
return gadget_constraint_set_const(gadget_constraint, MOV_OP_CONST, dst_reg, dst_reg, const_value) &&
gadget_constraint_set_op(gadget_constraint, op);
}
if (src_reg && is_compound_op) {
// dst = dst (math op) src
return rop_constraint_set_regs(rc, MOV_OP_REG, dst_reg, dst_reg, src_reg) &&
rop_constraint_set_op(rc, op);
return gadget_constraint_set_regs(gadget_constraint, MOV_OP_REG, dst_reg, dst_reg, src_reg) &&
gadget_constraint_set_op(gadget_constraint, op);
}
if (!inc_dec) {
@ -301,11 +302,11 @@ static bool parse_compound_op(const RzCore *core, const char *str, RzRopConstrai
const_value = 1;
// dst (math op)= 1
return rop_constraint_set_const(rc, MOV_OP_CONST, dst_reg, dst_reg, const_value) &&
rop_constraint_set_op(rc, op);
return gadget_constraint_set_const(gadget_constraint, MOV_OP_CONST, dst_reg, dst_reg, const_value) &&
gadget_constraint_set_op(gadget_constraint, op);
}
static bool parse_reg_to_const(const RzCore *core, const char *str, RzRopConstraint *rc) {
static bool parse_reg_to_const(const RzCore *core, const char *str, RzGadgetConstraint *gadget_constraint) {
ut64 idx = 0;
ut64 const_value = 0;
const RzRegItem *dst_reg = parse_register(core, str, &idx);
@ -317,10 +318,10 @@ static bool parse_reg_to_const(const RzCore *core, const char *str, RzRopConstra
return false;
}
return rop_constraint_set_const(rc, MOV_CONST, dst_reg, NULL, const_value);
return gadget_constraint_set_const(gadget_constraint, MOV_CONST, dst_reg, NULL, const_value);
}
static bool parse_reg_to_reg(const RzCore *core, const char *str, RzRopConstraint *rc) {
static bool parse_reg_to_reg(const RzCore *core, const char *str, RzGadgetConstraint *gadget_constraint) {
ut64 idx = 0;
const RzRegItem *src_reg = NULL;
const RzRegItem *dst_reg = parse_register(core, str, &idx);
@ -341,10 +342,10 @@ static bool parse_reg_to_reg(const RzCore *core, const char *str, RzRopConstrain
return false;
}
return rop_constraint_set_regs(rc, MOV_REG, dst_reg, src_reg, NULL);
return gadget_constraint_set_regs(gadget_constraint, MOV_REG, dst_reg, src_reg, NULL);
}
static bool parse_reg_op_const(const RzCore *core, const char *str, RzRopConstraint *rc) {
static bool parse_reg_op_const(const RzCore *core, const char *str, RzGadgetConstraint *gadget_constraint) {
ut64 idx = 0;
ut64 const_value = 0;
RzILOpPureCode op = RZ_IL_OP_PURE_MAX;
@ -360,14 +361,14 @@ static bool parse_reg_op_const(const RzCore *core, const char *str, RzRopConstra
goto compound;
}
return rop_constraint_set_const(rc, MOV_OP_CONST, dst_reg, src_reg, const_value) &&
rop_constraint_set_op(rc, op);
return gadget_constraint_set_const(gadget_constraint, MOV_OP_CONST, dst_reg, src_reg, const_value) &&
gadget_constraint_set_op(gadget_constraint, op);
compound:
return parse_compound_op(core, str, rc);
return parse_compound_op(core, str, gadget_constraint);
}
static bool parse_reg_op_reg(const RzCore *core, const char *str, RzRopConstraint *rc) {
static bool parse_reg_op_reg(const RzCore *core, const char *str, RzGadgetConstraint *gadget_constraint) {
ut64 idx = 0;
RzILOpPureCode op = RZ_IL_OP_PURE_MAX;
const RzRegItem *src_reg0 = NULL;
@ -391,63 +392,65 @@ static bool parse_reg_op_reg(const RzCore *core, const char *str, RzRopConstrain
goto compound;
}
return rop_constraint_set_regs(rc, MOV_OP_REG, dst_reg, src_reg0, src_reg1) &&
rop_constraint_set_op(rc, op);
return gadget_constraint_set_regs(gadget_constraint, MOV_OP_REG, dst_reg, src_reg0, src_reg1) &&
gadget_constraint_set_op(gadget_constraint, op);
compound:
return parse_compound_op(core, str, rc);
return parse_compound_op(core, str, gadget_constraint);
}
/**
* \brief Create a new RzRopSearchContext object.
* \brief Create a new RzGadgetSearchContext object.
* \param core RZ_NONNULL Pointer to the RzCore structure containing configuration settings.
* \param greparg RZ_NULLABLE Pointer to a string containing the grep argument.
* \param regexp Flag specifying whether regular expressions should be used.
* \param mask ROP request mask specifying the ROP request parameters.
* \param detail_mask search ROP gadgets given details.
* \param mask Gadget request mask specifying the Gadget request parameters.
* \param detail_mask search gadgets given details.
* \param state RZ_BORROW Pointer to the command state output structure.
* \return RZ_OUT A pointer to the newly created RzRopSearchContext object, or NULL if memory allocation fails.
* \return RZ_OUT A pointer to the newly created RzGadgetSearchContext object, or NULL if memory allocation fails.
*
* This function allocates and initializes a new RzRopSearchContext object.
* This function allocates and initializes a new RzGadgetSearchContext object.
*/
RZ_API RZ_OWN RzRopSearchContext *rz_core_rop_search_context_new(RZ_NONNULL const RzCore *core, RZ_NULLABLE const char *greparg, const bool regexp,
const RzRopRequestMask mask, const RzRopDetailSearchMask detail_mask, RZ_NULLABLE RZ_BORROW RzCmdStateOutput *state) {
RZ_API RZ_OWN RzGadgetSearchContext *rz_core_gadget_search_context_new(RZ_NONNULL const RzCore *core, const RzGadgetType gadget_type, RZ_NULLABLE const char *greparg, const bool regexp,
const RzGadgetRequestMask mask, const RzGadgetDetailSearchMask detail_mask, RZ_NULLABLE RZ_BORROW RzCmdStateOutput *state) {
rz_return_val_if_fail(core, NULL);
RzRopSearchContext *context = RZ_NEW0(RzRopSearchContext);
RzGadgetSearchContext *context = RZ_NEW0(RzGadgetSearchContext);
if (!context) {
return NULL;
}
context->type = gadget_type;
context->greparg = rz_str_dup(greparg);
context->arch = rz_config_get(core->config, "asm.arch");
context->regexp = regexp;
context->mask = mask;
context->detail_mask = detail_mask;
context->state = state;
context->max_instr = rz_config_get_i(core->config, "rop.len");
context->max_instr = rz_config_get_i(core->config, "gadget.len");
context->max_count = rz_config_get_i(core->config, "search.maxhits");
context->increment = 1;
context->from = 0;
context->to = 0;
context->end_list = NULL;
context->unique_hitlists = NULL;
context->crop = rz_config_get_i(core->config, "rop.conditional");
context->subchain = rz_config_get_i(core->config, "rop.subchain");
context->cache = rz_config_get_i(core->config, "rop.cache");
context->allow_conditional = rz_config_get_b(core->config, "gadget.conditional");
context->comments = rz_config_get_b(core->config, "gadget.comments");
context->subchains = rz_config_get_b(core->config, "gadget.subchains");
context->cache = rz_config_get_b(core->config, "gadget.cache");
context->ret_val = false;
context->buf = NULL;
return context;
}
/**
* \brief Free an RzRopSearchContext object.
* \param context RZ_NULLABLE Pointer to the RzRopSearchContext object to free.
* \brief Free an RzGadgetSearchContext object.
* \param context RZ_NULLABLE Pointer to the RzGadgetSearchContext object to free.
*
* Frees the memory allocated for an RzRopSearchContext object.
* Frees the memory allocated for an RzGadgetSearchContext object.
* Note: Other elements must be freed by the caller/callee.
*/
RZ_API void rz_core_rop_search_context_free(RZ_NULLABLE RzRopSearchContext *context) {
RZ_API void rz_core_gadget_search_context_free(RZ_NULLABLE RzGadgetSearchContext *context) {
if (!context) {
return;
}
@ -462,77 +465,77 @@ RZ_API void rz_core_rop_search_context_free(RZ_NULLABLE RzRopSearchContext *cont
* \brief Analyze and parse a constraint string.
* \param core Pointer to the RzCore object.
* \param str The constraint string to analyze.
* \param rop_constraint Pointer to the RzRopConstraint object to store the parsed result.
* \param gadget_constraint Pointer to the RzGadgetConstraint object to store the parsed result.
* \return true if the constraint string is successfully parsed, false otherwise.
*
* This function analyzes a given constraint string and attempts to parse it into
* the provided RzRopConstraint. It tries four different parsing methods:
* the provided RzGadgetConstraint. It tries four different parsing methods:
*
* The function returns true if any of these parsing methods succeed.
*/
RZ_API bool rz_core_rop_analyze_constraint(const RZ_NONNULL RzCore *core, const RZ_NONNULL char *str,
RZ_NULLABLE RZ_OUT RzRopConstraint *rop_constraint) {
RZ_API bool rz_core_gadget_analyze_constraint(const RZ_NONNULL RzCore *core, const RZ_NONNULL char *str,
RZ_NULLABLE RZ_OUT RzGadgetConstraint *gadget_constraint) {
rz_return_val_if_fail(core && str, false);
if (!rop_constraint) {
if (!gadget_constraint) {
return false;
}
return parse_reg_to_const(core, str, rop_constraint) ||
parse_reg_to_reg(core, str, rop_constraint) ||
parse_reg_op_const(core, str, rop_constraint) ||
parse_reg_op_reg(core, str, rop_constraint);
return parse_reg_to_const(core, str, gadget_constraint) ||
parse_reg_to_reg(core, str, gadget_constraint) ||
parse_reg_op_const(core, str, gadget_constraint) ||
parse_reg_op_reg(core, str, gadget_constraint);
}
/**
* \brief Parse the given token into a rop constraint
* \brief Parse the given token into a gadget constraint
* \param core Pointer to the RzCore object.
* \param token Input string in the form `key=value`(Eg: rbx=rdx, r12=1)`
* \return \p RzRopConstraint if parsing is successful else NULL
* \return \p RzGadgetConstraint if parsing is successful else NULL
*
* The function parses the given token and parses according to the predefined ROP constriant type
* The function parses the given token and parses according to the predefined gadget constraint type
*/
RZ_API RZ_OWN RzRopConstraint *rz_core_rop_constraint_parse_args(const RZ_NONNULL RzCore *core, const RZ_NONNULL char *token) {
RZ_API RZ_OWN RzGadgetConstraint *rz_core_gadget_constraint_parse_args(const RZ_NONNULL RzCore *core, const RZ_NONNULL char *token) {
rz_return_val_if_fail(core && token, NULL);
if (RZ_STR_ISEMPTY(token)) {
return NULL;
}
RzRopConstraint *rop_constraint = RZ_NEW0(RzRopConstraint);
if (!rop_constraint) {
free(rop_constraint);
RzGadgetConstraint *gadget_constraint = RZ_NEW0(RzGadgetConstraint);
if (!gadget_constraint) {
free(gadget_constraint);
return NULL;
}
RzList *l = rz_str_split_duplist(token, "=", true);
if (rz_list_empty(l)) {
rz_list_free(l);
free(rop_constraint);
free(gadget_constraint);
return NULL;
}
if (!rz_core_rop_analyze_constraint(core, token, rop_constraint)) {
free(rop_constraint);
if (!rz_core_gadget_analyze_constraint(core, token, gadget_constraint)) {
free(gadget_constraint);
rz_list_free(l);
return NULL;
}
rz_list_free(l);
return rop_constraint;
return gadget_constraint;
}
/**
* \brief Parse rop constraint map
* \brief Parse gadget constraint map
* \param core Pointer to the RzCore object.
* \param argc Number of arguments.
* \param argv Array of arguments.
* \return RzPVector of RzRopConstraint objects.
* \return RzPVector of RzGadgetConstraint objects.
*
* This function parses a list of arguments into a RzPVector of RzRopConstraint objects.
* This function parses a list of arguments into a RzPVector of RzGadgetConstraint objects.
*/
RZ_API RZ_OWN RzPVector /*<RzRopConstraint *>*/ *rz_core_rop_constraint_map_parse(const RZ_NONNULL RzCore *core, const int argc, const char **argv) {
RZ_API RZ_OWN RzPVector /*<RzGadgetConstraint *>*/ *rz_core_gadget_constraint_map_parse(const RZ_NONNULL RzCore *core, const int argc, const char **argv) {
rz_return_val_if_fail(core && argv && RZ_STR_ISNOTEMPTY(argv[0]), false);
RzPVector *constr_map = rz_pvector_new((RzPVectorFree)rz_core_rop_constraint_free);
RzPVector *constr_map = rz_pvector_new((RzPVectorFree)rz_core_gadget_constraint_free);
if (!constr_map) {
return NULL;
}
@ -548,11 +551,11 @@ RZ_API RZ_OWN RzPVector /*<RzRopConstraint *>*/ *rz_core_rop_constraint_map_pars
RzListIter *it;
char *token;
rz_list_foreach (l, it, token) {
RzRopConstraint *rop_constraint = rz_core_rop_constraint_parse_args(core, token);
if (!rop_constraint) {
RzGadgetConstraint *gadget_constraint = rz_core_gadget_constraint_parse_args(core, token);
if (!gadget_constraint) {
continue;
}
rz_pvector_push(constr_map, rop_constraint);
rz_pvector_push(constr_map, gadget_constraint);
}
rz_list_free(l);
}

View file

@ -21,7 +21,7 @@ static const RzCmdDescDetail cmd_search_cryptographic_material_details[2];
static const RzCmdDescDetail cmd_search_file_details[2];
static const RzCmdDescDetail cmd_query_gadget_details[5];
static const RzCmdDescDetail cmd_rop_search_stack_details[2];
static const RzCmdDescDetail cmd_rop_search_size_details[2];
static const RzCmdDescDetail cmd_gadget_search_size_details[2];
static const RzCmdDescDetail cmd_search_value_details[3];
static const RzCmdDescDetail cmd_search_hex_details[2];
static const RzCmdDescDetail cmd_search_hex_regex_details[2];
@ -163,7 +163,7 @@ static const RzCmdDescArg cmd_search_gadget_args[2];
static const RzCmdDescArg cmd_query_gadget_args[2];
static const RzCmdDescArg cmd_detail_gadget_args[2];
static const RzCmdDescArg cmd_rop_search_stack_args[2];
static const RzCmdDescArg cmd_rop_search_size_args[2];
static const RzCmdDescArg cmd_gadget_search_size_args[2];
static const RzCmdDescArg cmd_search_value_args[3];
static const RzCmdDescArg cmd_search_value_alias_v1_args[2];
static const RzCmdDescArg cmd_search_value_alias_v2_args[2];
@ -2259,16 +2259,16 @@ static const RzCmdDescHelp cmd_rop_search_stack_help = {
.args = cmd_rop_search_stack_args,
};
static const RzCmdDescDetailEntry cmd_rop_search_size_Usage_space_example_detail_entries[] = {
static const RzCmdDescDetailEntry cmd_gadget_search_size_Usage_space_example_detail_entries[] = {
{ .text = "Search ROP gadgets with the size less than 0x20", .arg_str = NULL, .comment = "/Rl \"<0x20\"" },
{ .text = "Search ROP gadgets with the size 0x10", .arg_str = NULL, .comment = "/Rl =0x10" },
{ 0 },
};
static const RzCmdDescDetail cmd_rop_search_size_details[] = {
{ .name = "Usage example", .entries = cmd_rop_search_size_Usage_space_example_detail_entries },
static const RzCmdDescDetail cmd_gadget_search_size_details[] = {
{ .name = "Usage example", .entries = cmd_gadget_search_size_Usage_space_example_detail_entries },
{ 0 },
};
static const RzCmdDescArg cmd_rop_search_size_args[] = {
static const RzCmdDescArg cmd_gadget_search_size_args[] = {
{
.name = "Gadget size",
.type = RZ_CMD_ARG_TYPE_STRING,
@ -2278,10 +2278,10 @@ static const RzCmdDescArg cmd_rop_search_size_args[] = {
},
{ 0 },
};
static const RzCmdDescHelp cmd_rop_search_size_help = {
static const RzCmdDescHelp cmd_gadget_search_size_help = {
.summary = "Search rop gadgets given gadget size",
.details = cmd_rop_search_size_details,
.args = cmd_rop_search_size_args,
.details = cmd_gadget_search_size_details,
.args = cmd_gadget_search_size_args,
};
static const RzCmdDescHelp slash_v_help = {
@ -21856,8 +21856,8 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) {
RzCmdDesc *cmd_rop_search_stack_cd = rz_cmd_desc_argv_state_new(core->rcmd, slash_R_cd, "/Rs", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON, rz_cmd_rop_search_stack_handler, &cmd_rop_search_stack_help);
rz_warn_if_fail(cmd_rop_search_stack_cd);
RzCmdDesc *cmd_rop_search_size_cd = rz_cmd_desc_argv_state_new(core->rcmd, slash_R_cd, "/Rl", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON, rz_cmd_rop_search_size_handler, &cmd_rop_search_size_help);
rz_warn_if_fail(cmd_rop_search_size_cd);
RzCmdDesc *cmd_gadget_search_size_cd = rz_cmd_desc_argv_state_new(core->rcmd, slash_R_cd, "/Rl", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON, rz_cmd_gadget_search_size_handler, &cmd_gadget_search_size_help);
rz_warn_if_fail(cmd_gadget_search_size_cd);
RzCmdDesc *slash_v_cd = rz_cmd_desc_group_state_new(core->rcmd, slash__cd, "/v", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_QUIET | RZ_OUTPUT_MODE_TABLE, rz_cmd_search_value_handler, &cmd_search_value_help, &slash_v_help);
rz_warn_if_fail(slash_v_cd);

View file

@ -152,7 +152,7 @@ RZ_IPI RzCmdStatus rz_cmd_detail_gadget_handler(RzCore *core, int argc, const ch
// "/Rs"
RZ_IPI RzCmdStatus rz_cmd_rop_search_stack_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
// "/Rl"
RZ_IPI RzCmdStatus rz_cmd_rop_search_size_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
RZ_IPI RzCmdStatus rz_cmd_gadget_search_size_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
// "/v"
RZ_IPI RzCmdStatus rz_cmd_search_value_handler(RzCore *core, int argc, const char **argv, RzCmdStateOutput *state);
// "/v1"

View file

@ -583,7 +583,7 @@ commands:
- text: "Search ROP gadgets with 0x100 stack changes"
comment: "/Rs =0x100"
- name: "/Rl"
cname: cmd_rop_search_size
cname: cmd_gadget_search_size
summary: Search rop gadgets given gadget size
type: RZ_CMD_DESC_TYPE_ARGV_STATE
modes:

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2026 MrQuantum1915 <darshanpatelgdh@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#ifndef RZ_GADGET_INTERNAL_H
#define RZ_GADGET_INTERNAL_H
#include <rz_analysis.h>
// ROP (Return-Oriented Programming)
RZ_IPI bool rz_gadget_rop_is_end_gadget(const RzAnalysisOp *aop, const bool allow_conditional);
RZ_IPI bool rz_gadget_rop_is_valid_terminator(const RzAnalysisOp *aop, const bool allow_conditional);
#endif

52
librz/core/gadget_rop.c Normal file
View file

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: 2026 MrQuantum1915 <darshanpatelgdh@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "gadget_internal.h"
static bool is_cond_end_gadget(const RzAnalysisOp *aop) {
switch (aop->type) {
case RZ_ANALYSIS_OP_TYPE_CJMP:
case RZ_ANALYSIS_OP_TYPE_UCJMP:
case RZ_ANALYSIS_OP_TYPE_CCALL:
case RZ_ANALYSIS_OP_TYPE_UCCALL:
case RZ_ANALYSIS_OP_TYPE_CRET:
return true;
default:
return false;
}
}
RZ_IPI bool rz_gadget_rop_is_end_gadget(const RzAnalysisOp *aop, const bool allow_conditional) {
switch (aop->type) {
case RZ_ANALYSIS_OP_TYPE_TRAP:
case RZ_ANALYSIS_OP_TYPE_RET:
case RZ_ANALYSIS_OP_TYPE_UCALL:
case RZ_ANALYSIS_OP_TYPE_RCALL:
case RZ_ANALYSIS_OP_TYPE_ICALL:
case RZ_ANALYSIS_OP_TYPE_IRCALL:
case RZ_ANALYSIS_OP_TYPE_UJMP:
case RZ_ANALYSIS_OP_TYPE_RJMP:
case RZ_ANALYSIS_OP_TYPE_IJMP:
case RZ_ANALYSIS_OP_TYPE_IRJMP:
case RZ_ANALYSIS_OP_TYPE_JMP:
case RZ_ANALYSIS_OP_TYPE_CALL:
if (allow_conditional) {
return is_cond_end_gadget(aop);
}
return true;
default:
return false;
}
}
RZ_IPI bool rz_gadget_rop_is_valid_terminator(const RzAnalysisOp *aop, const bool allow_conditional) {
switch (aop->type) {
case RZ_ANALYSIS_OP_TYPE_RET:
if (allow_conditional) {
return is_cond_end_gadget(aop);
}
return true;
default:
return false;
}
}

View file

@ -63,7 +63,8 @@ rz_core_sources = [
'libs.c',
'project.c',
'project_migrate.c',
'rop.c',
'gadget.c',
'gadget_rop.c',
'rtr.c',
#'rtr_http.c',
#'rtr_shell.c',

View file

@ -733,6 +733,27 @@ RZ_API bool rz_project_migrate_v20_v21(RzProject *prj, RzSerializeResultInfo *re
return true;
}
// --
// Migration 21 -> 22
//
// Changes from <TODO:AT-LAST>:
// Renamed ROP search configs to gadget search for generalization:
// - `rop.X` to `gadget.X`
RZ_API bool rz_project_migrate_v21_v22(RzProject *prj, RzSerializeResultInfo *res) {
Sdb *core_db;
RZ_SERIALIZE_SUB(prj, core_db, res, "core", return false;);
Sdb *config_db;
RZ_SERIALIZE_SUB(core_db, config_db, res, "config", return false;);
sdb_rename(config_db, "rop.len", "gadget.len");
sdb_rename(config_db, "rop.cache", "gadget.cache");
sdb_rename(config_db, "rop.subchains", "gadget.subchains");
sdb_rename(config_db, "rop.conditional", "gadget.conditional");
sdb_rename(config_db, "rop.comments", "gadget.comments");
return true;
}
static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) = {
rz_project_migrate_v1_v2,
rz_project_migrate_v2_v3,
@ -754,6 +775,7 @@ static bool (*const migrations[])(RzProject *prj, RzSerializeResultInfo *res) =
rz_project_migrate_v18_v19,
rz_project_migrate_v19_v20,
rz_project_migrate_v20_v21,
rz_project_migrate_v21_v22,
};
/// Migrate the given project to the current version in-place

View file

@ -40,7 +40,7 @@ include_files = [
'rz_platform.h',
'rz_project.h',
'rz_reg.h',
'rz_rop.h',
'rz_gadget.h',
'rz_search.h',
'rz_sign.h',
'rz_skiplist.h',

View file

@ -1370,8 +1370,8 @@ RZ_API RZ_BORROW HtUP *rz_analysis_get_xrefs_from(RZ_NONNULL RzAnalysis *analysi
RZ_API void rz_analysis_set_xrefs_from(RZ_NONNULL RzAnalysis *analysis, HtUP *xrefs_from);
RZ_API RZ_BORROW HtUP *rz_analysis_get_xrefs_to(RZ_NONNULL RzAnalysis *analysis);
RZ_API void rz_analysis_set_xrefs_to(RZ_NONNULL RzAnalysis *analysis, HtUP *xrefs_to);
RZ_API RZ_BORROW HtUP *rz_analysis_get_rop_semantics(RZ_NONNULL RzAnalysis *analysis);
RZ_API void rz_analysis_set_rop_semantics(RZ_NONNULL RzAnalysis *analysis, HtUP *rop_semantics);
RZ_API RZ_BORROW HtUP *rz_analysis_get_gadget_semantics(RZ_NONNULL RzAnalysis *analysis);
RZ_API void rz_analysis_set_gadget_semantics(RZ_NONNULL RzAnalysis *analysis, HtUP *rop_semantics);
RZ_API RZ_BORROW RzAnalysisCallbacks *rz_analysis_get_callbacks(RZ_NONNULL RzAnalysis *analysis);
RZ_API void rz_analysis_set_os(RZ_NONNULL RzAnalysis *analysis, RZ_NULLABLE const char *os);
RZ_API const char *rz_analysis_get_os(RZ_NONNULL RzAnalysis *analysis);

222
librz/include/rz_gadget.h Normal file
View file

@ -0,0 +1,222 @@
// SPDX-FileCopyrightText: 2026 MrQuantum1915 <darshanpatelgdh@gmail.com>
// SPDX-FileCopyrightText: 2024 z3phyr <giridh1337@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#ifndef RZ_GADGET_H
#define RZ_GADGET_H
/**
* \file rz_gadget.h
* \brief Gadget (ROP, JOP, COP) related APIs and structures.
*
* This file contains definitions, structures, and function prototypes for handling ROP, JOP and COP gadgets and constraints.
*/
#include <rz_cmd.h>
#include <rz_il.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Information about a register.
*/
typedef struct rz_gadget_reg_info_t {
char *name;
bool is_mem_read; ///< Register involved in Memory read.
bool is_pc_write; ///< PC write flag.
bool is_var_read; ///< Register involved in Variable read.
bool is_var_write; ///< Register involved in Variable write.
bool is_mem_write; ///< Register involved in Memory write.
ut64 init_val;
ut64 new_val;
ut64 bits; ///< Register bits for capturing cast
RzILOpPure *value_transformations; ///< TODO: Captures Value transformations.
} RzGadgetRegInfo;
/**
* \brief Information about a gadget.
*/
typedef struct rz_gadget_info_t {
ut64 address; ///< Gadget address.
ut64 stack_change; ///< Stack change.
ut64 curr_pc_val; ///< Current PC value.
ut32 size; ///< Gadget size.
bool is_pc_write; ///< PC write flag.
bool is_syscall; ///< Syscall flag.
RzIterator /*RzAnalysisBytes *>*/ *analysis_cache; ///< Stores \p RzAnalysisBytes for the gadget.
RzPVector /*<RzGadgetRegInfo *>*/ *modified_registers; ///< Modified registers.
RzList /*<RzGadgetRegInfo *>*/ *dependencies; ///< Dependencies.
} RzGadgetInfo;
/**
* \brief Type of gadget to search for.
*/
typedef enum {
RZ_GADGET_TYPE_ROP, ///< Return-Oriented Programming.
RZ_GADGET_TYPE_JOP, ///< Jump-Oriented Programming.
RZ_GADGET_TYPE_COP, ///< Call-Oriented Programming.
} RzGadgetType;
/**
* \brief Types of IL instructions for Gadget constraints.
*/
typedef enum rz_gadget_il_instr_type {
MOV_CONST, ///< reg <- const
MOV_REG, ///< reg <- reg
MOV_OP_CONST, ///< reg <- reg OP const
MOV_OP_REG, ///< reg <- reg OP reg
SYSCALL, ///< syscall
} RzGadgetILInstructionType;
/**
* \brief Argument types for Gadget constraints.
*/
typedef enum {
SRC_REG,
DST_REG,
SRC_CONST,
SRC_REG_SECOND,
OP,
NUM_ARGS
} RzGadgetArgType;
/**
* \brief Gadget request mask for filtering gadgets.
*/
typedef enum {
RZ_GADGET_PRINT = 1 << 0, ///< Print Gadget.
RZ_GADGET_PRINT_DETAIL = 1 << 1, ///< Detailed Gadget.
RZ_GADGET_ANALYZE = 1 << 2, ///< Detailed Gadget.
RZ_GADGET_ALL = RZ_GADGET_PRINT | RZ_GADGET_PRINT_DETAIL | RZ_GADGET_ANALYZE ///< All Gadget requests.
} RzGadgetRequestMask;
/**
* \brief Gadget search mask for filtering gadgets given details.
*/
typedef enum {
RZ_GADGET_DETAIL_SEARCH_NON = 0,
RZ_GADGET_DETAIL_SEARCH_STACK = 1 << 0, ///< Search gadgets by stack changes.
RZ_GADGET_DETAIL_SEARCH_SIZE = 1 << 1, ///< Search gadgets by gadget sizes.
// RZ_GADGET_DETAIL_SEARCH_WRITE = 1 << 2, ///< Search gadgets by written registers.
// RZ_GADGET_DETAIL_SEARCH_READ = 1 << 3, ///< Search gadgets by read registers.
} RzGadgetDetailSearchMask;
/**
* \brief Filter conditions while searching gadgets by stack changes.
*/
typedef enum {
RZ_GADGET_DETAIL_CMP_EQ = 1, ///< ==
RZ_GADGET_DETAIL_CMP_GT = 1 << 1, ///< >
RZ_GADGET_DETAIL_CMP_GE = RZ_GADGET_DETAIL_CMP_GT | RZ_GADGET_DETAIL_CMP_EQ, ///< >=
RZ_GADGET_DETAIL_CMP_LT = 1 << 2, ///< <
RZ_GADGET_DETAIL_CMP_LE = RZ_GADGET_DETAIL_CMP_LT | RZ_GADGET_DETAIL_CMP_EQ, ///< <=
} RzGadgetDetailSearchCmpOp;
/**
* \brief Pair representing an end gadget with instruction offset and delay size.
*/
typedef struct rz_gadget_endlist_pair_t {
int instr_offset; ///< Instruction offset.
int delay_size; ///< Delay size.
} RzGadgetEndListPair;
/**
* \brief Structure representing a Gadget constraint.
*/
typedef struct rz_gadget_constraint_t {
RzGadgetILInstructionType type; ///< IL instruction type.
char *args[NUM_ARGS]; ///< Arguments.
} RzGadgetConstraint;
/**
* \brief Structure representing a Gadget search context.
*/
typedef struct rz_gadget_search_context_t {
RzGadgetType type; ///< Type of gadget to search for.
ut8 max_instr; ///< Gadget search max length.
bool subchains; ///< Display every length gadget from gadget.len=X to 2.
bool allow_conditional; ///< Include conditional jump, calls and returns in gadget search.
bool comments; ///< Display comments in gadget search output.
char *greparg; ///< Grep argument string.
const char *arch; ///< Architecture of the binary.
bool regexp; ///< Regular expression argument flag.
bool cache; ///< Cache the search results.
RzGadgetRequestMask mask; ///< Mask for kind of gadget request operation.
RzGadgetDetailSearchMask detail_mask; ///< Mask for searching gadgets given details.
RzCmdStateOutput *state; ///< Command state output.
int increment; ///< Gadget search increment value.
ut64 max_count; ///< Maximum number of hits (0: no limit).
ut64 from; ///< Start address to start gadget search.
ut64 to; ///< End address to stop gadget search.
RzList /*<RzGadgetEndListPair *>*/ *end_list; ///< List of end gadgets.
HtSU *unique_hitlists; ///< Cache unique gadget hitlists.
bool ret_val; ///< Flag to indicate return the search results.
RzStrBuf *buf; ///< String buffer for storing search results.
RzPVector /*<RzGadgetConstraint *>*/ *constraints; ///< User constraints for filtering.
} RzGadgetSearchContext;
/**
* \brief Enum for different Gadget register events.
*/
typedef enum {
RZ_GADGET_EVENT_VAR_READ,
RZ_GADGET_EVENT_VAR_WRITE,
RZ_GADGET_EVENT_MEM_READ,
RZ_GADGET_EVENT_MEM_WRITE,
RZ_GADGET_EVENT_PC_WRITE,
RZ_GADGET_EVENT_COUNT ///< This should always be the last element.
} RzGadgetEvent;
/**
* \brief Function pointer type for event check functions.
*/
typedef bool (*rz_gadget_event_check_fn)(const RzGadgetRegInfo *);
/**
* \brief Array of event check functions.
*/
extern rz_gadget_event_check_fn rz_gadget_event_functions[RZ_GADGET_EVENT_COUNT];
// Command APIs
RZ_API RzCmdStatus rz_core_gadget_search(RZ_NONNULL RzCore *core, RZ_NONNULL RzGadgetSearchContext *context);
RZ_API RzCmdStatus rz_core_gadget_info(RZ_NONNULL RzCore *core, RZ_NONNULL RZ_OWN RzGadgetSearchContext *context);
RZ_API bool rz_core_gadget_analyze_constraint(const RZ_NONNULL RzCore *core, const RZ_NONNULL char *str,
RZ_NULLABLE RZ_OUT RzGadgetConstraint *gadget_constraint);
RZ_API RZ_OWN RzPVector /*<RzGadgetConstraint *>*/ *rz_core_gadget_constraint_map_parse(const RZ_NONNULL RzCore *core, int argc, const char **argv);
RZ_API bool rz_core_handle_gadget_request_type(RZ_NONNULL RzCore *core, RZ_NONNULL RzGadgetSearchContext *context, RZ_NONNULL RzPVector /*<RzCoreAsmHit *>*/ *hitlist);
RZ_API RZ_NULLABLE RZ_OWN RzList /*<char *>*/ *rz_core_gadget_handle_grep_args(RZ_NULLABLE const char *greparg, const bool regexp);
// Gadget Search Context APIs
RZ_API RZ_OWN RzGadgetSearchContext *rz_core_gadget_search_context_new(RZ_NONNULL const RzCore *core, const RzGadgetType gadget_type, RZ_NULLABLE const char *greparg, bool regexp,
RzGadgetRequestMask mask, RzGadgetDetailSearchMask detail_mask, RZ_NULLABLE RZ_BORROW RzCmdStateOutput *state);
RZ_API void rz_core_gadget_search_context_free(RZ_NULLABLE RzGadgetSearchContext *context);
// Gadget Constraint APIs
RZ_API void rz_core_gadget_constraint_free(RZ_NULLABLE void *data);
RZ_API RZ_OWN RzGadgetConstraint *rz_core_gadget_constraint_parse_args(const RZ_NONNULL RzCore *core, const RZ_NONNULL char *token);
// Gadget Info APIs
RZ_API void rz_core_gadget_info_free(RZ_NULLABLE RzGadgetInfo *gadget_info);
RZ_API void rz_core_gadget_info_add_register(const RZ_NONNULL RZ_OUT RzGadgetInfo *gadget_info,
RZ_NONNULL RzGadgetRegInfo *reg_info, bool is_dependency);
RZ_API void rz_core_gadget_info_update_register(const RZ_INOUT RzGadgetInfo *gadget_info, RZ_INOUT RZ_NONNULL RzGadgetRegInfo *new_reg_info);
RZ_API RZ_OWN RzGadgetInfo *rz_core_gadget_info_new(ut64 address);
RZ_API RZ_OWN RzGadgetRegInfo *rz_core_gadget_reg_info_dup(RZ_BORROW RZ_NONNULL RzGadgetRegInfo *src);
RZ_API void rz_core_gadget_reg_info_free(RZ_NULLABLE RzGadgetRegInfo *reg_info);
RZ_API RZ_OWN RzGadgetRegInfo *rz_core_gadget_reg_info_new(RZ_NONNULL const RzCore *core, RZ_NONNULL const RzILEvent *evt,
ut64 init_val, ut64 new_val);
RZ_API RZ_BORROW RzGadgetRegInfo *rz_core_gadget_info_get_modified_register(const RZ_NONNULL RzGadgetInfo *gadget_info,
const RZ_NONNULL char *name);
RZ_API bool rz_core_gadget_info_has_register(const RZ_NONNULL RzGadgetInfo *gadget_info, const RZ_NONNULL char *name);
RZ_API RZ_OWN RzPVector /*<RzGadgetRegInfo *>*/ *rz_core_gadget_get_reg_info_by_event(const RZ_NONNULL RzGadgetInfo *gadget_info, RzGadgetEvent event);
RZ_API RZ_OWN RzPVector /*<RzGadgetRegInfo *>*/ *rz_core_gadget_get_reg_info_by_reg_names(const RZ_NONNULL RzGadgetInfo *gadget_info, RZ_NONNULL const RzPVector /*<char *>*/ *registers);
RZ_API bool rz_core_gadget_reg_info_has_event(const RZ_NONNULL RzGadgetInfo *gadget_info,
RzGadgetEvent event, const RZ_NULLABLE char *reg_name);
RZ_API RZ_OWN RzPVector /*<RzGadgetRegInfo *>*/ *rz_core_gadget_reg_info_find(const RZ_NONNULL RzGadgetInfo *gadget_info, const RZ_NONNULL char *name);
#ifdef __cplusplus
}
#endif
#endif // RZ_GADGET_H

View file

@ -12,7 +12,7 @@
extern "C" {
#endif
#define RZ_PROJECT_VERSION 21
#define RZ_PROJECT_VERSION 22
typedef Sdb RzProject;
@ -66,6 +66,7 @@ RZ_API bool rz_project_migrate_v17_v18(RzProject *prj, RzSerializeResultInfo *re
RZ_API bool rz_project_migrate_v18_v19(RzProject *prj, RzSerializeResultInfo *res);
RZ_API bool rz_project_migrate_v19_v20(RzProject *prj, RzSerializeResultInfo *res);
RZ_API bool rz_project_migrate_v20_v21(RzProject *prj, RzSerializeResultInfo *res);
RZ_API bool rz_project_migrate_v21_v22(RzProject *prj, RzSerializeResultInfo *res);
RZ_API bool rz_project_migrate(RzProject *prj, unsigned long version, RzSerializeResultInfo *res);
#ifdef __cplusplus

View file

@ -1,210 +0,0 @@
// SPDX-FileCopyrightText: 2024 z3phyr <giridh1337@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#ifndef RZ_ROP_H
#define RZ_ROP_H
/**
* \file rz_rop.h
* \brief Return-Oriented Programming (ROP) related APIs and structures..
*
* This file contains definitions, structures, and function prototypes for handling ROP gadgets and constraints.
*/
#include <rz_cmd.h>
#include <rz_il.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief Information about a register.
*/
typedef struct rz_rop_reg_info_t {
char *name;
bool is_mem_read; ///< Register involved in Memory read.
bool is_pc_write; ///< PC write flag.
bool is_var_read; ///< Register involved in Variable read.
bool is_var_write; ///< Register involved in Variable write.
bool is_mem_write; ///< Register involved in Memory write.
ut64 init_val;
ut64 new_val;
ut64 bits; ///< Register bits for capturing cast
RzILOpPure *value_transformations; ///< TODO: Captures Value transformations.
} RzRopRegInfo;
/**
* \brief Information about a ROP gadget.
*/
typedef struct rz_rop_gadget_info_t {
ut64 address; ///< Gadget address.
ut64 stack_change; ///< Stack change.
ut64 curr_pc_val; ///< Current PC value.
ut32 size; ///< Gadget size.
bool is_pc_write; ///< PC write flag.
bool is_syscall; ///< Syscall flag.
RzIterator /*RzAnalysisBytes *>*/ *analysis_cache; ///< Stores \p RzAnalysisBytes for the gadget.
RzPVector /*<RzRopRegInfo *>*/ *modified_registers; ///< Modified registers.
RzList /*<RzRopRegInfo *>*/ *dependencies; ///< Dependencies.
} RzRopGadgetInfo;
/**
* \brief Types of IL instructions for ROP constraints.
*/
typedef enum rz_rop_il_instr_type {
MOV_CONST, ///< reg <- const
MOV_REG, ///< reg <- reg
MOV_OP_CONST, ///< reg <- reg OP const
MOV_OP_REG, ///< reg <- reg OP reg
SYSCALL, ///< syscall
} RzRopILInstructionType;
/**
* \brief Argument types for ROP constraints.
*/
typedef enum {
SRC_REG,
DST_REG,
SRC_CONST,
SRC_REG_SECOND,
OP,
NUM_ARGS
} RzRopArgType;
/**
* \brief ROP request mask for filtering gadgets.
*/
typedef enum {
RZ_ROP_GADGET_PRINT = 1 << 0, ///< Print ROP gadgets.
RZ_ROP_GADGET_PRINT_DETAIL = 1 << 1, ///< Detailed ROP gadgets.
RZ_ROP_GADGET_ANALYZE = 1 << 2, ///< Detailed ROP gadgets.
RZ_ROP_GADGET_ALL = RZ_ROP_GADGET_PRINT | RZ_ROP_GADGET_PRINT_DETAIL | RZ_ROP_GADGET_ANALYZE ///< All ROP gadgets requests.
} RzRopRequestMask;
/**
* \brief ROP search mask for filtering gadgets given details.
*/
typedef enum {
RZ_ROP_DETAIL_SEARCH_NON = 0,
RZ_ROP_DETAIL_SEARCH_STACK = 1 << 0, ///< Search ROP gadgets by stack changes.
RZ_ROP_DETAIL_SEARCH_SIZE = 1 << 1, ///< Search ROP gadgets by gadget sizes.
// RZ_ROP_DETAIL_SEARCH_WRITE = 1 << 2, ///< Search ROP gadgets by written registers.
// RZ_ROP_DETAIL_SEARCH_READ = 1 << 3, ///< Search ROP gadgets by read registers.
} RzRopDetailSearchMask;
/**
* \brief Filter conditions while searching ROP gadgets by stack changes.
*/
typedef enum {
ROP_DETAIL_CMP_EQ = 1, // ==
ROP_DETAIL_CMP_GT = 1 << 1, // >
ROP_DETAIL_CMP_GE = ROP_DETAIL_CMP_GT | ROP_DETAIL_CMP_EQ, // >=
ROP_DETAIL_CMP_LT = 1 << 2, // <
ROP_DETAIL_CMP_LE = ROP_DETAIL_CMP_LT | ROP_DETAIL_CMP_EQ, // <=
} RopDetailSearchCmpOp;
/**
* \brief Pair representing an end gadget with instruction offset and delay size.
*/
typedef struct rz_rop_endlist_pair_t {
int instr_offset; ///< Instruction offset.
int delay_size; ///< Delay size.
} RzRopEndListPair;
/**
* \brief Structure representing a ROP constraint.
*/
typedef struct rz_rop_constraint_t {
RzRopILInstructionType type; ///< IL instruction type.
char *args[NUM_ARGS]; ///< Arguments.
} RzRopConstraint;
/**
* \brief Structure representing a ROP search context.
*/
typedef struct rz_rop_search_context_t {
ut8 max_instr; ///< Rop search max length.
ut8 subchain; ///< Display every length gadget from rop.len=X to 2 in /Rl.
ut8 crop; ///< Include conditional jump, calls and returns in ropsearch.
char *greparg; ///< Grep argument string.
const char *arch; ///< Architecture of the binary.
bool regexp; ///< Regular expression argument flag.
bool cache; ///< Cache the search results.
RzRopRequestMask mask; ///< Mask for kind of rop request operation.
RzRopDetailSearchMask detail_mask; ///< Mask for searching gadgets given details.
RzCmdStateOutput *state; ///< Command state output.
int increment; ///< ROP search increment value.
ut64 max_count; ///< Maximum number of hits (0: no limit).
ut64 from; ///< Start address to start rop search.
ut64 to; ///< End address to stop rop search.
RzList /*<RzRopEndListPair *>*/ *end_list; ///< List of end gadgets.
HtSU *unique_hitlists; ///< Cache unique ROP hitlists.
bool ret_val; ///< Flag to indicate return the search results.
RzStrBuf *buf; ///< String buffer for storing search results.
RzPVector /*<RzRopConstraint *>*/ *constraints; ///< User constraints for filtering.
} RzRopSearchContext;
/**
* \brief Enum for different ROP register events.
*/
typedef enum {
RZ_ROP_EVENT_VAR_READ,
RZ_ROP_EVENT_VAR_WRITE,
RZ_ROP_EVENT_MEM_READ,
RZ_ROP_EVENT_MEM_WRITE,
RZ_ROP_EVENT_PC_WRITE,
RZ_ROP_EVENT_COUNT // This should always be the last element
} RzRopEvent;
/**
* \brief Function pointer type for event check functions.
*/
typedef bool (*rz_rop_event_check_fn)(const RzRopRegInfo *);
/**
* \brief Array of event check functions.
*/
extern rz_rop_event_check_fn rz_rop_event_functions[RZ_ROP_EVENT_COUNT];
// Command APIs
RZ_API RzCmdStatus rz_core_rop_search(RZ_NONNULL RzCore *core, RZ_NONNULL RzRopSearchContext *context);
RZ_API RzCmdStatus rz_core_rop_gadget_info(RZ_NONNULL RzCore *core, RZ_NONNULL RZ_OWN RzRopSearchContext *context);
RZ_API bool rz_core_rop_analyze_constraint(const RZ_NONNULL RzCore *core, const RZ_NONNULL char *str,
RZ_NULLABLE RZ_OUT RzRopConstraint *rop_constraint);
RZ_API RZ_OWN RzPVector /*<RzRopConstraint *>*/ *rz_core_rop_constraint_map_parse(const RZ_NONNULL RzCore *core, int argc, const char **argv);
RZ_API bool rz_core_handle_rop_request_type(RZ_NONNULL RzCore *core, RZ_NONNULL RzRopSearchContext *context, RZ_NONNULL RzList /*<RzCoreAsmHit *>*/ *hitlist);
RZ_API RZ_NULLABLE RZ_OWN RzList /*<char *>*/ *rz_core_rop_handle_grep_args(RZ_NULLABLE const char *greparg, const bool regexp);
// ROP Search Context APIs
RZ_API RZ_OWN RzRopSearchContext *rz_core_rop_search_context_new(RZ_NONNULL const RzCore *core, RZ_NULLABLE const char *greparg, bool regexp,
RzRopRequestMask mask, RzRopDetailSearchMask detail_mask, RZ_NULLABLE RZ_BORROW RzCmdStateOutput *state);
RZ_API void rz_core_rop_search_context_free(RZ_NULLABLE RzRopSearchContext *context);
// ROP Constraint APIs
RZ_API void rz_core_rop_constraint_free(RZ_NULLABLE void *data);
RZ_API RZ_OWN RzRopConstraint *rz_core_rop_constraint_parse_args(const RZ_NONNULL RzCore *core, const RZ_NONNULL char *token);
// ROP Gadget Info APIs
RZ_API void rz_core_rop_gadget_info_free(RZ_NULLABLE RzRopGadgetInfo *gadget_info);
RZ_API void rz_core_rop_gadget_info_add_register(const RZ_NONNULL RZ_OUT RzRopGadgetInfo *gadget_info,
RZ_NONNULL RzRopRegInfo *reg_info, bool is_dependency);
RZ_API void rz_core_rop_gadget_info_update_register(const RZ_INOUT RzRopGadgetInfo *gadget_info, RZ_INOUT RZ_NONNULL RzRopRegInfo *new_reg_info);
RZ_API RZ_OWN RzRopGadgetInfo *rz_core_rop_gadget_info_new(ut64 address);
RZ_API RZ_OWN RzRopRegInfo *rz_core_rop_reg_info_dup(RZ_BORROW RZ_NONNULL RzRopRegInfo *src);
RZ_API void rz_core_rop_reg_info_free(RZ_NULLABLE RzRopRegInfo *reg_info);
RZ_API RZ_OWN RzRopRegInfo *rz_core_rop_reg_info_new(RZ_NONNULL const RzCore *core, RZ_NONNULL const RzILEvent *evt,
ut64 init_val, ut64 new_val);
RZ_API RZ_BORROW RzRopRegInfo *rz_core_rop_gadget_info_get_modified_register(const RZ_NONNULL RzRopGadgetInfo *gadget_info,
const RZ_NONNULL char *name);
RZ_API bool rz_core_rop_gadget_info_has_register(const RZ_NONNULL RzRopGadgetInfo *gadget_info, const RZ_NONNULL char *name);
RZ_API RZ_OWN RzPVector /*<RzRopRegInfo *>*/ *rz_core_rop_gadget_get_reg_info_by_event(const RZ_NONNULL RzRopGadgetInfo *gadget_info, RzRopEvent event);
RZ_API RZ_OWN RzPVector /*<RzRopRegInfo *>*/ *rz_core_rop_get_reg_info_by_reg_names(const RZ_NONNULL RzRopGadgetInfo *gadget_info, RZ_NONNULL const RzPVector /*<char *>*/ *registers);
RZ_API bool rz_core_rop_gadget_reg_info_has_event(const RZ_NONNULL RzRopGadgetInfo *gadget_info,
RzRopEvent event, const RZ_NULLABLE char *reg_name);
RZ_API RZ_OWN RzPVector /*<RzRopRegInfo *>*/ *rz_core_rop_reg_info_find(const RZ_NONNULL RzRopGadgetInfo *gadget_info, const RZ_NONNULL char *name);
#ifdef __cplusplus
}
#endif
#endif // RZ_ROP_H

View file

@ -318,7 +318,7 @@ NAME=search rop gadgets with another end gadget as part of it.
FILE=bins/elf/analysis/unoriginal
CMDS=<<EOF
e asm.bits=32
e rop.len=15
e gadget.len=15
e search.from=0x08048340
e search.to=0x08048400
/R push esp
@ -3526,3 +3526,90 @@ Gadget 0x8048692 (size 4 bytes)
EOF
RUN
NAME=subchains test
FILE==
CMDS=<<EOF
e asm.arch=x86
e asm.bits=64
wa "pop r12; pop r13; pop r14; pop r15; ret"
e gadget.subchains=false
/Rq
echo "===================================================="
e gadget.subchains=true
/Rq
EOF
EXPECT=<<EOF
0x00000000: pop r12; pop r13; pop r14; pop r15; ret;
0x00000001: pop rsp; pop r13; pop r14; pop r15; ret;
0x00000002: pop r13; pop r14; pop r15; ret;
0x00000003: pop rbp; pop r14; pop r15; ret;
0x00000004: pop r14; pop r15; ret;
0x00000005: pop rsi; pop r15; ret;
0x00000006: pop r15; ret;
0x00000007: pop rdi; ret;
0x00000008: ret;
====================================================
0x00000000: pop r12; pop r13; pop r14; pop r15; ret;
0x00000002: pop r13; pop r14; pop r15; ret;
0x00000004: pop r14; pop r15; ret;
0x00000006: pop r15; ret;
0x00000001: pop rsp; pop r13; pop r14; pop r15; ret;
0x00000002: pop r13; pop r14; pop r15; ret;
0x00000004: pop r14; pop r15; ret;
0x00000006: pop r15; ret;
0x00000002: pop r13; pop r14; pop r15; ret;
0x00000004: pop r14; pop r15; ret;
0x00000006: pop r15; ret;
0x00000003: pop rbp; pop r14; pop r15; ret;
0x00000004: pop r14; pop r15; ret;
0x00000006: pop r15; ret;
0x00000004: pop r14; pop r15; ret;
0x00000006: pop r15; ret;
0x00000005: pop rsi; pop r15; ret;
0x00000006: pop r15; ret;
0x00000006: pop r15; ret;
0x00000007: pop rdi; ret;
0x00000008: ret;
EOF
RUN
NAME=search rop gadgets with comments
FILE==
CMDS=<<EOF
e asm.arch=x86
e asm.bits=64
wa "pop r12; ret"
CC Rizin is Cool! @ 0
CC Its elegant! @ 2
/R
echo "========================================================="
e gadget.comments=true
/R
EOF
EXPECT=<<EOF
0x00000000 415c pop r12
0x00000002 c3 ret
Gadget size: 3
0x00000001 5c pop rsp
0x00000002 c3 ret
Gadget size: 2
0x00000002 c3 ret
Gadget size: 1
=========================================================
0x00000000 415c pop r12 ; Rizin is Cool!
0x00000002 c3 ret ; Its elegant!
Gadget size: 3
0x00000001 5c pop rsp
0x00000002 c3 ret ; Its elegant!
Gadget size: 2
0x00000002 c3 ret ; Its elegant!
Gadget size: 1
EOF
RUN

View file

@ -400,7 +400,7 @@ CMDS=<<EOF
e asm.bits=32
e asm.arch=mips
e scr.color=false
e rop.len=8
e gadget.len=8
wx 1b000000040000001a0000004c08410018000000040000000800000000000070
/R
q

View file

@ -379,6 +379,7 @@ Detailed project load info:
project migrated from version 18 to 19.
project migrated from version 19 to 20.
project migrated from version 20 to 21.
project migrated from version 21 to 22.
EOF
RUN

View file

@ -655,6 +655,42 @@ static bool test_migrate_v17_v18_rop_config() {
mu_end;
}
static bool test_migrate_v21_v22_gadget_config() {
RzProject *prj = rz_project_load_file_raw("prj/v20-debase64.rzdb");
mu_assert_notnull(prj, "load raw project");
RzSerializeResultInfo *res = rz_serialize_result_info_new();
// get to v21 state
bool s_20_21 = rz_project_migrate_v20_v21(prj, res);
mu_assert_true(s_20_21, "v20->v21 migrate success");
// actual test
bool s_21_22 = rz_project_migrate_v21_v22(prj, res);
mu_assert_true(s_21_22, "v21->v22 migrate success");
Sdb *core_db = sdb_ns(prj, "core", false);
Sdb *config_db = sdb_ns(core_db, "config", false);
mu_assert_null(sdb_get(config_db, "rop.len"), "old rop.len deleted");
mu_assert_streq_free(sdb_get(config_db, "gadget.len"), "5", "new gadget.len added");
mu_assert_null(sdb_get(config_db, "rop.cache"), "old rop.cache deleted");
mu_assert_streq_free(sdb_get(config_db, "gadget.cache"), "false", "new gadget.cache added");
mu_assert_null(sdb_get(config_db, "rop.subchains"), "old rop.subchains deleted");
mu_assert_streq_free(sdb_get(config_db, "gadget.subchains"), "false", "new gadget.subchains added");
mu_assert_null(sdb_get(config_db, "rop.conditional"), "old rop.conditional deleted");
mu_assert_streq_free(sdb_get(config_db, "gadget.conditional"), "false", "new gadget.conditional added");
mu_assert_null(sdb_get(config_db, "rop.comments"), "old rop.comments deleted");
mu_assert_streq_free(sdb_get(config_db, "gadget.comments"), "false", "new gadget.comments added");
rz_serialize_result_info_free(res);
rz_project_free(prj);
mu_end;
}
/// Load project of given version from file into core and check the log for migration success messages
#define BEGIN_LOAD_TEST(core, version, file) \
do { \
@ -1055,6 +1091,18 @@ static bool test_load_v17() {
mu_end;
}
static bool test_load_v22_gadget_config() {
RzCore *core = rz_core_new();
BEGIN_LOAD_TEST(core, 17, "prj/v17-rop-config.rzdb");
mu_assert_eq(rz_config_get_i(core->config, "gadget.len"), 5, "gadget.len");
mu_assert_eq(rz_config_get_b(core->config, "gadget.cache"), false, "gadget.cache");
mu_assert_eq(rz_config_get_b(core->config, "gadget.subchains"), false, "gadget.subchains");
mu_assert_eq(rz_config_get_b(core->config, "gadget.conditional"), false, "gadget.conditional");
mu_assert_eq(rz_config_get_b(core->config, "gadget.comments"), false, "gadget.comments");
rz_core_free(core);
mu_end;
}
int all_tests() {
mu_run_test(test_migrate_v1_v2_noreturn);
mu_run_test(test_migrate_v1_v2_noreturn_empty);
@ -1078,6 +1126,7 @@ int all_tests() {
mu_run_test(test_migrate_v17_v18_rop_config);
mu_run_test(test_migrate_v18_v19_str_config);
mu_run_test(test_migrate_v20_v21_debase64);
mu_run_test(test_migrate_v21_v22_gadget_config);
mu_run_test(test_load_v1_noreturn);
mu_run_test(test_load_v1_noreturn_empty);
mu_run_test(test_load_v1_unknown_type);
@ -1101,6 +1150,7 @@ int all_tests() {
mu_run_test(test_load_v15_19_str_config);
mu_run_test(test_load_v16);
mu_run_test(test_load_v17);
mu_run_test(test_load_v22_gadget_config);
return tests_passed != tests_run;
}

View file

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: 2026 MrQuantum1915 <darshanpatelgdh@gmail.com>
// SPDX-FileCopyrightText: 2024 z3phyr <giridh1337@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "minunit.h"
#include <rz_core.h>
#include "analysis_private.h"
#include <rz_rop.h>
#include <rz_gadget.h>
// Only one gadget is added once for each test case.
#define ROP_GADGET_MAX_SIZE 16
@ -34,7 +34,7 @@ static RzCoreAsmHit *setup_rop_hitasm(RzCore *core, int addr, ut8 *buf_str, int
return hit;
}
static RzList /*<RzCoreAsmHit *>*/ *
static RzPVector /*<RzCoreAsmHit *>*/ *
setup_rop_hitlist(RzCore *core, ut8 *buf_str, int addr, int len, HtUP *ht_rop_analysis) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
@ -47,23 +47,23 @@ setup_rop_hitlist(RzCore *core, ut8 *buf_str, int addr, int len, HtUP *ht_rop_an
return NULL;
}
RzList /*<RzCoreAsmHit *>*/ *hitlist = rz_list_newf(rz_core_asm_hit_free);
RzPVector /*<RzCoreAsmHit *>*/ *hitlist = rz_pvector_new(rz_core_asm_hit_free);
if (!hitlist) {
return NULL;
}
RzCoreAsmHit *hit = setup_rop_hitasm(core, addr, buf_str, len - 1, ht_rop_analysis);
if (!hit) {
rz_list_free(hitlist);
rz_pvector_free(hitlist);
return NULL;
}
rz_list_append(hitlist, hit);
rz_pvector_push(hitlist, hit);
hit = setup_rop_hitasm(core, addr + len - 1, buf_str, 1, ht_rop_analysis);
if (!hit) {
rz_list_free(hitlist);
rz_pvector_free(hitlist);
return NULL;
}
rz_list_append(hitlist, hit);
rz_pvector_push(hitlist, hit);
rz_analysis_op_fini(&aop);
return hitlist;
}
@ -86,7 +86,7 @@ static void cleanup_test(RzCore *core, HtUP *ht_rop_analysis) {
static bool rop_gadget_info_cb(void *user, const ut64 k, const void *v) {
HtUP *ht_rop_analysis = (HtUP *)user;
RzRopGadgetInfo *gadget_info = (RzRopGadgetInfo *)v;
RzGadgetInfo *gadget_info = (RzGadgetInfo *)v;
mu_assert_eq(k, gadget_info->address, "ROP gadget address mismatch");
RzAnalysisOp *aop = ht_up_find(ht_rop_analysis, k, NULL);
mu_assert_notnull(aop, "ROP gadget analysis op is NULL");
@ -95,16 +95,16 @@ static bool rop_gadget_info_cb(void *user, const ut64 k, const void *v) {
mu_assert_notnull(src, "ROP gadget analysis op src is NULL");
RzRegItem *reg_item = aop->dst->reg;
mu_assert_notnull(reg_item, "ROP gadget register item is NULL");
RzRopRegInfo *reg_info = rz_core_rop_gadget_info_get_modified_register(gadget_info, aop->dst->reg->name);
RzGadgetRegInfo *reg_info = rz_core_gadget_info_get_modified_register(gadget_info, aop->dst->reg->name);
mu_assert_notnull(reg_info, "ROP gadget modified register is NULL");
mu_assert_streq(reg_info->name, reg_item->name, "ROP gadget modified register name mismatch");
if (src[0].type == RZ_ANALYSIS_VAL_IMM) {
mu_assert_eq(src->imm, reg_info->new_val - reg_info->init_val, "ROP gadget modified register value mismatch");
} else if (src[0].type == RZ_ANALYSIS_VAL_REG) {
RzPVector /*<RzRopRegInfo *>*/ *reg_info_vector = rz_core_rop_gadget_get_reg_info_by_event(gadget_info, RZ_ROP_EVENT_VAR_READ);
RzPVector /*<RzGadgetRegInfo *>*/ *reg_info_vector = rz_core_gadget_get_reg_info_by_event(gadget_info, RZ_GADGET_EVENT_VAR_READ);
mu_assert_notnull(reg_info_vector, "ROP gadget register item is NULL");
mu_assert_eq(rz_pvector_len(reg_info_vector), 2, "ROP gadget register item count mismatch");
RzRopRegInfo *reg_info_analysis_reg = rz_pvector_at(reg_info_vector, 0);
RzGadgetRegInfo *reg_info_analysis_reg = rz_pvector_at(reg_info_vector, 0);
mu_assert_streq(src->reg->name, reg_info_analysis_reg->name, "ROP gadget modified register value mismatch");
rz_pvector_free(reg_info_vector);
}
@ -117,28 +117,28 @@ bool test_rz_direct_solver() {
mu_assert_notnull(core, "setup_rz_core failed");
int size = sizeof(x86_64_buf_str) / sizeof(x86_64_buf_str[0]);
int addr = 0;
RzRopSearchContext *context = rz_core_rop_search_context_new(
core, NULL, false, RZ_ROP_GADGET_PRINT_DETAIL | RZ_ROP_GADGET_ANALYZE, RZ_ROP_DETAIL_SEARCH_NON,
RzGadgetSearchContext *context = rz_core_gadget_search_context_new(
core, RZ_GADGET_TYPE_ROP, NULL, false, RZ_GADGET_PRINT_DETAIL | RZ_GADGET_ANALYZE, RZ_GADGET_DETAIL_SEARCH_NON,
NULL);
mu_assert_notnull(context, "rz_core_rop_search_context_new failed");
mu_assert_notnull(context, "rz_core_gadget_search_context_new failed");
HtUP *ht_rop_analysis = ht_up_new(NULL, (HtUPFreeValue)rz_analysis_op_free);
for (int i = 0; i < size; i++) {
ut8 buf[ROP_GADGET_MAX_SIZE] = { 0 };
int len = rz_hex_str2bin(x86_64_buf_str[i], buf);
rz_io_write_at(core->io, addr, buf, len);
RzList /*<RzCoreAsmHit *>*/ *hitlist =
RzPVector /*<RzCoreAsmHit *>*/ *hitlist =
setup_rop_hitlist(core, buf, addr, len, ht_rop_analysis);
mu_assert_notnull(hitlist, "setup_rop_hitlist failed");
rz_core_handle_rop_request_type(core, context, hitlist);
rz_core_handle_gadget_request_type(core, context, hitlist);
addr += len + 1;
rz_list_free(hitlist);
rz_pvector_free(hitlist);
}
HtUP *rop_semantics = core->analysis->ht_rop_semantics;
HtUP *rop_semantics = rz_analysis_get_gadget_semantics(core->analysis);
mu_assert_notnull(rop_semantics, "ROP semantics hashtable is NULL");
mu_assert_eq(ht_up_size(rop_semantics), 2, "ROP semantics hashtable count is not 2");
ht_up_foreach(rop_semantics, rop_gadget_info_cb, ht_rop_analysis);
rz_core_rop_search_context_free(context);
rz_core_gadget_search_context_free(context);
cleanup_test(core, ht_rop_analysis);
mu_end;
}

View file

@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: 2026 MrQuantum1915 <darshanpatelgdh@gmail.com>
// SPDX-FileCopyrightText: 2024 z3phyr <giridh1337@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_core.h>
#include "minunit.h"
#include <rz_rop.h>
#include "analysis_private.h"
#include <rz_gadget.h>
// Define the register profile string for your architecture
#define REGISTER_PROFILE_STRING \
@ -35,7 +35,8 @@
static void setup_rz_core(RzCore *core) {
rz_config_set(core->config, "analysis.arch", "x86");
rz_analysis_set_bits(core->analysis, 64);
rz_reg_set_profile_string(core->analysis->reg, REGISTER_PROFILE_STRING);
RzReg *reg = rz_analysis_get_reg(core->analysis);
rz_reg_set_profile_string(reg, REGISTER_PROFILE_STRING);
}
bool test_parse_reg_to_const(void) {
@ -45,19 +46,19 @@ bool test_parse_reg_to_const(void) {
// Test case 1: Valid register to constant
char str1[] = " eax = 123 ";
RzRopConstraint *rop_constraint = rz_core_rop_constraint_parse_args(core, str1);
RzGadgetConstraint *rop_constraint = rz_core_gadget_constraint_parse_args(core, str1);
mu_assert_notnull(rop_constraint, "parse_reg_constraints failed on valid input");
mu_assert_eq(rop_constraint->type, MOV_CONST, "Invalid constraint type");
mu_assert_streq(rop_constraint->args[DST_REG], "eax", "Invalid destination register");
mu_assert_null(rop_constraint->args[SRC_REG], "Source register should be NULL");
mu_assert_streq(rop_constraint->args[SRC_CONST], "123", "Invalid constant value");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
// Test case 2: Invalid format
char str2[] = "eax =";
rop_constraint = rz_core_rop_constraint_parse_args(core, str2);
rop_constraint = rz_core_gadget_constraint_parse_args(core, str2);
mu_assert_null(rop_constraint, "parse_reg_constraints failed on invalid input");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
rz_core_free(core);
mu_end;
@ -70,18 +71,18 @@ bool test_parse_reg_to_reg(void) {
// Test case 1: Valid register to register
char str1[] = "eax = ebx ";
RzRopConstraint *rop_constraint = rz_core_rop_constraint_parse_args(core, str1);
RzGadgetConstraint *rop_constraint = rz_core_gadget_constraint_parse_args(core, str1);
mu_assert_notnull(rop_constraint, "parse_reg_constraints failed on valid input");
mu_assert_eq(rop_constraint->type, MOV_REG, "Invalid constraint type");
mu_assert_streq(rop_constraint->args[DST_REG], "eax", "Invalid destination register");
mu_assert_streq(rop_constraint->args[SRC_REG], "ebx", "Invalid source register");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
// Test case 2: Invalid format
char str2[] = "eax =";
rop_constraint = rz_core_rop_constraint_parse_args(core, str2);
rop_constraint = rz_core_gadget_constraint_parse_args(core, str2);
mu_assert_null(rop_constraint, "parse_reg_constraints failed on invalid input");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
rz_core_free(core);
mu_end;
@ -94,53 +95,53 @@ bool test_parse_reg_op_const(void) {
// Test case 1: Valid register operation with constant
char str1[] = "eax=eax+3";
RzRopConstraint *rop_constraint = rz_core_rop_constraint_parse_args(core, str1);
RzGadgetConstraint *rop_constraint = rz_core_gadget_constraint_parse_args(core, str1);
mu_assert_notnull(rop_constraint, "parse_reg_constraints failed on valid input");
mu_assert_eq(rop_constraint->type, MOV_OP_CONST, "Invalid constraint type");
mu_assert_streq(rop_constraint->args[DST_REG], "eax", "Invalid destination register");
mu_assert_streq(rop_constraint->args[SRC_REG], "eax", "Invalid source register");
mu_assert_streq(rop_constraint->args[OP], "add", "Invalid operator");
mu_assert_streq(rop_constraint->args[SRC_CONST], "3", "Invalid constant value");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
// Test case 2: Invalid format
char str2[] = "eax=eax+";
rop_constraint = rz_core_rop_constraint_parse_args(core, str2);
rop_constraint = rz_core_gadget_constraint_parse_args(core, str2);
mu_assert_null(rop_constraint, "parse_reg_constraints failed on invalid input");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
// Test case 3: Valid register operation with increment operator
char str3[] = "eax++";
rop_constraint = rz_core_rop_constraint_parse_args(core, str3);
rop_constraint = rz_core_gadget_constraint_parse_args(core, str3);
mu_assert_notnull(rop_constraint, "parse_reg_constraints failed on valid input");
mu_assert_eq(rop_constraint->type, MOV_OP_CONST, "Invalid constraint type");
mu_assert_streq(rop_constraint->args[DST_REG], "eax", "Invalid destination register");
mu_assert_streq(rop_constraint->args[SRC_REG], "eax", "Invalid source register");
mu_assert_streq(rop_constraint->args[OP], "add", "Invalid operator");
mu_assert_streq(rop_constraint->args[SRC_CONST], "1", "Invalid constant value");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
// Test case 4: Valid register operation with decrement operator
char str4[] = "eax--";
rop_constraint = rz_core_rop_constraint_parse_args(core, str4);
rop_constraint = rz_core_gadget_constraint_parse_args(core, str4);
mu_assert_notnull(rop_constraint, "parse_reg_constraints failed on valid input");
mu_assert_eq(rop_constraint->type, MOV_OP_CONST, "Invalid constraint type");
mu_assert_streq(rop_constraint->args[DST_REG], "eax", "Invalid destination register");
mu_assert_streq(rop_constraint->args[SRC_REG], "eax", "Invalid source register");
mu_assert_streq(rop_constraint->args[OP], "sub", "Invalid operator");
mu_assert_streq(rop_constraint->args[SRC_CONST], "1", "Invalid constant value");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
// Test case 5: Valid register operation with compound operator
char str5[] = "eax *= 1";
rop_constraint = rz_core_rop_constraint_parse_args(core, str5);
rop_constraint = rz_core_gadget_constraint_parse_args(core, str5);
mu_assert_notnull(rop_constraint, "parse_reg_constraints failed on valid input");
mu_assert_eq(rop_constraint->type, MOV_OP_CONST, "Invalid constraint type");
mu_assert_streq(rop_constraint->args[DST_REG], "eax", "Invalid destination register");
mu_assert_streq(rop_constraint->args[SRC_REG], "eax", "Invalid source register");
mu_assert_streq(rop_constraint->args[OP], "mul", "Invalid operator");
mu_assert_streq(rop_constraint->args[SRC_CONST], "1", "Invalid constant value");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
rz_core_free(core);
mu_end;
@ -153,31 +154,31 @@ bool test_parse_reg_op_reg(void) {
// Test case 1: Valid register operation with register
char str1[] = "eax=ebx-ecx";
RzRopConstraint *rop_constraint = rz_core_rop_constraint_parse_args(core, str1);
RzGadgetConstraint *rop_constraint = rz_core_gadget_constraint_parse_args(core, str1);
mu_assert_notnull(rop_constraint, "parse_reg_constraints failed on valid input");
mu_assert_eq(rop_constraint->type, MOV_OP_REG, "Invalid constraint type");
mu_assert_streq(rop_constraint->args[DST_REG], "eax", "Invalid destination register");
mu_assert_streq(rop_constraint->args[SRC_REG], "ebx", "Invalid source register");
mu_assert_streq(rop_constraint->args[OP], "sub", "Invalid operator");
mu_assert_streq(rop_constraint->args[SRC_REG_SECOND], "ecx", "Invalid destination constant register");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
// Test case 2: Invalid format
char str2[] = "eax = eax+ ";
rop_constraint = rz_core_rop_constraint_parse_args(core, str2);
rop_constraint = rz_core_gadget_constraint_parse_args(core, str2);
mu_assert_null(rop_constraint, "parse_reg_constraints failed on invalid input");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
// Test case 3: Valid register operation with register
char str3[] = "eax += ebx";
rop_constraint = rz_core_rop_constraint_parse_args(core, str3);
rop_constraint = rz_core_gadget_constraint_parse_args(core, str3);
mu_assert_notnull(rop_constraint, "parse_reg_constraints failed on valid input");
mu_assert_eq(rop_constraint->type, MOV_OP_REG, "Invalid constraint type");
mu_assert_streq(rop_constraint->args[DST_REG], "eax", "Invalid destination register");
mu_assert_streq(rop_constraint->args[SRC_REG], "eax", "Invalid source register");
mu_assert_streq(rop_constraint->args[SRC_REG_SECOND], "ebx", "Invalid destination constant register");
mu_assert_streq(rop_constraint->args[OP], "add", "Invalid operator");
rz_core_rop_constraint_free(rop_constraint);
rz_core_gadget_constraint_free(rop_constraint);
rz_core_free(core);
mu_end;