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 | ||
|
|
7be0c178fb | ||
|
|
3552d7485c | ||
|
|
878a588adf | ||
|
|
d0215dbfc2 | ||
|
|
3cef0ce07a | ||
|
|
7d019ea748 | ||
|
|
f7c6cc5b3d | ||
|
|
24e3502737 | ||
|
|
78719df43e | ||
|
|
3fe3c11bac |
No files matched your search
@@ -1,7 +1,7 @@
|
||||
name: ci
|
||||
|
||||
env:
|
||||
R2V: 6.2.2
|
||||
R2V: 6.2.4
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -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.4.0' ; VERSION_MAJOR=0; VERSION_MINOR=4; VERSION_PATCH=0; VERSION_NUMBER=00400; 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.4.0 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.4.0"
|
||||
echo "0.6.0"
|
||||
exit 0
|
||||
fi
|
||||
echo "r2flutter-0.4.0 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.4.0"
|
||||
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.4.0
|
||||
VERSION 0.6.0
|
||||
CONTACT pancake ; [email protected]
|
||||
|
||||
LANG_C!
|
||||
|
||||
+270
@@ -1797,3 +1797,273 @@ misaligned). Not yet handled: the x64 ObjectPool *slot* decoder (`-O pp+off`)
|
||||
still misreads compressed pool pointers, so a slot that the collector did not
|
||||
already resolve stays a bare offset; and there is no committed x86_64 test
|
||||
fixture.
|
||||
|
||||
## Dart 2.19 Type fill must not consume the legacy combined byte
|
||||
|
||||
The Flutter 3.7.12/Dart 2.19.0 snapshot hash
|
||||
`adb4292f3ec25074ca70abcd2d5c7251` exposed a one-byte fill-stream drift:
|
||||
`r2flutter -HHH` stopped after the `TypeArguments` cluster and reported the
|
||||
later ObjectPool as `fill_not_parsed`. Consequently `-AAA` could not build the
|
||||
synthetic PP image or apply ObjectPool string xrefs; `axt 0x31903` for
|
||||
`https://www.game250522linecfg.xyz` was empty.
|
||||
|
||||
The cause was treating all Dart 2.x `Type::ReadFill` records alike. Through
|
||||
2.18 their three references are followed by `ReadUnsigned(type_class_id)` and
|
||||
a one-byte combined nullability/type-state field. Dart 2.19 replaces that pair
|
||||
with one `ReadUnsigned(flags)`. Consuming the obsolete byte desynchronizes the
|
||||
rest of the fill stream. Keep the old rule only before 2.19; with the corrected
|
||||
rule the ObjectPool reaches cluster 652, `-p` reconstructs 44955 entries, and
|
||||
`r2flutter -AAA` applies 11560 graph xrefs, including an `axt` result for
|
||||
0x31903.
|
||||
|
||||
## Code xrefs need discarded instructions and correctly shifted PP offsets
|
||||
|
||||
The Dart 2.19 sample above has 29622 instruction-table entries, but only 7545
|
||||
retained `Code` objects. `first_entry_with_code=22077` separates instructions
|
||||
whose `Code` metadata was discarded from entries with retained metadata; it
|
||||
does not separate non-executable stubs from application code. The VM writer
|
||||
in `runtime/vm/app_snapshot.cc` orders discarded Code objects first. Both URL
|
||||
loaders are in that prefix (IT indices 14623 and 15109).
|
||||
|
||||
An unlimited instruction-table extraction must use the table header's length,
|
||||
not the clustered header's retained Code count. `-AAA` must collect all table
|
||||
entrypoints in addition to named methods so these functions get basic blocks
|
||||
and their PP loads are scanned. `has_code` still controls Code-index metadata
|
||||
lookups; it must not decide whether instructions can be analyzed.
|
||||
|
||||
ARM64 decoders can return an already shifted `RArchValue.imm` for
|
||||
`add x17, x27, 0x46, lsl 12`. Applying the text's shift to that value again
|
||||
produces `0x46000000`, breaking the subsequent pool lookup. Read the unshifted
|
||||
immediate from the instruction text and apply the shift exactly once.
|
||||
|
||||
Verified with `/tmp/mal/libapp.so` (identical to `libapp.so.noxrefs`):
|
||||
|
||||
- `0x85b028` in `fcn.0085afb0` loads `PP+0x46200` and now has a direct string
|
||||
xref to `0x73986` (`https://reg.gptreg0706.xyz`).
|
||||
- `0x836f98` in `fcn.00836f4c` loads `PP+0x4e6c8` and now has a direct string
|
||||
xref to `0x31903` (`https://www.game250522linecfg.xyz`).
|
||||
|
||||
The unaligned `0x20022d` and `0x2086f5` sources are synthetic pool slots in
|
||||
the serialized snapshot address range, not executable use sites. `-AA` keeps
|
||||
those pool links; `-AAA` adds the instruction-to-string links. Regression tests
|
||||
cover a cached discarded-Code table on Android, and real shifted PP loads on
|
||||
Android w0rdle and iOS Runner without depending on the private samples.
|
||||
|
||||
## Dart 2.19 class metadata uses the modern fill walker
|
||||
|
||||
`-c` printed ~280 `size=0` classes for a Dart 2.19 Android sample because
|
||||
`modern_can_extract_classes` only accepted `OBJECT_HEADER` (3.4+) snapshots.
|
||||
The legacy class parser then failed on the first (unified `String`) cluster
|
||||
and the string heuristic took over. The modern walker already handles
|
||||
`CID_SHIFT1` (2.14-3.3) for `-H`, pools and xrefs, and the Class fill layout
|
||||
(13 refs: name=0, library=7, super_type=9) matches 2.19 product snapshots.
|
||||
Gating on `modern_supported` recovers 6914 classes, 1515 fields and 7398
|
||||
methods on that sample.
|
||||
|
||||
## Class methods resolve entry points from InstructionTable slots
|
||||
|
||||
`Function` fills only carry a code index; `DartMethodInfo.entry_point` was
|
||||
never set, so every class method printed `0x00000000` on all versions. The
|
||||
index is the raw InstructionTable slot, not the `first_entry_with_code`
|
||||
relative `DartInstructionTableEntry.code_index`. Slots below
|
||||
`first_entry_with_code` belong to functions whose Code object was discarded
|
||||
(`dwarf_stack_traces_mode`), but they still own real instructions, so they are
|
||||
mapped too. On the 2.19 sample 22077 of 29622 slots are like that; `-f` used
|
||||
to list only the 7545 Code-backed ones and now also gets the 4614 named
|
||||
methods in the discarded range through `dart_app_merge_class_methods`.
|
||||
InstructionTable names stay authoritative there: `dedup_instructions` makes
|
||||
several methods share one body, so class-derived names only replace
|
||||
placeholder (`func.*`, `method.fn_*`) names.
|
||||
|
||||
## Signed 64-bit varints use the 192 end-byte marker
|
||||
|
||||
`cs_read_tagged64` decoded the final varint byte as `raw - 64` instead of
|
||||
`raw - 192` (`ReadStream::Read<T>` uses `kEndByteMarker`), adding
|
||||
`128 << shift` to every Mint/Smi. Field offsets (`host_offset_or_field_id`)
|
||||
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.4.0"
|
||||
#define R2FLUTTER_VERSION "0.6.0"
|
||||
#define R2FLUTTER_VERSION_MAJOR 0
|
||||
#define R2FLUTTER_VERSION_MINOR 4
|
||||
#define R2FLUTTER_VERSION_MINOR 6
|
||||
#define R2FLUTTER_VERSION_PATCH 0
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
project(
|
||||
'r2flutter',
|
||||
'c',
|
||||
version : '0.4.0',
|
||||
version : '0.6.0',
|
||||
meson_version : '>=0.56.0',
|
||||
default_options : ['warning_level=2', 'werror=false']
|
||||
)
|
||||
|
||||
+13
-4
@@ -265,6 +265,9 @@ static int dart_function_cmp(const DartFunction *fa, const DartFunction *fb) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Above placeholder names (func.*, method.fn_*) but below any resolved name.
|
||||
#define DART_CLASS_METHOD_NAME_QUALITY 20
|
||||
|
||||
static int dart_function_name_quality(const char *name) {
|
||||
if (R_STR_ISEMPTY (name)) {
|
||||
return 0;
|
||||
@@ -476,14 +479,17 @@ static char *dart_format_method_full_name(const DartClassInfo *ci, const DartMet
|
||||
if (!mi || R_STR_ISEMPTY (mi->name)) {
|
||||
return NULL;
|
||||
}
|
||||
char *lib = dart_sanitize_component (ci && ci->library_name? ci->library_name: "app", true);
|
||||
// Without a library name use the InstructionTable form (method.Owner.leaf)
|
||||
// so both sources agree on the name of the same entry.
|
||||
char *lib = ci && ci->library_name? dart_sanitize_component (ci->library_name, true): NULL;
|
||||
char *owner = dart_sanitize_component (ci && ci->name? ci->name: mi->owner_name, false);
|
||||
char *leaf = dart_format_method_leaf_name (ci? ci->name: mi->owner_name, mi);
|
||||
const char *name = leaf? leaf: mi->name;
|
||||
char *out = NULL;
|
||||
if (R_STR_ISNOTEMPTY (owner)) {
|
||||
out = r_str_newf ("method.%s.%s.%s", lib, owner, leaf? leaf: mi->name);
|
||||
out = lib? r_str_newf ("method.%s.%s.%s", lib, owner, name): r_str_newf ("method.%s.%s", owner, name);
|
||||
} else {
|
||||
out = r_str_newf ("method.%s..%s", lib, leaf? leaf: mi->name);
|
||||
out = lib? r_str_newf ("method.%s..%s", lib, name): r_str_newf ("method.%s", name);
|
||||
}
|
||||
r_name_filter (out, 0);
|
||||
free (lib);
|
||||
@@ -533,11 +539,14 @@ static void dart_app_merge_class_methods(DartApp *app) {
|
||||
continue;
|
||||
}
|
||||
char *fullname = dart_format_method_full_name (ci, mi);
|
||||
// InstructionTable names stay authoritative: deduplicated code is
|
||||
// shared by several methods, so class metadata only fills the
|
||||
// placeholder names the table could not resolve.
|
||||
dart_app_add_or_update_fn (app,
|
||||
fullname? fullname: mi->name,
|
||||
dart_normalize_code_addr (mi->entry_point),
|
||||
0,
|
||||
dart_function_name_quality (fullname? fullname: mi->name),
|
||||
R_MIN (dart_function_name_quality (fullname? fullname: mi->name), DART_CLASS_METHOD_NAME_QUALITY),
|
||||
mi->signature);
|
||||
free (fullname);
|
||||
}
|
||||
|
||||
+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");
|
||||
|
||||
@@ -1488,6 +1488,46 @@ static void resolve_class_and_field_names(DartCtx *ctx, RList *class_list, RList
|
||||
}
|
||||
}
|
||||
|
||||
// Function fills only carry an InstructionTable slot; decode the table once
|
||||
// and turn each method's slot into its entry address.
|
||||
static void resolve_method_entry_points(DartCtx *ctx, RList *class_list) {
|
||||
const int dump_snapshot_json = ctx->dump_snapshot_json;
|
||||
const int dump_fns_limit = ctx->dump_fns_limit;
|
||||
ctx->dump_snapshot_json = 0;
|
||||
ctx->dump_fns_limit = 0;
|
||||
RVecDartInstructionTableEntry *it = dart_pool_extract_instruction_table (ctx);
|
||||
ctx->dump_snapshot_json = dump_snapshot_json;
|
||||
ctx->dump_fns_limit = dump_fns_limit;
|
||||
if (!it) {
|
||||
return;
|
||||
}
|
||||
ut64 count = 0;
|
||||
DartInstructionTableEntry *ie;
|
||||
R_VEC_FOREACH (it, ie) {
|
||||
count = R_MAX (count, ie->index + 1);
|
||||
}
|
||||
// Function::code_index is the raw InstructionTable slot, not the
|
||||
// first_entry_with_code-relative index kept in ie->code_index. Slots below
|
||||
// first_entry_with_code lost their Code object (discarded in AOT) but
|
||||
// still own real instructions, so map them too.
|
||||
ut64 *addr_by_code_index = calloc (count + 1, sizeof (ut64));
|
||||
R_VEC_FOREACH (it, ie) {
|
||||
addr_by_code_index[ie->index] = ie->address;
|
||||
}
|
||||
RListIter *iter, *miter;
|
||||
DartClassInfo *ci;
|
||||
DartMethodInfo *mi;
|
||||
r_list_foreach (class_list, iter, ci) {
|
||||
r_list_foreach (ci->methods, miter, mi) {
|
||||
if (!mi->entry_point && mi->code_index < count) {
|
||||
mi->entry_point = addr_by_code_index[mi->code_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
free (addr_by_code_index);
|
||||
dart_instruction_table_list_free (it);
|
||||
}
|
||||
|
||||
RList *dart_pool_extract_classes(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->core) {
|
||||
return NULL;
|
||||
@@ -1693,6 +1733,9 @@ RList *dart_pool_extract_classes(DartCtx *ctx) {
|
||||
}
|
||||
}
|
||||
dart_ctx_fini_layout (ctx, layout_owned);
|
||||
if (modern_classes) {
|
||||
resolve_method_entry_points (ctx, class_list);
|
||||
}
|
||||
return class_list;
|
||||
}
|
||||
|
||||
|
||||
+366
-193
@@ -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 {
|
||||
@@ -392,6 +394,7 @@ typedef struct {
|
||||
// handful of clusters. Their readers are gated on this flag; everything
|
||||
// else is shared.
|
||||
bool legacy_format;
|
||||
bool legacy_type_combined_byte;
|
||||
bool shift1_format;
|
||||
bool closure_variable;
|
||||
// every CID this layout defines, indexed by kind; -1 when the kind is absent
|
||||
@@ -468,6 +471,7 @@ static ModernCidCache modern_cid_cache_init(const DartVerLayout *layout) {
|
||||
.typed_data_internal_limit = dart_cid_typed_data_limit (layout),
|
||||
.typed_data_stride = dart_cid_typed_data_stride (layout),
|
||||
.legacy_format = modern_layout_is_legacy (layout),
|
||||
.legacy_type_combined_byte = layout && dart_version_compare (layout->dart_version, "2.19.0") == -1,
|
||||
.shift1_format = layout && layout->tag_style == DART_TAG_STYLE_CID_SHIFT1,
|
||||
.closure_variable = layout && layout->closure_variable,
|
||||
};
|
||||
@@ -756,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;
|
||||
@@ -1077,14 +1081,26 @@ static ModernFillSpec modern_get_fill_spec(const ModernCidCache *cids, int cid)
|
||||
static const ModernFillSpecRule legacy_rules[] = {
|
||||
// FfiTrampolineData: ReadFromTo (4) + ReadUnsigned (callback_id).
|
||||
{ DART_CID_FFI_TRAMPOLINE_DATA, MODERN_FILL_REFS, 4, -1, -1, 1, { MODERN_SCALAR_UNSIGNED } },
|
||||
// Type: ReadFromTo (3) + ReadUnsigned (type_class_id) + Read<uint8_t>.
|
||||
{ DART_CID_TYPE, MODERN_FILL_REFS, 3, -1, -1, 2, { MODERN_SCALAR_UNSIGNED, MODERN_SCALAR_UINT8 } },
|
||||
// TypeParameter: ReadFromTo (3) + Read<int32_t> + 3x Read<uint8_t>.
|
||||
{ DART_CID_TYPE_PARAMETER, MODERN_FILL_REFS, 3, -1, -1, 4, { MODERN_SCALAR_TAGGED32, MODERN_SCALAR_UINT8, MODERN_SCALAR_UINT8, MODERN_SCALAR_UINT8 } },
|
||||
// SubtypeTestCache::ReadFill reads only the cache_ ref.
|
||||
{ DART_CID_SUBTYPE_TEST_CACHE, MODERN_FILL_REFS, 1, -1, -1, 0, { 0 } },
|
||||
};
|
||||
if (cids->legacy_format) {
|
||||
if (modern_cid_eq (cid, cids->type) && cids->legacy_type_combined_byte) {
|
||||
// Through Dart 2.18 Type::ReadFill has type_class_id followed by a
|
||||
// combined nullability/type-state byte. Dart 2.19 replaced those with
|
||||
// a single flags ReadUnsigned. Keeping the old byte for 2.19 shifts
|
||||
// every following fill cluster and makes the ObjectPool unreachable.
|
||||
return modern_fill_spec (&(ModernFillSpecRule){
|
||||
.kind = MODERN_FILL_REFS,
|
||||
.num_refs = 3,
|
||||
.name_idx = -1,
|
||||
.owner_idx = -1,
|
||||
.scalar_count = 2,
|
||||
.scalars = { MODERN_SCALAR_UNSIGNED, MODERN_SCALAR_UINT8 },
|
||||
});
|
||||
}
|
||||
for (size_t i = 0; i < R_ARRAY_SIZE (legacy_rules); i++) {
|
||||
if (modern_cid_of_kind (cids, legacy_rules[i].cid_kind) == cid) {
|
||||
return modern_fill_spec (&legacy_rules[i]);
|
||||
@@ -1287,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;
|
||||
}
|
||||
@@ -1957,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));
|
||||
}
|
||||
|
||||
@@ -2021,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);
|
||||
@@ -2109,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 },
|
||||
@@ -2135,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;
|
||||
}
|
||||
@@ -2159,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;
|
||||
}
|
||||
|
||||
@@ -2208,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";
|
||||
}
|
||||
@@ -2220,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;
|
||||
@@ -2255,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])) {
|
||||
@@ -2885,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;
|
||||
@@ -2995,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 = {
|
||||
@@ -3144,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;
|
||||
@@ -3154,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")) {
|
||||
@@ -3183,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);
|
||||
@@ -3265,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);
|
||||
}
|
||||
@@ -3332,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;
|
||||
@@ -3477,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;
|
||||
@@ -3484,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) {
|
||||
@@ -3496,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,
|
||||
@@ -3504,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);
|
||||
@@ -3512,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) {
|
||||
@@ -3640,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) {
|
||||
@@ -3695,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);
|
||||
@@ -3715,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;
|
||||
@@ -3730,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];
|
||||
@@ -3742,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;
|
||||
@@ -3768,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);
|
||||
@@ -3783,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) {
|
||||
@@ -3881,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
|
||||
@@ -4450,7 +4623,7 @@ static bool modern_load_vm_base_strings(DartCtx *ctx, char **strings_by_ref, ut6
|
||||
}
|
||||
|
||||
static bool modern_can_extract_classes(DartCtx *ctx) {
|
||||
if (!ctx || !ctx->layout || ctx->layout->tag_style != DART_TAG_STYLE_OBJECT_HEADER) {
|
||||
if (!modern_supported (ctx)) {
|
||||
return false;
|
||||
}
|
||||
// Both pointer widths are supported: the class/field fill readers decode
|
||||
|
||||
@@ -460,7 +460,9 @@ static int decode_pool_and_emit(DartItEmitRequest *req) {
|
||||
goto cleanup;
|
||||
}
|
||||
const ut64 layout_cap = ctx->layout && ctx->layout->it_cap? ctx->layout->it_cap: 20000;
|
||||
const ut64 max_entries = req->max_entries > 0? req->max_entries: (req->on_it? itlen: layout_cap);
|
||||
// itlen counts retained Code objects, not instructions whose Code metadata
|
||||
// was discarded. Let the table header bound an unlimited IT extraction.
|
||||
const ut64 max_entries = req->max_entries > 0? req->max_entries: (req->on_it? 0: layout_cap);
|
||||
req->data_image_base = data_image_base;
|
||||
req->data_image_end = data_image_end;
|
||||
req->itlen = itlen;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -483,33 +483,24 @@ bool cs_read_tagged32(ClusterStream *s, ut32 *out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ReadStream::Read<int64_t>: 7-bit groups, then a final byte holding a signed
|
||||
// value in [-64, 63] biased by kEndByteMarker (192).
|
||||
bool cs_read_tagged64(ClusterStream *s, int64_t *out) {
|
||||
ut8 raw = 0;
|
||||
if (!cs_read_u8 (s, &raw)) {
|
||||
return false;
|
||||
}
|
||||
int8_t b = (int8_t)raw;
|
||||
if (b < 0) {
|
||||
if (out) {
|
||||
*out = (int64_t)b + 192;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
int64_t v = (int64_t)raw;
|
||||
int shift = 7;
|
||||
for (int i = 1; i < 10; i++) {
|
||||
ut64 v = 0;
|
||||
int shift = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
ut8 raw = 0;
|
||||
if (!cs_read_u8 (s, &raw)) {
|
||||
return false;
|
||||
}
|
||||
b = (int8_t)raw;
|
||||
if (b < 0) {
|
||||
v |= ((int64_t)b + 192) << shift;
|
||||
if (raw > 0x7f) {
|
||||
v |= (ut64) ((int64_t)raw - 192) << shift;
|
||||
if (out) {
|
||||
*out = v;
|
||||
*out = (int64_t)v;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
v |= ((int64_t)raw) << shift;
|
||||
v |= (ut64)raw << shift;
|
||||
shift += 7;
|
||||
}
|
||||
return false;
|
||||
|
||||
+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) {
|
||||
|
||||
@@ -564,6 +564,7 @@ R_API RLibStruct radare_plugin = {
|
||||
.type = R_LIB_TYPE_CORE,
|
||||
.data = &r_core_plugin_r2flutter,
|
||||
.version = R2_VERSION,
|
||||
.abiversion = R2_ABIVERSION
|
||||
.abiversion = R2_ABIVERSION,
|
||||
.pkgname = "r2flutter"
|
||||
};
|
||||
#endif
|
||||
+76
-23
@@ -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) {
|
||||
@@ -302,6 +307,22 @@ static bool flutter_parse_shifted_imm(const char *line, ut64 *imm) {
|
||||
if (!lsl) {
|
||||
return false;
|
||||
}
|
||||
// RArchValue.imm may already include the shift (depending on the ARM
|
||||
// decoder). Read the unshifted third operand before applying it once.
|
||||
const char *arg = strchr (line, ',');
|
||||
arg = arg? strchr (arg + 1, ','): NULL;
|
||||
if (!arg || arg >= lsl) {
|
||||
return false;
|
||||
}
|
||||
arg++;
|
||||
while (isspace ((ut8)*arg) || *arg == '#') {
|
||||
arg++;
|
||||
}
|
||||
char *end;
|
||||
ut64 value = strtoull (arg, &end, 0);
|
||||
if (end == arg) {
|
||||
return false;
|
||||
}
|
||||
const char *p = lsl + 3;
|
||||
while (isspace ((ut8)*p)) {
|
||||
p++;
|
||||
@@ -312,11 +333,11 @@ static bool flutter_parse_shifted_imm(const char *line, ut64 *imm) {
|
||||
if (!isdigit ((ut8)*p)) {
|
||||
return false;
|
||||
}
|
||||
int shift = atoi (p);
|
||||
if (shift <= 0 || shift >= 63) {
|
||||
ut64 shift = strtoull (p, NULL, 0);
|
||||
if (shift >= 64) {
|
||||
return false;
|
||||
}
|
||||
*imm <<= shift;
|
||||
*imm = value << shift;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -440,7 +461,7 @@ static bool flutter_parse_opinfo(const RAnalOp *op, FlutterOpInfo *info) {
|
||||
if (info->n_regs == 0 && (type == R_ANAL_OP_TYPE_RCALL || type == R_ANAL_OP_TYPE_UCALL || type == R_ANAL_OP_TYPE_ICALL || type == R_ANAL_OP_TYPE_IRCALL)) {
|
||||
flutter_parse_first_operand_reg (op->mnemonic, info);
|
||||
}
|
||||
if (info->has_imm && R_STR_ISNOTEMPTY (op->mnemonic)) {
|
||||
if (type == R_ANAL_OP_TYPE_ADD && info->has_imm && R_STR_ISNOTEMPTY (op->mnemonic)) {
|
||||
(void)flutter_parse_shifted_imm (op->mnemonic, &info->imm);
|
||||
}
|
||||
return info->n_regs > 0 || info->has_mem || info->has_imm;
|
||||
@@ -539,7 +560,7 @@ static bool flutter_model_load(FlutterAnalModel *model, RCore *core, DartCtx *dc
|
||||
}
|
||||
DartCtx ctx = *dctx;
|
||||
ctx.core = core;
|
||||
dart_recovery_model_load (&ctx, model, DART_RECOVERY_STRINGS | DART_RECOVERY_CLASSES | DART_RECOVERY_CLASS_FIELDS | DART_RECOVERY_METHOD_INDEX);
|
||||
dart_recovery_model_load (&ctx, model, DART_RECOVERY_STRINGS | DART_RECOVERY_CLASSES | DART_RECOVERY_CLASS_FIELDS | DART_RECOVERY_METHOD_INDEX | DART_RECOVERY_IT);
|
||||
if (!model->strings || !model->classes) {
|
||||
flutter_model_fini (model);
|
||||
return false;
|
||||
@@ -551,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) {
|
||||
@@ -579,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) {
|
||||
@@ -600,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;
|
||||
}
|
||||
|
||||
@@ -873,6 +904,17 @@ static RVecFlutterEntry *flutter_collect_entries(RCore *core, const DartApp *app
|
||||
flutter_collect_entries_from_flags (seen, entries, core);
|
||||
flutter_collect_entries_from_app (seen, entries, app);
|
||||
flutter_collect_entries_from_model (seen, entries, model);
|
||||
// Entries before first_entry_with_code still contain executable Dart code;
|
||||
// only their Code objects were discarded by the precompiler.
|
||||
if (model->it_entries) {
|
||||
DartInstructionTableEntry *entry;
|
||||
R_VEC_FOREACH (model->it_entries, entry) {
|
||||
if (entry->address && !ht_up_find (seen, entry->address, NULL)) {
|
||||
RVecFlutterEntry_push_back (entries, &entry->address);
|
||||
ht_up_insert (seen, entry->address, entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
ht_up_free (seen);
|
||||
return entries;
|
||||
}
|
||||
@@ -1188,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;
|
||||
@@ -1324,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;
|
||||
}
|
||||
@@ -1520,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);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"expect": {
|
||||
"contains": [
|
||||
"class _Closure@0150898 (size=64)",
|
||||
"+0x428 dynamic _context@0150898",
|
||||
"+0x28 dynamic _context@0150898",
|
||||
"class _Uri@0150898 (size=96)",
|
||||
"+0x0 dynamic _portMap@1026248 static final"
|
||||
],
|
||||
|
||||
@@ -8,8 +8,8 @@ EOF
|
||||
EXPECT=<<EOF
|
||||
class Pointer (size=32)
|
||||
methods:
|
||||
0x00000000 get:hashCode (GetterFunction)
|
||||
0x00000000 == (RegularFunction)
|
||||
0x00115f2c get:hashCode (GetterFunction)
|
||||
0x001299fc == (RegularFunction)
|
||||
class NativeType (size=16)
|
||||
class NativeFunction (size=16)
|
||||
EOF
|
||||
@@ -27,8 +27,8 @@ EXPECT=<<EOF
|
||||
# class Pointer (size=32)
|
||||
# type_params: 1
|
||||
"td struct dart_Pointer { };"
|
||||
# method 0x00000000 get:hashCode (GetterFunction)
|
||||
# method 0x00000000 == (RegularFunction)
|
||||
# method 0x00115f2c get:hashCode (GetterFunction)
|
||||
# method 0x001299fc == (RegularFunction)
|
||||
# class NativeType (size=16)
|
||||
EOF
|
||||
RUN
|
||||
@@ -8,8 +8,8 @@ EOF
|
||||
EXPECT=<<EOF
|
||||
class Pointer (size=32)
|
||||
methods:
|
||||
0x00000000 get:hashCode (GetterFunction)
|
||||
0x00000000 == (RegularFunction)
|
||||
0x00c6ab64 get:hashCode (GetterFunction)
|
||||
0x00d8bde8 == (RegularFunction)
|
||||
class NativeType (size=16)
|
||||
class NativeFunction (size=16)
|
||||
EOF
|
||||
@@ -43,8 +43,8 @@ EXPECT=<<EOF
|
||||
# class Pointer (size=32)
|
||||
# type_params: 1
|
||||
"td struct dart_Pointer { };"
|
||||
# method 0x00000000 get:hashCode (GetterFunction)
|
||||
# method 0x00000000 == (RegularFunction)
|
||||
# method 0x00c6ab64 get:hashCode (GetterFunction)
|
||||
# method 0x00d8bde8 == (RegularFunction)
|
||||
# class NativeType (size=16)
|
||||
EOF
|
||||
RUN
|
||||
@@ -33,14 +33,14 @@ ic dart_Future
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
class dart_Future
|
||||
0x00000000 method dart_Future sC Future_error
|
||||
0x00000000 method dart_Future sC Future_
|
||||
0x00000000 method dart_Future s _anonymous_closure_
|
||||
0x00000000 method dart_Future sC Future_value
|
||||
0x00000000 method dart_Future s wait
|
||||
0x00000000 method dart_Future s handleError
|
||||
0x00000000 method dart_Future sC Future_delayed
|
||||
0x00000000 method dart_Future sC Future_microtask
|
||||
0x00000000 method dart_Future sC Future_sync
|
||||
0x005d4f54 method dart_Future sC Future_error
|
||||
0x005d70cc method dart_Future sC Future_
|
||||
0x005d71b8 method dart_Future s _anonymous_closure_
|
||||
0x005e4a98 method dart_Future sC Future_value
|
||||
0x0060d7d4 method dart_Future s wait
|
||||
0x0060dfac method dart_Future s handleError
|
||||
0x0062edcc method dart_Future sC Future_delayed
|
||||
0x006d5018 method dart_Future sC Future_microtask
|
||||
0x00943c08 method dart_Future sC Future_sync
|
||||
EOF
|
||||
RUN
|
||||
@@ -117,7 +117,7 @@ dart
|
||||
0x00000020 dart field 2 genericItems
|
||||
0x00000028 dart field 3 callback
|
||||
0x00000000 [0x00000000 - 0x00000000] 0 dart class 1 dart_TestInterface
|
||||
pf [16].qppp fieldA items genericItems callback
|
||||
'pf [16].qppp fieldA items genericItems callback
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -25,7 +25,7 @@ map
|
||||
true
|
||||
array
|
||||
48
|
||||
129
|
||||
1
|
||||
instance
|
||||
SemanticsAction
|
||||
1
|
||||
|
||||
@@ -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
|
||||
@@ -10,8 +10,8 @@ EXPECT=<<EOF
|
||||
# class Pointer (size=32)
|
||||
# type_params: 1
|
||||
"td struct dart_Pointer { };"
|
||||
# method 0x00000000 get:hashCode (GetterFunction)
|
||||
# method 0x00000000 == (RegularFunction)
|
||||
# method 0x00115f2c get:hashCode (GetterFunction)
|
||||
# method 0x001299fc == (RegularFunction)
|
||||
# class NativeType (size=16)
|
||||
EOF
|
||||
RUN
|
||||
@@ -8,8 +8,8 @@ EOF
|
||||
EXPECT=<<EOF
|
||||
class Pointer (size=32)
|
||||
methods:
|
||||
0x00000000 get:hashCode (GetterFunction)
|
||||
0x00000000 == (RegularFunction)
|
||||
0x00c6ab64 get:hashCode (GetterFunction)
|
||||
0x00d8bde8 == (RegularFunction)
|
||||
class NativeType (size=16)
|
||||
class NativeFunction (size=16)
|
||||
EOF
|
||||
@@ -27,8 +27,8 @@ EXPECT=<<EOF
|
||||
# class Pointer (size=32)
|
||||
# type_params: 1
|
||||
"td struct dart_Pointer { };"
|
||||
# method 0x00000000 get:hashCode (GetterFunction)
|
||||
# method 0x00000000 == (RegularFunction)
|
||||
# method 0x00c6ab64 get:hashCode (GetterFunction)
|
||||
# method 0x00d8bde8 == (RegularFunction)
|
||||
# class NativeType (size=16)
|
||||
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
|
||||
@@ -8,8 +8,8 @@ EOF
|
||||
EXPECT=<<EOF
|
||||
metadata class.name class Pointer [ref=36652] -> string Pointer [ref=151] @ 0x1b24d0
|
||||
metadata class.library class Pointer [ref=36652] -> library library#41733 [ref=41733]
|
||||
metadata method.owner method Pointer.get:hashCode [ref=31064] -> class Pointer [ref=36652]
|
||||
metadata method.name method Pointer.get:hashCode [ref=31064] -> string get:hashCode [ref=7217] @ 0x2b0230
|
||||
metadata method.owner method Pointer.get:hashCode [ref=31064] @ 0x115f2c -> class Pointer [ref=36652]
|
||||
metadata method.name method Pointer.get:hashCode [ref=31064] @ 0x115f2c -> string get:hashCode [ref=7217] @ 0x2b0230
|
||||
EOF
|
||||
RUN
|
||||
|
||||
@@ -21,7 +21,7 @@ L ../src/r2/core_flutter.so
|
||||
r2flutter -j -l 3 -x
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
[{"kind":"class.name","origin":"metadata","src":{"type":"class","name":"Pointer","ref":36652},"dst":{"type":"string","name":"Pointer","ref":151,"addr":1778896}},{"kind":"class.library","origin":"metadata","src":{"type":"class","name":"Pointer","ref":36652},"dst":{"type":"library","name":"library#41733","ref":41733}},{"kind":"method.owner","origin":"metadata","src":{"type":"method","name":"Pointer.get:hashCode","ref":31064},"dst":{"type":"class","name":"Pointer","ref":36652}}]
|
||||
[{"kind":"class.name","origin":"metadata","src":{"type":"class","name":"Pointer","ref":36652},"dst":{"type":"string","name":"Pointer","ref":151,"addr":1778896}},{"kind":"class.library","origin":"metadata","src":{"type":"class","name":"Pointer","ref":36652},"dst":{"type":"library","name":"library#41733","ref":41733}},{"kind":"method.owner","origin":"metadata","src":{"type":"method","name":"Pointer.get:hashCode","ref":31064,"addr":1138476},"dst":{"type":"class","name":"Pointer","ref":36652}}]
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
||||
Reference in new issue
Block a user