Flutter App逆向初探:Dart编译产物的分析思路

Flutter App逆向初探:Dart编译产物的分析思路

前言

传统 Android 应用以 Java/Kotlin 编译为 Dex 字节码,可通过 Jadx 一键还原可读性极高的源码;原生 iOS 应用基于 Mach-O 二进制逆向,而 Flutter 采用 Dart 语言、两套完全不同的编译链路(Debug JIT/Kernel 快照、Release AOT 预编译),其逆向逻辑与传统移动端逆向存在巨大差异。

很多逆向新手直接套用 DEX、SO 逆向经验分析 Flutter App,往往卡在libapp.so 无有效符号、无法还原 Dart 类与方法、混淆后完全无业务线索等问题。本文从 Dart 完整编译链路切入,拆解 Debug/Release 两种模式下的编译产物结构,建立一套从静态提取、快照解析、二进制反汇编到动态调试的标准化分析思路,适合移动端安全、渗透测试、恶意样本分析入门学习。

一、Dart 两大编译模式与核心产物区分

Flutter 打包分为 Debug 调试包与 Release 正式包,二者编译链路、二进制产物、逆向难度天差地别,是逆向前首要区分的核心前提。

1. Debug 模式:Kernel 快照(JIT 执行,逆向难度极低)

开发阶段为支持热重载,Dart 不做 AOT 本地代码编译,生成kernel_blob.bin内核快照文件,存放于assets/flutter_assets/目录。

  • 编译流程:Dart 源码 → Kernel AST 抽象语法树 → kernel_blob.bin(可跨架构移植的中间快照)
  • 核心产物:kernel_blob.bin、flutter_assets 资源目录、轻量化 libflutter.so(虚拟机引擎)
  • 逆向优势:快照保留完整类名、方法名、字符串、字段结构,无符号剥离,使用 reFlutter、dart_kernel_reader 等工具可直接导出近似完整 Dart 源码。
  • 适用场景:测试包、内测未加固包、热更新调试包逆向。

2. Release 模式:AOT 预编译 libapp.so(线上主流,逆向难度高)

线上发布包默认开启 AOT 预编译,通过gen_snapshot工具将 Dart 全部逻辑预编译为 ARM64/x86 原生机器码,打包进 ELF 共享库libapp.so,这是商业 Flutter App 逆向的核心目标文件。

libapp.so 内部两大核心段(快照体系)

整个 so 内置两套 Dart VM 快照数据,是逆向的关键数据源:

  1. SnapshotInstructions(指令快照):Dart 类、函数编译后的原生 ARM/x86 机器码,对应业务逻辑执行体;
  2. SnapshotData(堆快照):预序列化的 Dart 运行时堆镜像,包含所有字符串常量、类元数据、字段、对象池、类型信息,所有硬编码 URL、密钥、接口地址均存储于此段,不会被编译器自动加密。

配套文件说明:

  • libflutter.so:Flutter 引擎 + Dart 虚拟机底层 C/C++ 代码,负责解析快照、内存管理、渲染,无业务逻辑;
  • flutter_assets:仅存放图片、字体、配置 json,无业务代码;
  • classes.dex:仅一层极薄 Java 壳,负责启动 Flutter 引擎,无任何 Dart 业务逻辑,Jadx 分析 DEX 几乎得不到有效业务线索。

两种模式产物对比表

表格

编译模式核心代码载体符号完整性逆向工具逆向成本
Debug(JIT Kernel)kernel_blob.bin完整类 / 方法名reFlutter、kernel_reader低,可还原完整源码
Release(AOT)libapp.so默认剥离符号,混淆后全为短标识符Blutter、IDA/Ghidra、JEB高,仅能恢复结构,难还原完整源码

二、Flutter App 逆向整体分析流程总纲

完整逆向遵循先静态粗扫→区分编译模式→快照数据提取→二进制深度反汇编→动态调试验证五层递进思路,避免一上来直接反汇编 libapp.so 浪费时间:

  1. 拆包基础信息收集,判断 Debug/Release;
  2. 静态字符串、配置、资源快速检索,提取明文敏感信息;
  3. 快照解析:Kernel 快照直接导出源码 / AOT 快照解析元数据、函数池;
  4. 原生二进制反汇编,梳理 Dart 运行时 ABI 与调用逻辑;
  5. Frida 动态插桩、内存 dump,补全静态分析缺失的运行时数据;
  6. 混淆还原、业务逻辑梳理,定位校验、加密、支付、登录核心函数。

三、分步拆解:Dart 编译产物完整分析思路

步骤 1:拆包 & 基础静态扫描,快速区分编译产物

1.1 APK/IPA 拆包工具

Android 端:apktool、unzip、jadx;iOS 端:class-dump、ipautil 解压 IPA。 执行基础检查命令快速定位关键文件:

bash

运行

# Android拆包后检索核心产物 find . -name "libapp.so" find . -name "kernel_blob.bin"
  • 存在 kernel_blob.bin → Debug 包,优先解析快照;
  • 仅存在 libapp.so、无 kernel 文件 → Release AOT 包,核心分析 libapp.so。
1.2 粗扫明文敏感数据(零成本快速收益)

无论 Debug/Release,Dart 所有字符串常量都会完整保留在快照数据段,不会被剥离,优先执行字符串检索:

bash

运行

# 提取libapp.so全部可打印字符串 strings lib/arm64-v8a/libapp.so > string_dump.txt # 检索接口、密钥、域名、token关键词 grep -E "http|api|key|secret|token|password" string_dump.txt

同时遍历flutter_assets下 json、txt 配置文件,大量 App 会将后端地址、渠道参数、加密盐硬编码在资源中,无需复杂反汇编即可拿到关键线索。

步骤 2:Debug 产物 kernel_blob.bin 快照深度解析思路

针对带 kernel 快照的调试包,这是逆向最简单的路径,核心目标:完整还原 Dart 源码结构。

  1. 工具链选择
    • reFlutter:主流开源 Flutter 逆向框架,修补 libflutter.so 后重打包 App,运行时自动 dump 完整快照元数据、类结构、方法树;
    • dart_kernel_reader:本地解析 kernel_blob.bin,输出类、函数、字段清单;
  2. 标准分析流程
    1. 使用 reFlutter 补丁替换原包 libflutter.so,重签名安装到手机;
    2. 启动 App,工具自动通过 socket 导出快照解析结果;
    3. 输出产物:完整类定义、方法参数、字符串常量、UI 页面路由、接口调用栈;
  3. 分析价值可直接看到页面名称、接口请求函数、加密工具类、本地存储逻辑,适合快速梳理 App 业务流程,是渗透测试优先使用的手段。

步骤 3:Release 核心产物 libapp.so(AOT 快照)分层分析思路

Release 包无 kernel 快照,所有业务逻辑封装在 ELF 格式 libapp.so,分析分为快照数据段提取机器码反汇编两大模块,遵循 “数据优先、汇编为辅” 原则。

3.3.1 第一层:提取 AOT 快照元数据(Blutter 工具链)

Blutter 是专门解析 Dart AOT 快照的开源工具,核心作用:从 libapp.so 的 SnapshotData 段解析 Dart 运行时元数据,绕过复杂 ARM 汇编阅读成本。 基础使用命令:

bash

运行

python3 blutter.py lib/arm64-v8a/libapp.so output_dir

工具输出四类关键文件:

  1. strings.txt:全部堆快照字符串集合;
  2. classes.json:所有 Dart 类、父类、字段列表;
  3. functions.json:函数地址、方法标识、所属类映射;
  4. 分段汇编代码:每个 Dart 函数对应的原生 ARM 指令块。

分析思路:

  1. 先读取 classes.json,梳理 App 业务分层(登录、支付、加密、网络请求类);
  2. 结合 strings.txt 中的接口关键词,交叉引用定位对应函数地址;
  3. 记录目标函数偏移,导入 IDA/Ghidra 做深度汇编分析。
3.3.2 第二层:原生二进制反汇编(IDA Pro/Ghidra/JEB)

Blutter 仅提供元数据映射,完整逻辑、分支判断、加密运算需要反汇编工具,必须先掌握 Dart AOT 特有的虚拟机 ABI 规则,否则无法读懂汇编:

  1. Dart VM AOT 寄存器约定(ARM64)
    • X14:Thread Pointer(TP),指向当前 Dart 线程上下文;
    • X15:Object Pool Pointer(OPP),指向堆快照对象池,所有字符串、常量、类元数据均通过 X15 寻址读取; 所有 Dart 对象访问、常量读取都依赖 X15 对象池,这是区分普通 C/C++ 汇编与 Dart AOT 汇编的核心特征。
  2. 特征函数识别方法libapp.so 中存在大量 Dart 运行时导出符号,可作为锚点定位业务代码:
    • Dart_NewStringFromCString:字符串创建函数;
    • Dart_Invoke:Dart 方法调用底层入口;
    • 快照段标记符号:kDartVmSnapshotData、kDartVmSnapshotInstructions,可快速定位两大快照段起始偏移;
  3. 反汇编分析流程
    1. IDA 加载 libapp.so,设置对应 CPU 架构(ARM64),自动识别 ELF 段;
    2. 通过字符串交叉引用(XREF),定位目标接口 / 密钥对应的函数调用位置;
    3. 沿着调用栈向上追溯,找到 Dart 业务方法对应的原生代码块;
    4. 梳理逻辑分支:参数校验、签名加密、登录验证、付费拦截等关键逻辑。
3.3.3 混淆场景下的补充分析思路

线上 Flutter App 大多开启--obfuscate混淆编译,所有类、方法名被替换为无意义短字符(a、b、aB 等),Blutter 导出的 classes.json 无业务命名,需依靠以下线索还原:

  1. 字符串交叉引用:接口域名、提示文案、错误提示字符串绑定对应混淆类;
  2. 调用关系图:网络请求、加密、本地存储函数存在固定调用链路,可按功能分组;
  3. UI 特征匹配:页面弹窗文字、按钮文本所属类即为对应页面逻辑类。

步骤 4:动态辅助分析思路(Frida + 内存 Dump 补全静态盲区)

静态分析仅能拿到编译期快照数据,运行时动态生成的密钥、内存临时变量、运行时参数无法从 libapp.so 静态提取,需动态调试配合:

  1. Frida 插桩 Dart 运行时注入 Frida Gadget 到重打包 App,Hook Dart 底层函数:
    • Hook Dart_Invoke,拦截所有 Dart 方法调用,打印入参、返回值;
    • Hook 字符串创建函数,实时捕获运行时生成的密钥、接口参数;
  2. 运行时内存 Dump 完整快照使用 reFlutter 或自定义脚本,App 启动后 dump Dart VM 完整堆内存快照,可拿到静态 libapp.so 中不存在的动态数据;
  3. 动态修改逻辑验证针对支付校验、登录鉴权、会员判断等函数,通过 Frida 直接修改返回值,快速验证逆向分析的逻辑是否正确。

四、不同工具适用场景总结(新手避坑)

  1. reFlutter:Debug 包首选,一键导出近似完整 Dart 源码;支持动态 dump Release 包运行时快照;
  2. Blutter:Release AOT libapp.so 静态元数据解析,快速获取类、函数映射,降低汇编阅读成本;
  3. IDA Pro/Ghidra:深度二进制反汇编,分析加密算法、复杂分支逻辑;
  4. JEB Pro:商业工具,原生支持 Dart AOT 快照解析,自动映射 Dart 函数,上手门槛更低;
  5. Frida:动态插桩必备,补全静态分析缺失的运行时参数与临时数据;
  6. strings/apktool:基础快速扫描,零成本提取明文敏感信息,逆向第一步必用。

五、逆向过程常见难点与解决思路

难点 1:混淆后完全无法识别业务类

解决:放弃依赖类名,以字符串、网络请求、加密函数为锚点,通过交叉引用构建函数功能图谱。

难点 2:libapp.so 过大,汇编检索效率极低

解决:先用 Blutter 导出函数与字符串映射表,锁定目标函数偏移后,再在 IDA 中跳转对应地址,避免全局翻汇编。

难点 3:新版本 Dart SDK 快照结构变动,Blutter 解析失败

解决:匹配目标 App 编译使用的 Dart SDK 版本,下载对应版本 Dart 源码,对照 runtime/vm 快照结构源码手动解析 SnapshotData 段。

难点 4:App 加固、libapp.so 加壳,无法直接提取 ELF

解决:先通过 Frida 内存 dump 脱壳,拿到内存中完整未加密 libapp.so 镜像,再执行静态快照解析。

六、逆向视角下 Flutter 安全开发启示

本文站在逆向分析视角,也可反向指导 Flutter 应用安全加固,规避逆向泄露风险:

  1. 敏感密钥、核心加密逻辑不要硬编码在 Dart 常量中,避免被 strings 命令直接提取;
  2. Release 打包强制开启混淆--obfuscate,增加逆向梳理成本;
  3. 关键加密逻辑下沉至原生 C/C++ 层编写,减少 libapp.so 中敏感运算代码;
  4. 对 libapp.so 做加壳、内存校验、完整性校验,阻止静态 dump 与逆向解析;
  5. 后端接口增加动态签名、设备绑定校验,即使逆向拿到接口地址也无法伪造请求。

结语

Flutter 逆向的核心本质,是吃透 Dart 两套快照(Kernel/JIT、AOT 堆快照)的二进制存储结构,而非照搬传统 Android DEX 逆向思路。完整分析链路遵循 “轻量静态扫描→快照元数据解析→二进制反汇编→动态调试验证” 四层递进逻辑,优先使用快照解析工具降低二进制阅读成本,动态插桩补足静态分析短板。

对于逆向初学者,建议从无混淆 Debug 测试包入手,先掌握 kernel_blob.bin 快照解析流程,再逐步过渡到 Release AOT libapp.so 的二进制分析,循序渐进理解 Dart VM 快照、AOT 编译、原生机器码三层底层结构,形成完整的 Flutter App 逆向分析思维。