script-or-script/FlutterTap

GitHub: script-or-script/FlutterTap

一个持久的 Zygisk 模块,将 Flutter 应用流量重定向到代理并绕过 BoringSSL TLS 证书校验,无需 Frida 会话或设备证书安装。

Stars: 0 | Forks: 0

FlutterTap — Zygisk module for intercepting Flutter app traffic

English · Português (BR) · 中文

一个 **Zygisk** 模块,将所选 **Flutter** 应用的网络流量重定向到可配置的代理,并绕过 BoringSSL 的 TLS 证书验证——无需在设备上安装证书,无需重新打包应用,也无需通过 USB 连接 Frida 会话。 它附带一个 **manager app**(基于 Jetpack Compose),可直接在手机上选择目标应用并设置代理的 IP 和端口。

HTTPS traffic from a Flutter app captured and decrypted in Burp Suite

HTTPS traffic from a Flutter app arriving decrypted in Burp — with no CA certificate installed on the device.

## 为什么会有这个项目 Flutter 应用不使用 Android 的 TLS 协议栈。其引擎自带了 **BoringSSL** 并维护自己的信任链,这意味着在系统上安装 Burp 的 CA 证书**根本拦截不到任何内容**——应用会完全忽略 Android 的证书存储。而且,由于 Flutter 也不支持 Wi-Fi 代理,流量会直接离开设备,根本不会经过你的拦截器。 这种变通方法众所周知,但通常需要在每次会话时运行带有活动 `frida-server` 的 Frida 脚本并连接数据线。FlutterTap 将这种方法变成了一个持久化的模块:只需安装一次,选择你的应用,它就会在每次开机时自动运行。 ## 为什么使用模块,而不是 Frida 或 LSPosed **Frida** 在*探索*应用的工作原理方面是无与伦比的——你修改 JavaScript,几秒钟就能看到效果。这项技术就是这样诞生的。问题在于*操作*它:它依赖于 `frida-server` 和活跃的会话(关闭终端,hook 就会丢失),无法在重启后继续运行,需要使用 spawn (`-f`) 来捕获启动时的 hook,而且——在实际操作中最关键的——**会打开一个监听端口并留下众所周知的痕迹**(线程名称、内存字符串、`/proc` 条目)。检测 Frida 实际上是任何移动保护 SDK 所做的第一件事。 **LSPosed/Xposed** 不是便利性的问题,而是层级的问题:它 hook **Java/ART 方法**。在 Flutter 中,`verify_cert_chain` 和 `GetSockAddr` 是 `libflutter.so` 内部**未导出的原生函数**——根本没有 Java 方法可以拦截。LSPosed 模块必须完成与 FlutterTap 完全相同的原生工作,同时还白白拖入了整个 Xposed 运行时。 | | Frida | LSPosed | FlutterTap | |---|---|---|---| | 触及原生 BoringSSL | 是 | **不直接** | 是 | | 重启后依然有效 | 否 | 是 | 是 | | 无需数据线/外部工具 | 否 | 是 | 是 | | 监听端口 / 外部进程 | 是(可检测) | 否 | 否 | | 从应用启动的第一刻起捕获 | 仅通过手动 spawn | 是 | 是 | | 对未选中的应用零影响 | — | 取决于情况 | 是 (`DLCLOSE`) | | 开发迭代速度 | **极好** | 中等 | 慢 | 最后一行是客观存在的缺点:迭代原生模块比编辑 `.js` 慢得多。这正是为什么这种拆分是有意义的——**用 Frida 探索,用模块操作**。 ### 为什么开发它 其动机来自于实际的移动流量分析工作。在实际的操作过程中,应用必须**随着时间的推移表现自然**:被反复打开和关闭、接收通知、在后台同步,供从未听说过 Frida 的人使用。而且设置必须是**可复现的**——任何人都可以通过勾选复选框来重新配置,无需终端,也无需记住的命令。 FlutterTap 将一个手动且脆弱的过程变成了**基础设施**。针对特定应用的定位正是为此而存在的:它不是一个全局拦截器——它在你未选择的每个应用中保持休眠状态,并立即从内存中卸载自己,从而降低了副作用的风险和被检测的面。 ## 工作原理 所涉及的函数都没有被导出,因此必须在每个进程的运行时中定位它们: 1. 该模块通过 Zygisk 加载到每个应用进程中,并检查该包名是否在目标列表中——如果不是,它会立即卸载自己(对未选中的应用零影响)。 2. 在一个监控线程上,它等待 `libflutter.so` 加载并解析其 ELF 段。 3. 它扫描内存中的字符串 `"ssl_client"` 和 `"Socket_CreateConnect"`,并由此通过 **使用 Capstone 反汇编** 来解析 `verify_cert_chain` 和 `GetSockAddr` 的真实地址——这与 Frida 底层使用的库相同。 4. 它使用 Dobby 安装三个 hook:捕获目标 `sockaddr`,将 IP/端口重写为代理,并强制返回“证书有效”的结果。 实现细节,包括与原始方法分歧的位置和原因,都在 [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) 中。 ## 兼容性 | 项目 | 支持 | |---|---| | Android | 10 (API 29) 至 17 (API 37) | | 架构 | `arm64-v8a`, `x86_64` | | Root | Magisk, KernelSU, SukiSu Ultra, APatch | | Zygisk | Magisk 内置 Zygisk, Zygisk Next(**包括启用了 Zygisk Next Linker 的情况**)或 NeoZygisk | 已在两种刻意设置的不同环境下的硬件上进行了验证: - **OnePlus 5** — Android 10,Kitsune Magisk v27.2 + NeoZygisk 2.3 - **Pixel 8a** — Android 17,SukiSu Ultra + Zygisk Next 1.4.3,并启用了 **Zygisk Next Linker** ## 安装 从 [最新发布版本](../../releases/latest) 下载这两个文件。 1. **安装模块**:在你的 root 管理应用(Magisk / KernelSU / SukiSu Ultra / APatch)中刷入 `FlutterTap-.zip`。重启。 2. **安装 manager app**(`FlutterTap-manager-.apk`),并在首次启动时授予 root 权限。 3. **配置**:设置代理的 IP 和端口(运行 Burp 的机器的 IP,需在同一 Wi-Fi 网络上),并勾选你要拦截的应用。 4. **强制停止目标应用并重新打开。** Hook 只会进入新进程——在不强制停止的情况下重新打开是不够的。 在 Burp 中,请使用启用了 **invisible proxying** 的监听器,因为应用发送的是普通请求,而不是指向代理的请求。 ### 带有验证的分步说明 来自运行 Android 17 和 SukiSu Ultra 的 Pixel 8a 的实际抓包,按步骤发生的顺序排列。 **1. 环境和模块安装**

Android 17 on a Pixel 8a, SukiSu Ultra running, the install log, and the module enabled

Pixel 8a 上的 Android 17,正在运行 SukiSu Ultra(LKM 模式),安装程序日志以 *Module installed successfully* 结尾,最后是 FlutterTap 在模块列表中被启用——已经显示了 **Action** 按钮,可以直接从那里打开 manager app。 **2. 授予 root 权限并配置代理**

Root denied, manual grant in SukiSu Ultra, and the app configured

首次启动时,应用可能会显示 **"Root access denied"**:KernelSU 系列的管理器会按应用持久化决定,并且永远不会再次显示提示。解决方法已在屏幕上高亮显示——打开 SukiSu Ultra,转到 **App Profile**,然后手动启用 **SuperUser**。之后,应用会显示 *Root access granted*、*Module installed* 和 *Enabled*,并且可以保存真实的代理 IP/端口。 **3. 准备 Burp**

Burp listener with invisible proxying enabled

监听器需要勾选 **Support invisible proxying**(位于 *Request handling* 选项卡下)。如果没有勾选,Burp 会断开连接:应用发送的是普通的 HTTP 请求,而不是发送给代理的请求。 **4. 端到端——普通 HTTP**

VulnApp targeted, request triggered, and traffic captured in Burp

将 **VulnApp** 设置为目标后,在设备上触发的请求显示在 Burp 中,到达了 **port 8083**——即监听器的端口,这证明了重定向已发生。`user-agent: Dart/3.12 (dart:io)` 确认它来自 Flutter 自己的 HTTP 客户端。 **5. 端到端——解密的 HTTPS**

Ostorlab Insecure App with HTTPS traffic decrypted in Burp

使用 **Ostorlab Insecure App** 也是如此,现在是通过 **HTTPS**:Burp 显示了 `https://ostorlab.co` 以及 **HTTP/2 200** 响应和明文 HTML。这就是 pinning 绕过所实现的效果——而且**设备上并未安装任何 CA 证书**。 ### 无需 manager app 进行配置 该应用只是一个单一 JSON 文件的前端。要进行自动化(配置、CI、无头设备),请直接写入 `/data/adb/fluttertap/config.json`: ``` { "enabled": true, "proxy_ip": "192.168.1.10", "proxy_port": 8080, "target_packages": ["com.example.app"] } ``` 该文件会在每个进程启动时从磁盘重新读取,因此对目标应用执行 `am force-stop` 并重新打开就足够了——无需重启。匹配是针对进程名称进行的,列出 `com.example.app` 也会捕获命名的子进程(`com.example.app:remote`)。 ## 诊断 ``` adb logcat -s FlutterTap:V ``` 对于目标应用,预期输出为:`selected for hooking` → `libflutter.so loaded at ...` → `verify_cert_chain=0x... GetSockAddr=0x...` → `hooks: installed for ... -> proxy IP:PORT`,然后随着应用发出请求,出现 `hooks: overwrite sockaddr` / `hooks: verify_cert_chain bypass`。 如果 **没有** 显示任何内容,几乎总是以下三种情况之一:包名与进程不匹配,`config.json` 无效(模块回退到默认设置,不拦截任何内容),或者配置更改时应用已经在运行。 ## 构建 需要 Android SDK/NDK 和 **JDK 17 或 21**(Gradle 8.11.1 无法解析 JDK 22+;如果默认的 `java` 版本较新,请将 `JAVA_HOME` 指向 Android Studio 捆绑的 JBR)。 ``` git clone --recurse-submodules https://github.com/script-or-script/FlutterTap.git cd FlutterTap export ANDROID_HOME=/path/to/Android/Sdk ./scripts/build_module_zip.sh # produces dist/FlutterTap-.zip ./gradlew :manager-app:assembleDebug ``` 完整的说明,包括发布签名,都在 [`docs/BUILD.md`](docs/BUILD.md) 中。 ## 文档 - [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — 每个部分的工作原理及其原因,包括 Zygisk Next Linker 兼容性 - [`docs/BUILD.md`](docs/BUILD.md) — 构建、打包和签名 ## 免责声明 仅供**授权的流量分析**使用——移动渗透测试、逆向工程以及对你有权限测试的应用进行安全研究。在大多数司法管辖区,未经授权拦截第三方应用流量是非法的。 ## 许可协议 MIT — 见 [`LICENSE`](LICENSE)。原生模块静态链接了 Dobby (Apache-2.0) 和 Capstone (BSD-3);第三方组件列在 [`THIRD_PARTY.md`](THIRD_PARTY.md) 中,完整的许可证文本包含在分发的 `.zip` 中。 由 Eduardo Lopes 构建
标签:Android, BoringSSL, DSL, Flutter, UML, Zygisk, 云资产清单, 后台面板检测, 安全测试, 攻击性安全, 流量劫持, 逆向工程