Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RzIL: add aezsue to support Step until PC equals given address and output VM changes #4466

Merged
merged 3 commits into from
May 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 85 additions & 24 deletions librz/arch/il/analysis_il.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
imbillow marked this conversation as resolved.
Show resolved Hide resolved
ut8 code[32] = { 0 };
analysis->read_at(analysis, addr, code, sizeof(code));
RzAnalysisOp op = { 0 };
rz_analysis_op_init(&op);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move it outside

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get it. It seems to me that rz_analysis_op_init needs to be called before every call to rz_analysis_op and rz_analysis_op_fini needs to be called after every call to rz_analysis_op.

So ‘Move it outside’ doesn't work? @pelijah

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, rz_analysis_op_fini() doesn't work as expected so leave it as is.

Copy link
Contributor

@pelijah pelijah May 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@XVilka We definetely should standardize *_fini() APIs behaviour.

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) {
Expand Down
17 changes: 17 additions & 0 deletions librz/core/cil.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions librz/core/cmd/cmd_analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
7 changes: 7 additions & 0 deletions librz/core/cmd_descs/cmd_analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions librz/core/cmd_descs/cmd_descs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions librz/core/cmd_descs/cmd_descs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 5 additions & 1 deletion librz/il/il_vm_eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
imbillow marked this conversation as resolved.
Show resolved Hide resolved
goto end;
}
rz_il_vm_event_add(vm, rz_il_event_mem_write_new(key, old_value, value));
end:
rz_bv_free(old_value);
}

Expand Down
13 changes: 10 additions & 3 deletions librz/include/rz_analysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions librz/include/rz_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading