diff --git a/librz/analysis/il/analysis_il.c b/librz/analysis/il/analysis_il.c index 5500b7c848..d346636946 100644 --- a/librz/analysis/il/analysis_il.c +++ b/librz/analysis/il/analysis_il.c @@ -229,15 +229,17 @@ RZ_API bool rz_analysis_il_vm_sync_to_reg(RzAnalysisILVM *vm, RZ_NONNULL RzReg * } /** - * Perform a single step in the VM + * 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 disassembles an instruction at the program counter of the vm and executes it. - * Finally, if no error occured, the contents are optionally synced back to \p reg. + * 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 + * \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(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_return_val_if_fail(analysis && vm, false); RzAnalysisPlugin *cur = analysis->cur; if (!cur || !analysis->read_at) { @@ -247,29 +249,59 @@ RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step(RZ_NONNULL RzAnalysis *anal if (reg) { rz_analysis_il_vm_sync_from_reg(vm, reg); } - ut64 addr = rz_bv_to_ut64(vm->vm->pc); - ut8 code[32] = { 0 }; - analysis->read_at(analysis, addr, code, sizeof(code)); - RzAnalysisOp op = { 0 }; - 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; + 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 }; + 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; - RzAnalysisILStepResult res; - if (ilop) { - bool succ = rz_il_vm_step(vm->vm, ilop, addr + (op.size > 0 ? op.size : 1)); - res = succ ? RZ_ANALYSIS_IL_STEP_RESULT_SUCCESS : RZ_ANALYSIS_IL_STEP_IL_RUNTIME_ERROR; - if (reg) { - rz_analysis_il_vm_sync_to_reg(vm, reg); + 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 { + res = RZ_ANALYSIS_IL_STEP_INVALID_OP; } - } else { - res = RZ_ANALYSIS_IL_STEP_INVALID_OP; - } - rz_analysis_op_fini(&op); + rz_analysis_op_fini(&op); + if (res != RZ_ANALYSIS_IL_STEP_RESULT_SUCCESS) { + break; + } + } + if (reg) { + rz_analysis_il_vm_sync_to_reg(vm, reg); + } return res; } +static bool step_cond_once(RzAnalysisILVM *vm, void *user) { + bool *stepped = user; + if (*stepped) { + return false; + } + *stepped = true; + return true; +} + +/** + * Perform a single step in the VM + * + * If given, this syncs the contents of \p reg into the vm. + * Then it disassembles an instruction at the program counter of the vm and executes it. + * Finally the contents are optionally synced back to \p reg. + * + * \return and indicator for which error occured, if any + */ +RZ_API RzAnalysisILStepResult rz_analysis_il_vm_step(RZ_NONNULL RzAnalysis *analysis, RZ_NONNULL RzAnalysisILVM *vm, RZ_NULLABLE RzReg *reg) { + bool stepped = false; + return rz_analysis_il_vm_step_while(analysis, vm, reg, step_cond_once, &stepped); +} + /// @} ///////////////////////////////////////////////////////// diff --git a/librz/core/cil.c b/librz/core/cil.c index 4b5f2bc18b..86f2870ac6 100644 --- a/librz/core/cil.c +++ b/librz/core/cil.c @@ -622,16 +622,15 @@ RZ_IPI void rz_core_analysis_il_vm_status(RzCore *core, const char *var_name, Rz #undef p_tbl #undef p_pj -/** - * Perform a single step at the PC given by analysis->reg in RzIL - * \return false if an error occured (e.g. invalid op) - */ -RZ_IPI bool rz_core_il_step(RzCore *core) { +static bool step_assert_vm(RzCore *core) { if (!core->analysis || !core->analysis->il_vm) { RZ_LOG_ERROR("RzIL: Run 'aezi' first to initialize the VM\n"); return false; } - RzAnalysisILStepResult r = rz_analysis_il_vm_step(core->analysis, core->analysis->il_vm, core->analysis->reg); + return true; +} + +static bool step_handle_result(RzCore *core, RzAnalysisILStepResult r) { switch (r) { case RZ_ANALYSIS_IL_STEP_RESULT_SUCCESS: rz_core_reg_update_flags(core); @@ -647,12 +646,63 @@ RZ_IPI bool rz_core_il_step(RzCore *core) { return false; } +static bool step_cond_n(RzAnalysisILVM *vm, void *user) { + if (rz_cons_is_breaked()) { + rz_cons_printf("Stepping was interrupted.\n"); + return false; + } + ut64 *n = user; + if (!*n) { + return false; + } + (*n)--; + return true; +} + +/** + * Perform \p n steps starting at the PC given by analysis->reg in RzIL + * \return false if an error occured (e.g. invalid op) + */ +RZ_IPI bool rz_core_il_step(RzCore *core, ut64 n) { + if (!step_assert_vm(core)) { + return false; + } + RzAnalysisILStepResult r = rz_analysis_il_vm_step_while(core->analysis, core->analysis->il_vm, core->analysis->reg, + step_cond_n, &n); + return step_handle_result(core, r); +} + +static bool step_cond_until(RzAnalysisILVM *vm, void *user) { + if (rz_cons_is_breaked()) { + rz_cons_printf("Stepping was interrupted.\n"); + return false; + } + ut64 *until = user; + ut64 pc = rz_bv_to_ut64(vm->vm->pc); + return pc != *until; +} + +/** + * Perform zero or more steps starting at the PC given by analysis->reg in RzIL + * until reaching the given PC + * \param until destination address where to stop + * \return false if an error occured (e.g. invalid op) + */ +RZ_IPI bool rz_core_il_step_until(RzCore *core, ut64 until) { + if (!step_assert_vm(core)) { + return false; + } + RzAnalysisILStepResult r = rz_analysis_il_vm_step_while(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) */ RZ_IPI bool rz_core_analysis_il_step_with_events(RzCore *core, PJ *pj) { - if (!rz_core_il_step(core)) { + if (!rz_core_il_step(core, 1)) { return false; } diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index f5b359fdb9..ce4d6a04ea 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -4504,11 +4504,7 @@ RZ_IPI RzCmdStatus rz_il_vm_initialize_handler(RzCore *core, int argc, const cha RZ_IPI RzCmdStatus rz_il_vm_step_handler(RzCore *core, int argc, const char **argv) { ut64 repeat_times = argc == 1 ? 1 : rz_num_math(NULL, argv[1]); - for (ut64 i = 0; i < repeat_times; ++i) { - if (!rz_core_il_step(core)) { - break; - } - } + rz_core_il_step(core, repeat_times); return RZ_CMD_STATUS_OK; } @@ -4538,25 +4534,7 @@ RZ_IPI RzCmdStatus rz_il_vm_step_with_events_handler(RzCore *core, int argc, con RZ_IPI RzCmdStatus rz_il_vm_step_until_addr_handler(RzCore *core, int argc, const char **argv) { ut64 address = rz_num_math(core->num, argv[1]); - - if (!core->analysis->il_vm) { - RZ_LOG_ERROR("RzIL: the VM is not initialized.\n"); - return RZ_CMD_STATUS_ERROR; - } - - while (1) { - ut64 pc = rz_reg_get_value_by_role(core->analysis->reg, RZ_REG_NAME_PC); - if (pc == address) { - break; - } - if (rz_cons_is_breaked()) { - rz_cons_printf("CTRL+C was pressed.\n"); - break; - } - if (!rz_core_il_step(core)) { - break; - } - } + rz_core_il_step_until(core, address); return RZ_CMD_STATUS_OK; } diff --git a/librz/core/core_private.h b/librz/core/core_private.h index 4fa9937b2c..2475fd1538 100644 --- a/librz/core/core_private.h +++ b/librz/core/core_private.h @@ -28,7 +28,8 @@ RZ_IPI void rz_core_debug_esil_watch_print(RzDebug *dbg, RzCmdStateOutput *state RZ_IPI void rz_core_analysis_il_reinit(RzCore *core); RZ_IPI bool rz_core_analysis_il_vm_set(RzCore *core, const char *var_name, ut64 value); RZ_IPI void rz_core_analysis_il_vm_status(RzCore *core, const char *varname, RzOutputMode mode); -RZ_IPI bool rz_core_il_step(RzCore *core); +RZ_IPI bool rz_core_il_step(RzCore *core, ut64 n); +RZ_IPI bool rz_core_il_step_until(RzCore *core, ut64 until); RZ_IPI bool rz_core_analysis_il_step_with_events(RzCore *core, PJ *pj); RZ_IPI bool rz_core_analysis_var_rename(RzCore *core, const char *name, const char *newname); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 37c4967f95..e0e260c5a2 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -1565,6 +1565,8 @@ 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 bool rz_analysis_il_vm_setup(RzAnalysis *analysis); RZ_API void rz_analysis_il_vm_cleanup(RzAnalysis *analysis);