Flutter Android 逆向介绍
2024-5-9 09:40:29 Author: mp.weixin.qq.com(查看原文) 阅读量:14 收藏

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 <apk>

选择 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,分析起来简单点。

2.1 匿名函数 / 对象定位

在分析 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 发出的流量,这里有两种方案:

  1. 用 reflutter,由于 patch 的时候已经注入了代理,直接正常运行就能抓包了;

  2. 用 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 逆向分析难度更大,恶意行为也更难被发现,实际分析起来还是需要多方面结合。

往期推荐:

AI安全:LLM的风险揭示与安全性评估

vivo X Fold3:超薄体验,安全随行!

GDPR行政罚款计算指南:统一罚款计算的方法论

关注我们,了解更多安全内容!

文章来源: https://mp.weixin.qq.com/s?__biz=MzI0Njg4NzE3MQ==&mid=2247491601&idx=1&sn=5db0278ae2dc5169c516ba4bf9162540&chksm=e9bac67ddecd4f6b4236b8147306e6f9fd7e9879e9d845c6b72697485e97eb438165806284fd&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh