12 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 thef5f5dcdcmagic — 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. Address resolution examines every overlappingPT_LOADinterpretation, 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