mirror of
https://github.com/radareorg/r2hermes.git
synced 2026-11-07 06:26:01 -10:00
Fix slp address location, honor io.va, baddr and construction instruction
This commit is contained in:
1 parent
e073f29cb3
commit
6a243fbc1e
5 files changed
+89
-54
No files matched your search
@@ -87,6 +87,12 @@ Usage: r2hermes[-arg] # see also pd:h for decompilation
|
||||
r2hermes-S[jr?] - emit SBOM from SLP literals (j=CycloneDX JSON, r=raw input)
|
||||
```
|
||||
|
||||
`r2hermes-L` lists the constructor instruction addresses after `xrefs=`.
|
||||
Object literal addresses point to their serialized values; their keys or shapes
|
||||
can be shared by objects with different values. Pools can also share values, so
|
||||
`r2hermes-Lx <addr>` lists every matching entry. Use the constructor addresses
|
||||
on each row to navigate to that specific object. Text output follows `io.va`.
|
||||
|
||||
### Examples
|
||||
|
||||
```bash
|
||||
|
||||
@@ -40,7 +40,7 @@ typedef struct {
|
||||
u32 num_items;
|
||||
u32 primary_id; /* array_id (array) or keys/shape id (object) */
|
||||
u32 secondary_id; /* values_id for objects, 0 for arrays */
|
||||
u32 paddr; /* absolute file offset of the literal data in the SLP pool (0 if unknown) */
|
||||
u32 paddr; /* absolute file offset of array data or object values (0 if unknown) */
|
||||
char *formatted; /* cached JS literal text; NULL if not yet formatted */
|
||||
u32 *xref_addrs; /* instruction addresses that construct this literal */
|
||||
u32 xref_count;
|
||||
|
||||
+5
-14
@@ -133,9 +133,8 @@ static HBCLiteralEntry *cache_find(HBCLiteralCache *c, HBCLiteralKind kind, u32
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Compute paddr for an entry based on its kind/primary_id. For v97+ objects,
|
||||
* primary_id is a shape index — resolve via the shape table. */
|
||||
static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id) {
|
||||
/* Anchor objects at their values, not their shared keys/shape. */
|
||||
static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id, u32 secondary_id) {
|
||||
const HBCReader *r = &hbc->reader;
|
||||
if (kind == HBC_LIT_ARRAY) {
|
||||
if (!r->arrays) {
|
||||
@@ -146,18 +145,10 @@ static u32 entry_paddr(HBC *hbc, HBCLiteralKind kind, u32 primary_id) {
|
||||
}
|
||||
return r->arrays_paddr + primary_id;
|
||||
}
|
||||
/* object */
|
||||
if (r->header.version >= 97 && r->object_shapes && primary_id < r->object_shape_count) {
|
||||
u32 key_off = r->object_shapes[primary_id].key_buffer_offset;
|
||||
if (r->object_keys && key_off < r->header.objKeyBufferSize) {
|
||||
return r->object_keys_paddr + key_off;
|
||||
}
|
||||
if (!r->object_values || secondary_id >= r->header.objValueBufferSize) {
|
||||
return 0;
|
||||
}
|
||||
if (!r->object_keys || primary_id >= r->header.objKeyBufferSize) {
|
||||
return 0;
|
||||
}
|
||||
return r->object_keys_paddr + primary_id;
|
||||
return r->object_values_paddr + secondary_id;
|
||||
}
|
||||
|
||||
static Result cache_get_or_create(HBC *hbc, HBCLiteralKind kind, u32 num_items, u32 primary_id, u32 secondary_id, bool format, HBCLiteralEntry **out_entry) {
|
||||
@@ -182,7 +173,7 @@ static Result cache_get_or_create(HBC *hbc, HBCLiteralKind kind, u32 num_items,
|
||||
e->num_items = num_items;
|
||||
e->primary_id = primary_id;
|
||||
e->secondary_id = secondary_id;
|
||||
e->paddr = entry_paddr (hbc, kind, primary_id);
|
||||
e->paddr = entry_paddr (hbc, kind, primary_id, secondary_id);
|
||||
if (format) {
|
||||
char *txt = NULL;
|
||||
Result fr = format_raw_impl (&hbc->reader, kind, num_items, primary_id, secondary_id, &txt);
|
||||
|
||||
+32
-38
@@ -741,7 +741,8 @@ static void register_r2_artifacts(RCore *core, const HBCLiteralEntry *e) {
|
||||
if (!e->paddr) {
|
||||
return;
|
||||
}
|
||||
ut64 vaddr = HBC_VADDR_BASE + (ut64)e->paddr;
|
||||
ut64 base = r_config_get_b (core->config, "io.va")? HBC_VADDR_BASE: 0;
|
||||
ut64 vaddr = base + e->paddr;
|
||||
char flag_name[64];
|
||||
snprintf (flag_name, sizeof (flag_name), "%s0x%x", lit_kind_prefix (e->kind), e->paddr);
|
||||
r_flag_set (core->flags, flag_name, vaddr, 1);
|
||||
@@ -749,7 +750,7 @@ static void register_r2_artifacts(RCore *core, const HBCLiteralEntry *e) {
|
||||
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, vaddr, e->formatted);
|
||||
}
|
||||
for (u32 i = 0; i < e->xref_count; i++) {
|
||||
ut64 from = HBC_VADDR_BASE + (ut64)e->xref_addrs[i];
|
||||
ut64 from = base + e->xref_addrs[i];
|
||||
r_anal_xrefs_set (core->anal, from, vaddr, R_ANAL_REF_TYPE_DATA);
|
||||
}
|
||||
}
|
||||
@@ -812,9 +813,10 @@ static void cmd_lit_list_quiet(HbcContext *ctx, RCore *core) {
|
||||
if (r.code != RESULT_SUCCESS) {
|
||||
R_LOG_ERROR ("%s", safe_errmsg (r.error_message));
|
||||
} else {
|
||||
ut64 base = r_config_get_b (core->config, "io.va")? HBC_VADDR_BASE: 0;
|
||||
for (u32 i = 0; i < n; i++) {
|
||||
const HBCLiteralEntry *e = &arr[i];
|
||||
const ut64 addr = (ut64)HBC_VADDR_BASE + e->paddr;
|
||||
const ut64 addr = base + e->paddr;
|
||||
const char *fmt = e->formatted? e->formatted: "";
|
||||
r_cons_printf (core->cons, "0x%" PFMT64x " %s\n", addr, fmt);
|
||||
}
|
||||
@@ -845,18 +847,16 @@ static void cmd_lit_list_xrefs(HbcContext *ctx, RCore *core, const char *args) {
|
||||
ut64 want = r_num_get (core->num, args);
|
||||
ut64 base = va? (ut64)HBC_VADDR_BASE: 0;
|
||||
ut64 target_paddr = (want >= base)? (want - base): want;
|
||||
const HBCLiteralEntry *match = NULL;
|
||||
bool found = false;
|
||||
for (u32 i = 0; i < n; i++) {
|
||||
if (arr[i].paddr == target_paddr) {
|
||||
match = &arr[i];
|
||||
break;
|
||||
print_xref_line (core, &arr[i], va);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
if (!found) {
|
||||
R_LOG_ERROR ("no literal at that address");
|
||||
return;
|
||||
}
|
||||
print_xref_line (core, match, va);
|
||||
return;
|
||||
}
|
||||
for (u32 i = 0; i < n; i++) {
|
||||
@@ -904,19 +904,23 @@ static void cmd_lit_list(HbcContext *ctx, RCore *core, bool as_json) {
|
||||
}
|
||||
r_cons_printf (core->cons, "literals: %u\n", n);
|
||||
bool va = r_config_get_b (core->config, "io.va");
|
||||
ut64 base = va? HBC_VADDR_BASE: 0;
|
||||
for (u32 i = 0; i < n; i++) {
|
||||
const HBCLiteralEntry *e = &arr[i];
|
||||
const char *pavastr = va? "vaddr": "paddr";
|
||||
r_cons_printf (core->cons,
|
||||
"%-6s n=%-4u id=(%u,%u) %s=0x%08" PFMT64x " xrefs=%u %s\n",
|
||||
"%-6s n=%-4u id=(%u,%u) %s=0x%08" PFMT64x " xrefs=%u",
|
||||
kind_label (e->kind),
|
||||
e->num_items,
|
||||
e->primary_id,
|
||||
e->secondary_id,
|
||||
pavastr,
|
||||
(ut64)HBC_VADDR_BASE + e->paddr,
|
||||
e->xref_count,
|
||||
e->formatted? e->formatted: "");
|
||||
base + e->paddr,
|
||||
e->xref_count);
|
||||
for (u32 j = 0; j < e->xref_count; j++) {
|
||||
r_cons_printf (core->cons, " 0x%" PFMT64x, base + e->xref_addrs[j]);
|
||||
}
|
||||
r_cons_printf (core->cons, " %s\n", e->formatted? e->formatted: "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -957,18 +961,16 @@ static void print_r2_for_entry(RCore *core, const HBCLiteralEntry *e, bool va) {
|
||||
r_cons_printf (core->cons, "f %s0x%x 1 @ 0x%" PFMT64x "\n", prefix, e->paddr, vaddr);
|
||||
/* embed the formatted text as a comment at the literal vaddr */
|
||||
if (e->formatted && *e->formatted) {
|
||||
char *clean = r_str_sanitize_r2 (e->formatted);
|
||||
if (clean) {
|
||||
r_str_replace_char (clean, '\n', ' ');
|
||||
r_str_replace_char (clean, '\r', ' ');
|
||||
r_cons_printf (core->cons, "\" /* %s */ CC %s @ 0x%" PFMT64x "\n", prefix, clean, vaddr);
|
||||
free (clean);
|
||||
char *b64 = r_base64_encode_dyn ((const ut8 *)e->formatted, -1);
|
||||
if (b64) {
|
||||
r_cons_printf (core->cons, "CCu base64:%s @ 0x%" PFMT64x "\n", b64, vaddr);
|
||||
free (b64);
|
||||
}
|
||||
}
|
||||
/* xref from each call site to the literal */
|
||||
for (u32 j = 0; j < e->xref_count; j++) {
|
||||
ut64 from = base + (ut64)e->xref_addrs[j];
|
||||
r_cons_printf (core->cons, "axd 0x%" PFMT64x " 0x%" PFMT64x "\n", from, vaddr);
|
||||
r_cons_printf (core->cons, "axd 0x%" PFMT64x " 0x%" PFMT64x "\n", vaddr, from);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -990,18 +992,16 @@ static void cmd_lit_print_r2(HbcContext *ctx, RCore *core, const char *args) {
|
||||
ut64 want = r_num_get (core->num, args);
|
||||
ut64 base = va? (ut64)HBC_VADDR_BASE: 0;
|
||||
ut64 target_paddr = (want >= base)? (want - base): want;
|
||||
const HBCLiteralEntry *match = NULL;
|
||||
bool found = false;
|
||||
for (u32 i = 0; i < n; i++) {
|
||||
if (arr[i].paddr == target_paddr) {
|
||||
match = &arr[i];
|
||||
break;
|
||||
print_r2_for_entry (core, &arr[i], va);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
if (!found) {
|
||||
R_LOG_ERROR ("no literal at that address");
|
||||
return;
|
||||
}
|
||||
print_r2_for_entry (core, match, va);
|
||||
return;
|
||||
}
|
||||
for (u32 i = 0; i < n; i++) {
|
||||
@@ -1058,20 +1058,19 @@ static void cmd_lit_toggle_inline(RCore *core) {
|
||||
/* Show help */
|
||||
static const char r2hermes_helpmsg[] =
|
||||
"Usage: r2hermes-L[subcmd]\n"
|
||||
" r2hermes-L List cached literals (auto-scans code if cache is empty)\n"
|
||||
" r2hermes-L List cached literals and constructor addresses (auto-scans if empty)\n"
|
||||
" r2hermes-Lj List as JSON (auto-scans if needed)\n"
|
||||
" r2hermes-Lq List as `<vaddr> <formatted>` per line (one per literal)\n"
|
||||
" r2hermes-Lx [addr] List as `<vaddr> <xref1> <xref2> ...` (one per literal)\n"
|
||||
" With addr: print xrefs for that single literal only\n"
|
||||
" r2hermes-Lq List as `<addr> <formatted>` per line (addresses use io.va)\n"
|
||||
" r2hermes-Lx [addr] List as `<addr> <xref1> <xref2> ...` (addresses use io.va)\n"
|
||||
" With addr: print all literals sharing that pool address\n"
|
||||
" r2hermes-Lp[ao] Scan SLP pool (default: arrays; a=arrays, o=objects)\n"
|
||||
" r2hermes-LR Reset literal cache (does not remove r2 flags/comments)\n"
|
||||
" r2hermes-Lr [addr] Print r2 oneliners (f/CC/axd) to register flag+comment+xrefs\n"
|
||||
" With addr: print oneliners for that single literal only\n"
|
||||
" With addr: print all literals sharing that pool address\n"
|
||||
" r2hermes-Lg <k> <n> <primary> [<sec>]\n"
|
||||
" Format a literal from raw params (k=a|o)\n"
|
||||
" r2hermes-Li Toggle inline literal comments in disasm\n"
|
||||
" r2hermes-L? This help\n"
|
||||
"\nDeprecated alias: pd:hL[subcmd]\n";
|
||||
" r2hermes-L? This help\n";
|
||||
|
||||
static void r2hermes_help(RCore *core) {
|
||||
const char msg[] =
|
||||
@@ -1099,7 +1098,6 @@ static void cmd_help(RCore *core) {
|
||||
" pd:ho [id] - Decompile with offsets (addresses) per statement\n"
|
||||
" pd:hoa - Decompile all with offsets\n"
|
||||
" pd:hs [id] - List source-line information\n"
|
||||
" pd:hL[?] - Deprecated alias for r2hermes-L[?]\n"
|
||||
" pd:h? - Show this help\n"
|
||||
"\nNote: r2 comments (CC command) are automatically inlined in decompiler output.\n");
|
||||
}
|
||||
@@ -1227,10 +1225,6 @@ static void cmd_pdh(RCore *core, HbcContext *ctx, const char *arg) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'L': /* pd:hL* — deprecated alias for r2hermes-L* */
|
||||
R_LOG_WARN ("pd:hL is deprecated; use r2hermes-L instead");
|
||||
cmd_literals (ctx, core, arg + 1);
|
||||
break;
|
||||
case 's': /* pd:hs [id] */
|
||||
cmd_source_lines (ctx, core, arg + 1);
|
||||
break;
|
||||
|
||||
+45
-1
@@ -209,9 +209,53 @@ static int test_decode_rejects_bad_overflow_string_index(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_literal_addresses(void) {
|
||||
u8 keys[] = { 0x71, 1, 0, 0, 0 };
|
||||
u8 values[] = { 0x71, 20, 0, 0, 0, 0x71, 36, 0, 0, 0 };
|
||||
ShapeTableEntry shape = { .key_buffer_offset = 0, .prop_count = 1 };
|
||||
for (u32 version = 96; version <= 97; version++) {
|
||||
HBC hbc = { 0 };
|
||||
RVecHBCLiteralEntry_init (&hbc.lit_cache.entries);
|
||||
hbc.reader.header.version = version;
|
||||
hbc.reader.header.arrayBufferSize = sizeof (values);
|
||||
hbc.reader.header.objKeyBufferSize = sizeof (keys);
|
||||
hbc.reader.header.objValueBufferSize = sizeof (values);
|
||||
hbc.reader.arrays = values;
|
||||
hbc.reader.arrays_paddr = 0x100;
|
||||
hbc.reader.object_keys = keys;
|
||||
hbc.reader.object_keys_paddr = 0x200;
|
||||
hbc.reader.object_values = values;
|
||||
hbc.reader.object_values_paddr = version >= 97? 0x100: 0x300;
|
||||
hbc.reader.object_shapes = &shape;
|
||||
hbc.reader.object_shape_count = 1;
|
||||
u32 num_items = version >= 97? 0: 1;
|
||||
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 0, 0x400).code == RESULT_SUCCESS);
|
||||
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 5, 0x410).code == RESULT_SUCCESS);
|
||||
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, 5, 0x420).code == RESULT_SUCCESS);
|
||||
CHECK (hbc_literals_register (&hbc, HBC_LIT_ARRAY, 1, 5, 0, 0x430).code == RESULT_SUCCESS);
|
||||
CHECK (hbc_literals_register (&hbc, HBC_LIT_OBJECT, num_items, 0, sizeof (values), 0x440).code == RESULT_SUCCESS);
|
||||
const HBCLiteralEntry *entries = NULL;
|
||||
u32 count = 0;
|
||||
CHECK (hbc_literals_list (&hbc, &entries, &count).code == RESULT_SUCCESS);
|
||||
CHECK (count == 4);
|
||||
CHECK (entries[0].paddr == hbc.reader.object_values_paddr);
|
||||
CHECK (entries[1].paddr == hbc.reader.object_values_paddr + 5);
|
||||
CHECK (entries[2].paddr == 0x105);
|
||||
CHECK (entries[3].paddr == 0);
|
||||
CHECK (!strcmp (entries[0].formatted, "{1: 20}"));
|
||||
CHECK (!strcmp (entries[1].formatted, "{1: 36}"));
|
||||
CHECK (!strcmp (entries[2].formatted, "[36]"));
|
||||
CHECK (entries[1].xref_count == 2);
|
||||
CHECK (entries[1].xref_addrs[0] == 0x410);
|
||||
CHECK (entries[1].xref_addrs[1] == 0x420);
|
||||
hbc_literals_reset (&hbc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
const char *root = argc > 1? argv[1]: ".";
|
||||
if (test_small_debug_info (root) || test_empty_debug_info (root) || test_exception_handlers (root) || test_opcode_operand_meanings () || test_function_bytecode_bounds (root) || test_decode_rejects_bad_overflow_string_index ()) {
|
||||
if (test_small_debug_info (root) || test_empty_debug_info (root) || test_exception_handlers (root) || test_opcode_operand_meanings () || test_function_bytecode_bounds (root) || test_decode_rejects_bad_overflow_string_index () || test_literal_addresses ()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
Reference in new issue
Block a user