Flutter 是由谷歌开发的一个跨平台框架,使用 dart 代码开发,一套代码可同时支持 PC、浏览器、Android、IOS 等平台,目前许多主流 APP 为了同时上线多个平台都选择了使用 flutter 开发。对于 Android,Flutter 的最终产物是 so 文件,release 版本会生成 libflutter.so 与 libapp.so,分别是框架依赖和 app 本身的逻辑代码,一般只需分析 libapp.so,libapp 在编译时去除了符号,用 IDA 中直接分析非常困难,而且 Flutter 默认不使用系统代理,有自己的证书校验方法,传统的 Android 抓包方法也无法抓到由 Flutter 发出的请求。
想要分析 Flutter App,首先需要将函数逆向回来,这里介绍两种常见的方法,分别使用不同的开源工具,这里以 gskinner 的 demo 为例:
https://github.com/gskinnerTeam/flutter-wonderous-app
01
reflutter
https://github.com/Impact-I/reFlutter
reflutter 的原理是重打包 apk,patch libapp.so,在运行时将 dart 函数信息打印出来,生成 dump.dart,同时注入代理代码,使 flutter 使用我们自己指定的代理,从而可以抓包。
使用方法很简单,pip 安装即可 pip install reflutter
安装成功后执行 reflutter
选择 2,输入自己代理服务器的地址,reflutter 注入的默认端口是 8083,使用抓包工具需要手动改端口。
reflutter 会自动识别 libapp 的快照版本,重打包 apk
完成后得到的是未签名的 apk,需要自己进行对齐和签名,这里需要注意必须 debug 签名才能生成 dump.dart,可以先用 debug 签运行,生成 dump.dart 后再用 apk 本身的签名签回去(防止部分功能有签名校验),这样就可以正常使用了。
官方推荐使用 uber-apk-signer 同时实现对齐 & 签名 https://github.com/patrickfav/uber-apk-signer
java -jar uber-apk-signer.jar --allowResign -a release.RE.apk
dump.dart 会生成在 app 的私有目录,虽然 reflutter 会授权 777,但因为上级目录没有权限,没办法直接 pull 出来,这里可借助模拟器 root 后再 adb pull
/data/data/com.gskinner.flutter.wonders/dump.dart,或者利用 su 获取 root shell 后 cat /data/data/com.gskinner.flutter.wonders/dump.dart > /sdcard/dump.dart 再 pull
这个文件本质可以当成 json 来处理,但是缺少了逗号,可以将 "}" 替换成 "}, ",头尾再加上 [],方便编辑器解析。
这里面是 dart 的函数信息,还有偏移地址,其中基址可以通过 readelf 得到:
_kDartIsolateSnapshotInstructions + offset 就是对应 IDA 中的函数地址,可以直接用 frida 去 hook,reflutter 提供了 frida.js 可以直接使用。
虽然 reflutter 提供了函数信息,但对于实际分析来讲,很难分析出函数的功能,只能靠函数名去推测或者结合 IDA 来分析。
02
blutter
https://github.com/worawit/blutter
这个工具更简单粗暴一点,但是没有代理绕过注入
建议使用 linux 系统进行操作,其中涉及到编译环节,需要安装 g++ 13 以上的版本
blutter 的输入是 apk 解压后 so 目录,安装完依赖后直接执行
python3 blutter.py arm64-v8a out
blutter 会自动根据版本下载依赖和工具,这里等待时间比较长,完成后会有以下产物:
其中 asm 就是逆向出来的汇编代码,bluter_frida.js 可以直接使用 hook 对应函数。
ida_script 中有头文件和脚本,导入头文件后执行脚本可以还原 ida 中的函数名,其中 addNames.py 部分函数名有 “#”,需要手动替换成别的字符。
这时再去 IDA 分析就稍微明朗一点,至少有函数名,但其实还是不建议直接看 IDA,建议看 asm 再配合 hook,分析起来简单点。
在分析 asm 中的反汇编代码时,当遇到匿名闭包时(这里的举例是
wonders/logic/collectibles_logic.dart),具体的逻辑可以根据地址在同一个 dart 文件找到,blutter 的产物中还有一个 pp.txt 文件,这里可以根据地址找到对应的对象,比如下图的相加操作是 0x17 lsl #12,代表 0x17 左移 12 位,对 16 进制相当于移 3 位,即 0x17000,下面的 ldr 是从内存中加载数据到寄存器,ldr 0xda8 即从 0x17000 开始再偏移 0xda8,即 0x17da8,在 pp.txt 中根据这个地址就能找到这个函数,对应的地址是 0x58a1fc
利用这个方法,可以得出逆向大部分对象的实际内容,例如字符串、函数等。
03
抓包
前面提到 flutter 默认走自己的代理,不会走系统代理,所以 WiFi 配置代理也抓不到 flutter 发出的流量,这里有两种方案:
用 reflutter,由于 patch 的时候已经注入了代理,直接正常运行就能抓包了;
用 vpn,全局代理,这样 vpn 在 flutter 的上层,可以感知到流量,但是需要进行一些绕过。
首先由于 flutter 本身有证书校验的逻辑,用 vpn 相当于普通的配置代理,flutter 有自己的证书信任列表,我们还需要绕过证书校验
flutter 使用的是 boringssl 库,我们需要找到这个库证书校验的代码,这里先打开 github 仓库 https://github.com/google/boringssl
ssl 校验的代码在 ssl/ssl_x509.cc 函数, ssl_crypto_x509_session_verify_cert_chain 里,返回值就是校验是否通过。
我们需要在 libflutter.so 找到编译后的函数,这里可以搜索字符串 ssl_client 来定位。
点进去看,这个逻辑和源码逻辑是一样的,所以 5DC570 就是函数地址。
用 frida hook 这个函数,直接把返回值改成 true 就能绕过证书校验。
这时候就能抓到所有的包了,UA 是 dart 的就是用 flutter 发出的请求。
总结
Flutter 目前已经成为主流的多平台开发框架,今年也将重登 Google I/O 大会,相信以后选择使用 Flutter 开发的应用会越来越多。区别于传统开发模式,Flutter 开发出来的 App 逆向分析难度更大,恶意行为也更难被发现,实际分析起来还是需要多方面结合。
往期推荐:
关注我们,了解更多安全内容!