English · Português (BR) · 中文
一个 **Zygisk** 模块,可将所选 **Flutter** 应用的网络流量重定向到可配置的代理,并绕过 BoringSSL 的 TLS 证书校验 —— 无需在设备上安装证书、无需重新打包应用,也不需要通过 USB 保持 Frida 会话。 附带一个 **管理应用**(Jetpack Compose),可直接在手机上选择目标应用并设置代理 IP 与端口。
Flutter 应用的 HTTPS 流量以明文抵达 Burp —— 设备上并未安装任何 CA 证书。
## 为什么需要它 Flutter 应用不使用 Android 的 TLS 栈:引擎内置了自己的 **BoringSSL**,并维护独立的信任链。这意味着把 Burp 的 CA 证书装进系统**根本拦截不到任何东西** —— 应用完全忽略 Android 的证书存储。而且 Flutter 也不遵循 Wi-Fi 上配置的代理,因此流量会直接离开设备,根本不经过你的拦截器。 绕过它的技术已为人所知,但通常需要运行 Frida 脚本,并且每次都要启动 `frida-server`、连着数据线。 FlutterTap 把同样的思路做成了一个常驻模块:安装一次、选好应用,之后每次开机都会自动生效。 ## 为什么用模块,而不是 Frida 或 LSPosed **Frida** 在*探索*应用行为方面无可替代 —— 改一行 JavaScript,几秒内就能看到效果,这项技术最初也正是这样 诞生的。问题在于*长期使用*:它依赖 `frida-server` 与活动会话(关掉终端,Hook 就没了),重启后不复存在, 要拦截启动阶段的调用还必须用 spawn(`-f`);而在真实评估中最关键的是,它**会开放一个监听端口,并留下众所 周知的痕迹**(线程名、内存中的字符串、`/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` 的真实地址 —— Capstone 正是 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** > **必须启用 Zygisk。** 仅有 Root 是不够的:整套机制都运行在 Zygisk 的回调中。在 Magisk 中请打开 > “Zygisk” 选项;在 KernelSU/SukiSu Ultra 上请安装 Zygisk Next 或 NeoZygisk;APatch 自带兼容实现。 > > 与 **Zygisk Next Linker** 的兼容需要专门的修复。如果你在维护一个会被该功能破坏的 Zygisk 模块, > [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) 中的 “Zygisk Next Linker compatibility” 一节记录了 > 发现的三个陷阱(以及一条曾浪费大量时间的错误线索),可直接复用。 ## 安装 **推荐 — 通过 MMRL:** 在 [MMRL](https://mmrl.dev/) 中打开 *Repositories → Add*,粘贴下面的仓库地址。FlutterTap 即可一键安装,并自动获得更新通知: ``` https://raw.githubusercontent.com/script-or-script/FlutterTap-mmrl/main/json/modules.json ``` **或手动安装:** 请从[最新 release](../../releases/latest) 下载两个文件。 1. **安装模块**:在你的 Root 管理器(Magisk / KernelSU / SukiSu Ultra / APatch)中刷入 `FlutterTap-<版本>.zip`,然后重启设备。 2. **安装管理应用**(`FlutterTap-manager-<版本>.apk`),首次启动时授予 Root 权限。 3. **进行配置**:填入代理的 IP 与端口(运行 Burp 的那台机器的 IP,需与手机处于同一 Wi-Fi),并勾选要 拦截的应用。 4. **强制停止目标应用,然后重新打开。** Hook 只会进入新创建的进程 —— 仅仅切回应用是不够的。 > **管理应用同样需要 Root**,而不仅仅是保存配置时需要:配置位于 `/data/adb/` 下,没有 Root 连已保存的 > 内容都读不到。 在 Burp 中,请使用启用了 **invisible proxying**(隐形代理)的监听器,因为应用发送的是普通请求,而不是 面向代理的请求。 ### 分步骤实证 以下为 Pixel 8a(Android 17 + SukiSu Ultra)上的真实截图,按实际操作顺序排列。 **1. 环境与模块安装**