Resolve ObjectPool Field names and owners

This commit is contained in:
pancake committed 2026-10-21 12:12:31 +02:00
1 parent 2bf613618f
commit 84a25ea38c
5 files changed
+183 -27

No files matched your search

+22
View File
@@ -1954,3 +1954,25 @@ and records their immediate leaf kind/index. `-AA` links their pool slots;
`-AAA` adds instruction xrefs and `contains string` comments while retaining
the collection's original kind. Regression fixtures cover both platforms and
cyclic arrays with shared elements behind mutable/const map and set storage.
## Field ObjectPool entries resolve their owner and name
W0rdle `PP+0x60` references Field object 52876 (`Platform.isAndroid`). The
field fill was decoded for class extraction but the PP resolver classified
Field by its generic allocation kind (`simple`) and retained no name/owner
references. iOS Runner `PP+0x120` similarly references
`Zone._current@4048458` (object 40563).
The shared resolver now records Field name/owner references while walking
fills and resolves owners through Class, Library, or bounded PatchClass
chains. Field labels use `Owner.name`, preserve private-name suffixes, and
apply the obfuscation map to both parts. Missing owners leave the field name
alone; missing names leave a `field` kind without a fabricated label. PP
inspection, cluster summaries, reference inspection, and analysis comments
share that resolution.
Uncompressed isolate field names are ROData strings. Loading those strings
in the shared resolver enables field labels on iOS as well as compressed
Android snapshots and replaces duplicate ROData loading in value graphs and
string collection. Regression fixtures cover both platforms, patched and
cyclic owners, missing names/owners, and deobfuscation.
+89 -27
View File
@@ -120,6 +120,8 @@ typedef struct {
ut64 *function_name_ref;
ut64 *function_owner_ref;
ut64 *function_code_index;
ut64 *field_name_ref;
ut64 *field_owner_ref;
} ModernRefResolver;
typedef struct {
@@ -1971,6 +1973,8 @@ static void modern_ref_resolver_fini(ModernRefResolver *resolver) {
free (resolver->function_name_ref);
free (resolver->function_owner_ref);
free (resolver->function_code_index);
free (resolver->field_name_ref);
free (resolver->field_owner_ref);
memset (resolver, 0, sizeof (*resolver));
}
@@ -2035,6 +2039,37 @@ static bool modern_resolver_read_function_fill(ModernWalk *w, ClusterStream *s,
return true;
}
static bool modern_resolver_read_field_fill(ModernWalk *w, ClusterStream *s, const ModernClusterMeta *meta) {
ModernRefResolver *resolver = w->user;
const ModernFillSpec spec = modern_get_fill_spec (&w->cids, meta->cid);
for (ut64 j = 0; j < meta->count; j++) {
ut64 name_ref = 0;
ut64 owner_ref = 0;
for (int k = 0; k < spec.num_refs; k++) {
ut64 ref = 0;
if (!cs_read_ref_id (s, &ref)) {
return false;
}
if (k == spec.name_idx) {
name_ref = ref;
} else if (k == spec.owner_idx) {
owner_ref = ref;
}
}
for (int k = 0; k < spec.scalar_count; k++) {
if (!modern_skip_scalar (s, spec.scalars[k])) {
return false;
}
}
const ut64 ref = meta->start_ref + j;
if (ref < resolver->refs_count) {
resolver->field_name_ref[ref] = name_ref;
resolver->field_owner_ref[ref] = owner_ref;
}
}
return true;
}
static bool modern_resolver_read_library_fill(ModernWalk *w, ClusterStream *s, const ModernClusterMeta *meta) {
ModernRefResolver *resolver = (ModernRefResolver *)w->user;
const ModernFillSpec spec = modern_get_fill_spec (&w->cids, meta->cid);
@@ -2123,6 +2158,7 @@ static bool modern_direct_resolver_read_strings(ModernWalk *w, ClusterStream *s,
static const ModernFillHandler modern_resolver_fill_handlers[] = {
{ DART_CID_CLASS, modern_resolver_read_class_fill },
{ DART_CID_FUNCTION, modern_resolver_read_function_fill },
{ DART_CID_FIELD, modern_resolver_read_field_fill },
{ DART_CID_LIBRARY, modern_resolver_read_library_fill },
{ DART_CID_PATCH_CLASS, modern_resolver_read_patch_class_fill },
{ 0, NULL },
@@ -2149,7 +2185,9 @@ static bool modern_ref_resolver_init(ModernRefResolver *resolver, DartCtx *ctx,
resolver->function_name_ref = (ut64 *)calloc ((size_t)resolver->refs_count, sizeof (ut64));
resolver->function_owner_ref = (ut64 *)calloc ((size_t)resolver->refs_count, sizeof (ut64));
resolver->function_code_index = (ut64 *)calloc ((size_t)resolver->refs_count, sizeof (ut64));
if (!resolver->cluster_by_ref || !resolver->strings_by_ref || !resolver->string_addr_by_ref || !resolver->string_flags_by_ref || !resolver->class_name_ref || !resolver->class_id_by_ref || !resolver->library_name_ref || !resolver->patch_wrapped_ref || !resolver->function_name_ref || !resolver->function_owner_ref || !resolver->function_code_index) {
resolver->field_name_ref = (ut64 *)calloc ((size_t)resolver->refs_count, sizeof (ut64));
resolver->field_owner_ref = (ut64 *)calloc ((size_t)resolver->refs_count, sizeof (ut64));
if (!resolver->cluster_by_ref || !resolver->strings_by_ref || !resolver->string_addr_by_ref || !resolver->string_flags_by_ref || !resolver->class_name_ref || !resolver->class_id_by_ref || !resolver->library_name_ref || !resolver->patch_wrapped_ref || !resolver->function_name_ref || !resolver->function_owner_ref || !resolver->function_code_index || !resolver->field_name_ref || !resolver->field_owner_ref) {
modern_ref_resolver_fini (resolver);
return false;
}
@@ -2173,6 +2211,16 @@ static bool modern_ref_resolver_init(ModernRefResolver *resolver, DartCtx *ctx,
w.on_string_fill = modern_resolver_read_strings;
w.user = resolver;
modern_walk_fill_recorded (&w, meta, num_clusters);
// Uncompressed snapshots keep field and owner names in the data image.
const ut64 bases[] = { ctx->iso_data, ctx->vm_data };
for (size_t i = 0; i < R_ARRAY_SIZE (bases); i++) {
DartSnapshotHeader sh = { 0 };
ut64 base = bases[i];
if (base && dart_snapshot_header_read (ctx, base, &sh) && sh.ok && sh.cluster_start == meta[0].tag_offset) {
modern_load_rodata_strings (ctx, meta, num_clusters, base, sh.total_len, resolver->strings_by_ref, resolver->string_addr_by_ref, resolver->refs_count);
break;
}
}
return true;
}
@@ -2222,6 +2270,9 @@ static const char *modern_ref_kind_for_meta(const ModernCidCache *cids, const Mo
if (modern_cid_eq (meta->cid, cids->function)) {
return "function";
}
if (modern_cid_eq (meta->cid, cids->field)) {
return "field";
}
if (modern_cid_eq (meta->cid, cids->library)) {
return "library";
}
@@ -2234,6 +2285,22 @@ static const char *modern_ref_kind_for_meta(const ModernCidCache *cids, const Mo
return modern_alloc_kind_name (meta->alloc_kind);
}
static char *modern_build_field_name(DartCtx *ctx, const char *owner, const char *name) {
if (R_STR_ISEMPTY (name)) {
return NULL;
}
char *owner_name = owner? strdup (owner): NULL;
char *field_name = strdup (name);
dart_obf_apply (ctx, &owner_name);
dart_obf_apply (ctx, &field_name);
char *full = R_STR_ISNOTEMPTY (owner_name) && strcmp (owner_name, "::")
? r_str_newf ("%s.%s", owner_name, field_name)
: strdup (field_name);
free (owner_name);
free (field_name);
return full;
}
static void modern_ref_resolver_resolve(ModernRefResolver *resolver, const ModernCidCache *cids, ut64 ref, ModernResolvedRef *resolved) {
if (!resolved) {
return;
@@ -2269,22 +2336,29 @@ static void modern_ref_resolver_resolve(ModernRefResolver *resolver, const Moder
}
return;
}
const ModernRefNameMap names = {
.strings_by_ref = resolver->strings_by_ref,
.class_name_ref = resolver->class_name_ref,
.library_name_ref = resolver->library_name_ref,
.patch_wrapped_ref = resolver->patch_wrapped_ref,
.function_name_ref = resolver->function_name_ref,
.refs_count = resolver->refs_count,
};
if (!strcmp (resolved->kind, "function")) {
ut64 name_ref = resolver->function_name_ref[ref];
const char *method_name = name_ref < resolver->refs_count? resolver->strings_by_ref[name_ref]: NULL;
const ModernRefNameMap names = {
.strings_by_ref = resolver->strings_by_ref,
.class_name_ref = resolver->class_name_ref,
.library_name_ref = resolver->library_name_ref,
.patch_wrapped_ref = resolver->patch_wrapped_ref,
.function_name_ref = resolver->function_name_ref,
.refs_count = resolver->refs_count,
};
const char *owner_name = modern_resolve_ref_name (&names, resolver->function_owner_ref[ref], 0);
resolved->name = modern_build_full_name (resolver->ctx, owner_name, method_name);
resolved->code_index = resolver->function_code_index[ref];
return;
}
if (!strcmp (resolved->kind, "field")) {
ut64 name_ref = resolver->field_name_ref[ref];
const char *field_name = name_ref < resolver->refs_count? resolver->strings_by_ref[name_ref]: NULL;
const char *owner_name = modern_resolve_ref_name (&names, resolver->field_owner_ref[ref], 0);
resolved->name = modern_build_field_name (resolver->ctx, owner_name, field_name);
return;
}
if (!strcmp (resolved->kind, "library")) {
ut64 name_ref = resolver->library_name_ref[ref];
if (name_ref < resolver->refs_count && R_STR_ISNOTEMPTY (resolver->strings_by_ref[name_ref])) {
@@ -3158,6 +3232,10 @@ static ModernValueNode *modern_value_graph_node(ModernValueGraph *graph, ut64 re
}
if (!strcmp (node->kind, "instance")) {
node->name = modern_value_class_name (graph, meta->cid);
} else if (!strcmp (node->kind, "field")) {
ModernResolvedRef resolved;
modern_ref_resolver_resolve (&graph->resolver, &graph->cids, ref, &resolved);
node->name = resolved.name;
}
if (meta->fill_kind == MODERN_FILL_NONE) {
node->complete = true;
@@ -3197,15 +3275,7 @@ ModernValueGraph *modern_value_graph_new(const ModernReq *req, ut64 root_ref) {
for (ut64 i = 0; i < req->num_clusters; i++) {
graph->valid_refs_count = R_MAX (graph->valid_refs_count, graph->meta[i].start_ref + graph->meta[i].count);
}
const ut64 snapshot_bases[] = { ctx->iso_data, ctx->vm_data };
for (size_t i = 0; i < R_ARRAY_SIZE (snapshot_bases); i++) {
DartSnapshotHeader sh = { 0 };
const ut64 base = snapshot_bases[i];
if (base && dart_snapshot_header_read (ctx, base, &sh) && sh.ok && sh.cluster_start == req->cluster_start) {
modern_load_rodata_strings (ctx, graph->meta, req->num_clusters, base, sh.total_len, graph->resolver.strings_by_ref, graph->resolver.string_addr_by_ref, graph->resolver.refs_count);
break;
}
}
graph->nodes = (ModernValueNode **)calloc ((size_t)graph->resolver.refs_count, sizeof (ModernValueNode *));
if (!graph->nodes) {
modern_value_graph_free (graph);
@@ -3800,15 +3870,7 @@ static bool modern_collect_pool_strrefs_internal(const ModernReq *req, HtUP *pp_
modern_cluster_meta_free (meta, req->num_clusters);
return false;
}
const ut64 snapshot_bases[] = { ctx->iso_data, ctx->vm_data };
for (size_t i = 0; i < R_ARRAY_SIZE (snapshot_bases); i++) {
DartSnapshotHeader sh = { 0 };
const ut64 base = snapshot_bases[i];
if (base && dart_snapshot_header_read (ctx, base, &sh) && sh.ok && sh.cluster_start == req->cluster_start) {
modern_load_rodata_strings (ctx, meta, req->num_clusters, base, sh.total_len, resolver.strings_by_ref, resolver.string_addr_by_ref, resolver.refs_count);
break;
}
}
const ModernCidCache cids = modern_cid_cache_init (ctx->layout);
for (ut64 i = 0; i < req->num_clusters; i++) {
ModernClusterMeta *m = &meta[i];
+21
View File
@@ -0,0 +1,21 @@
NAME=pp-field-resolve-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 -j -O pp+0x60~{pp_slot.resolved_kind}
r2flutter -q -O pp+0x60
r2flutter -q -j -O ref:52876~{snapshot.name}
r2flutter -q -j -HHH -l 500~{snapshots[1].clusters[365].object_pools[0].entries[10].resolved_name}
r2flutter -q -AAA
CC. @ 0x1eee60
EOF
EXPECT=<<EOF
field
Platform.isAndroid
Platform.isAndroid
Platform.isAndroid
dart: PP slot +0x60 field Platform.isAndroid
EOF
RUN
+21
View File
@@ -0,0 +1,21 @@
NAME=pp-field-resolve-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 -j -O pp+0x120~{pp_slot.resolved_kind}
r2flutter -q -O pp+0x120
r2flutter -q -j -O ref:40563~{snapshot.name}
r2flutter -q -j -HHH -l 500~{snapshots[1].clusters[320].object_pools[0].entries[34].resolved_name}
r2flutter -q -AAA
CC. @ 0xfb3c
EOF
EXPECT=<<EOF
field
Zone._current@4048458
Zone._current@4048458
Zone._current@4048458
dart: PP slot +0x120 field Zone._current@4048458
EOF
RUN
+30
View File
@@ -0,0 +1,30 @@
NAME=pp-field-resolve-patched-owner-and-missing-metadata
FILE=malloc://0xbc
CMDS=<<EOF
L ../src/r2/core_flutter.so
L ../src/r2/core_flutter.dylib
wx 50414421f5f5dcdcb4000000000000000200000000000000333063303531636239313932663664363831326265336630623837613633353670726f647563742061726d363420636f6d707265737365642d706f696e746572730082898580800020d7838888900020c180810040c1810060c2830060c5818388666e5f3188666e5f3090746f704c6576656c8380808080808080808080808048c1c4c4c0c0c0c080868084878080c08085808080c080bf868080c0808311881189118a
r2flutter -q -j -O pp+0x10~{pp_slot.resolved_kind}
r2flutter -q -O pp+0x10
r2flutter -q -j -O ref:8~{snapshot.name}
r2flutter -q -O pp+0x18
r2flutter -q -O pp+0x20
r2flutter -q -j -O pp+0x20~{=}~resolved_name~?
wx 87 @ 0xa1
r2flutter -q -O pp+0x10
wx 86 @ 0xa1
r2flutter -q -O pp+0x10 -m custom/obf-map.txt
r2flutter -q -j -O ref:8 -m custom/obf-map.txt~{snapshot.name}
EOF
EXPECT=<<EOF
field
fn_1.fn_0
fn_1.fn_0
topLevel
field
0
fn_0
build.initState
build.initState
EOF
RUN