Files
rea/docs/flutter-analysis.md
T

13 KiB

Flutter build identification

REA identifies a Flutter app's build facts by pure parsing of its APK: the Dart VM snapshot hash, per-ABI library digests, the engine's GNU build-id, and any labeled version strings. No engine, no tool installation, no execution, and no external lookups — this family answers "which Dart SDK build is this, and what exactly is in the payload?" before any deeper native analysis.

This is the triage step for Flutter reversing: the snapshot hash is the artifact that maps to a Dart SDK release through an external dataset (such as the one reFlutter maintains), and knowing the SDK version is what makes SDK-layout-specific snapshot parsers applicable.

Answer an analyst question

rea identify-flutter-build /targets/Example.apk
rea inspect-dart-aot /targets/Example.apk
rea inspect-dart-aot /targets/Example.apk --abi armeabi-v7a

The equivalent MCP methods are identify_flutter_build (with path) and inspect_dart_aot (with path and an optional abi; the default prefers arm64-v8a, then x86_64, then x86, then armeabi-v7a, then the remaining ABIs alphabetically). An explicit abi is the directory label exactly as identify_flutter_build reports it, so a payload under a nonstandard label stays selectable and keeps that label in the result.

Optional retained AOT delivery

MCP inspect_dart_aot keeps complete output when detail is omitted or "complete". Explicit detail: "summary" returns the selected target/ABI/library identity, all snapshot headers, structural coverage, string-pool coverage and original lower-bound counts without the string lists. It retains complete source Evidence and returns a distinct linked inspect_analysis_view summary. Analysis and the caller's ABI choice are unchanged; one-shot CLI output stays complete.

Use normalized_result.parent_evidence_id as the retained source for inspect_analysis_view, with view: {kind: "dart-aot", collection: "package_uris", offset: 0, limit: 64}. Collections are package_uris, dart_uris and class_like_tokens. Pages preserve the original strings and order, selected ABI, library identity and source coverage. returned_counts describe producer-returned rows; counts ending in _at_least remain lower bounds. Exhausting a page sequence does not make a partial pool complete or turn byte-pattern tokens into typed classes/functions. Views do not reopen the APK. Original Evidence remains exportable under its existing session lifetime. Summary requires a retaining server; older servers keep the complete request after schema discovery.

What the AOT inspection returns

inspect_dart_aot reads one ABI's libapp.so in full and reports:

  • Snapshot sections — the four _kDart*Snapshot* symbols located through the ELF dynamic symbol table (ELF32 and ELF64, little-endian, every read bounds-checked), each with file offset, size, and — for data sections, which carry the f5f5dcdc magic — the parsed header kind and length (the stored length excludes the four magic bytes). Data headers whose declared snapshot length does not fit their symbol retain their observed fields and report partial coverage. Complete structural coverage currently requires kind 3, the FullAOT value in the supported Dart 2.14 layout. Kind numbers vary across Dart releases; other values are preserved with partial coverage, without guessing their interpretation. Unified _kDartSnapshotData / _kDartSnapshotText exports are a different unsupported profile. The upstream VM-isolate removal changes both the source layout and the FullAOT kind value. REA reports the observed export names and library identity with a typed unsupported-profile refusal when only unified exports are available; it does not alias one header into two independent sources. Address resolution examines every overlapping PT_LOAD interpretation, including partial overlaps. Equivalent mappings are coalesced; conflicting mappings leave affected symbols unresolved and qualify coverage. Dynamic hash, symbol and string tables use the same mapping checks. A payload without exported snapshot symbols is refused rather than guessed at.
  • The snapshot hash from the header at the start of each exported data symbol. Complete hash coverage requires a valid header from both the VM and isolate; duplicate records inside one symbol cannot stand in for the other's header. Aliased or overlapping data symbols report partial coverage.
  • A string-pool projection from the isolate data section: distinct package: source URIs (the app's own and its dependencies' Dart files — a dependency inventory), dart: SDK URIs (which SDK libraries the app uses), and identifier-shaped tokens, each bounded with counts and coverage: "partial" when truncated. On the reference target this counts 756 package URIs and 201 dart URIs on arm64-v8a and x86_64, and 212 / 48 on armeabi-v7a, retaining up to 500 of each in the response. libapp.coverage describes structural snapshot checks; string_pool.coverage describes the bounded projection. Overall coverage is partial when either facet is partial.