mirror of
https://github.com/radareorg/r2flutter.git
synced 2026-11-07 22:56:23 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02b0b84fa6 | ||
|
|
0ee5e01498 | ||
|
|
cc338995cf | ||
|
|
8a9e54be29 | ||
|
|
441d614937 | ||
|
|
25a5b3f026 | ||
|
|
3bfdd5601a | ||
|
|
84a25ea38c | ||
|
|
2bf613618f | ||
|
|
a2fd82106e | ||
|
|
bd6f596279 |
No files matched your search
@@ -9,7 +9,7 @@ LDFLAGS += $(R2_LDFLAGS)
|
||||
LDLIBS += $(R2_LIBS)
|
||||
DEPFLAGS = -MMD -MP
|
||||
R2R_JOBS ?= 1
|
||||
R2R_TIMEOUT ?= 30
|
||||
R2R_TIMEOUT ?= 60
|
||||
R2_LIBR_PLUGINS=$(shell r2 -H R2_LIBR_PLUGINS 2> /dev/null)
|
||||
R2_LIBEXT=$(shell r2 -H R2_LIBEXT 2> /dev/null)
|
||||
-include config.mk
|
||||
|
||||
@@ -127,12 +127,12 @@ done
|
||||
: ${INSTALL_PROGRAM_STRIP:=${INSTALL} -m 755 -s}
|
||||
: ${INSTALL_MAN:=${INSTALL} -m 444}
|
||||
: ${INSTALL_LIB:=${INSTALL} -m 755 -c}
|
||||
PKGNAME='r2flutter' ; VERSION='0.5.1' ; VERSION_MAJOR=0; VERSION_MINOR=5; VERSION_PATCH=1; VERSION_NUMBER=00501; CONTACT_MAIL="[email protected]" ; CONTACT_NAME="pancake" ; CONTACT="pancake <[email protected]>" ;
|
||||
PKGNAME='r2flutter' ; VERSION='0.6.0' ; VERSION_MAJOR=0; VERSION_MINOR=6; VERSION_PATCH=0; VERSION_NUMBER=00600; CONTACT_MAIL="[email protected]" ; CONTACT_NAME="pancake" ; CONTACT="pancake <[email protected]>" ;
|
||||
}
|
||||
|
||||
show_usage() {
|
||||
cat <<EOF2
|
||||
'configure' configures r2flutter-0.5.1 to adapt to many kinds of systems.
|
||||
'configure' configures r2flutter-0.6.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: ./configure [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -214,10 +214,10 @@ ocho() {
|
||||
|
||||
show_version() {
|
||||
if [ "$QUIET" = 1 ]; then
|
||||
echo "0.5.1"
|
||||
echo "0.6.0"
|
||||
exit 0
|
||||
fi
|
||||
echo "r2flutter-0.5.1 configuration script done with acr v2.2.8.
|
||||
echo "r2flutter-0.6.0 configuration script done with acr v2.2.8.
|
||||
The 'Free Software Foundation' message is only for autodetection.
|
||||
Originally written by pancake <nopcode.org>."
|
||||
exit 0
|
||||
@@ -246,7 +246,7 @@ case $flag in
|
||||
show_version ; ;;
|
||||
-r|--r|--report)
|
||||
echo "PKGNAME: r2flutter"
|
||||
echo "VERSION: 0.5.1"
|
||||
echo "VERSION: 0.6.0"
|
||||
echo "AUTHOR: pancake"
|
||||
echo "EMAIL: [email protected]"
|
||||
echo "LANGS: c"
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
PKGNAME r2flutter
|
||||
VERSION 0.5.1
|
||||
VERSION 0.6.0
|
||||
CONTACT pancake ; [email protected]
|
||||
|
||||
LANG_C!
|
||||
|
||||
+184
@@ -1883,3 +1883,187 @@ 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.
|
||||
|
||||
## 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.
|
||||
|
||||
## Collection values propagate their strings into PP analysis
|
||||
|
||||
Android w0rdle `PP+0x5d0` and iOS Runner `PP+0x3d0` hold the ten
|
||||
`PointerChange` enum instances. The value inspector decoded their names, but
|
||||
the analysis collector rejected every collection before inspecting its
|
||||
contents. The same restriction missed strings in map/set backing arrays.
|
||||
|
||||
The value collector now follows arrays, weak arrays, growable arrays, maps,
|
||||
sets, and instances, while excluding Class/Function/type metadata. Map/set
|
||||
storage adds an extra hop, so value traversal allows eight edges of depth
|
||||
and at most 4096 reference visits per pool entry. Per-entry visited depths
|
||||
stop cycles and allow revisiting a shared object reached by a shorter path.
|
||||
Null references count toward the visit budget too.
|
||||
|
||||
Array and reference-object fill offsets are indexed once per cluster, as
|
||||
instance offsets already were, to avoid repeatedly scanning array prefixes.
|
||||
`-xz` marks strings reached through collections as `object_pool.collection`
|
||||
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.
|
||||
|
||||
## Direct Code pool references use Code allocation indices
|
||||
|
||||
ObjectPool resolution recognized Code objects but only assigned a target index
|
||||
for Function references. Code-slot loads and indirect calls therefore had a
|
||||
`code` annotation without a navigable instruction target.
|
||||
|
||||
The main Code allocation range has the same order as the Code-backed part of
|
||||
the InstructionTable. Its zero-based index is `ref - cluster.start_ref`, which
|
||||
matches `DartInstructionTableEntry.code_index`; the table extractor handles
|
||||
`first_entry_with_code` when mapping that index to a payload address. Do not
|
||||
reuse the serialized Function index here: that index is biased by one and
|
||||
refers to the whole table. Deferred Code objects follow the main allocation
|
||||
range but have no instructions in this loading unit, so they keep an unresolved
|
||||
target. A discarded record in older Code clusters still occupies its ordinal.
|
||||
|
||||
On Android w0rdle, `PP+0x40` holds ref 33249 in the Code cluster starting at
|
||||
22433, so it resolves to index 10816 and table address `0x3f1074`. Analysis now
|
||||
adds a CALL xref from `blr x0` at `0x3f28cc`, preserving the PP association
|
||||
through the Code entry-point load. `PP+0x48` resolves to the following index.
|
||||
On iOS AuthPass, `PP+0x68` holds ref 168520 in the Code cluster starting at
|
||||
132121, yielding index 36399 and table address `0x6d9620`. The synthetic
|
||||
regression checks index zero, a discarded record, the following main record,
|
||||
and an unresolved deferred record. These targets use the existing table
|
||||
payload-address convention described above.
|
||||
|
||||
## Record inspection decodes the shape and field references
|
||||
|
||||
Record fills contain `ReadUnsigned (shape)` followed by one reference per
|
||||
field. `RecordShape::NumFieldsBitField` in the VM is the low 16 bits; the upper
|
||||
bits identify the registered array of named field names. Allocation records
|
||||
separately store each Record's field count. Retain those counts and check each
|
||||
fill against its own allocation instead of accepting only matching cluster
|
||||
minimum, maximum, and total counts.
|
||||
|
||||
The value graph now decodes Record fields into the same bounded edges used by
|
||||
other objects. JSON includes `shape`, `field_names_index`, `length`, and
|
||||
`fields`; text output prints the shape, length, and child values. Labels remain
|
||||
ordinal (`field_0`, etc.); the shape's named-field table index is preserved
|
||||
rather than treated as a reference or used to invent names. Empty Records
|
||||
report length zero. Cycles, depth limits, and the 128-child display limit use
|
||||
the existing graph handling. An unreadable or inconsistent Record fill stays
|
||||
`complete: false`.
|
||||
|
||||
In `t/libapp_1.so`, ref 146422 has shape 2 and fields referring to base object
|
||||
996 and instance 164557; that instance exposes the string `DOUBLE_QUOTE`.
|
||||
The public Android mafia fixture has the equivalent Record at ref 158051,
|
||||
whose second field is an `XmlAttributeType` instance. Synthetic compressed and
|
||||
uncompressed snapshots cover nested Records, named shapes, empty Records,
|
||||
cycles, truncation, and a malformed field count. Existing iOS graph tests
|
||||
continue to cover arrays, maps, and sets; those fixtures have no Record cluster.
|
||||
|
||||
## Standalone xrefs scan full instruction-table payloads
|
||||
|
||||
The `-x` ObjectPool-use scanner used to ignore entries without retained Code
|
||||
metadata, stop after 4096 functions by default, and inspect at most 96
|
||||
instructions per function. This missed real instructions in the discarded-Code
|
||||
prefix and PP references late in a function or instruction table.
|
||||
|
||||
The scanner now visits every instruction-table entry with an address and scans
|
||||
to the next distinct entry address. The last payload ends at the instructions
|
||||
image size recorded in its header, checked against its IO mapping. Instruction
|
||||
sizes must fit inside that range. Table extraction is unlimited even when
|
||||
`-l` caps emitted xrefs, so an output limit cannot remove the boundary needed
|
||||
for a scanned function. PP-use buckets use a hash lookup to keep the expanded
|
||||
scan practical. Instruction addresses identify visited use sites directly,
|
||||
avoiding collisions from the previous XOR of address and PP offset.
|
||||
|
||||
Android w0rdle now includes the string load at `0x1b675c`, instruction 389 of
|
||||
IT entry 808 (`Base64Codec.normalize`), and the load at `0x3f4908` in entry
|
||||
10906. iOS Runner includes `0x929d0`, instruction 107 of entry 2642
|
||||
(`TextPainter.layout`), and `0xea070` in entry 4235 (`Layer.dispose`).
|
||||
Regressions also mark the Android table entries as lacking Code metadata and
|
||||
place matching PP loads in the last function and outside the image to check
|
||||
coverage and bounds. This expands instruction coverage; operand decoding
|
||||
continues to use the existing standalone scanner.
|
||||
|
||||
## Duplicate Dart method names need unique r2 flags and function names
|
||||
|
||||
Several functions can share a Dart name (e.g. many `Native._anon_closure`).
|
||||
r2 flag and function names are unique, so `r_flag_set` on a repeated name
|
||||
moves the flag to the later function. Older r2 left a stale per-address entry
|
||||
behind, which let `af` still pick the moved `method.*` name; current r2 does
|
||||
not, and `af` also prefers symbol-space flags such as Mach-O `sym.func.*`.
|
||||
|
||||
The first function keeps `method.<name>` and repeats get a `.0x<addr>` suffix,
|
||||
computed once in app order and shared by the flags, the `afs` prototypes and
|
||||
`dart_dumper_name_functions`, which renames analyzed functions after the `af`
|
||||
pass. The signature `af` storm also disables `anal.vars`, `anal.calls` and
|
||||
`anal.jmp.tbl` like `flutter_ensure_functions`; var recovery was about half
|
||||
of its cost (AuthPass `-AAA` 47s -> 29s).
|
||||
@@ -11,6 +11,7 @@ char *dart_dumper_dump_r2(DartApp *app);
|
||||
char *dart_dumper_dump_funcs(DartApp *app, int fmt);
|
||||
void dart_dumper_apply_to_core(DartApp *app, bool apply_signatures);
|
||||
void dart_dumper_apply_signatures(DartApp *app);
|
||||
void dart_dumper_name_functions(DartApp *app);
|
||||
// Disable anal.trycatch while r2flutter runs many `af` commands, when no try.*
|
||||
// flag exists for it to find. Returns the previous value to pass to the restore.
|
||||
bool dart_core_trycatch_suspend(RCore *core);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef R2FLUTTER_VERSION_H
|
||||
#define R2FLUTTER_VERSION_H
|
||||
|
||||
#define R2FLUTTER_VERSION "0.5.1"
|
||||
#define R2FLUTTER_VERSION "0.6.0"
|
||||
#define R2FLUTTER_VERSION_MAJOR 0
|
||||
#define R2FLUTTER_VERSION_MINOR 5
|
||||
#define R2FLUTTER_VERSION_PATCH 1
|
||||
#define R2FLUTTER_VERSION_MINOR 6
|
||||
#define R2FLUTTER_VERSION_PATCH 0
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
project(
|
||||
'r2flutter',
|
||||
'c',
|
||||
version : '0.5.1',
|
||||
version : '0.6.0',
|
||||
meson_version : '>=0.56.0',
|
||||
default_options : ['warning_level=2', 'werror=false']
|
||||
)
|
||||
|
||||
+80
-8
@@ -124,8 +124,61 @@ static char *make_method_flagname(const char *name) {
|
||||
return flagname;
|
||||
}
|
||||
|
||||
static void dump_func_r2(RStrBuf *sb, const DartFunction *fn, bool quiet) {
|
||||
char *flagname = make_method_flagname (fn->name);
|
||||
// Flag and function names are unique in r2: setting an existing flag name
|
||||
// moves it, so a later function sharing a Dart name (e.g. several
|
||||
// Native._anon_closure) would steal it from the first one. Suffix repeated
|
||||
// names with the entry address.
|
||||
static char *make_unique_method_flagname(HtPP *seen, const char *name, ut64 addr) {
|
||||
char *flagname = make_method_flagname (name);
|
||||
bool found = false;
|
||||
ut64 owner = (ut64) (size_t)ht_pp_find (seen, flagname, &found);
|
||||
if (found && owner != addr) {
|
||||
char *unique = r_str_newf ("%s.0x%" PFMT64x, flagname, addr);
|
||||
free (flagname);
|
||||
return unique;
|
||||
}
|
||||
ht_pp_insert (seen, flagname, (void *) (size_t)addr);
|
||||
return flagname;
|
||||
}
|
||||
|
||||
static void method_flagname_free(HtUPKv *kv) {
|
||||
free (kv->value);
|
||||
}
|
||||
|
||||
// Map each function address to its unique method.* name, in app order.
|
||||
static HtUP *method_flagnames(DartApp *app) {
|
||||
HtUP *names = ht_up_new (NULL, method_flagname_free, NULL);
|
||||
HtPP *seen = ht_pp_new0 ();
|
||||
DartFunction *fn;
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
if (fn->name && !ht_up_find (names, fn->addr, NULL)) {
|
||||
ht_up_insert (names, fn->addr, make_unique_method_flagname (seen, fn->name, fn->addr));
|
||||
}
|
||||
}
|
||||
ht_pp_free (seen);
|
||||
return names;
|
||||
}
|
||||
|
||||
// Give each analyzed function its Dart name: r2's `af` prefers flags in the
|
||||
// symbols space (e.g. Mach-O sym.func.*) over the method.* flags set here.
|
||||
void dart_dumper_name_functions(DartApp *app) {
|
||||
if (!app || !app->core || !app->functions) {
|
||||
return;
|
||||
}
|
||||
HtUP *names = method_flagnames (app);
|
||||
DartFunction *fn;
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
RAnalFunction *fcn = r_anal_get_function_at (app->core->anal, fn->addr);
|
||||
const char *name = fcn? ht_up_find (names, fn->addr, NULL): NULL;
|
||||
if (name && strcmp (fcn->name, name)) {
|
||||
r_anal_function_rename (fcn, name);
|
||||
}
|
||||
}
|
||||
ht_up_free (names);
|
||||
}
|
||||
|
||||
static void dump_func_r2(RStrBuf *sb, HtPP *seen, const DartFunction *fn, bool quiet) {
|
||||
char *flagname = make_unique_method_flagname (seen, fn->name, fn->addr);
|
||||
r_strbuf_appendf (sb, "f %s = 0x%" PFMT64x "\n", flagname, (uint64_t)fn->addr);
|
||||
if (!quiet) {
|
||||
r_strbuf_appendf (sb, "'@0x%" PFMT64x "'CC %s\n", (uint64_t)fn->addr, fn->name);
|
||||
@@ -145,13 +198,15 @@ char *dart_dumper_dump_r2(DartApp *app) {
|
||||
r_strbuf_appendf (sb, "f app.heap_base = 0x%" PFMT64x "\n", (uint64_t)app->heap_base);
|
||||
|
||||
if (app->functions) {
|
||||
HtPP *seen = ht_pp_new0 ();
|
||||
DartFunction *fn;
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
if (!fn->name) {
|
||||
continue;
|
||||
}
|
||||
dump_func_r2 (sb, fn, quiet);
|
||||
dump_func_r2 (sb, seen, fn, quiet);
|
||||
}
|
||||
ht_pp_free (seen);
|
||||
}
|
||||
|
||||
r_strbuf_append (sb, "dr x27=`e anal.gp`\n");
|
||||
@@ -219,6 +274,7 @@ char *dart_dumper_dump_funcs(DartApp *app, int fmt) {
|
||||
}
|
||||
|
||||
RStrBuf *sb = r_strbuf_new ("");
|
||||
HtPP *seen = ht_pp_new0 ();
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
if (!fn->name) {
|
||||
continue;
|
||||
@@ -227,12 +283,13 @@ char *dart_dumper_dump_funcs(DartApp *app, int fmt) {
|
||||
break;
|
||||
}
|
||||
if (fmt == 'r') {
|
||||
dump_func_r2 (sb, fn, app->dctx.quiet);
|
||||
dump_func_r2 (sb, seen, fn, app->dctx.quiet);
|
||||
} else {
|
||||
dump_func_text (sb, fn);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
ht_pp_free (seen);
|
||||
return drain_trimmed_strbuf (sb);
|
||||
}
|
||||
|
||||
@@ -419,17 +476,27 @@ void dart_dumper_apply_signatures(DartApp *app) {
|
||||
}
|
||||
}
|
||||
qsort (order, n, sizeof (DartFunction *), dart_fn_addr_desc);
|
||||
HtUP *names = method_flagnames (app);
|
||||
// Keep the analysis local to each function start; the surrounding pass owns
|
||||
// broader discovery. String ESIL emulation is very slow and irrelevant to
|
||||
// prototypes, so disable it for the duration of this pass.
|
||||
// Variable recovery, call-following and jump tables are not needed for the
|
||||
// prototypes either, and var recovery alone is about half of this `af` cost.
|
||||
int prev_hasnext = r_config_get_i (core->config, "anal.hasnext");
|
||||
int prev_emustr = r_config_get_i (core->config, "emu.str");
|
||||
bool prev_calls = r_config_get_b (core->config, "anal.calls");
|
||||
bool prev_vars = r_config_get_b (core->config, "anal.vars");
|
||||
bool prev_jmptbl = r_config_get_b (core->config, "anal.jmp.tbl");
|
||||
r_config_set_i (core->config, "anal.hasnext", 0);
|
||||
r_config_set_i (core->config, "emu.str", 0);
|
||||
r_config_set_b (core->config, "anal.calls", false);
|
||||
r_config_set_b (core->config, "anal.vars", false);
|
||||
r_config_set_b (core->config, "anal.jmp.tbl", false);
|
||||
const bool prev_trycatch = dart_core_trycatch_suspend (core);
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
fn = order[i];
|
||||
char *name = strdup (fn->name? fn->name: "fcn");
|
||||
const char *flagname = ht_up_find (names, fn->addr, NULL);
|
||||
char *name = strdup (flagname? flagname: "fcn");
|
||||
r_name_filter (name, 0);
|
||||
if (R_STR_ISEMPTY (name)) {
|
||||
free (name);
|
||||
@@ -452,7 +519,11 @@ void dart_dumper_apply_signatures(DartApp *app) {
|
||||
}
|
||||
r_config_set_i (core->config, "anal.hasnext", prev_hasnext);
|
||||
r_config_set_i (core->config, "emu.str", prev_emustr);
|
||||
r_config_set_b (core->config, "anal.calls", prev_calls);
|
||||
r_config_set_b (core->config, "anal.vars", prev_vars);
|
||||
r_config_set_b (core->config, "anal.jmp.tbl", prev_jmptbl);
|
||||
dart_core_trycatch_restore (core, prev_trycatch);
|
||||
ht_up_free (names);
|
||||
free (order);
|
||||
}
|
||||
|
||||
@@ -468,16 +539,17 @@ void dart_dumper_apply_to_core(DartApp *app, bool apply_signatures) {
|
||||
}
|
||||
|
||||
if (app->functions) {
|
||||
HtUP *names = method_flagnames (app);
|
||||
DartFunction *fn;
|
||||
R_VEC_FOREACH (app->functions, fn) {
|
||||
if (!fn->name) {
|
||||
const char *flagname = ht_up_find (names, fn->addr, NULL);
|
||||
if (!fn->name || !flagname) {
|
||||
continue;
|
||||
}
|
||||
char *flagname = make_method_flagname (fn->name);
|
||||
r_flag_set (core->flags, flagname, fn->addr, fn->size);
|
||||
r_meta_set_string (core->anal, R_META_TYPE_COMMENT, fn->addr, fn->name);
|
||||
free (flagname);
|
||||
}
|
||||
ht_up_free (names);
|
||||
}
|
||||
|
||||
r_core_cmd0 (core, "e emu.str=true");
|
||||
|
||||
+349
-190
@@ -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 {
|
||||
@@ -758,7 +760,7 @@ static bool modern_skip_alloc(ClusterStream *s, const ModernCidCache *cids, int
|
||||
return false;
|
||||
}
|
||||
meta->count = count;
|
||||
if (kind == MODERN_ALLOC_VARIABLE && count) {
|
||||
if ((kind == MODERN_ALLOC_VARIABLE || kind == MODERN_ALLOC_RECORD) && count) {
|
||||
meta->alloc_items = (ut64 *)calloc ((size_t)count, sizeof (ut64));
|
||||
if (!meta->alloc_items) {
|
||||
return false;
|
||||
@@ -1301,6 +1303,9 @@ static bool modern_skip_fill_record(ClusterStream *s, const ModernClusterMeta *m
|
||||
return false;
|
||||
}
|
||||
ut64 fields = shape & 0xffffULL;
|
||||
if (meta->alloc_items && fields != meta->alloc_items[i]) {
|
||||
return false;
|
||||
}
|
||||
if (meta->alloc_items_count == meta->count && fields > meta->alloc_items_max) {
|
||||
return false;
|
||||
}
|
||||
@@ -1971,6 +1976,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 +2042,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 +2161,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 +2188,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 +2214,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 +2273,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 +2288,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 +2339,38 @@ static void modern_ref_resolver_resolve(ModernRefResolver *resolver, const Moder
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!strcmp (resolved->kind, "code")) {
|
||||
// Instruction-table code indices follow the main Code allocation order.
|
||||
// Deferred objects have no instructions in this loading unit's table.
|
||||
ut64 index = ref - meta->start_ref;
|
||||
if (index < meta->main_count) {
|
||||
resolved->code_index = index;
|
||||
}
|
||||
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])) {
|
||||
@@ -2899,10 +2985,12 @@ typedef struct {
|
||||
bool edges_truncated;
|
||||
bool has_type_arguments;
|
||||
bool has_scalar;
|
||||
bool has_shape;
|
||||
ut64 ref;
|
||||
ut64 count;
|
||||
ut64 type_arguments_ref;
|
||||
ut64 scalar;
|
||||
ut64 shape;
|
||||
int cid;
|
||||
const char *kind;
|
||||
char *name;
|
||||
@@ -3009,6 +3097,46 @@ static bool modern_value_read_array(ModernValueGraph *graph, ModernValueNode *no
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool modern_value_read_record(ModernValueGraph *graph, ModernValueNode *node, const ModernClusterMeta *meta) {
|
||||
ClusterStream s = {
|
||||
.ctx = graph->ctx,
|
||||
.cursor = meta->fill_offset,
|
||||
.end = meta->fill_end,
|
||||
};
|
||||
const ut64 target = node->ref - meta->start_ref;
|
||||
for (ut64 i = 0; i < meta->count; i++) {
|
||||
ut64 shape = 0;
|
||||
if (!cs_read_unsigned (&s, &shape)) {
|
||||
return false;
|
||||
}
|
||||
const ut64 fields = shape & 0xffffULL;
|
||||
if (!meta->alloc_items || fields != meta->alloc_items[i]) {
|
||||
return false;
|
||||
}
|
||||
if (i == target) {
|
||||
node->has_shape = true;
|
||||
node->shape = shape;
|
||||
if (!modern_value_edges_alloc (node, fields)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (ut64 j = 0; j < fields; j++) {
|
||||
ut64 ref = 0;
|
||||
if (!cs_read_ref_id (&s, &ref)) {
|
||||
return false;
|
||||
}
|
||||
if (i == target && j < node->edge_count) {
|
||||
node->edges[j].index = j;
|
||||
node->edges[j].ref = ref;
|
||||
}
|
||||
}
|
||||
if (i == target) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool modern_value_read_refs(ModernValueGraph *graph, ModernValueNode *node, const ModernClusterMeta *meta) {
|
||||
const ModernFillSpec spec = modern_get_fill_spec (&graph->cids, meta->cid);
|
||||
ClusterStream s = {
|
||||
@@ -3158,6 +3286,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;
|
||||
@@ -3168,6 +3300,8 @@ static ModernValueNode *modern_value_graph_node(ModernValueGraph *graph, ut64 re
|
||||
}
|
||||
if (!strcmp (node->kind, "array")) {
|
||||
node->complete = modern_value_read_array (graph, node, meta);
|
||||
} else if (meta->fill_kind == MODERN_FILL_RECORD) {
|
||||
node->complete = modern_value_read_record (graph, node, meta);
|
||||
} else if (meta->fill_kind == MODERN_FILL_REFS) {
|
||||
node->complete = modern_value_read_refs (graph, node, meta);
|
||||
} else if (!strcmp (node->kind, "instance")) {
|
||||
@@ -3197,15 +3331,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);
|
||||
@@ -3279,7 +3405,11 @@ static void modern_value_json_node(PJ *pj, ModernValueGraph *graph, ut64 ref, in
|
||||
if (node->has_type_arguments) {
|
||||
pj_kn (pj, "type_arguments_ref", node->type_arguments_ref);
|
||||
}
|
||||
if (node->count || node->edges) {
|
||||
if (node->has_shape) {
|
||||
pj_kn (pj, "shape", node->shape);
|
||||
pj_kn (pj, "field_names_index", node->shape >> 16);
|
||||
}
|
||||
if (node->count || node->edges || node->has_shape) {
|
||||
pj_kn (pj, "length", node->count);
|
||||
pj_kb (pj, "values_truncated", node->edges_truncated);
|
||||
}
|
||||
@@ -3346,6 +3476,9 @@ static void modern_value_text_node(RStrBuf *sb, ModernValueGraph *graph, ut64 re
|
||||
if (node->has_scalar) {
|
||||
r_strbuf_appendf (sb, " value=%" PFMT64d, (st64)node->scalar);
|
||||
}
|
||||
if (node->has_shape) {
|
||||
r_strbuf_appendf (sb, " shape=%" PRIu64 " length=%" PRIu64, (uint64_t)node->shape, (uint64_t)node->count);
|
||||
}
|
||||
r_strbuf_appendf (sb, " complete=%s\n", r_str_bool (node->complete));
|
||||
if (modern_value_path_contains (path, path_count, ref) || depth >= MODERN_VALUE_MAX_DEPTH) {
|
||||
return;
|
||||
@@ -3491,6 +3624,9 @@ bool modern_resolve_pp_slot(const ModernReq *req, ut64 pp_offset, ModernPoolSlot
|
||||
return ok;
|
||||
}
|
||||
|
||||
#define MODERN_POOL_VALUE_MAX_DEPTH 8
|
||||
#define MODERN_POOL_VALUE_MAX_REFS 4096
|
||||
|
||||
typedef struct {
|
||||
const ModernRefResolver *resolver;
|
||||
const ModernCidCache *cids;
|
||||
@@ -3498,9 +3634,14 @@ typedef struct {
|
||||
void *user;
|
||||
HtUP *seen;
|
||||
ut64 pp_offset;
|
||||
ut64 pp_base;
|
||||
ut64 pool_ref;
|
||||
ut64 pool_index;
|
||||
ut64 entry_index;
|
||||
bool values_only;
|
||||
bool via_collection;
|
||||
ut64 remaining;
|
||||
ut64 *value_offsets;
|
||||
} ModernPoolStringRefCollector;
|
||||
|
||||
static void modern_emit_pool_string_ref(ModernPoolStringRefCollector *collector, ut64 target_ref, const char *target_kind, ut64 field_index, ut64 string_ref) {
|
||||
@@ -3510,6 +3651,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,
|
||||
@@ -3518,6 +3660,8 @@ 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],
|
||||
.via_collection = collector->via_collection,
|
||||
.string_value = resolver->strings_by_ref[string_ref],
|
||||
};
|
||||
collector->cb (&info, collector->user);
|
||||
@@ -3526,71 +3670,70 @@ static void modern_emit_pool_string_ref(ModernPoolStringRefCollector *collector,
|
||||
static bool modern_collect_ref_strings(ModernPoolStringRefCollector *collector, ut64 target_ref, const char *target_kind, ut64 field_index, int depth);
|
||||
|
||||
static bool modern_collect_array_strings(ModernPoolStringRefCollector *collector, const ModernClusterMeta *meta, ut64 target_ref, bool weak, int depth) {
|
||||
const ModernRefResolver *resolver = collector? collector->resolver: NULL;
|
||||
if (!collector || !resolver || !meta || target_ref < meta->start_ref || target_ref >= meta->start_ref + meta->count) {
|
||||
return false;
|
||||
}
|
||||
ClusterStream s = {
|
||||
.ctx = resolver->ctx,
|
||||
.cursor = meta->fill_offset,
|
||||
.end = meta->fill_end,
|
||||
};
|
||||
ut64 target_index = target_ref - meta->start_ref;
|
||||
for (ut64 i = 0; i < meta->count; i++) {
|
||||
ut64 length = 0;
|
||||
ut64 ref = 0;
|
||||
if (!cs_read_unsigned (&s, &length)) {
|
||||
return false;
|
||||
}
|
||||
if (!weak && !cs_read_ref_id (&s, &ref)) {
|
||||
return false;
|
||||
}
|
||||
for (ut64 j = 0; j < length; j++) {
|
||||
if (!cs_read_ref_id (&s, &ref)) {
|
||||
const ModernRefResolver *resolver = collector->resolver;
|
||||
ClusterStream s = { .ctx = resolver->ctx, .cursor = meta->fill_offset, .end = meta->fill_end };
|
||||
if (!collector->value_offsets[meta->start_ref]) {
|
||||
for (ut64 i = 0; i < meta->count; i++) {
|
||||
collector->value_offsets[meta->start_ref + i] = s.cursor;
|
||||
ut64 length = 0;
|
||||
ut64 ref = 0;
|
||||
if (!cs_read_unsigned (&s, &length) || (!weak && !cs_read_ref_id (&s, &ref))) {
|
||||
collector->value_offsets[meta->start_ref] = 0;
|
||||
return false;
|
||||
}
|
||||
if (i == target_index) {
|
||||
(void)modern_collect_ref_strings (collector, ref, weak? "weak_array": "array", j, depth + 1);
|
||||
for (ut64 j = 0; j < length; j++) {
|
||||
if (!cs_read_ref_id (&s, &ref)) {
|
||||
collector->value_offsets[meta->start_ref] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == target_index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
s.cursor = collector->value_offsets[target_ref];
|
||||
ut64 length = 0;
|
||||
ut64 ref = 0;
|
||||
if (!cs_read_unsigned (&s, &length) || (!weak && !cs_read_ref_id (&s, &ref))) {
|
||||
return false;
|
||||
}
|
||||
for (ut64 j = 0; j < length && collector->remaining; j++) {
|
||||
if (!cs_read_ref_id (&s, &ref)) {
|
||||
return false;
|
||||
}
|
||||
(void)modern_collect_ref_strings (collector, ref, weak? "weak_array": "array", j, depth + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool modern_collect_refs_object_strings(ModernPoolStringRefCollector *collector, const ModernClusterMeta *meta, const ModernFillSpec *spec, ut64 target_ref, int depth) {
|
||||
const ModernRefResolver *resolver = collector? collector->resolver: NULL;
|
||||
if (!collector || !resolver || !meta || !spec || target_ref < meta->start_ref || target_ref >= meta->start_ref + meta->count) {
|
||||
return false;
|
||||
}
|
||||
ClusterStream s = {
|
||||
.ctx = resolver->ctx,
|
||||
.cursor = meta->fill_offset,
|
||||
.end = meta->fill_end,
|
||||
};
|
||||
ut64 target_index = target_ref - meta->start_ref;
|
||||
for (ut64 i = 0; i < meta->count; i++) {
|
||||
for (int j = 0; j < spec->num_refs; j++) {
|
||||
ut64 ref = 0;
|
||||
if (!cs_read_ref_id (&s, &ref)) {
|
||||
return false;
|
||||
const ModernRefResolver *resolver = collector->resolver;
|
||||
ClusterStream s = { .ctx = resolver->ctx, .cursor = meta->fill_offset, .end = meta->fill_end };
|
||||
if (!collector->value_offsets[meta->start_ref]) {
|
||||
for (ut64 i = 0; i < meta->count; i++) {
|
||||
collector->value_offsets[meta->start_ref + i] = s.cursor;
|
||||
for (int j = 0; j < spec->num_refs; j++) {
|
||||
ut64 ref = 0;
|
||||
if (!cs_read_ref_id (&s, &ref)) {
|
||||
collector->value_offsets[meta->start_ref] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (i == target_index) {
|
||||
(void)modern_collect_ref_strings (collector, ref, modern_fill_kind_name (meta->fill_kind), (ut64)j, depth + 1);
|
||||
for (int j = 0; j < spec->scalar_count; j++) {
|
||||
if (!modern_skip_scalar (&s, spec->scalars[j])) {
|
||||
collector->value_offsets[meta->start_ref] = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < spec->scalar_count; j++) {
|
||||
if (!modern_skip_scalar (&s, spec->scalars[j])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (i == target_index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
s.cursor = collector->value_offsets[target_ref];
|
||||
for (int j = 0; j < spec->num_refs && collector->remaining; j++) {
|
||||
ut64 ref = 0;
|
||||
if (!cs_read_ref_id (&s, &ref)) {
|
||||
return false;
|
||||
}
|
||||
(void)modern_collect_ref_strings (collector, ref, modern_fill_kind_name (meta->fill_kind), (ut64)j, depth + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool modern_collect_class_strings(ModernPoolStringRefCollector *collector, const ModernClusterMeta *meta, const ModernFillSpec *spec, ut64 target_ref, int depth) {
|
||||
@@ -3654,49 +3797,73 @@ 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->value_offsets[meta->start_ref]) {
|
||||
for (ut64 i = 0; i < meta->count; i++) {
|
||||
collector->value_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->value_offsets[meta->start_ref] = 0;
|
||||
return false;
|
||||
}
|
||||
} else if (!cs_read_ref_id (&s, &ref)) {
|
||||
collector->value_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->value_offsets[target_ref];
|
||||
for (int j = 0; j < num_fields && collector->remaining; 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) {
|
||||
const ModernRefResolver *resolver = collector? collector->resolver: NULL;
|
||||
if (!collector || !resolver || !target_ref || target_ref >= resolver->refs_count || depth > 2) {
|
||||
if (!collector || !resolver || !collector->remaining) {
|
||||
return false;
|
||||
}
|
||||
ut64 seen_key = target_ref ^ ((ut64)depth << 56);
|
||||
if (collector->seen && ht_up_find (collector->seen, seen_key, NULL)) {
|
||||
collector->remaining--;
|
||||
if (!target_ref || target_ref >= resolver->refs_count || depth > (collector->values_only? MODERN_POOL_VALUE_MAX_DEPTH: 2)) {
|
||||
return false;
|
||||
}
|
||||
ut64 previous_depth = (ut64)ht_up_find (collector->seen, target_ref, NULL);
|
||||
if (previous_depth && previous_depth <= (ut64)depth + 1) {
|
||||
return true;
|
||||
}
|
||||
if (collector->seen) {
|
||||
ht_up_insert (collector->seen, seen_key, (void *)1);
|
||||
if (previous_depth) {
|
||||
ht_up_update (collector->seen, target_ref, (void *) (ut64) (depth + 1));
|
||||
} else {
|
||||
ht_up_insert (collector->seen, target_ref, (void *) (ut64) (depth + 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) {
|
||||
@@ -3709,11 +3876,27 @@ 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 (modern_is_array_cid (collector->cids, meta->cid)) {
|
||||
return modern_collect_array_strings (collector, meta, target_ref, false, depth);
|
||||
const ModernCidCache *cids = collector->cids;
|
||||
bool array = modern_is_array_cid (cids, meta->cid);
|
||||
bool weak = modern_cid_eq (meta->cid, cids->weak_array);
|
||||
bool collection = array || weak || modern_cid_eq (meta->cid, cids->map) ||
|
||||
modern_cid_eq (meta->cid, cids->const_map) || modern_cid_eq (meta->cid, cids->set) ||
|
||||
modern_cid_eq (meta->cid, cids->const_set) || modern_cid_eq (meta->cid, cids->growable_array);
|
||||
if (collector->values_only && !collection && meta->fill_kind != MODERN_FILL_INSTANCE) {
|
||||
return false;
|
||||
}
|
||||
if (modern_cid_eq (meta->cid, collector->cids->weak_array)) {
|
||||
return modern_collect_array_strings (collector, meta, target_ref, true, depth);
|
||||
if (collection) {
|
||||
bool previous = collector->via_collection;
|
||||
collector->via_collection = true;
|
||||
bool ok;
|
||||
if (array || weak) {
|
||||
ok = modern_collect_array_strings (collector, meta, target_ref, weak, depth);
|
||||
} else {
|
||||
ModernFillSpec spec = modern_get_fill_spec (cids, meta->cid);
|
||||
ok = modern_collect_refs_object_strings (collector, meta, &spec, target_ref, depth);
|
||||
}
|
||||
collector->via_collection = previous;
|
||||
return ok;
|
||||
}
|
||||
if (meta->fill_kind == MODERN_FILL_CLASS) {
|
||||
ModernFillSpec spec = modern_get_fill_spec (collector->cids, meta->cid);
|
||||
@@ -3729,7 +3912,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 values_only) {
|
||||
DartCtx *ctx = req? req->ctx: NULL;
|
||||
if (!ctx || !cb || !modern_supported (ctx)) {
|
||||
return false;
|
||||
@@ -3744,6 +3927,13 @@ bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernP
|
||||
modern_cluster_meta_free (meta, req->num_clusters);
|
||||
return false;
|
||||
}
|
||||
ut64 *value_offsets = (ut64 *)calloc ((size_t)resolver.refs_count, sizeof (ut64));
|
||||
if (!value_offsets) {
|
||||
modern_ref_resolver_fini (&resolver);
|
||||
modern_cluster_meta_free (meta, req->num_clusters);
|
||||
return false;
|
||||
}
|
||||
|
||||
const ModernCidCache cids = modern_cid_cache_init (ctx->layout);
|
||||
for (ut64 i = 0; i < req->num_clusters; i++) {
|
||||
ModernClusterMeta *m = &meta[i];
|
||||
@@ -3756,6 +3946,9 @@ bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernP
|
||||
.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;
|
||||
@@ -3782,9 +3975,13 @@ bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernP
|
||||
.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,
|
||||
.values_only = values_only,
|
||||
.remaining = MODERN_POOL_VALUE_MAX_REFS,
|
||||
.value_offsets = value_offsets,
|
||||
};
|
||||
(void)modern_collect_ref_strings (&collector, entry.ref, "object_pool.entry", entry_index, 0);
|
||||
ht_up_free (seen);
|
||||
@@ -3797,11 +3994,22 @@ bool modern_collect_pool_strrefs(const ModernReq *req, HtUP *pp_offsets, ModernP
|
||||
break;
|
||||
}
|
||||
}
|
||||
free (value_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);
|
||||
}
|
||||
|
||||
// Follow bounded value graphs through instances and collections, excluding
|
||||
// unrelated metadata graphs and preserving the pool object's actual kind.
|
||||
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) {
|
||||
@@ -3895,118 +4103,69 @@ 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 = info->via_collection? "object_pool.collection": 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 (info->via_collection) {
|
||||
sr->object_name = r_str_appendf (sr->object_name, " leaf=%s[%" PRIu64 "]", info->target_kind, (uint64_t)info->field_index);
|
||||
} else 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,8 @@ typedef struct {
|
||||
ut64 field_index;
|
||||
ut64 string_ref;
|
||||
ut64 string_addr;
|
||||
ut32 string_flags;
|
||||
bool via_collection;
|
||||
const char *string_value;
|
||||
} ModernPoolStrRef;
|
||||
|
||||
@@ -291,6 +294,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);
|
||||
|
||||
+38
-50
@@ -33,7 +33,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
DartCtx *ctx;
|
||||
RList *buckets;
|
||||
HtUP *buckets;
|
||||
RList *xrefs;
|
||||
ut64 *count;
|
||||
ut64 limit;
|
||||
@@ -498,30 +498,20 @@ static const char *xref_pool_flag_kind(const char *name, const char **dst_type,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool xref_pool_use_seen(HtUP *seen, ut64 at, ut64 pp_off) {
|
||||
ut64 key = at ^ (pp_off << 8) ^ (pp_off >> 8);
|
||||
if (ht_up_find (seen, key, NULL)) {
|
||||
static bool xref_pool_use_seen(HtUP *seen, ut64 at) {
|
||||
// Each instruction contributes at most one PP use. Key by its address so
|
||||
// distinct sites cannot collide through a lossy address/offset combination.
|
||||
if (ht_up_find (seen, at, NULL)) {
|
||||
return true;
|
||||
}
|
||||
ht_up_insert (seen, key, (void *)1);
|
||||
ht_up_insert (seen, at, (void *)1);
|
||||
return false;
|
||||
}
|
||||
|
||||
#define DART_XREF_MAX_OPS 96
|
||||
|
||||
// `end_addr` is the next entry's address, or 0 when unknown. Without it the
|
||||
// scan disassembles a fixed window that runs past short functions into their
|
||||
// neighbours, crediting those neighbours' pool references to this entry.
|
||||
// `end_addr` is the next entry's address, or the instructions-image end for
|
||||
// the last entry. Scan the whole payload without crossing into its neighbour.
|
||||
static void collect_pool_uses_from_entry(DartCtx *ctx, const DartInstructionTableEntry *entry, ut64 end_addr, RList *uses, HtUP *seen) {
|
||||
if (!ctx || !ctx->core || !entry || !entry->address || !uses || !seen) {
|
||||
return;
|
||||
}
|
||||
ut64 nops = DART_XREF_MAX_OPS;
|
||||
if (end_addr > entry->address) {
|
||||
const ut64 avail = (end_addr - entry->address) / 4;
|
||||
nops = R_MIN (avail, (ut64)DART_XREF_MAX_OPS);
|
||||
}
|
||||
if (!nops) {
|
||||
if (!ctx || !ctx->core || !entry || !entry->address || end_addr <= entry->address || !uses || !seen) {
|
||||
return;
|
||||
}
|
||||
// Decode the function's instructions one at a time via r_core_anal_op and
|
||||
@@ -531,9 +521,9 @@ static void collect_pool_uses_from_entry(DartCtx *ctx, const DartInstructionTabl
|
||||
bool reg_valid[31] = { false };
|
||||
ut64 reg_off[31] = { 0 };
|
||||
ut64 at = entry->address;
|
||||
for (ut64 i = 0; i < nops; i++) {
|
||||
while (at < end_addr) {
|
||||
RAnalOp *op = r_core_anal_op (ctx->core, at, R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_DISASM);
|
||||
if (!op || op->size <= 0 || R_STR_ISEMPTY (op->mnemonic)) {
|
||||
if (!op || op->size <= 0 || (ut64)op->size > end_addr - at || R_STR_ISEMPTY (op->mnemonic)) {
|
||||
r_anal_op_free (op);
|
||||
break;
|
||||
}
|
||||
@@ -541,7 +531,7 @@ static void collect_pool_uses_from_entry(DartCtx *ctx, const DartInstructionTabl
|
||||
ut64 pp_off = 0;
|
||||
bool have_pp = xref_parse_pp_offset (m, &pp_off) ||
|
||||
xref_parse_mem_pp_offset (m, reg_valid, reg_off, &pp_off);
|
||||
if (have_pp && !xref_pool_use_seen (seen, at, pp_off)) {
|
||||
if (have_pp && !xref_pool_use_seen (seen, at)) {
|
||||
DartPoolUseInfo *ui = R_NEW0 (DartPoolUseInfo);
|
||||
ui->at = at;
|
||||
ui->fn_index = entry->index;
|
||||
@@ -565,35 +555,40 @@ static void collect_pool_uses_from_entry(DartCtx *ctx, const DartInstructionTabl
|
||||
}
|
||||
}
|
||||
|
||||
static RList *collect_pool_uses(DartCtx *ctx, RVecDartInstructionTableEntry *entries, ut64 limit) {
|
||||
static ut64 xref_instructions_end(DartCtx *ctx) {
|
||||
ut64 size = 0;
|
||||
if (find_snapshots (ctx) != 0 || !ctx->iso_instr || !read_u64_at (ctx, ctx->iso_instr, &size) || size < DART_IMAGE_ALIGNMENT || size > UT64_MAX - ctx->iso_instr) {
|
||||
return 0;
|
||||
}
|
||||
const ut64 end = ctx->iso_instr + size;
|
||||
RIOMap *map = r_io_map_get_at (ctx->core->io, ctx->iso_instr);
|
||||
return map && end - 1 <= r_io_map_to (map)? end: 0;
|
||||
}
|
||||
|
||||
static RList *collect_pool_uses(DartCtx *ctx, RVecDartInstructionTableEntry *entries) {
|
||||
RList *uses = r_list_newf (dart_pool_use_info_free);
|
||||
if (!ctx || !entries || RVecDartInstructionTableEntry_length (entries) == 0) {
|
||||
return uses;
|
||||
}
|
||||
HtUP *seen = ht_up_new0 ();
|
||||
ut64 scanned = 0;
|
||||
ut64 scan_limit = limit > 0? limit: 4096;
|
||||
const ut64 image_end = xref_instructions_end (ctx);
|
||||
const ut64 nentries = RVecDartInstructionTableEntry_length (entries);
|
||||
for (ut64 i = 0; i < nentries; i++) {
|
||||
const DartInstructionTableEntry *entry = RVecDartInstructionTableEntry_at (entries, i);
|
||||
if (!entry || !entry->has_code || !entry->address) {
|
||||
if (!entry || !entry->address) {
|
||||
continue;
|
||||
}
|
||||
if (scan_limit > 0 && scanned >= scan_limit) {
|
||||
break;
|
||||
}
|
||||
// Entries are emitted in ascending address order, so the next one with
|
||||
// code bounds this one.
|
||||
ut64 end_addr = 0;
|
||||
// a distinct address bounds this one, including discarded-Code entries.
|
||||
ut64 end_addr = image_end;
|
||||
for (ut64 k = i + 1; k < nentries; k++) {
|
||||
const DartInstructionTableEntry *nx = RVecDartInstructionTableEntry_at (entries, k);
|
||||
if (nx && nx->address > entry->address) {
|
||||
end_addr = nx->address;
|
||||
end_addr = image_end? R_MIN (image_end, nx->address): nx->address;
|
||||
break;
|
||||
}
|
||||
}
|
||||
collect_pool_uses_from_entry (ctx, entry, end_addr, uses, seen);
|
||||
scanned++;
|
||||
}
|
||||
ht_up_free (seen);
|
||||
return uses;
|
||||
@@ -653,17 +648,6 @@ static bool resolve_pool_entry_xref(DartCtx *ctx, DartRecoveryModel *model, RLis
|
||||
return false;
|
||||
}
|
||||
|
||||
static DartPpUseBucket *xref_bucket_by_pp(RList *buckets, ut64 pp_off) {
|
||||
RListIter *it;
|
||||
DartPpUseBucket *bucket;
|
||||
r_list_foreach (buckets, it, bucket) {
|
||||
if (bucket && bucket->pp_off == pp_off) {
|
||||
return bucket;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void xref_serialized_pool_string_cb(const ModernPoolStrRef *info, void *user) {
|
||||
DartSerializedPoolXrefCtx *ux = (DartSerializedPoolXrefCtx *)user;
|
||||
if (!info || !ux || !ux->buckets || !ux->xrefs || !ux->count || xref_limit_reached (*ux->count, ux->limit)) {
|
||||
@@ -672,7 +656,7 @@ static void xref_serialized_pool_string_cb(const ModernPoolStrRef *info, void *u
|
||||
if (!info->string_addr || R_STR_ISEMPTY (info->string_value)) {
|
||||
return;
|
||||
}
|
||||
DartPpUseBucket *bucket = xref_bucket_by_pp (ux->buckets, info->pp_offset);
|
||||
DartPpUseBucket *bucket = ht_up_find (ux->buckets, info->pp_offset, NULL);
|
||||
if (!bucket || !bucket->uses) {
|
||||
return;
|
||||
}
|
||||
@@ -701,13 +685,13 @@ static bool collect_serialized_object_pool_xrefs(DartCtx *ctx, RList *uses, RLis
|
||||
if (!use) {
|
||||
continue;
|
||||
}
|
||||
DartPpUseBucket *bucket = xref_bucket_by_pp (buckets, use->pp_off);
|
||||
DartPpUseBucket *bucket = ht_up_find (wanted, use->pp_off, NULL);
|
||||
if (!bucket) {
|
||||
bucket = R_NEW0 (DartPpUseBucket);
|
||||
bucket->pp_off = use->pp_off;
|
||||
bucket->uses = r_list_new ();
|
||||
r_list_append (buckets, bucket);
|
||||
ht_up_insert (wanted, use->pp_off, (void *)1);
|
||||
ht_up_insert (wanted, use->pp_off, bucket);
|
||||
}
|
||||
r_list_append (bucket->uses, use);
|
||||
}
|
||||
@@ -715,7 +699,7 @@ static bool collect_serialized_object_pool_xrefs(DartCtx *ctx, RList *uses, RLis
|
||||
DartVerLayout *layout_owned = ctx->layout? NULL: dart_ctx_init_layout (ctx, &layout_tmp);
|
||||
DartSerializedPoolXrefCtx ux = {
|
||||
.ctx = ctx,
|
||||
.buckets = buckets,
|
||||
.buckets = wanted,
|
||||
.xrefs = xrefs,
|
||||
.count = count,
|
||||
.limit = limit,
|
||||
@@ -752,7 +736,7 @@ static void collect_object_pool_xrefs(DartCtx *ctx, DartRecoveryModel *model, RL
|
||||
if (!ctx || !ctx->core || !model || !model->it_entries || !xrefs || xref_limit_reached (*count, limit)) {
|
||||
return;
|
||||
}
|
||||
RList *uses = collect_pool_uses (ctx, model->it_entries, limit);
|
||||
RList *uses = collect_pool_uses (ctx, model->it_entries);
|
||||
(void)collect_serialized_object_pool_xrefs (ctx, uses, xrefs, count, limit);
|
||||
ut64 pool_base = xref_object_pool_base (ctx);
|
||||
if (!pool_base) {
|
||||
@@ -784,7 +768,11 @@ static RList *dart_pool_extract_xrefs(DartCtx *ctx) {
|
||||
const ut64 limit = ctx->dump_fns_limit > 0? (ut64)ctx->dump_fns_limit: 0;
|
||||
ut64 count = 0;
|
||||
DartRecoveryModel model = { 0 };
|
||||
dart_recovery_model_load (ctx, &model, DART_RECOVERY_STRINGS | DART_RECOVERY_CLASSES | DART_RECOVERY_CLASS_FIELDS | DART_RECOVERY_IT);
|
||||
dart_recovery_model_load (ctx, &model, DART_RECOVERY_STRINGS | DART_RECOVERY_CLASSES | DART_RECOVERY_CLASS_FIELDS);
|
||||
// Even limited output needs the full table to bound every scanned payload.
|
||||
DartCtx work = *ctx;
|
||||
work.dump_fns_limit = 0;
|
||||
model.it_entries = dart_pool_extract_instruction_table (&work);
|
||||
RList *classes = model.classes;
|
||||
RVecDartInstructionTableEntry *it_entries = model.it_entries;
|
||||
if (classes) {
|
||||
|
||||
+44
-18
@@ -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;
|
||||
@@ -1351,9 +1370,15 @@ static void flutter_scan_function(RCore *core, FlutterAnalModel *model, FlutterP
|
||||
}
|
||||
|
||||
HtUP *seen_ops = ht_up_new0 ();
|
||||
RListIter *it;
|
||||
RAnalBlock *bb;
|
||||
#if R2_ABIVERSION >= 168
|
||||
RAnalBlock **it;
|
||||
R_VEC_FOREACH (&fcn->bbs, it) {
|
||||
bb = *it;
|
||||
#else
|
||||
RListIter *it;
|
||||
r_list_foreach (fcn->bbs, it, bb) {
|
||||
#endif
|
||||
if (!bb || bb->size == 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -1547,6 +1572,7 @@ bool r2flutter_analysis_run(RCore *core, DartCtx *dctx, bool quiet) {
|
||||
|
||||
RVecFlutterEntry *entries = flutter_collect_entries (core, app, &model);
|
||||
flutter_ensure_functions (core, entries);
|
||||
dart_dumper_name_functions (app);
|
||||
flutter_scan_functions (core, &model, &pp_strings, &pp_refs, entries, &stats);
|
||||
|
||||
int graph_xrefs = r2flutter_apply_pool_xrefs (core, dctx);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
NAME=pp-code-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+0x40~{pp_slot.resolved_kind}
|
||||
r2flutter -q -j -O pp+0x40~{pp_slot.resolved_code_index}
|
||||
r2flutter -q -j -O pp+0x48~{pp_slot.resolved_code_index}
|
||||
r2flutter -q -j -HHH -l 500~{snapshots[1].clusters[365].object_pools[0].entries[6].resolved_code_index}
|
||||
r2flutter -q -AAA
|
||||
CC. @ 0x3f28cc
|
||||
axfj @ 0x3f28cc~{[0].to}
|
||||
axfj @ 0x3f28cc~{[0].type}
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
code
|
||||
10816
|
||||
10817
|
||||
10816
|
||||
dart: indirect call via PP+0x40 code method.fn_10816
|
||||
4132980
|
||||
CALL
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,18 @@
|
||||
NAME=pp-code-resolve-ios
|
||||
FILE=bins/ios/AuthPass.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+0x68~{pp_slot.resolved_kind}
|
||||
r2flutter -q -j -O pp+0x68~{pp_slot.resolved_code_index}
|
||||
r2flutter -q -j -O pp+0x70~{pp_slot.resolved_code_index}
|
||||
r2flutter -q -j -HHH -l 1100~{snapshots[1].clusters[1028].object_pools[0].entries[11].resolved_code_index}
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
code
|
||||
36399
|
||||
36400
|
||||
36399
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,23 @@
|
||||
NAME=pp-code-resolve-main-discarded-and-deferred
|
||||
FILE=malloc://0x8c
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
# Main Code refs include a discarded entry; the last Code is deferred.
|
||||
wx 50414421f5f5dcdc84000000000000000200000000000000333063303531636239313932663664363831326265336630623837613633353670726f647563742061726d363420636f6d707265737365642d706f696e746572730082878283800040c483c0c8c081c00060c5818480808080808080808080808080808080808080808080841183118411851186
|
||||
r2flutter -q -j -O pp+0x10~{pp_slot.resolved_kind}
|
||||
r2flutter -q -j -O pp+0x10~{pp_slot.resolved_code_index}
|
||||
r2flutter -q -j -O pp+0x18~{pp_slot.resolved_code_index}
|
||||
r2flutter -q -j -O pp+0x20~{pp_slot.resolved_code_index}
|
||||
r2flutter -q -j -O pp+0x28~{pp_slot.resolved_kind}
|
||||
r2flutter -q -j -O pp+0x28~{=}~resolved_code_index~?
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
code
|
||||
0
|
||||
1
|
||||
2
|
||||
code
|
||||
0
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,24 @@
|
||||
NAME=pp-collection-cycles-and-map-set-storage
|
||||
FILE=malloc://0xb4
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
wx 50414421f5f5dcdcac000000000000000200000000000000333063303531636239313932663664363831326265336630623837613633353670726f647563742061726d363420636f6d707265737365642d706f696e746572730082878780800020d781a00040d681840040d5810060d5810000d6810020d6810060c58185a0636f6c6c656374696f6e2d6379636c6584808483838580808480808080848080808084808080808480808511841185118611871188
|
||||
r2flutter -q -j -xz~{[0].value}
|
||||
r2flutter -q -j -xz~{[0].refs[0].name}
|
||||
r2flutter -q -j -xz~{[0].refs[1].name}
|
||||
r2flutter -q -j -xz~{[0].refs[2].name}
|
||||
r2flutter -q -j -xz~{[0].refs[3].name}
|
||||
r2flutter -q -j -xz~{[0].refs[4].name}
|
||||
r2flutter -q -j -xz~{[0].refs[4].kind}
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
collection-cycle
|
||||
pool=9 index=0 entry=0 pp_off=0x10 leaf=array[1]
|
||||
pool=9 index=0 entry=1 pp_off=0x18 leaf=array[1]
|
||||
pool=9 index=0 entry=2 pp_off=0x20 leaf=array[1]
|
||||
pool=9 index=0 entry=3 pp_off=0x28 leaf=array[1]
|
||||
pool=9 index=0 entry=4 pp_off=0x30 leaf=array[1]
|
||||
object_pool.collection
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,28 @@
|
||||
NAME=pp-collection-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 @ 0x42ad3~0xdb177
|
||||
axtq @ 0x4d912~0xdb177
|
||||
r2flutter -q -AAA
|
||||
CC. @ 0x19c21c~PP slot +0x5d0 array~?
|
||||
CC. @ 0x19c21c~contains string "cancel"~?
|
||||
CC. @ 0x19c21c~contains string "hover"~?
|
||||
axtq @ 0x42ad3~0x19c21c
|
||||
axtq @ 0x4d912~0x19c21c
|
||||
r2flutter -q -r -xz~0x42ad3 0xdb177
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0xdb177
|
||||
0xdb177
|
||||
1
|
||||
1
|
||||
1
|
||||
0x19c21c
|
||||
0x19c21c
|
||||
ax 0x42ad3 0xdb177
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,28 @@
|
||||
NAME=pp-collection-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 @ 0x2ab3a0~0x22d6a1
|
||||
axtq @ 0x2bbf30~0x22d6a1
|
||||
r2flutter -q -AAA
|
||||
CC. @ 0x15f4c~PP slot +0x3d0 array~?
|
||||
CC. @ 0x15f4c~contains string "cancel"~?
|
||||
CC. @ 0x15f4c~contains string "hover"~?
|
||||
axtq @ 0x2ab3a0~0x15f4c
|
||||
axtq @ 0x2bbf30~0x15f4c
|
||||
r2flutter -q -r -xz~0x2ab3a0 0x22d6a1
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
0x22d6a1
|
||||
0x22d6a1
|
||||
1
|
||||
1
|
||||
1
|
||||
0x15f4c
|
||||
0x15f4c
|
||||
ax 0x2ab3a0 0x22d6a1
|
||||
EOF
|
||||
RUN
|
||||
@@ -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,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
|
||||
@@ -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
|
||||
@@ -0,0 +1,23 @@
|
||||
NAME=record-values-android
|
||||
FILE=bins/android/mafia/libapp.so
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
r2flutter -q -j -O ref:158051~{snapshot.kind}
|
||||
r2flutter -q -j -O ref:158051~{snapshot.complete}
|
||||
r2flutter -q -j -O ref:158051~{snapshot.shape}
|
||||
r2flutter -q -j -O ref:158051~{snapshot.length}
|
||||
r2flutter -q -j -O ref:158051~{snapshot.fields[0].ref}
|
||||
r2flutter -q -j -O ref:158051~{snapshot.fields[1].value.name}
|
||||
r2flutter -q -j -O ref:158051~{snapshot.fields[1].value.fields[2].value.value}
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
record
|
||||
true
|
||||
2
|
||||
2
|
||||
1001
|
||||
XmlAttributeType
|
||||
DOUBLE_QUOTE
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,14 @@
|
||||
NAME=record-values-malformed
|
||||
FILE=malloc://0xfd
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
wx 50414421f5f5dcdcf5000000000000000200000000000000333063303531636239313932663664363831326265336630623837613633353670726f647563742061726d363420636f6d707265737365642d706f696e746572730082898280800020cf82c1be0060d08580828281018180818384020084868381880181838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383
|
||||
r2flutter -q -j -O ref:6~{snapshot.kind}
|
||||
r2flutter -q -j -O ref:6~{snapshot.complete}
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
record
|
||||
false
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,34 @@
|
||||
NAME=record-values-synthetic
|
||||
FILE=malloc://0xfd
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
wx 50414421f5f5dcdcf5000000000000000200000000000000333063303531636239313932663664363831326265336630623837613633353670726f647563742061726d363420636f6d707265737365642d706f696e746572730082898280800020cf82c1be0060d08580828281018180828384020084868381880181838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383
|
||||
r2flutter -q -j -O ref:5~{snapshot.complete}
|
||||
r2flutter -q -j -O ref:5~{snapshot.length}
|
||||
r2flutter -q -j -O ref:6~{snapshot.fields[0].value.value}
|
||||
r2flutter -q -j -O ref:6~{snapshot.fields[1].value.value}
|
||||
r2flutter -q -j -O ref:7~{snapshot.shape}
|
||||
r2flutter -q -j -O ref:7~{snapshot.field_names_index}
|
||||
r2flutter -q -j -O ref:7~{snapshot.fields[0].value.fields[1].value.value}
|
||||
r2flutter -q -j -O ref:8~{snapshot.fields[0].value.cycle}
|
||||
r2flutter -q -j -O ref:9~{snapshot.length}
|
||||
r2flutter -q -j -O ref:9~{snapshot.values_truncated}
|
||||
r2flutter -q -j -O ref:9~{snapshot.fields[127].value.value}
|
||||
r2flutter -q -j -O ref:9~{=}~name = ~?
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
true
|
||||
0
|
||||
1
|
||||
-2
|
||||
65538
|
||||
1
|
||||
-2
|
||||
true
|
||||
129
|
||||
true
|
||||
1
|
||||
128
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,20 @@
|
||||
NAME=record-values-uncompressed
|
||||
FILE=malloc://0x100
|
||||
CMDS=<<EOF
|
||||
L ../src/r2/core_flutter.so
|
||||
L ../src/r2/core_flutter.dylib
|
||||
wx 50414421f5f5dcdcf8000000000000000200000000000000333063303531636239313932663664363831326265336630623837613633353670726f647563742061726d3634206e6f2d636f6d707265737365642d706f696e746572730082898280800020cf82c1be0060d08580828281018180828384020084868381880181838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383838383
|
||||
r2flutter -q -j -O ref:6~{snapshot.kind}
|
||||
r2flutter -q -j -O ref:6~{snapshot.complete}
|
||||
r2flutter -q -j -O ref:6~{snapshot.length}
|
||||
r2flutter -q -j -O ref:6~{snapshot.fields[1].value.value}
|
||||
r2flutter -O ref:6~kind=record
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
record
|
||||
true
|
||||
2
|
||||
-2
|
||||
ref=6 kind=record cid=67 shape=2 length=2 complete=true
|
||||
EOF
|
||||
RUN
|
||||
@@ -56,7 +56,7 @@ L ../src/r2/core_flutter.so
|
||||
r2flutter -q -j -xz~{[0]}
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
{"ref":10404,"len":25,"value":"useShouldInterceptRequest","category":"lib","two_byte":false,"canonical":true,"addr":302465,"refs":[{"obj":195985,"type":"other","kind":"object_pool.entry","name":"pool=195985 index=0 entry=44440 pp_off=0x56cd0"}]}
|
||||
{"ref":2429,"len":15,"value":"<optimized out>","category":"app","two_byte":false,"canonical":true,"addr":2133,"refs":[{"obj":195985,"type":"other","kind":"object_pool.entry","name":"pool=195985 index=0 entry=7053 pp_off=0xdc78"}]}
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -68,9 +68,9 @@ L ../src/r2/core_flutter.so
|
||||
r2flutter -q -z~:0..3
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
useShouldInterceptRequest
|
||||
There was an error but it could not be loaded from IsarCore.
|
||||
exercise
|
||||
<optimized out>
|
||||
\x1a
|
||||
4
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -82,10 +82,10 @@ L ../src/r2/core_flutter.so
|
||||
r2flutter -q -r -x -z~:0..4
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
iz+ 0x00049d81 25
|
||||
f str.useShouldInterceptRequest.0x49d81 25 @ 0x00049d81
|
||||
Cs8 25 @ 0x00049d81
|
||||
ax 0x49d81 0x3dbe24
|
||||
iz+ 0x00000855 15
|
||||
f str._optimized_out_.0x855 15 @ 0x00000855
|
||||
Cs8 15 @ 0x00000855
|
||||
ax 0x855 0x392dcc
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -97,8 +97,8 @@ L ../src/r2/core_flutter.so
|
||||
r2flutter -qz~:0..3
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
useShouldInterceptRequest
|
||||
There was an error but it could not be loaded from IsarCore.
|
||||
exercise
|
||||
<optimized out>
|
||||
\x1a
|
||||
4
|
||||
EOF
|
||||
RUN
|
||||
@@ -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
|
||||
@@ -0,0 +1,14 @@
|
||||
NAME=xrefs-discarded-code-android
|
||||
FILE=bins/android/w0rdle/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
|
||||
# Mark all table rows as lacking retained Code metadata; instructions remain.
|
||||
wv4 11017 @ 0x12c998
|
||||
r2flutter -q -x~0x1b675c~0xc7b
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
code code.string 0x1b675c -> 0xc7b
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,15 @@
|
||||
NAME=xrefs-full-coverage-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
|
||||
# Instruction 389 in IT entry 808, followed by IT entry 10906.
|
||||
r2flutter -q -x~0x1b675c~0xc7b
|
||||
r2flutter -q -x~0x3f4908~0x23a0b
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
code code.string 0x1b675c -> 0xc7b
|
||||
code code.string 0x3f4908 -> 0x23a0b
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,15 @@
|
||||
NAME=xrefs-full-coverage-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
|
||||
# Instruction 107 in IT entry 2642, followed by IT entry 4235.
|
||||
r2flutter -q -x~0x929d0~0x2a1600
|
||||
r2flutter -q -x~0xea070~0x2a1600
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
code code.string 0x929d0 -> 0x2a1600
|
||||
code code.string 0xea070 -> 0x2a1600
|
||||
EOF
|
||||
RUN
|
||||
@@ -0,0 +1,15 @@
|
||||
NAME=xrefs-instructions-end
|
||||
FILE=bins/android/w0rdle/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
|
||||
# Load the same PP string in the last function and just outside the image.
|
||||
wx 602f40f9 @ 0x3f8814
|
||||
wx 602f40f9 @ 0x3f8870
|
||||
r2flutter -q -x~0x3f8814,0x3f8870~0x23a0b
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
code code.string 0x3f8814 -> 0x23a0b
|
||||
EOF
|
||||
RUN
|
||||
Reference in new issue
Block a user