Apply dart pool refs to actual data strings with -AAA

This commit is contained in:
pancake committed 2026-10-01 16:34:11 +02:00
1 parent db30ebcb55
commit b7f425ca97
5 files changed
+96 -2

No files matched your search

+25
View File
@@ -1724,3 +1724,28 @@ This is exactly the primitive the FatalSec talk's Frida bypass keys on
Verified count on AuthPass: ~6k true, ~7k false, ~31k null, ~23k null-compares.
x26 (THR, the thread register with a large fixed-offset table of runtime stubs)
is the natural next target but is version-specific and left as a follow-up.
## Apply data-image string references so axt resolves pool strings
A string loaded only through a const data structure (e.g. a `Uri` or a list of
endpoints) is never loaded by a `ldr [x27, off]` in code, so the -AAA scan,
which only walks disassembled functions, created no xref for it. `axt
0x<string>` came back empty even though the string is clearly used. Example
(an Android app): `https://pss.aakredit.in/` at 0x76a97 is referenced from a
const object at 0x13a945 in `.rodata`; the scan never visits `.rodata`.
The reference was already recoverable - `r2flutter -z`/`-xz -r` lists each
reliable ObjectPool string with its reference sites (`DartStringRef.source_addr`,
including data-image addresses) and emits `ax <string> <source>` - but only as a
script that nothing applied, and -AAA never ran it. -AAA now calls
`dart_pool_apply_xrefs`, which walks `dart_pool_extract_pool_strings` and sets a
real STRN xref from every `source_addr` to the string, so `axt` resolves
pool-referenced strings that no instruction loads directly (4195 such xrefs on
that app).
Gotcha: `-xz` resolves to the strings action (`z`), not xrefs (`x`) - the `ax`
lines come from `dart_pool_strings.c` (string references), not
`dart_pool_extract_xrefs` (which only yields metadata refs with no source
address). The apply runs on a shallow DartCtx copy with its own read cache for
the same reason as `r2flutter_setup_pp_gp`: the scan has already mutated
app->dctx, and extraction needs the pristine state the standalone command sees.
+1
View File
@@ -203,6 +203,7 @@ char *dart_pool_dump_strings(DartCtx *ctx, int fmt);
char *dart_pool_dump_strings_fuzzy(DartCtx *ctx, int fmt);
void dart_string_list_free(RList *list);
char *dart_pool_dump_xrefs(DartCtx *ctx, int fmt);
int dart_pool_apply_xrefs(DartCtx *ctx);
char *dart_pool_dump_sbom(DartCtx *ctx, const char *input_path, int fmt);
// ============================================================================
+39
View File
@@ -1015,3 +1015,42 @@ char *dart_pool_dump_xrefs(DartCtx *ctx, int fmt) {
}
return out;
}
// Apply the recovered xref graph to the RCore as real r2 xrefs. Unlike the
// analysis disassembly scan, which only sees loads inside functions, this also
// surfaces references that live in the data image (e.g. a const object in
// .rodata pointing at a string), so `axt` resolves pool-referenced strings that
// no instruction loads directly. Returns the number of xrefs applied.
int dart_pool_apply_xrefs(DartCtx *ctx) {
if (!ctx || !ctx->core || !ctx->core->anal) {
return 0;
}
// The reliable ObjectPool strings carry their reference sites (source_addr),
// including references that live in the data image (e.g. a const object in
// .rodata pointing at the string). The analysis disassembly scan only sees
// loads inside functions, so without this `axt <string>` is empty for
// strings no instruction loads directly.
RList *strings = dart_pool_extract_pool_strings (ctx);
if (!strings) {
return 0;
}
int applied = 0;
RListIter *it;
DartStringInfo *si;
r_list_foreach (strings, it, si) {
if (!si || !si->address || !si->references) {
continue;
}
RListIter *rit;
DartStringRef *sr;
r_list_foreach (si->references, rit, sr) {
if (!sr || sr->source_addr == 0) {
continue;
}
r_anal_xrefs_set (ctx->core->anal, sr->source_addr, si->address, R_ANAL_REF_TYPE_STRN | R_ANAL_REF_TYPE_READ);
applied++;
}
}
dart_string_list_free (strings);
return applied;
}
+18 -2
View File
@@ -1425,15 +1425,31 @@ bool r2flutter_analysis_run(RCore *core, DartCtx *dctx, bool quiet) {
flutter_ensure_functions (core, entries);
flutter_scan_functions (core, &model, &pp_strings, &pp_refs, entries, &stats);
// Apply the recovered xref graph (including data-image references such as a
// const object pointing at a string) so `axt` resolves pool-referenced
// strings that no instruction loads directly. Run it on a clean copy of the
// original context - the scan above mutates app->dctx (read cache, layout),
// and dart_pool_extract_xrefs needs the same pristine state the standalone
// `-x` command sees. The copy shares heap-owned pointers, so we free only
// the read cache it allocates.
DartCtx xrctx = *dctx;
xrctx.core = core;
xrctx.rmem = NULL;
xrctx.layout = NULL;
xrctx.dump_string_refs = true;
int graph_xrefs = dart_pool_apply_xrefs (&xrctx);
dart_read_cache_free (xrctx.rmem);
if (!quiet) {
R_LOG_INFO ("Flutter analysis: %d functions, %d calls, %d fields, %d classes, %d types, %d strings, %d PP refs",
R_LOG_INFO ("Flutter analysis: %d functions, %d calls, %d fields, %d classes, %d types, %d strings, %d PP refs, %d graph xrefs",
(int)stats.functions,
(int)stats.call_xrefs,
(int)stats.field_refs,
(int)stats.class_refs,
(int)stats.type_refs,
(int)stats.string_refs,
(int)stats.pp_refs);
(int)stats.pp_refs,
graph_xrefs);
}
RVecFlutterEntry_free (entries);
@@ -0,0 +1,13 @@
NAME=r2-pp-string-dataref-xref-android
FILE=bins/android/w0rdle/libapp.so
ARGS=-NN -e scr.color=0 -e bin.relocs.apply=true
CMDS=<<EOF2
L ../src/r2/core_flutter.so
L ../src/r2/core_flutter.dylib
r2flutter -AAA
axt 0x177f3~0xdc8f7[1]
EOF2
EXPECT=<<EOF2
0xdc8f7
EOF2
RUN