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.
This commit is contained in:
Florian Märkl 2023-01-06 15:33:24 +01:00 committed by Anton Kochkov
parent d6bd4a57b0
commit d4932d3d2c
5 changed files with 116 additions and 53 deletions

View file

@ -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);
}
/// @}
/////////////////////////////////////////////////////////

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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);