mirror of
https://github.com/radareorg/r2flutter.git
synced 2026-11-07 06:26:00 -10:00
Resolve ObjectPool Field names and owners
This commit is contained in:
1 parent
2bf613618f
commit
84a25ea38c
5 files changed
+183
-27
No files matched your search
@@ -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
@@ -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];
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in new issue
Block a user