mirror of
https://github.com/radareorg/r2flutter.git
synced 2026-11-07 22:56:23 -10:00
Include ObjectPool instance strings in -z output
This commit is contained in:
1 parent
bd6f596279
commit
a2fd82106e
6 files changed
+96
-127
No files matched your search
@@ -1915,3 +1915,20 @@ collector can silently miss all slots on either platform.
|
||||
Instance fill offsets are indexed once per cluster while collecting strings.
|
||||
Rescanning a cluster prefix for each PP slot makes large const collections
|
||||
quadratic, even when only immediate fields are read.
|
||||
|
||||
## Reliable string extraction shares the PP instance-field collector
|
||||
|
||||
`-z` previously accepted only direct String ObjectPool entries, so it omitted
|
||||
`privilegedAccess` even after analysis could recover its enum instance field.
|
||||
Reliable extraction now uses the same direct-string and immediate-instance-field
|
||||
collector as PP analysis. This also resolves uncompressed iOS ROData strings
|
||||
and VM base-object strings through that collector.
|
||||
|
||||
Strings remain deduplicated by snapshot string reference. `-xz` distinguishes
|
||||
`object_pool.entry` from `object_pool.instance_field`, recording the containing
|
||||
pool entry and field index. Both reference kinds use the synthetic PP slot as
|
||||
the xref source. The shared extraction makes the separate instance-field xref
|
||||
pass redundant; `-AA` now applies the references from the extracted strings.
|
||||
Regression cases cover the instance-only `AppLifecycleState.detached` name on
|
||||
Android w0rdle and iOS Runner, plus a name (`dark`) reachable both directly and
|
||||
through an instance to check that it is emitted once.
|
||||
+40
-85
@@ -3498,6 +3498,7 @@ typedef struct {
|
||||
void *user;
|
||||
HtUP *seen;
|
||||
ut64 pp_offset;
|
||||
ut64 pp_base;
|
||||
ut64 pool_ref;
|
||||
ut64 pool_index;
|
||||
ut64 entry_index;
|
||||
@@ -3512,6 +3513,7 @@ static void modern_emit_pool_string_ref(ModernPoolStringRefCollector *collector,
|
||||
}
|
||||
ModernPoolStrRef info = {
|
||||
.pp_offset = collector->pp_offset,
|
||||
.pp_base = collector->pp_base,
|
||||
.pool_ref = collector->pool_ref,
|
||||
.pool_index = collector->pool_index,
|
||||
.entry_index = collector->entry_index,
|
||||
@@ -3520,6 +3522,7 @@ static void modern_emit_pool_string_ref(ModernPoolStringRefCollector *collector,
|
||||
.field_index = field_index,
|
||||
.string_ref = string_ref,
|
||||
.string_addr = resolver->string_addr_by_ref[string_ref],
|
||||
.string_flags = resolver->string_flags_by_ref[string_ref],
|
||||
.string_value = resolver->strings_by_ref[string_ref],
|
||||
};
|
||||
collector->cb (&info, collector->user);
|
||||
@@ -3794,6 +3797,9 @@ static bool modern_collect_pool_strrefs_internal(const ModernReq *req, HtUP *pp_
|
||||
.end = m->fill_end,
|
||||
};
|
||||
for (ut64 pool_index = 0; pool_index < m->count; pool_index++) {
|
||||
ut64 pool_paddr = s.cursor;
|
||||
bool has_paddr = modern_vaddr_to_paddr (ctx, s.cursor, &pool_paddr);
|
||||
ut64 pp_base = modern_pp_vaddr_from_paddr (ctx, pool_paddr, has_paddr);
|
||||
ut64 length = 0;
|
||||
if (!cs_read_unsigned (&s, &length)) {
|
||||
ok = false;
|
||||
@@ -3820,6 +3826,7 @@ static bool modern_collect_pool_strrefs_internal(const ModernReq *req, HtUP *pp_
|
||||
.user = user,
|
||||
.seen = seen,
|
||||
.pp_offset = pp_offset,
|
||||
.pp_base = pp_base,
|
||||
.pool_ref = m->start_ref + pool_index,
|
||||
.pool_index = pool_index,
|
||||
.entry_index = entry_index,
|
||||
@@ -3946,118 +3953,66 @@ bool modern_collect_direct_pool_strrefs(const ModernReq *req, HtUP *pp_offsets,
|
||||
return ok && found_pool;
|
||||
}
|
||||
|
||||
static bool modern_object_pool_string_ref_exists(RList *refs, ut64 pool_ref, ut64 entry_index) {
|
||||
if (!refs) {
|
||||
return false;
|
||||
}
|
||||
static bool modern_object_pool_string_ref_exists(RList *refs, const ModernPoolStrRef *info, const char *kind) {
|
||||
RListIter *it;
|
||||
DartStringRef *sr;
|
||||
r_list_foreach (refs, it, sr) {
|
||||
if (sr && sr->object_ref == pool_ref && sr->field_offset == (ut32)entry_index && R_STR_ISNOTEMPTY (sr->kind) && !strcmp (sr->kind, "object_pool.entry")) {
|
||||
if (sr && sr->object_ref == info->pool_ref && sr->field_offset == (ut32)info->entry_index && sr->kind && !strcmp (sr->kind, kind)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void modern_add_object_pool_string_ref(DartStringInfo *si, ut64 pool_ref, ut64 pool_index, ut64 entry_index, ut64 pp_base, ut64 pp_off) {
|
||||
if (!si || !si->references || modern_object_pool_string_ref_exists (si->references, pool_ref, entry_index)) {
|
||||
static void modern_add_object_pool_string_ref(DartStringInfo *si, const ModernPoolStrRef *info) {
|
||||
bool indirect = strcmp (info->target_kind, "object_pool.entry") != 0;
|
||||
const char *kind = indirect? "object_pool.instance_field": "object_pool.entry";
|
||||
if (modern_object_pool_string_ref_exists (si->references, info, kind)) {
|
||||
return;
|
||||
}
|
||||
DartStringRef *sr = R_NEW0 (DartStringRef);
|
||||
sr->object_ref = pool_ref;
|
||||
sr->source_addr = pp_base + pp_off;
|
||||
sr->object_ref = info->pool_ref;
|
||||
sr->source_addr = info->pp_base + info->pp_offset;
|
||||
sr->object_type = DART_REF_OTHER;
|
||||
sr->field_offset = (ut32)entry_index;
|
||||
sr->kind = strdup ("object_pool.entry");
|
||||
sr->object_name = r_str_newf ("pool=%" PRIu64 " index=%" PRIu64 " entry=%" PRIu64 " pp_off=0x%" PFMT64x, (uint64_t)pool_ref, (uint64_t)pool_index, (uint64_t)entry_index, (ut64)pp_off);
|
||||
sr->field_offset = (ut32)info->entry_index;
|
||||
sr->kind = strdup (kind);
|
||||
sr->object_name = r_str_newf ("pool=%" PRIu64 " index=%" PRIu64 " entry=%" PRIu64 " pp_off=0x%" PFMT64x, (uint64_t)info->pool_ref, (uint64_t)info->pool_index, (uint64_t)info->entry_index, info->pp_offset);
|
||||
if (indirect) {
|
||||
sr->object_name = r_str_appendf (sr->object_name, " field=%" PRIu64, (uint64_t)info->field_index);
|
||||
}
|
||||
r_list_append (si->references, sr);
|
||||
}
|
||||
|
||||
static void modern_append_object_pool_string(RList *strings, HtUP *seen_refs, ut64 *ref_counter, const ModernRefResolver *resolver, const ModernEmitCtx *emit, const ModernPoolEntry *entry) {
|
||||
const ut64 string_ref = entry? entry->ref: UT64_MAX;
|
||||
if (!strings || !seen_refs || !resolver || !emit || !entry || string_ref >= resolver->refs_count || R_STR_ISEMPTY (resolver->strings_by_ref[string_ref])) {
|
||||
return;
|
||||
}
|
||||
DartStringInfo *si = ht_up_find (seen_refs, string_ref, NULL);
|
||||
typedef struct {
|
||||
RList *strings;
|
||||
HtUP *seen_refs;
|
||||
ut64 *next_ref;
|
||||
} ModernPoolStringExtractCtx;
|
||||
|
||||
static void modern_append_object_pool_string(const ModernPoolStrRef *info, void *user) {
|
||||
ModernPoolStringExtractCtx *ux = user;
|
||||
DartStringInfo *si = ht_up_find (ux->seen_refs, info->string_ref, NULL);
|
||||
if (!si) {
|
||||
si = R_NEW0 (DartStringInfo);
|
||||
si->ref_id = ref_counter? (*ref_counter)++: 0;
|
||||
si->value = strdup (resolver->strings_by_ref[string_ref]);
|
||||
si->ref_id = ux->next_ref? (*ux->next_ref)++: 0;
|
||||
si->value = strdup (info->string_value);
|
||||
si->length = (ut32)strlen (si->value);
|
||||
si->flags = resolver->string_flags_by_ref[string_ref] | DART_STRING_CANONICAL | DART_STRING_OBJECT_POOL;
|
||||
si->address = resolver->string_addr_by_ref[string_ref];
|
||||
si->flags = info->string_flags | DART_STRING_CANONICAL | DART_STRING_OBJECT_POOL;
|
||||
si->address = info->string_addr;
|
||||
si->category = dart_string_classify_value (si->value);
|
||||
si->references = r_list_newf ((RListFree)dart_string_ref_free);
|
||||
r_list_append (strings, si);
|
||||
ht_up_insert (seen_refs, string_ref, si);
|
||||
r_list_append (ux->strings, si);
|
||||
ht_up_insert (ux->seen_refs, info->string_ref, si);
|
||||
}
|
||||
modern_add_object_pool_string_ref (si, emit->pool_ref, emit->pool_index, entry->index, emit->pp_base, modern_pool_entry_pp_offset (resolver->ctx, entry->index));
|
||||
modern_add_object_pool_string_ref (si, info);
|
||||
}
|
||||
|
||||
bool modern_extract_pool_strings(const ModernReq *req, RList *strings, HtUP *seen, ut64 *next_ref) {
|
||||
DartCtx *ctx = req? req->ctx: NULL;
|
||||
if (!ctx || !strings || !seen || !modern_supported (ctx)) {
|
||||
if (!strings || !seen) {
|
||||
return false;
|
||||
}
|
||||
ModernClusterMeta *meta = modern_parse_cluster_meta (ctx, req->cluster_start, req->cluster_end, req->num_clusters, req->num_base_objects);
|
||||
if (!meta) {
|
||||
return false;
|
||||
}
|
||||
ModernRefResolver resolver = { 0 };
|
||||
bool ok = modern_ref_resolver_init (&resolver, ctx, meta, req->num_clusters, req->num_base_objects);
|
||||
if (!ok) {
|
||||
modern_cluster_meta_free (meta, req->num_clusters);
|
||||
return false;
|
||||
}
|
||||
const ModernCidCache cids = modern_cid_cache_init (ctx->layout);
|
||||
ModernEmitCtx emit = { .ctx = ctx };
|
||||
for (ut64 i = 0; i < req->num_clusters; i++) {
|
||||
ModernClusterMeta *m = &meta[i];
|
||||
if (m->fill_kind != MODERN_FILL_OBJECT_POOL || !m->fill_parsed || !m->fill_ok || m->fill_offset >= m->fill_end || !m->count) {
|
||||
continue;
|
||||
}
|
||||
ClusterStream s = {
|
||||
.ctx = ctx,
|
||||
.cursor = m->fill_offset,
|
||||
.end = m->fill_end,
|
||||
};
|
||||
for (ut64 pool_index = 0; pool_index < m->count; pool_index++) {
|
||||
emit.pool_index = pool_index;
|
||||
emit.pool_ref = m->start_ref + pool_index;
|
||||
ut64 pool_paddr = s.cursor;
|
||||
bool has_paddr = modern_vaddr_to_paddr (ctx, s.cursor, &pool_paddr);
|
||||
emit.pp_base = modern_pp_vaddr_from_paddr (ctx, pool_paddr, has_paddr);
|
||||
ut64 length = 0;
|
||||
if (!cs_read_unsigned (&s, &length)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
for (ut64 entry_index = 0; entry_index < length; entry_index++) {
|
||||
ModernPoolEntry entry;
|
||||
if (!modern_read_pool_entry (&s, ctx, entry_index, &entry)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (entry.behavior != 0 || entry.type != 1 || entry.ref >= resolver.refs_count) {
|
||||
continue;
|
||||
}
|
||||
const ModernClusterMeta *target = modern_resolver_cluster_for_ref (&resolver, entry.ref);
|
||||
if (target && modern_is_string_cid (&cids, target->cid)) {
|
||||
modern_append_object_pool_string (strings, seen, next_ref, &resolver, &emit, &entry);
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
modern_ref_resolver_fini (&resolver);
|
||||
modern_cluster_meta_free (meta, req->num_clusters);
|
||||
return ok;
|
||||
ModernPoolStringExtractCtx ux = { strings, seen, next_ref };
|
||||
return modern_collect_pool_value_strrefs (req, NULL, modern_append_object_pool_string, &ux);
|
||||
}
|
||||
|
||||
// Walk every direct (tagged, snapshotable) ObjectPool entry once and hand the
|
||||
|
||||
@@ -169,6 +169,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
ut64 pp_offset;
|
||||
ut64 pp_base;
|
||||
ut64 pool_ref;
|
||||
ut64 pool_index;
|
||||
ut64 entry_index;
|
||||
@@ -177,6 +178,7 @@ typedef struct {
|
||||
ut64 field_index;
|
||||
ut64 string_ref;
|
||||
ut64 string_addr;
|
||||
ut32 string_flags;
|
||||
const char *string_value;
|
||||
} ModernPoolStrRef;
|
||||
|
||||
|
||||
@@ -1016,47 +1016,6 @@ char *dart_pool_dump_xrefs(DartCtx *ctx, int fmt) {
|
||||
return out;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
RCore *core;
|
||||
ut64 pp_base;
|
||||
int applied;
|
||||
} DartPoolValueXrefCtx;
|
||||
|
||||
static void xref_pool_value_string_cb(const ModernPoolStrRef *info, void *user) {
|
||||
DartPoolValueXrefCtx *ux = user;
|
||||
if (!info->string_addr || !strcmp (info->target_kind, "object_pool.entry")) {
|
||||
return;
|
||||
}
|
||||
r_anal_xrefs_set (ux->core->anal, ux->pp_base + info->pp_offset, info->string_addr, R_ANAL_REF_TYPE_STRN | R_ANAL_REF_TYPE_READ);
|
||||
ux->applied++;
|
||||
}
|
||||
|
||||
static int apply_pool_value_xrefs(DartCtx *ctx) {
|
||||
if (find_snapshots (ctx) != 0) {
|
||||
return 0;
|
||||
}
|
||||
// Keep a live layout across PP resolution: its fallback layout can otherwise
|
||||
// point at dart_resolve_pp_info's temporary stack buffer on return.
|
||||
DartVerLayout layout_tmp;
|
||||
bool layout_ours = !ctx->layout;
|
||||
DartVerLayout *layout_owned = layout_ours? dart_ctx_init_layout (ctx, &layout_tmp): NULL;
|
||||
DartPpInfo pp = { 0 };
|
||||
DartPoolValueXrefCtx ux = { .core = ctx->core };
|
||||
if (dart_resolve_pp_info (ctx, &pp)) {
|
||||
ux.pp_base = pp.base;
|
||||
DartSnapshotHeader sh = { 0 };
|
||||
if (dart_snapshot_header_read (ctx, pp.snapshot_base, &sh) && sh.ok) {
|
||||
const ModernReq req = { ctx, sh.cluster_start, pp.snapshot_base + sh.total_len, sh.nc, sh.nb };
|
||||
modern_collect_pool_value_strrefs (&req, NULL, xref_pool_value_string_cb, &ux);
|
||||
}
|
||||
}
|
||||
dart_pp_info_fini (&pp);
|
||||
if (layout_ours) {
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
}
|
||||
return ux.applied;
|
||||
}
|
||||
|
||||
// Apply the recovered xref graph to the RCore as real r2 xrefs. Unlike the
|
||||
// analysis disassembly scan, which only sees loads inside functions, this also
|
||||
// surfaces references that live in the data image (e.g. a const object in
|
||||
@@ -1093,5 +1052,5 @@ int dart_pool_apply_xrefs(DartCtx *ctx) {
|
||||
}
|
||||
}
|
||||
dart_string_list_free (strings);
|
||||
return applied + apply_pool_value_xrefs (ctx);
|
||||
return applied;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
NAME=strings-instance-pool-android
|
||||
FILE=bins/android/w0rdle/libapp.so
|
||||
ARGS=-NN -e scr.color=0 -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
r2flutter -q -z~^detached$
|
||||
r2flutter -q -z~^dark$
|
||||
r2flutter -xz~object_pool.instance_field~pp_off=0x4780
|
||||
r2flutter -q -r -xz~0x19649 0xdf327
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
detached
|
||||
dark
|
||||
# object_pool.instance_field other pool=54352 index=0 entry=2286 pp_off=0x4780 field=2 [ref=54352] @ 0xdf327
|
||||
ax 0x19649 0xdf327
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,18 @@
|
||||
NAME=strings-instance-pool-ios
|
||||
FILE=bins/ios/Runner.app/Frameworks/App.framework/App
|
||||
ARGS=-NN -e scr.color=0 -e bin.relocs.apply=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
r2flutter -q -z~^detached$
|
||||
r2flutter -q -z~^dark$
|
||||
r2flutter -xz~object_pool.instance_field~pp_off=0x38b8
|
||||
r2flutter -q -r -xz~0x26fa30 0x230b89
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
detached
|
||||
dark
|
||||
# object_pool.instance_field other pool=41741 index=0 entry=1813 pp_off=0x38b8 field=1 [ref=41741] @ 0x230b89
|
||||
ax 0x26fa30 0x230b89
|
||||
EOF
|
||||
RUN
|
||||
Reference in new issue
Block a user