From d4932d3d2c8a4e2b83b5c78d04e9abfe7e7f1f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Fri, 6 Jan 2023 15:33:24 +0100 Subject: [PATCH] Avoid syncing regs in between multiple IL steps When stepping multiple times in the analysis RzIL vm, it is not necessary to sync the regs from and to RzReg between every instruction, but only at the beginning and at the end. The conditions for stepping until an addr and fixed-count stepping are handled as callbacks inside rz_analysis_il_vm_step_while(), so the sync logic does not have to be exposed to the caller. --- librz/analysis/il/analysis_il.c | 74 +++++++++++++++++++++++---------- librz/core/cil.c | 64 ++++++++++++++++++++++++---- librz/core/cmd/cmd_analysis.c | 26 +----------- librz/core/core_private.h | 3 +- librz/include/rz_analysis.h | 2 + 5 files changed, 116 insertions(+), 53 deletions(-) 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);