Android逆向16-Flutter
Android逆向16-Flutter
Flutter是Google构建在开源的Dart VM之上,使用Dart语言开发的移动应用开发框架,可以帮助开发者使用一套Dart代码就能快速在移动iOS 、Android上构建高质量的原生用户界面,同时还支持开发Web和桌面应用。
Flutter
引擎是一个用于高质量跨平台应用的可移植运行时,由C/C++
编写。它实现了Flutter
的核心库,包括动画和图形、文件和网络I/O、辅助功能支持、插件架构,以及用于开发、编译和运行Flutter
应用程序的Dart
运行时和工具链。引擎将底层C++
代码包装成 Dart
代码,通过dart:ui
暴露给 Flutter
框架层。
在逆向分析前,我们首先要确定测试目标是否用Flutter开发的。当使用Flutter构建Android APP时,在assets文件夹下会有dexopt和flutter_assets两个文件夹
原理:
- Dart语言标准库的网络请求不走Wi-Fi代{过}{滤}理:Flutter使用的是Dart语言,其标准库中的网络请求不会通过代{过}{滤}理发送,这与许多其他应用不同。常规的抓包工具通常依赖于代{过}{滤}理来捕获网络流量,因此无法捕获Flutter应用的网络请求。
- Dart SDK中的证书信任:Dart SDK在Android平台上强制只信任系统目录下的证书。这意味着Flutter应用不会信任用户安装的证书,除非这些证书位于Android系统的
/system/etc/security/cacerts
目录中。这是通过Dart源码中的runtime/bin/security_context_linux.cc
文件实现的。
通过分析Flutter应用程序抛出的错误,可以定位到触发错误的源代码位置,错误指向了handshake.cc:352
,这是一个处理SSL握手的源代码位置。
1 | E/flutter (10371): [ERROR:flutter/runtime/dart_isolate.cc(805)] Unhandled exception: |
为了绕过SSL验证,需要找到一个合适的hook点,即源代码中可以被拦截和修改以改变程序行为的位置。ssl_verify_peer_cert
函数是一个可能的hook点,但经过测试,仅仅修改这个函数的返回值并不能成功绕过SSL验证。
进一步分析源代码后,发现session_verify_cert_chain
函数可以作为另一个hook点。这个函数在验证证书链时,如果证书验证失败,会返回一个错误。
1 | ret = ssl->ctx->x509_method->session_verify_cert_chain( |
session_verify_cert_chain函数定义在ssl_x509.cc,在该方法里可以看到有ssl_client和ssl_server
两个字符串可以辅助定位方法
hook_ssl_client
在libflutter.so里搜索ssl_client
定位到方法,内存搜刮函数前10字节定位,在运行时将返回函数改为true即可绕过证书链检查实现抓包(这里以64位的so为例)
1 | function hook_dlopen() { |
reflutter之patch
pip3 install reflutter pip安装对应的库
输入命令:reflutter flutter.apk
选择1流量监控和拦截,输入PC端的IP地址后(cmd窗口输入ipconfig),将获取到release.RE.apk,但此apk尚未签名,需要我们手动签名(输入命令的过程需要全局代{过}{滤}理)
- 使用MT管理器或者uber-apk-signer.jar签名,输入命令:java -jar uber-apk-signer-1.2.1.jar –apk release.RE.apk。然后将重签名的apk安装到真机或者模拟器上。
- 设置BurpSuite的代{过}{滤}理,端口为8083,绑定所有地址,并且勾选All interfaces,使非代{过}{滤}理意识的客户端直接连接到侦听器。
Reqable&proxyPin(推荐)
Reqable或者proxyPin直接抓包即可
Flutter反编译
快照
使用readelf -s
命令读取保存快照信息的libapp.so
将会输出下面的内容
1 | Symbol table '.dynsym' contains 6 entries: |
“快照”指的是 Flutter 应用在编译过程中生成的特定数据结构,用于加速应用的启动和运行。具体来说,快照包括四种类型:
- **
_kDartVmSnapshotData
**: 代表 isolate 之间共享的 Dart 堆 (heap) 的初始状态。有助于更快地启动 Dart isolate,但不包含任何 isolate 专属的信息。 - **
_kDartVmSnapshotInstructions
**:包含 VM 中所有 Dart isolate 之间共享的通用例程的 AOT 指令。这种快照的体积通常非常小,并且大多会包含程序桩 (stub)。 - **
_kDartIsolateSnapshotData
**:代表 Dart 堆的初始状态,并包含 isolate 专属的信息。 - **
_kDartIsolateSnapshotInstructions
**:包含由 Dart isolate 执行的 AOT 代码。
其中_kDartIsolateSnapshotInstructions
是最为重要的,因为包含了所有要执行的AOT代码,即业务相关的代码。
逆向方法
1.(静态)解析libapp.so,即写一个解析器,将libapp.so中的快照数据按照其既定格式进行解析,获取业务代码的类的各种信息,包括类的名称、其中方法的偏移等数据,从而辅助逆向工作。
关于Flutter快照的具体刨析只需要看下面引用的两篇文章
Reverse engineering Flutter apps (Part 1) (tst.sh)
Reverse engineering Flutter apps (Part 2) (tst.sh)
2.(动态)编译修改过的libflutter.so
并且重新打包到APK中,在启动APP的过程中,由修改过的引擎动态链接库将快照数据获取并且保存。
PS:不同版本的Dart引擎其快照格式不同,所以静态的方法就需要频繁跟着版本更新迭代,成本极高,而动态也需要重新编译对应版本的链接库。同时如果APP作者抹除版本信息和hash信息,则无从下手,且重打包APK极易被检测到。
静态方法推荐工具:blutter
动态方法推荐工具:reFlutter