mirror of
https://github.com/radareorg/r2flutter.git
synced 2026-11-07 06:26:00 -10:00
Fix ObjectPool string xrefs through instance fields
This commit is contained in:
1 parent
7be0c178fb
commit
bd6f596279
10 files changed
+272
-41
No files matched your search
@@ -1883,3 +1883,35 @@ placeholder (`func.*`, `method.fn_*`) names.
|
||||
came out as 128 + words, e.g. `_Closure._context` at `+0x428` instead of
|
||||
`+0x28` on 64-bit, and the `SemanticsAction` const map key `tap` decoded as
|
||||
129 instead of 1. `cs_read_tagged32` was already correct.
|
||||
|
||||
## ObjectPool instances can carry strings through their fields
|
||||
|
||||
`t/libapp_1.so` loads `PP+0x17200` at `0xfb4ae8`. The slot references
|
||||
snapshot object 165628, a `Ubb` instance derived from `_Enum`; its string field
|
||||
references object 33665 (`privilegedAccess`, payload `0xbcedf`). Blutter's
|
||||
`pp.txt` confirms enum index 9 and the name at instance offset `0x10`.
|
||||
A direct-String-only PP lookup consequently misses this use.
|
||||
|
||||
Analysis now collects direct strings and immediate string fields of pool
|
||||
instances. `-AA` links the synthetic pool slot (`0x30a1b0` in this sample) to
|
||||
the string, and `-AAA` links the loading instruction and adds a `contains
|
||||
string` comment while retaining the `instance` label. Each slot can contain
|
||||
multiple strings; they must not collapse to its first string field. This
|
||||
analysis path stops at immediate fields rather than traversing metadata or
|
||||
arbitrary object graphs.
|
||||
|
||||
Instance field counts include an eight-byte header expressed in compressed
|
||||
words: two words on compressed Android snapshots, one on uncompressed iOS.
|
||||
Using a fixed two-word header skips the enum name on iOS. The collector also
|
||||
loads ROData strings for uncompressed snapshots and preserves VM base-object
|
||||
strings. Regression tests use `Brightness.dark` in Android w0rdle and iOS
|
||||
Runner to cover both layouts, pool xrefs, and instruction comments/xrefs.
|
||||
|
||||
PP resolution can initialize `ctx->layout` from a local stack buffer. Callers
|
||||
that need to continue parsing after it returns must keep their own live
|
||||
layout across resolution and free it afterward; otherwise the field-string
|
||||
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.
|
||||
+74
-23
@@ -3501,6 +3501,8 @@ typedef struct {
|
||||
ut64 pool_ref;
|
||||
ut64 pool_index;
|
||||
ut64 entry_index;
|
||||
bool instance_fields_only;
|
||||
ut64 *instance_offsets;
|
||||
} ModernPoolStringRefCollector;
|
||||
|
||||
static void modern_emit_pool_string_ref(ModernPoolStringRefCollector *collector, ut64 target_ref, const char *target_kind, ut64 field_index, ut64 string_ref) {
|
||||
@@ -3654,36 +3656,49 @@ static bool modern_collect_instance_strings(ModernPoolStringRefCollector *collec
|
||||
if (!cs_read_unsigned (&s, &bitmap)) {
|
||||
return false;
|
||||
}
|
||||
int header_words = 2;
|
||||
int num_fields = meta->next_field_offset_words - header_words;
|
||||
if (num_fields < 0) {
|
||||
num_fields = 0;
|
||||
}
|
||||
ut64 target_index = target_ref - meta->start_ref;
|
||||
for (ut64 i = 0; i < meta->count; i++) {
|
||||
for (int j = 0; j < num_fields; j++) {
|
||||
int field_word_idx = header_words + j;
|
||||
bool is_unboxed = ((bitmap >> field_word_idx) & 1ULL) != 0;
|
||||
if (is_unboxed) {
|
||||
const int word_size = resolver->ctx->compressed_word_size? resolver->ctx->compressed_word_size: 4;
|
||||
const int header_words = 8 / word_size;
|
||||
const int num_fields = R_MAX (meta->next_field_offset_words - header_words, 0);
|
||||
// Index the cluster once. Rescanning its prefix for every pool slot is
|
||||
// quadratic for large collections of const instances such as icons.
|
||||
if (!collector->instance_offsets[meta->start_ref]) {
|
||||
for (ut64 i = 0; i < meta->count; i++) {
|
||||
collector->instance_offsets[meta->start_ref + i] = s.cursor;
|
||||
for (int j = 0; j < num_fields; j++) {
|
||||
const int field_word = header_words + j;
|
||||
const bool unboxed = field_word < 64 && ((bitmap >> field_word) & 1ULL);
|
||||
ut32 tmp32 = 0;
|
||||
if (!cs_read_tagged32 (&s, &tmp32) || !cs_read_tagged32 (&s, &tmp32)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ut64 ref = 0;
|
||||
if (!cs_read_ref_id (&s, &ref)) {
|
||||
if (unboxed) {
|
||||
if (!cs_read_tagged32 (&s, &tmp32) || !cs_read_tagged32 (&s, &tmp32)) {
|
||||
collector->instance_offsets[meta->start_ref] = 0;
|
||||
return false;
|
||||
}
|
||||
} else if (!cs_read_ref_id (&s, &ref)) {
|
||||
collector->instance_offsets[meta->start_ref] = 0;
|
||||
return false;
|
||||
}
|
||||
if (i == target_index) {
|
||||
(void)modern_collect_ref_strings (collector, ref, "instance", (ut64)j, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == target_index) {
|
||||
return true;
|
||||
}
|
||||
s.cursor = collector->instance_offsets[target_ref];
|
||||
for (int j = 0; j < num_fields; j++) {
|
||||
const int field_word = header_words + j;
|
||||
const bool unboxed = field_word < 64 && ((bitmap >> field_word) & 1ULL);
|
||||
if (unboxed) {
|
||||
ut32 tmp32 = 0;
|
||||
if (!cs_read_tagged32 (&s, &tmp32) || !cs_read_tagged32 (&s, &tmp32)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
ut64 ref = 0;
|
||||
if (!cs_read_ref_id (&s, &ref)) {
|
||||
return false;
|
||||
}
|
||||
(void)modern_collect_ref_strings (collector, ref, "instance", (ut64)j, depth + 1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool modern_collect_ref_strings(ModernPoolStringRefCollector *collector, ut64 target_ref, const char *target_kind, ut64 field_index, int depth) {
|
||||
@@ -3698,6 +3713,11 @@ static bool modern_collect_ref_strings(ModernPoolStringRefCollector *collector,
|
||||
if (collector->seen) {
|
||||
ht_up_insert (collector->seen, seen_key, (void *)1);
|
||||
}
|
||||
// Isolate base objects can include VM strings without a local cluster.
|
||||
if (R_STR_ISNOTEMPTY (resolver->strings_by_ref[target_ref])) {
|
||||
modern_emit_pool_string_ref (collector, target_ref, target_kind, field_index, target_ref);
|
||||
return true;
|
||||
}
|
||||
const ModernClusterMeta *meta = modern_resolver_cluster_for_ref (resolver, target_ref);
|
||||
if (!meta) {
|
||||
return false;
|
||||
@@ -3709,6 +3729,9 @@ static bool modern_collect_ref_strings(ModernPoolStringRefCollector *collector,
|
||||
if (!meta->fill_parsed || !meta->fill_ok || meta->fill_offset >= meta->fill_end || !meta->count) {
|
||||
return false;
|
||||
}
|
||||
if (collector->instance_fields_only && (depth > 0 || meta->fill_kind != MODERN_FILL_INSTANCE)) {
|
||||
return false;
|
||||
}
|
||||
if (modern_is_array_cid (collector->cids, meta->cid)) {
|
||||
return modern_collect_array_strings (collector, meta, target_ref, false, depth);
|
||||
}
|
||||
@@ -3729,7 +3752,7 @@ static bool modern_collect_ref_strings(ModernPoolStringRefCollector *collector,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernPoolStrRefCb cb, void *user) {
|
||||
static bool modern_collect_pool_strrefs_internal(const ModernReq *req, HtUP *pp_offsets, ModernPoolStrRefCb cb, void *user, bool instance_fields_only) {
|
||||
DartCtx *ctx = req? req->ctx: NULL;
|
||||
if (!ctx || !cb || !modern_supported (ctx)) {
|
||||
return false;
|
||||
@@ -3744,6 +3767,21 @@ bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernP
|
||||
modern_cluster_meta_free (meta, req->num_clusters);
|
||||
return false;
|
||||
}
|
||||
ut64 *instance_offsets = (ut64 *)calloc ((size_t)resolver.refs_count, sizeof (ut64));
|
||||
if (!instance_offsets) {
|
||||
modern_ref_resolver_fini (&resolver);
|
||||
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];
|
||||
@@ -3785,6 +3823,8 @@ bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernP
|
||||
.pool_ref = m->start_ref + pool_index,
|
||||
.pool_index = pool_index,
|
||||
.entry_index = entry_index,
|
||||
.instance_fields_only = instance_fields_only,
|
||||
.instance_offsets = instance_offsets,
|
||||
};
|
||||
(void)modern_collect_ref_strings (&collector, entry.ref, "object_pool.entry", entry_index, 0);
|
||||
ht_up_free (seen);
|
||||
@@ -3797,11 +3837,22 @@ bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernP
|
||||
break;
|
||||
}
|
||||
}
|
||||
free (instance_offsets);
|
||||
modern_ref_resolver_fini (&resolver);
|
||||
modern_cluster_meta_free (meta, req->num_clusters);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernPoolStrRefCb cb, void *user) {
|
||||
return modern_collect_pool_strrefs_internal (req, pp_offsets, cb, user, false);
|
||||
}
|
||||
|
||||
// Include immediate string fields of instances (notably _Enum._name) without
|
||||
// walking unrelated metadata graphs or treating the instance as a String.
|
||||
bool modern_collect_pool_value_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernPoolStrRefCb cb, void *user) {
|
||||
return modern_collect_pool_strrefs_internal (req, pp_offsets, cb, user, true);
|
||||
}
|
||||
|
||||
static ut64 modern_find_sb(const ModernReq *req) {
|
||||
DartCtx *ctx = req? req->ctx: NULL;
|
||||
if (!ctx) {
|
||||
|
||||
@@ -291,6 +291,7 @@ const char *modern_value_graph_root_kind(ModernValueGraph *graph);
|
||||
bool modern_collect_direct_pool_strrefs(const ModernReq *req, HtUP *pp_offs, ModernPoolStrRefCb cb, void *user);
|
||||
bool modern_collect_direct_pool_refs(const ModernReq *req, ModernPoolRefCb cb, void *user);
|
||||
bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offs, ModernPoolStrRefCb cb, void *user);
|
||||
bool modern_collect_pool_value_strrefs(const ModernReq *req, HtUP *pp_offs, ModernPoolStrRefCb cb, void *user);
|
||||
bool modern_extract_pool_strings(const ModernReq *req, RList *strings, HtUP *seen, ut64 *next_ref);
|
||||
bool modern_scan_names(DartCtx *ctx, ut64 start, ut64 end, ut64 nclusters, ut64 itlen);
|
||||
bool modern_extract_classes(const ModernReq *req, RList *classes);
|
||||
|
||||
@@ -1016,6 +1016,47 @@ 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
|
||||
@@ -1052,5 +1093,5 @@ int dart_pool_apply_xrefs(DartCtx *ctx) {
|
||||
}
|
||||
}
|
||||
dart_string_list_free (strings);
|
||||
return applied;
|
||||
return applied + apply_pool_value_xrefs (ctx);
|
||||
}
|
||||
+36
-17
@@ -60,6 +60,7 @@ typedef struct {
|
||||
typedef struct {
|
||||
ut64 string_addr;
|
||||
char *string_value;
|
||||
bool indirect;
|
||||
} FlutterPpStringRef;
|
||||
|
||||
typedef struct {
|
||||
@@ -93,15 +94,19 @@ static void flutter_stack_slot_free(void *p) {
|
||||
free (p);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_ref_free(HtUPKv *kv) {
|
||||
FlutterPpStringRef *ref = kv->value;
|
||||
static void flutter_pp_string_ref_free(void *p) {
|
||||
FlutterPpStringRef *ref = p;
|
||||
free (ref->string_value);
|
||||
free (ref);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_bucket_free(HtUPKv *kv) {
|
||||
r_list_free (kv->value);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_map_init(FlutterPpStringMap *map) {
|
||||
memset (map, 0, sizeof (*map));
|
||||
map->by_pp_off = ht_up_new (NULL, flutter_pp_string_ref_free, NULL);
|
||||
map->by_pp_off = ht_up_new (NULL, flutter_pp_string_bucket_free, NULL);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_map_fini(FlutterPpStringMap *map) {
|
||||
@@ -567,13 +572,23 @@ static void flutter_pp_string_map_add(FlutterPpStringMap *map, const ModernPoolS
|
||||
if (!map || !map->by_pp_off || !info || R_STR_ISEMPTY (info->string_value) || !info->string_addr) {
|
||||
return;
|
||||
}
|
||||
if (ht_up_find (map->by_pp_off, info->pp_offset, NULL)) {
|
||||
return;
|
||||
RList *refs = ht_up_find (map->by_pp_off, info->pp_offset, NULL);
|
||||
if (!refs) {
|
||||
refs = r_list_newf (flutter_pp_string_ref_free);
|
||||
ht_up_insert (map->by_pp_off, info->pp_offset, refs);
|
||||
}
|
||||
RListIter *it;
|
||||
FlutterPpStringRef *existing;
|
||||
r_list_foreach (refs, it, existing) {
|
||||
if (existing->string_addr == info->string_addr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
FlutterPpStringRef *ref = R_NEW0 (FlutterPpStringRef);
|
||||
ref->string_addr = info->string_addr;
|
||||
ref->string_value = strdup (info->string_value);
|
||||
ht_up_insert (map->by_pp_off, info->pp_offset, ref);
|
||||
ref->indirect = strcmp (info->target_kind, "object_pool.entry") != 0;
|
||||
r_list_append (refs, ref);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_ref_cb(const ModernPoolStrRef *info, void *user) {
|
||||
@@ -595,7 +610,7 @@ static bool flutter_collect_pp_strings_from_snapshot(DartCtx *ctx, ut64 snapshot
|
||||
.num_clusters = sh.nc,
|
||||
.num_base_objects = sh.nb,
|
||||
};
|
||||
return modern_collect_direct_pool_strrefs (&req, wanted, flutter_pp_string_ref_cb, map);
|
||||
return modern_collect_pool_value_strrefs (&req, wanted, flutter_pp_string_ref_cb, map);
|
||||
}
|
||||
|
||||
static void flutter_pp_string_map_load(DartCtx *ctx, HtUP *wanted, FlutterPpStringMap *map) {
|
||||
@@ -616,7 +631,7 @@ static void flutter_pp_string_map_load(DartCtx *ctx, HtUP *wanted, FlutterPpStri
|
||||
}
|
||||
}
|
||||
|
||||
static const FlutterPpStringRef *flutter_pp_string_map_get(FlutterPpStringMap *map, ut64 pp_off) {
|
||||
static RList *flutter_pp_string_map_get(FlutterPpStringMap *map, ut64 pp_off) {
|
||||
return map && map->by_pp_off? ht_up_find (map->by_pp_off, pp_off, NULL): NULL;
|
||||
}
|
||||
|
||||
@@ -1215,22 +1230,26 @@ static bool flutter_process_pp_load(RCore *core, FlutterAnalState *state, Flutte
|
||||
if (pp_refs && pp_refs->pp_base) {
|
||||
flutter_add_ref (core, at, pp_refs->pp_base + slot_off, R_ANAL_REF_TYPE_DATA | R_ANAL_REF_TYPE_READ);
|
||||
}
|
||||
const FlutterPpStringRef *string_ref = flutter_pp_string_map_get (pp_strings, slot_off);
|
||||
if (string_ref) {
|
||||
RList *strings = flutter_pp_string_map_get (pp_strings, slot_off);
|
||||
FlutterPpStringRef *first = strings? r_list_first (strings): NULL;
|
||||
if (!first || first->indirect) {
|
||||
if (!flutter_annotate_pp_ref (core, pp_refs, at, slot_off, false, stats)) {
|
||||
char *msg = r_str_newf ("dart: PP slot +0x%" PFMT64x, slot_off);
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
}
|
||||
}
|
||||
RListIter *it;
|
||||
FlutterPpStringRef *string_ref;
|
||||
r_list_foreach (strings, it, string_ref) {
|
||||
flutter_set_flag (core, "dart.str", string_ref->string_value, string_ref->string_addr, (ut32)strlen (string_ref->string_value), true);
|
||||
flutter_add_ref (core, at, string_ref->string_addr, R_ANAL_REF_TYPE_STRN | R_ANAL_REF_TYPE_READ);
|
||||
stats->string_refs++;
|
||||
char *escaped = r_str_escape_utf8 (string_ref->string_value, false, true);
|
||||
char *msg = r_str_newf ("dart: PP slot +0x%" PFMT64x " string \"%s\"", pp_off, escaped);
|
||||
char *msg = r_str_newf ("dart: PP slot +0x%" PFMT64x " %sstring \"%s\"", pp_off, string_ref->indirect? "contains ": "", escaped);
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
free (escaped);
|
||||
} else if (!flutter_annotate_pp_ref (core, pp_refs, at, slot_off, false, stats)) {
|
||||
char *msg = r_str_newf ("dart: "
|
||||
"PP slot +0x%" PFMT64x,
|
||||
slot_off);
|
||||
flutter_append_comment (core, at, msg, stats);
|
||||
free (msg);
|
||||
}
|
||||
stats->pp_refs++;
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
NAME=r2-pp-analysis-discarded-code-android
|
||||
FILE=bins/android/first/libapp.so
|
||||
ARGS=-NN -e scr.color=0 -e bin.relocs.apply=true -e io.cache=true
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
# Simulate fewer retained Code objects than instruction entries, with all
|
||||
# entries before first_entry_with_code. Patches stay in the IO cache.
|
||||
wx 1e8a @ 0x8f7f
|
||||
wv4 1311 @ 0x1f558
|
||||
r2flutter -q -i~?
|
||||
r2flutter -q -l 2 -i~?
|
||||
r2flutter -q -AAA
|
||||
axtq @ 0x2710
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
1311
|
||||
2
|
||||
0x8b914
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,18 @@
|
||||
NAME=r2-pp-instance-string-xref-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 -AA
|
||||
axtq @ 0x3d29e~0xdb87f
|
||||
r2flutter -q -AAA
|
||||
CC. @ 0x19d630
|
||||
axtq @ 0x3d29e~0x19d630
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0xdb87f
|
||||
dart: PP slot +0xcd8 instance | dart: PP slot +0xcd8 contains string "dark"
|
||||
0x19d630
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,18 @@
|
||||
NAME=r2-pp-instance-string-xref-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 -AA
|
||||
axtq @ 0x2a3300~0x22dd19
|
||||
r2flutter -q -AAA
|
||||
CC. @ 0x330ac
|
||||
axtq @ 0x2a3300~0x330ac
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x22dd19
|
||||
dart: PP slot +0xa48 instance | dart: PP slot +0xa48 contains string "dark"
|
||||
0x330ac
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,15 @@
|
||||
NAME=r2-pp-shifted-string-xref-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 -AAA
|
||||
CC. @ 0x178c70
|
||||
axtq @ 0x51746~0x178c70
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
dart: PP slot +0xb5f8 string "Unexpected state: "
|
||||
0x178c70
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,15 @@
|
||||
NAME=r2-pp-shifted-string-xref-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 -AAA
|
||||
CC. @ 0x44604
|
||||
axtq @ 0x27af40~0x44604
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
dart: PP slot +0xa590 string "callOnHover"
|
||||
0x44604
|
||||
EOF
|
||||
RUN
|
||||
Reference in new issue
Block a user