关于 reversing-flutter-apps
面向移动安全测试人员的 Flutter 应用分析指南,涵盖 Dart AOT 结构恢复、流量拦截和运行时检查,帮助定位发布版中的应用逻辑与 API 行为。
主要功能
- 01识别 Flutter 构建模式
- 02用 blutter 恢复 Dart 符号
- 03检查对象池与反汇编结果
- 04转发并拦截 Flutter 流量
- 05用 Frida Hook Dart 函数
适用场景
- 01定位 Flutter 发布版中的端点
- 02排查 Burp 或 mitmproxy 无流量
- 03检查应用请求构造器与令牌处理函数
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trilwu/ secskills --skill reversing-flutter-apps安装后在 Claude Code 中输入/reversing-flutter-apps确认已加载。
详细介绍
逆向分析 Flutter 应用
Flutter 会同时打破标准移动应用工作流的两大环节。Dart 会通过 AOT 编译为原生快照,因此没有可供 jadx 反编译的 dex;Flutter 引擎还会自带 BoringSSL 和独立的信任存储,因此会同时忽略系统代理和用户 CA 存储,导致拦截代理看不到任何流量。这两种问题都无法从表面症状轻易判断,因此人们往往要花上数小时,才意识到自己遇到的是什么问题。
适用场景
- 使用
jadx查看 APK 时,发现MainActivity几乎为空,其他内容也很少 - APK 中包含
lib/arm64-v8a/libflutter.so和libapp.so,或包含assets/flutter_assets/ - IPA 中包含
Frameworks/App.framework和Frameworks/Flutter.framework - 对于一个显然正在进行网络调用的应用,Burp、mitmproxy 或 Charles 却显示没有任何流量
- 你需要还原 Dart 类名和方法名,或在发布构建中找到 API 层
不适用场景
- 普通 Java/Kotlin Android 应用 — 使用
testing-mobile-applications - React Native(
index.android.bundle、Hermes)— 使用reversing-react-native-apps - Unity(
global-metadata.dat、libil2cpp.so)— 使用reversing-unity-il2cpp - 应用所调用的后端 — 能看到流量后,使用
testing-apis - 通用原生
.so逆向分析 — 使用analyzing-binaries
首先识别构建类型
构建模式决定了后续所有步骤。安装工具前先检查。
unzip -l target.apk | rg 'libflutter|libapp|flutter_assets|kernel_blob'
| 你看到的内容 | 构建类型 | 含义 |
|---|---|---|
libapp.so + libflutter.so | AOT 发布构建 | 常见情况。Dart 已编译为原生快照 — 本指南的主要处理路径 |
assets/flutter_assets/kernel_blob.bin | Debug / JIT | 容易得多:包含 Dart 内核字节码,而且可能可以访问 VM 服务 |
App.framework/App(iOS) | AOT 发布构建 | 与 libapp.so 相同;从已解密的 IPA 中提取 |
仅有 arm64-v8a | — | 这是预期情况;blutter 面向 arm64。对 armeabi-v7a 的支持较弱 |
# Confirm the engine version — it gates which tools work
strings lib/arm64-v8a/libflutter.so | rg -m5 'Flutter|Dart VM version|dart-sdk'
如果存在 kernel_blob.bin,就停止并改为解包它:Debug 构建保留的信息多得多,而且如果启用了 Dart VM 服务,还能进行实时检查。发布构建才是难点,以下其余内容都假设你遇到的是这种情况。
使用 blutter 还原 Dart 结构
blutter 会解析 Dart AOT 快照,还原类名、方法名、字段名以及对象池 — 这能让你从难以阅读的 arm64 汇编转而浏览应用结构。
# blutter needs BOTH libraries: libapp.so is the snapshot, libflutter.so
# identifies the exact Dart version used to compile it
unzip -j target.apk 'lib/arm64-v8a/*' -d ./libs
python3 blutter.py ./libs ./out
你实际会用到的输出:
| 产物 | 用途 |
|---|---|
pp.txt | 对象池转储 — 字符串常量、URL、密钥、端点。先看这里 |
objs.txt | 还原出的 Dart 对象和类 |
asm/ | 应用各库的反汇编结果,已应用 Dart 符号 |
ida_script/ | 将符号加载到 IDA;Ghidra 也有相应工具 |
blutter_frida.js | 针对还原出的 Dart 函数生成的 Frida Hook |
你会遇到的问题是 Dart 版本不匹配。 blutter 是针对特定 Dart SDK 编译的,而快照格式会随版本变化。如果解析时出错,解决方法是使用与应用引擎版本匹配的 Dart SDK 构建 blutter,而不是尝试其他快照。先从 libflutter.so 中读取版本信息;对于全新的 Flutter 发布版本,blutter 尚未跟进时,首次运行很可能会失败。
混淆构建(flutter build --obfuscate --split-debug-info=...)会去除 Dart 名称。blutter 仍能还原结构 — 调用图、对象池、字符串常量 — 但名称已丢失。转而查看 pp.txt:URL、端点路径和错误字符串会在混淆后保留,通常足以定位 API 层,这正是你原本要找的内容。
查看流量
有两个独立的问题,必须同时解决。只修复其中一个,你仍然会面对空代理,而且不知道原因。
问题 1 — Flutter 会忽略系统代理。 Dart 的 HTTP 客户端不会读取 Android/iOS 代理设置,因此设置 Wi-Fi 代理毫无作用。改为在网络层强制转发流量:
# Redirect transparently on the device/emulator
adb shell su -c 'iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to <proxy-ip>:8080'
# or use a VPN-based tunnel (tun2socks / a VPN-mode proxy app)
问题 2 — Flutter 有自己的 CA 存储。 将你的 CA 安装为用户证书,甚至系统证书,都不会让 Flutter 信任它。以下两种方法可行:
# A. reFlutter — patches the Flutter engine to trust your proxy and to
# reroute traffic. Repackage and re-sign afterwards.
reflutter target.apk # prompts for the proxy IP
# outputs a patched APK; then:
apksigner sign --ks debug.keystore release.RE.apk
# B. Frida — hook the certificate check inside libflutter.so at runtime.
# ssl_verify_peer_cert is not exported, so scripts locate it by pattern
# scanning; the pattern is engine-version specific.
frida -U -f com.target.app -l flutter-ssl-bypass.js
如果可以重新打包,优先使用 reFlutter(它还支持转储快照和套接字级日志记录);如果应用有完整性检查,重新打包会触发检查,或者你无法重新签名,则优先使用 Frida。如果两种方法都失败,备用方案是挂钩 blutter 恢复出的 Dart HTTP 客户端函数——你会失去协议级保真度,但仍能看到请求和响应。
分析应用
获得符号和流量后:
- 从
pp.txt入手。 对象池中包含基础 URL、端点路径、标头名称以及任何嵌入的密钥材料。通常几分钟内就能梳理出 API。 - 使用恢复出的函数进行挂钩,并以
blutter_frida.js为起点——在请求构造器、身份验证令牌处理器和任何本地加密代码附近转储参数和返回值。 - 检查平台通道。 Flutter 应用会将生物识别、密钥库和支付功能交由原生代码处理;这些都是普通的 Java/Kotlin 或 Swift 代码,因此可以交给
testing-mobile-applications。 - 然后使用
testing-apis正式测试 API。 上述操作都是为了找到请求;漏洞通常在另一端。
不要接受这些借口
- “代理没有显示任何内容,所以应用一定没有使用网络。” 它确实在使用网络,只是没有使用你的代理。
- “我把 CA 安装成系统证书了,所以 TLS 拦截应该能奏效。” Flutter 不会查询系统证书存储。
- “jadx 什么都没显示,所以应用一定经过了高度混淆。” dex 中本来就没什么可看的。应用在
libapp.so中。 - “blutter 运行失败,所以这个构建无法分析。” 这几乎总是 Dart 版本不匹配。读取引擎版本,并据此重新构建 blutter。
- “代码经过混淆,所以字符串都消失了。” 名称消失了,对象池还在。读取
pp.txt。 - “我直接手动逆向 arm64 就行。” 没有符号的 Dart AOT 输出极难阅读,而且耗时极长。先恢复符号——这就是全部工作。
参考资料
testing-mobile-applications— 更全面的移动应用评估和原生通道testing-apis— 流量可见后测试后端analyzing-binaries— 深入分析引擎本身的原生代码- blutter、reFlutter、Frida/objection、jadx、apktool、apksigner