diff --git a/librz/arch/il/analysis_il.c b/librz/arch/il/analysis_il.c index 6b33d25ff59..007924c8021 100644 --- a/librz/arch/il/analysis_il.c +++ b/librz/arch/il/analysis_il.c @@ -228,17 +228,18 @@ RZ_API bool rz_analysis_il_vm_sync_to_reg(RzAnalysisILVM *vm, RZ_NONNULL RzReg * return rz_il_vm_sync_to_reg(vm->vm, vm->reg_binding, reg); } -/** - * Repeatedly perform steps in the VM until the condition callback returns false - * - * If given, this syncs the contents of \p reg into the vm. - * Then it repeatedly disassembles an instruction at the program counter of the vm and executes it as long as cond() returns true. - * Finally the contents are optionally synced back to \p reg. - * - * \return and indicator for which error occured, if any, or RZ_ANALYSIS_IL_STEP_RESULT_SUCCESS if cond() returned false - */ -RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step_while(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg, - bool (*cond)(RzAnalysisILVM *vm, void *user), void *user) { +static void il_events(RzILVM *vm, RzStrBuf *sb) { + void **it; + rz_pvector_foreach (vm->events, it) { + RzILEvent *evt = *it; + rz_il_event_stringify(evt, sb); + rz_strbuf_append(sb, "\n"); + } +} + +static RzAnalysisILStepResult analysis_il_vm_step_while( + RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg, + bool with_events, RZ_NONNULL RzAnalysisILVMCondCallback cond, RZ_NULLABLE void *user) { rz_return_val_if_fail(analysis && vm, false); RzAnalysisPlugin *cur = analysis->cur; @@ -250,36 +251,96 @@ RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step_while(RZ_NONNULL RzAnalysis rz_analysis_il_vm_sync_from_reg(vm, reg); } + RzAnalysisOp op = { 0 }; RzAnalysisILStepResult res = RZ_ANALYSIS_IL_STEP_RESULT_SUCCESS; while (cond(vm, user)) { ut64 addr = rz_bv_to_ut64(vm->vm->pc); ut8 code[32] = { 0 }; analysis->read_at(analysis, addr, code, sizeof(code)); - RzAnalysisOp op = { 0 }; rz_analysis_op_init(&op); - int r = rz_analysis_op(analysis, &op, addr, code, sizeof(code), RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_HINT); - RzILOpEffect *ilop = r < 0 ? NULL : op.il_op; - - if (ilop) { - bool succ = rz_il_vm_step(vm->vm, ilop, addr + (op.size > 0 ? op.size : 1)); - if (!succ) { - res = RZ_ANALYSIS_IL_STEP_IL_RUNTIME_ERROR; - } - } else { + int r = rz_analysis_op(analysis, &op, addr, code, sizeof(code), RZ_ANALYSIS_OP_MASK_IL | RZ_ANALYSIS_OP_MASK_HINT | RZ_ANALYSIS_OP_MASK_DISASM); + + if (r < 0 || !op.il_op) { res = RZ_ANALYSIS_IL_STEP_INVALID_OP; + break; } - - rz_analysis_op_fini(&op); - if (res != RZ_ANALYSIS_IL_STEP_RESULT_SUCCESS) { + if (!rz_il_vm_step(vm->vm, op.il_op, addr + (op.size > 0 ? op.size : 1))) { + res = RZ_ANALYSIS_IL_STEP_IL_RUNTIME_ERROR; break; } + if (!with_events) { + rz_analysis_op_fini(&op); + continue; + } + + RzStrBuf sb = { 0 }; + rz_strbuf_init(&sb); + rz_il_op_effect_stringify(op.il_op, &sb, true); + rz_strbuf_append(&sb, "\n"); + il_events(vm->vm, &sb); + + rz_cons_printf("0x%llx [", addr); + for (int i = 0; i < op.size; ++i) { + rz_cons_printf("%x", code[i]); + } + rz_cons_printf("] %s\n%s\n", op.mnemonic, rz_strbuf_get(&sb)); + rz_strbuf_fini(&sb); + rz_analysis_op_fini(&op); } + rz_analysis_op_fini(&op); if (reg) { rz_analysis_il_vm_sync_to_reg(vm, reg); } return res; } +/** + * \brief Repeatedly perform steps in the VM until the \p cond callback returns false + * + * \param analysis Pointer to an RzAnalysis struct, likely representing the analysis context. + * \param vm Pointer to an RzAnalysisILVM struct, representing the IL virtual machine to be stepped. + * \param reg Optional pointer to an RzReg struct, potentially holding register values to be used during the step. + * \param cond Pointer to a function that determines the loop's continuation condition. This function takes two arguments: + * * vm: Pointer to the same RzAnalysisILVM struct passed to rz_analysis_il_vm_step_while. + * * user: Pointer to user-provided data that can be used by the condition function. + * \param user Pointer to user-defined data that can be passed to the condition function. + * + * \return RZ_ANALYSIS_IL_STEP_RESULT: Enumeration value indicating the outcome of the stepping operation. + * Possible values (implementation specific): + * - RZ_ANALYSIS_IL_STEP_OK: Successful execution of the while loop step. + * - RZ_ANALYSIS_IL_STEP_ERROR: Encountered an error during execution. + * - RZ_ANALYSIS_IL_STEP_INVALID: Invalid arguments or state resulted in undefined behavior. + */ +RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step_while( + RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg, + RZ_NONNULL RzAnalysisILVMCondCallback cond, RZ_NULLABLE void *user) { + return analysis_il_vm_step_while(analysis, vm, reg, false, cond, user); +} + +/** + * \brief Repeatedly perform steps in the VM until the \p cond callback returns false + * and output VM changes (read & write) + * + * \param analysis Pointer to an RzAnalysis struct, likely representing the analysis context. + * \param vm Pointer to an RzAnalysisILVM struct, representing the IL virtual machine to be stepped. + * \param reg Optional pointer to an RzReg struct, potentially holding register values to be used during the step. + * \param cond Pointer to a function that determines the loop's continuation condition. This function takes two arguments: + * * vm: Pointer to the same RzAnalysisILVM struct passed to rz_analysis_il_vm_step_while. + * * user: Pointer to user-provided data that can be used by the condition function. + * \param user Pointer to user-defined data that can be passed to the condition function. + * + * \return RZ_ANALYSIS_IL_STEP_RESULT: Enumeration value indicating the outcome of the stepping operation. + * Possible values (implementation specific): + * - RZ_ANALYSIS_IL_STEP_OK: Successful execution of the while loop step. + * - RZ_ANALYSIS_IL_STEP_ERROR: Encountered an error during execution. + * - RZ_ANALYSIS_IL_STEP_INVALID: Invalid arguments or state resulted in undefined behavior. + */ +RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step_while_with_events( + RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg, + RZ_NONNULL RzAnalysisILVMCondCallback cond, RZ_NULLABLE void *user) { + return analysis_il_vm_step_while(analysis, vm, reg, true, cond, user); +} + static bool step_cond_once(RzAnalysisILVM *vm, void *user) { bool *stepped = user; if (*stepped) { diff --git a/librz/core/cil.c b/librz/core/cil.c index 2e65b882baa..2341ae11765 100644 --- a/librz/core/cil.c +++ b/librz/core/cil.c @@ -741,6 +741,23 @@ RZ_API bool rz_core_il_step_until(RZ_NONNULL RzCore *core, ut64 until) { return step_handle_result(core, r); } +/** + * Perform zero or more steps starting at the PC given by analysis->reg in RzIL + * until reaching the given PC and output VM changes (read & write) + * \param until destination address where to stop + * \return false if an error occured (e.g. invalid op) + */ +RZ_API bool rz_core_il_step_until_with_events(RZ_NONNULL RzCore *core, ut64 until) { + rz_return_val_if_fail(core && until, false); + if (!step_assert_vm(core)) { + return false; + } + RzAnalysisILStepResult r = rz_analysis_il_vm_step_while_with_events( + core->analysis, core->analysis->il_vm, core->analysis->reg, + step_cond_until, &until); + return step_handle_result(core, r); +} + /** * Perform a single step at the PC given by analysis->reg in RzIL and print any events that happened * \return false if an error occured (e.g. invalid op) diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index 005db6de7b2..567f33dacef 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -4675,6 +4675,12 @@ RZ_IPI RzCmdStatus rz_il_vm_step_until_addr_handler(RzCore *core, int argc, cons return RZ_CMD_STATUS_OK; } +RZ_IPI RzCmdStatus rz_il_vm_step_until_addr_with_events_handler(RzCore *core, int argc, const char **argv) { + ut64 address = rz_num_math(core->num, argv[1]); + rz_core_il_step_until_with_events(core, address); + return RZ_CMD_STATUS_OK; +} + RZ_IPI RzCmdStatus rz_il_vm_status_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode) { if (argc == 3) { ut64 value = rz_num_math(core->num, argv[2]); diff --git a/librz/core/cmd_descs/cmd_analysis.yaml b/librz/core/cmd_descs/cmd_analysis.yaml index 6c8b903f11d..e5c8d97cae6 100644 --- a/librz/core/cmd_descs/cmd_analysis.yaml +++ b/librz/core/cmd_descs/cmd_analysis.yaml @@ -990,6 +990,13 @@ commands: args: - name: address type: RZ_CMD_ARG_TYPE_RZNUM + - name: aezsue + summary: Step until PC equals given address and output VM changes (read & write) + type: RZ_CMD_DESC_TYPE_ARGV + cname: il_vm_step_until_addr_with_events + args: + - name: address + type: RZ_CMD_ARG_TYPE_RZNUM - name: aezv summary: Print or modify the current status of the RzIL Virtual Machine cname: il_vm_status diff --git a/librz/core/cmd_descs/cmd_descs.c b/librz/core/cmd_descs/cmd_descs.c index 986f4f2fb9e..534e412f2c9 100644 --- a/librz/core/cmd_descs/cmd_descs.c +++ b/librz/core/cmd_descs/cmd_descs.c @@ -213,6 +213,7 @@ static const RzCmdDescArg il_step_until_opt_args[2]; static const RzCmdDescArg il_vm_step_args[2]; static const RzCmdDescArg il_vm_step_with_events_args[2]; static const RzCmdDescArg il_vm_step_until_addr_args[2]; +static const RzCmdDescArg il_vm_step_until_addr_with_events_args[2]; static const RzCmdDescArg il_vm_status_args[3]; static const RzCmdDescArg analysis_graph_dataref_args[2]; static const RzCmdDescArg analysis_graph_dataref_global_args[2]; @@ -4046,6 +4047,20 @@ static const RzCmdDescHelp il_vm_step_until_addr_help = { .args = il_vm_step_until_addr_args, }; +static const RzCmdDescArg il_vm_step_until_addr_with_events_args[] = { + { + .name = "address", + .type = RZ_CMD_ARG_TYPE_RZNUM, + .flags = RZ_CMD_ARG_FLAG_LAST, + + }, + { 0 }, +}; +static const RzCmdDescHelp il_vm_step_until_addr_with_events_help = { + .summary = "Step until PC equals given address and output VM changes (read & write)", + .args = il_vm_step_until_addr_with_events_args, +}; + static const RzCmdDescArg il_vm_status_args[] = { { .name = "var_name", @@ -19727,6 +19742,9 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) { RzCmdDesc *il_vm_step_until_addr_cd = rz_cmd_desc_argv_new(core->rcmd, aez_cd, "aezsu", rz_il_vm_step_until_addr_handler, &il_vm_step_until_addr_help); rz_warn_if_fail(il_vm_step_until_addr_cd); + RzCmdDesc *il_vm_step_until_addr_with_events_cd = rz_cmd_desc_argv_new(core->rcmd, aez_cd, "aezsue", rz_il_vm_step_until_addr_with_events_handler, &il_vm_step_until_addr_with_events_help); + rz_warn_if_fail(il_vm_step_until_addr_with_events_cd); + RzCmdDesc *il_vm_status_cd = rz_cmd_desc_argv_modes_new(core->rcmd, aez_cd, "aezv", RZ_OUTPUT_MODE_STANDARD | RZ_OUTPUT_MODE_TABLE | RZ_OUTPUT_MODE_JSON | RZ_OUTPUT_MODE_QUIET, rz_il_vm_status_handler, &il_vm_status_help); rz_warn_if_fail(il_vm_status_cd); diff --git a/librz/core/cmd_descs/cmd_descs.h b/librz/core/cmd_descs/cmd_descs.h index 153ccfe08b3..0c79d9df930 100644 --- a/librz/core/cmd_descs/cmd_descs.h +++ b/librz/core/cmd_descs/cmd_descs.h @@ -454,6 +454,8 @@ RZ_IPI RzCmdStatus rz_il_vm_step_handler(RzCore *core, int argc, const char **ar RZ_IPI RzCmdStatus rz_il_vm_step_with_events_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode); // "aezsu" RZ_IPI RzCmdStatus rz_il_vm_step_until_addr_handler(RzCore *core, int argc, const char **argv); +// "aezsue" +RZ_IPI RzCmdStatus rz_il_vm_step_until_addr_with_events_handler(RzCore *core, int argc, const char **argv); // "aezv" RZ_IPI RzCmdStatus rz_il_vm_status_handler(RzCore *core, int argc, const char **argv, RzOutputMode mode); // "aga" diff --git a/librz/il/il_vm_eval.c b/librz/il/il_vm_eval.c index 64ed1b04012..901f9b14330 100644 --- a/librz/il/il_vm_eval.c +++ b/librz/il/il_vm_eval.c @@ -261,8 +261,12 @@ RZ_API void rz_il_vm_mem_storew(RzILVM *vm, RzILMemIndex index, RzBitVector *key return; } RzBitVector *old_value = rz_il_mem_loadw(mem, key, rz_bv_len(value), vm->big_endian); - rz_il_mem_storew(mem, key, value, vm->big_endian); + if (!rz_il_mem_storew(mem, key, value, vm->big_endian)) { + RZ_LOG_ERROR("StoreW mem %u 0x%llx failed\n", (unsigned int)index, rz_bv_to_ut64(key)); + goto end; + } rz_il_vm_event_add(vm, rz_il_event_mem_write_new(key, old_value, value)); +end: rz_bv_free(old_value); } diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 31c97a4cfde..9ce050131c6 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1700,13 +1700,20 @@ RZ_API RZ_OWN RzAnalysisILConfig *rz_analysis_il_config_new(ut32 pc_size, bool b RZ_API void rz_analysis_il_config_free(RzAnalysisILConfig *cfg); RZ_API void rz_analysis_il_config_add_label(RZ_NONNULL RzAnalysisILConfig *cfg, RZ_NONNULL RZ_OWN RzILEffectLabel *label); +typedef bool (*RzAnalysisILVMCondCallback)(RzAnalysisILVM *vm, void *user); + RZ_API RZ_OWN RzAnalysisILVM *rz_analysis_il_vm_new(RzAnalysis *a, RZ_NULLABLE RzReg *init_state_reg); RZ_API void rz_analysis_il_vm_free(RZ_NULLABLE RzAnalysisILVM *vm); RZ_API void rz_analysis_il_vm_sync_from_reg(RzAnalysisILVM *vm, RZ_NONNULL RzReg *reg); RZ_API bool rz_analysis_il_vm_sync_to_reg(RzAnalysisILVM *vm, RZ_NONNULL RzReg *reg); -RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg); -RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step_while(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg, - bool (*cond)(RzAnalysisILVM *vm, void *user), void *user); +RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step( + RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg); +RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step_while( + RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg, + RZ_NONNULL RzAnalysisILVMCondCallback cond, RZ_NULLABLE void *user); +RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step_while_with_events( + RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg, + RZ_NONNULL RzAnalysisILVMCondCallback cond, RZ_NULLABLE void *user); RZ_API bool rz_analysis_il_vm_setup(RzAnalysis *analysis); RZ_API void rz_analysis_il_vm_cleanup(RzAnalysis *analysis); diff --git a/librz/include/rz_core.h b/librz/include/rz_core.h index 738a656e492..2bed94ef9c8 100644 --- a/librz/include/rz_core.h +++ b/librz/include/rz_core.h @@ -672,6 +672,7 @@ RZ_API void rz_core_analysis_esil_init_regs(RZ_NONNULL RzCore *core); RZ_API void rz_core_analysis_il_reinit(RZ_NONNULL RzCore *core); RZ_API bool rz_core_il_step(RZ_NONNULL RzCore *core, ut64 n); RZ_API bool rz_core_il_step_until(RZ_NONNULL RzCore *core, ut64 until); +RZ_API bool rz_core_il_step_until_with_events(RZ_NONNULL RzCore *core, ut64 until); /* canalysis.c */ typedef enum rz_core_analysis_name_type {