内容仅用于授权测试、课程学习和自有应用调试,请不要用于未授权目标。此篇文章只供技术交流和理解代码bug和安全范围的交流和沟通。代码原理和灵感来源于reflutter功能
线上视频地址:https://www.bilibili.com/video/BV1YoGK6rEHZ/
课件地址下载:https://pan.quark.cn/s/250b4035f288
提取码:7yPu
VM 虚拟机地址下载:https://pan.quark.cn/s/901c5a403427
提取码:h7Mm
这一期解决什么问题
这期视频的主题很直接:Flutter 应用遇到双向证书校验时,普通抓包代理往往只能看到连接,拿不到真正可读的请求。讲者给出的解决思路不是简单关闭校验,而是把客户端证书 dump 出来,再导入到抓包软件里,让代理工具具备参与双向 TLS 握手的证书身份。
视频一开始就说明了主线:用到的工具、代码、示例 APK 和环境都会放在网盘,后面的演示围绕这些材料展开。
如果把这期内容压成一句话,就是:
先对齐 Flutter engine 版本,再在证书校验链路里导出客户端证书材料,最后转换成抓包工具可用的 P12 并验证请求。
为什么不能只做“返回 true”
很多 Flutter 抓包教程会讲到证书校验绕过,比如定位校验函数,把结果强制改成成功。这个思路在单向 HTTPS 场景里可能有效,因为问题主要是客户端是否信任代理证书。修改这个函数返回值知你解决sslping单向校验 而我们这次要说的是双向证书校验
但双向证书校验不一样。服务端会要求客户端拿出自己的证书和私钥来证明身份。也就是说,哪怕客户端侧校验被放行,如果抓包代理没有正确的客户端证书,服务端仍然可能拒绝连接或返回异常结果。
关键判断是:单向证书可以靠校验返回绕过,双向证书还要把客户端证书拿出来。
所以本期不是只讲“怎么过校验”,而是讲一个更完整的闭环:
- 找到 Flutter 版本和对应 engine 源码。
- 找到 X509 证书链校验相关逻辑。
- 在合适位置 dump 证书材料。
- 编译并替换对应的
libflutter.so。 - 从设备侧取出证书材料。
- 转换为 P12,导入 Reqable。
- 重新触发请求,观察是否能拿到明文数据。
第一步:先确认 APK 的 Flutter 版本和 Engine hash
讲者反复强调,不能“胡乱编译”。Flutter APK 里包含的 engine 版本不同,对应源码、函数位置、构建产物都会不同。拿错版本,后面的补丁和替换就很容易失效。
视频中使用 Flutter APK 版本查询工具读取示例 APK,拿到 Flutter 版本、Dart SDK 版本,以及最重要的 engine hash。工具还会给出对应的 engine 源码链接和 Android SDK 相关链接。
字幕里特别提到一个坑:旧版文章里的仓库地址可能会 404,新版本不能照着旧地址直接访问。正确做法是根据当前 APK 查询出来的 hash 和工具给出的地址去下载源码。换句话说,这一步不是形式主义,它决定了你后面编译出来的so能不能和样本对上。
第二步:围绕 X509 校验函数做补丁
确定版本之后,视频进入 reFlutter 相关代码。讲者打开utils.py,搜索session_verify_cert_chain,把视角落到 BoringSSL 的 X509 证书链校验逻辑上。
字幕里说得很清楚:“比如我们要去做的是这个 X509。”这就是本期的核心位置。Flutter engine 内部的 BoringSSL 负责 TLS 证书链校验,双向证书场景下,客户端证书链也会在这个流程里出现。
讲者展示的思路不是盲目改 APK,而是在源码补丁阶段处理。reFlutter 的补丁模式本质上是在编译前替换 engine 源码中的指定文件和逻辑。这样产出的libflutter.so会带上我们需要的 dump 行为。
第三步:不仅让校验通过,还要把证书写出来
视频里对“返回 true”做了一个重要补充。以前单向证书校验里,强制返回成功可以解决一部分问题;但这次目标是双向证书,所以还需要从校验流程里把证书材料写出来。
画面中可以看到讲者打开了整理好的ssl_crypto_x509_session_verify_cert_chain相关代码,强调在合适位置把证书链内容写入文件,最后仍然让流程继续往下走。
这里可以把思路理解成两件事:
第一,让证书校验链路不要提前失败。
第二,把后续代理工具需要的客户端证书材料 dump 出来。
双向证书抓包真正麻烦的地方就在第二点。你看到的证书文件不一定都能直接用,证书链里可能混着 CA 证书、服务端证书、客户端证书和私钥材料。后面还需要再做筛选和转换。
第四步:编译并替换对应的 libflutter.so
讲者在字幕里提到,直接修改 APK 可能会遇到签名校验问题。很多 APK 安装后会校验自身签名,重新打包并不一定能顺利运行。因此视频采用的是在受控测试环境里替换安装目录中的libflutter.so。
这一步需要用前面查询出来的 engine hash 编译对应版本,生成 Android arm64 产物,然后把新的so放回设备目录。视频中通过 MT 管理器进入应用相关目录,并替换对应库文件。
字幕里也提醒:不是随便拿一个so放进去就能跑。必须是当前 APK 对应 engine 版本编译出来的产物。否则即使文件名一样,也可能因为版本、符号或 ABI 不匹配导致运行异常。
第五步:dump 出来的 PEM 不能直接当客户端证书用
替换完成并重新运行应用后,证书材料会被导出。讲者随后打开课件中的证书转换工具,说明 dump 出来的文件不是“马上就能用的证书文件”,还需要转换。
这个工具会分析 PEM 里的证书和私钥,判断哪些可以转换成客户端身份。字幕里提到,工具会把“有用的证书”吐出来,不需要手动管里面到底有多少文件。
这里有个很重要的概念:能参与双向 TLS 的不是任意一张证书,而是证书加匹配私钥组成的身份。
如果只有 CA 证书或服务端证书,它们可以用于校验链路,但不能代表客户端向服务端证明身份。视频里的工具会把可用身份转换成 P12,也可以导出 CRT,但讲者后面更建议直接用 P12。
第六步:把 P12 导入 Reqable
证书转换之后,下一步是在 Reqable 里配置客户端 SSL 证书。视频中打开了 Reqable 的 SSL 证书配置界面,导入转换出来的 P12,并填写对应域名和密码。
这一步是双向证书抓包的关键分水岭。
普通 HTTPS 抓包里,我们通常只关心设备是否信任代理 CA;双向证书里,代理工具还要能在访问指定服务时拿出客户端证书。否则流量可能能经过代理,但握手或业务请求仍然失败。
视频里还提到,如果一个 APK 涉及多个证书,只导入一个可能会有问题。实际测试时要根据请求域名和证书用途逐个确认。
第七步:重新触发请求,看状态变化
配置完证书之后,讲者重新打开应用并观察 Reqable。中间也出现过一些不稳定现象,比如应用缓存、数据清理、请求返回异常等。字幕里多次提到“重新抓一下”“清除一下数据”“让它去请求看一下”。
第一次能看到请求出现,但响应仍可能是 403 或错误页。这不一定代表证书 dump 失败,它说明请求已经能进入可观测阶段,后面还要继续判断是证书身份、域名匹配、业务鉴权,还是服务端策略的问题。
后面继续导入对应证书、重新触发请求后,Reqable 中可以看到正常 HTTP 请求和 200 响应。视频字幕里说“拿到了他的一个数据了”,并展示了响应体进入可读状态。
这一段是整期视频的验证闭环:不是只看到 CONNECT,也不是只看到某个错误状态,而是能看到请求头、响应状态和业务数据结构。对抓包调试来说,这才算走通。
P12 和 CRT:为什么更推荐 P12
视频结尾讲者补充了证书转换的使用建议。CRT 也可以导出,但数量可能很多,后续需要一个个测试哪个才是正确证书。相比之下,P12 更适合直接作为客户端身份导入抓包工具,因为它能把证书和私钥打包在一起。
字幕里还强调,这个工具和代码是配套的:不是随便拿一个 PEM 就能转,也不是随便一个so就能替换。正确顺序应该是:
- 根据 APK 查询 engine 版本。
- 用对应源码和补丁编译。
- 替换对应
libflutter.so。 - 运行应用生成证书材料。
- 用配套工具转换 P12。
- 导入 Reqable 并按域名验证。
这个顺序一旦乱掉,后面看到的错误就会变得很难解释。
本期可以带走的几个判断
Flutter 双向证书抓包不是单一工具问题,而是运行时、证书链、客户端身份和代理配置共同作用的结果。
只做证书校验返回成功,可能解决单向 HTTPS,但解决不了服务端要求客户端证书的场景。
编译libflutter.so前必须确认 APK 对应的 Flutter engine hash,不能随便拿一个版本替换。
dump 出来的 PEM 只是原始材料,还需要筛选并转换成抓包工具可用的客户端身份。
Reqable 里导入客户端证书后,还要结合请求域名、状态码和响应体判断是否真的走通。
返回 403 或 Cloudflare 错误页,不一定是 TLS 层失败,也可能是业务鉴权、缓存、域名匹配或服务端策略问题。
资料下载
课件地址下载:
- 链接:https://pan.quark.cn/s/250b4035f288
- 提取码:
7yPu
VM 虚拟机地址下载:
- 链接:https://pan.quark.cn/s/901c5a403427
- 提取码:
h7Mm
线上视频:
- Bilibili:https://www.bilibili.com/video/BV1YoGK6rEHZ/
结尾
这期视频最有价值的地方,是把 Flutter 双向证书抓包拆成了一个可验证的工程流程:先确认版本,再定位校验逻辑,再 dump 客户端证书,再转换 P12,最后用真实请求验证。
对学习 Flutter 逆向的人来说,这比单纯记一个绕过点更重要。因为真正排查问题时,你需要知道失败发生在哪一层:是 engine 版本不对,是证书没有导出来,是 P12 没导入,还是请求已经能看见但业务层拒绝。
能把这条链路讲清楚,才算真正理解了双向证书校验下的 Flutter 抓包问题。