diff --git a/librz/core/disasm.c b/librz/core/disasm.c index 9b196fd4c3..a68512a28f 100644 --- a/librz/core/disasm.c +++ b/librz/core/disasm.c @@ -319,6 +319,7 @@ static char *ds_esc_str(RzDisasmState *ds, const char *str, int len, const char static void ds_print_ptr(RzDisasmState *ds, int len, int idx); static void ds_print_str(RzDisasmState *ds, const char *str, int len, ut64 refaddr); static void ds_opstr_sub_jumps(RzDisasmState *ds); +static void ds_opstr_resolve_aav_symbols(RzDisasmState *ds); static void ds_start_line_highlight(RzDisasmState *ds); static void ds_end_line_highlight(RzDisasmState *ds); static bool line_highlighted(RzDisasmState *ds); @@ -876,6 +877,54 @@ static void __replaceImports(RzDisasmState *ds) { } } +static void ds_opstr_resolve_aav_symbols(RzDisasmState *ds) { + if (!ds->opstr || !ds->core || !ds->core->flags || !ds->core->io || !ds->analysis_op.refptr) { + return; + } + RzCore *core = ds->core; + const char *pattern = "aav.aav."; + char *pos = strstr(ds->opstr, pattern); + if (!pos) { + return; + } + + char *addr_start = pos + strlen(pattern); + char *addr_end = addr_start; + while (*addr_end == 'x' || IS_HEXCHAR(*addr_end)) { + addr_end++; + } + if (addr_end == addr_start) { + return; + } + + char addr_str[32] = { 0 }; + const size_t addr_len = RZ_MIN((size_t)(addr_end - addr_start), sizeof(addr_str) - 1); + memcpy(addr_str, addr_start, addr_len); + ut64 aav_addr = rz_num_get(NULL, addr_str); + if (!aav_addr || aav_addr < ds->min_ref_addr) { + return; + } + + RzFlagItem *f1 = rz_flag_get_preferred_item(core->flags, aav_addr); + if (!f1 || !rz_str_startswith(f1->name, "aav.")) { + return; + } + + ut8 hop_buf[sizeof(ut64)] = { 0 }; + if (!rz_io_read_at_mapped(core->io, aav_addr, hop_buf, ds->analysis_op.refptr)) { + return; + } + ut64 dereferenced = rz_read_ble(hop_buf, core->print->big_endian, ds->analysis_op.refptr * 8); + RzFlagItem *f2 = rz_flag_get_preferred_item(core->flags, dereferenced); + if (!f2 || rz_str_startswith(f2->name, "aav.")) { + return; + } + + char pattern_to_replace[64] = { 0 }; + rz_strf(pattern_to_replace, "aav.aav.%s", addr_str); + ds->opstr = rz_str_replace(ds->opstr, pattern_to_replace, f2->name, 1); +} + static void ds_opstr_try_colorize(RzDisasmState *ds, bool print_color) { bool colorize_asm = print_color && ds->show_color && ds->colorop; if (!colorize_asm) { @@ -1054,6 +1103,7 @@ static void ds_build_op_str(RzDisasmState *ds, bool print_color) { core->parser->flagspace = ofs; free(ds->opstr); ds->opstr = rz_str_dup(ds->str); + ds_opstr_resolve_aav_symbols(ds); } else { ds_opstr_try_colorize(ds, print_color); } diff --git a/librz/flag/flag.c b/librz/flag/flag.c index f832ada926..d728ef513d 100644 --- a/librz/flag/flag.c +++ b/librz/flag/flag.c @@ -436,6 +436,47 @@ beach: return ret ? evalFlag(f, ret) : NULL; } +static bool is_auto_aav_flag(const RzFlagItem *flag) { + return flag && !RZ_STR_ISEMPTY(flag->name) && rz_str_startswith(flag->name, "aav."); +} + +/** + * \brief Get the preferred flag item at an offset. + * + * The preferred item follows the standard space priority and avoids returning + * auto-generated `aav.*` entries when a non-`aav.*` fallback exists at the + * same offset. + * + * \param f The flag instance. + * \param off The offset to query. + * + * \return The preferred flag item, or NULL if none exists. + */ +RZ_API RZ_BORROW RzFlagItem *rz_flag_get_preferred_item(RZ_NONNULL RzFlag *f, ut64 off) { + rz_return_val_if_fail(f, NULL); + + RzFlagItem *preferred = rz_flag_get_by_spaces(f, off, + "symbols", + "imports", + "relocs", + "symbols.sections", + "functions", + "globals", + "strings", + "resources", + "sections", + "segments", + NULL); + if (!preferred || !is_auto_aav_flag(preferred)) { + return preferred; + } + RzFlagItem *fallback = rz_flag_get_i(f, off); + if (fallback && !is_auto_aav_flag(fallback)) { + return fallback; + } + return preferred; +} + static bool isFunctionFlag(const char *n) { return (!strncmp(n, "sym.func.", 9) || !strncmp(n, "method.", 7) || !strncmp(n, "sym.", 4) || !strncmp(n, "func.", 5) || !strncmp(n, "fcn.0", 5)); } diff --git a/librz/include/rz_flag.h b/librz/include/rz_flag.h index a6cb9f2ca3..75cd9120c7 100644 --- a/librz/include/rz_flag.h +++ b/librz/include/rz_flag.h @@ -101,6 +101,7 @@ RZ_API bool rz_flag_exist_at(RzFlag *f, const char *flag_prefix, ut16 fp_size, u RZ_API RzFlagItem *rz_flag_get(RzFlag *f, const char *name); RZ_API RzFlagItem *rz_flag_get_i(RzFlag *f, ut64 off); RZ_API RzFlagItem *rz_flag_get_by_spaces(RzFlag *f, ut64 off, ...); +RZ_API RZ_BORROW RzFlagItem *rz_flag_get_preferred_item(RZ_NONNULL RzFlag *f, ut64 off); RZ_API RzFlagItem *rz_flag_get_at(RzFlag *f, ut64 off, bool closest); RZ_API RZ_BORROW RzFlagItem *rz_flag_get_at_by_spaces(RZ_NONNULL RzFlag *f, bool closest, ut64 off, ...); RZ_API RzList /**/ *rz_flag_all_list(RzFlag *f, bool by_space); diff --git a/test/unit/test_analysis_op.c b/test/unit/test_analysis_op.c index 1d9c0237d8..87a937452e 100644 --- a/test/unit/test_analysis_op.c +++ b/test/unit/test_analysis_op.c @@ -180,10 +180,63 @@ bool test_rz_core_print_disasm() { mu_end; } +bool test_rz_core_print_disasm_resolve_aav_symbols() { + RzCore *core = rz_core_new(); + mu_assert_notnull(core, "rz_core_new failed"); + RzIODesc *io_desc = rz_io_open_at(core->io, "malloc://0x200000", RZ_PERM_RWX, 0644, 0, NULL); + mu_assert_notnull(io_desc, "io open failed"); + bool arch_configured = rz_core_arch_configure(core, "x86", 32, NULL, NULL, NULL); + mu_assert_true(arch_configured, "rz_core_arch_configure failed"); + bool analysis_arch_set = rz_analysis_use(core->analysis, "x86"); + mu_assert_true(analysis_arch_set, "rz_analysis_use failed"); + bool analysis_bits_set = rz_analysis_set_bits(core->analysis, 32); + mu_assert_true(analysis_bits_set, "rz_analysis_set_bits failed"); + rz_config_set_i(core->config, "scr.color", 0); + rz_config_set_b(core->config, "asm.sub.names", true); + rz_config_set_b(core->config, "asm.sub.rel", true); + rz_config_set_i(core->config, "asm.sub.varmin", 0); + + ut8 code[] = { 0xa1, 0x20, 0x00, 0x10, 0x00 }; // mov eax, dword [0x100020] + ut8 ptr[] = { 0x40, 0x00, 0x10, 0x00 }; // *(0x100020) = 0x100040 + rz_io_write_at(core->io, 0x100020, ptr, sizeof(ptr)); + + rz_flag_space_set(core->flags, "symbols"); + rz_flag_set(core->flags, "aav.0x00100020", 0x100020, 4); + rz_flag_set(core->flags, "obj.__stack_chk_guard", 0x100040, 4); + + RzPVector *vec = rz_pvector_new((RzPVectorFree)rz_analysis_disasm_text_free); + mu_assert_notnull(vec, "rz_core_print_disasm vec not null"); + RzCoreDisasmOptions options = { + .vec = vec, + .cbytes = 1, + }; + rz_core_print_disasm(core, 0, code, sizeof(code), sizeof(code), NULL, &options); + + size_t line_count = rz_pvector_len(vec); + mu_assert_true(line_count >= 1, "rz_core_print_disasm should produce at least one line"); + RzAnalysisDisasmText *t = rz_pvector_at(vec, 0); + mu_assert_notnull(t, "first disasm line"); + char *insn = rz_str_dup(t->text); + mu_assert_notnull(insn, "instruction line copy"); + char *comment = strchr(insn, ';'); + if (comment) { + *comment = '\0'; + } + mu_assert_strcontains(insn, "obj.__stack_chk_guard", "aav.aav symbol should be resolved to preferred symbol"); + char *aav_symbol = strstr(insn, "aav.aav."); + mu_assert_null(aav_symbol, "aav.aav symbol should not be present in instruction operand"); + free(insn); + + rz_core_free(core); + rz_pvector_free(vec); + mu_end; +} + int all_tests() { mu_run_test(test_rz_analysis_op_val); mu_run_test(test_rz_core_analysis_bytes); mu_run_test(test_rz_core_print_disasm); + mu_run_test(test_rz_core_print_disasm_resolve_aav_symbols); return tests_passed != tests_run; } diff --git a/test/unit/test_flags.c b/test/unit/test_flags.c index 1afff21745..89119dc994 100644 --- a/test/unit/test_flags.c +++ b/test/unit/test_flags.c @@ -169,11 +169,68 @@ bool test_rz_flag_set_next() { mu_end; } +bool test_rz_flag_get_preferred_item() { + RzFlag *flags = rz_flag_new(); + mu_assert_notnull(flags, "rz_flag_new() failed"); + + ut64 off = 0x1337; + rz_flag_space_set(flags, "symbols"); + rz_flag_set(flags, "sym.real_name", off, 0); + rz_flag_space_set(flags, "custom"); + rz_flag_set(flags, "custom.name", off, 0); + + RzFlagItem *fi = rz_flag_get_preferred_item(flags, off); + mu_assert_notnull(fi, "preferred flag should exist"); + mu_assert_streq(fi->name, "sym.real_name", "must prefer symbol space item"); + + rz_flag_free(flags); + mu_end; +} + +bool test_rz_flag_get_preferred_item_fallback_non_aav() { + RzFlag *flags = rz_flag_new(); + mu_assert_notnull(flags, "rz_flag_new() failed"); + + ut64 off = 0x4242; + rz_flag_space_set(flags, "symbols"); + rz_flag_set(flags, "aav.0x00004242", off, 0); + rz_flag_space_set(flags, "custom"); + rz_flag_set(flags, "obj.__stack_chk_guard", off, 0); + + RzFlagItem *fi = rz_flag_get_preferred_item(flags, off); + mu_assert_notnull(fi, "preferred flag should exist"); + mu_assert_streq(fi->name, "obj.__stack_chk_guard", "must fallback to non-aav item"); + + rz_flag_free(flags); + mu_end; +} + +bool test_rz_flag_get_preferred_item_keep_aav_when_only_aav() { + RzFlag *flags = rz_flag_new(); + mu_assert_notnull(flags, "rz_flag_new() failed"); + + ut64 off = 0x5151; + rz_flag_space_set(flags, "symbols"); + rz_flag_set(flags, "aav.0x00005151", off, 0); + rz_flag_space_set(flags, "custom"); + rz_flag_set(flags, "aav.0x0000beef", off, 0); + + RzFlagItem *fi = rz_flag_get_preferred_item(flags, off); + mu_assert_notnull(fi, "preferred flag should exist"); + mu_assert_streq(fi->name, "aav.0x00005151", "must keep preferred aav when no non-aav fallback exists"); + + rz_flag_free(flags); + mu_end; +} + int all_tests(void) { mu_run_test(test_rz_flag_get_set); mu_run_test(test_rz_flag_by_spaces); mu_run_test(test_rz_flag_get_at); mu_run_test(test_rz_flag_set_next); + mu_run_test(test_rz_flag_get_preferred_item); + mu_run_test(test_rz_flag_get_preferred_item_fallback_non_aav); + mu_run_test(test_rz_flag_get_preferred_item_keep_aav_when_only_aav); return tests_passed != tests_run; }