本文对该 APP 的逆向仅仅是为了学习和记录更多网络安全方面的思路和技巧,如果有人非法使用本文涉及到的技术或技巧,则由此所带来的一切法律后果都由操作者自行承担,和本文以及作者没有任何关系!

# 前言

我接触逆向的时间也不算长,这是第一次碰到 Flutter 框架的 APP,之前从没接触 dart 语言,而且网上关于 Flutter APP 的逆向相关的文章也很少,这次就记录一下逆向的思路。

# Flutter

Flutter 是谷歌的移动 UI 框架,可以快速在 iOS 和 Android 上构建高质量的原生用户界面。 Flutter 可以与现有的代码一起工作。在全世界,Flutter 正在被越来越多的开发者和组织使用,并且 Flutter 是完全免费、开源的。

这是 Flutter 官网对于 Flutter 的介绍。Flutter 可以使用 dart 语言进行移动平台(android、ios)APP 的开发,其基本原理就是在 C++ 层(so)的基础上加了一层 dart 虚拟机,所有的图形渲染、主要逻辑等都在 dart 虚拟机中执行。由于 Flutter 是一个诞生不久的框架,其逆向相关工具比较少,所以逆向难度较高。

# 过程

# 确定目标

本次主要是对 APP 的数据加密算法和签名算法进行逆向,对该 APP 进行抓包,如下图所示。
Body中data被加密了

分析可知,该 APP 的数据包中 data 为发送的有效数据, Body 中和 Header 中分别有一次 sign 验证,所以此次我们的目标就是这三处算法的实现。

# jadx 静态分析

将 APK 拖入 jadx 中,搜索 data sign ,查看结果并没有得到有效信息,查看 libs 发现有 libflutter.so libapp.so 等库文件,可以基本确认该 APP 使用了 Flutter 框架。查阅一些资料后发现,flutter 中有些加密函数也是基于 android/ios 原生能力实现,所以我们可以尝试对其 hook 安卓常见的原生加密函数。

# data 加密算法分析

废话不多说,frida 启动,加载算法自吐脚本,直接 hook 到了 data 数据的加密方法,果然,还是调用了原生能力进行数据的加密。可以看到加密方法为 AES/ECB/PKCS5Padding ,密钥的 HEX 为 35727a771e637829613969366e316c71 ,使用 AES 解密工具测试一下,成功解密。

遗憾的是并没有发现 sign 算法的调用,于是基本确定 sign 应该是在 dart 层实现,并没有调用原生能力。

# sign 算法分析

sign 参数为 32 位 hexString,猜测应该是某种消息摘要算法。但由于 flutter 在 so 层中加了一层虚拟机,所以对于 so 层的分析就异常困难。将 so 导入 IDA 中,反复分析并没有找到有用的信息(我的 so 层动态函数分析经验本来就不多),于是决定面向搜索引擎逆向,吸取一下前人的经验。

搜索一番后发现,关于 Flutter 逆向的文章寥寥无几,仅有的几篇也都没有什么有效方法,于是卡壳好长时间。后来多方查找,在吾爱破解找到一个 flutter 的逆向分析工具 ——Doldrums,可以从 so 中分析出 dart 层的函数,该工具的 github 地址如下。

Github:https://github.com/rscloura/Doldrums

直接对 libapp.so 进行分析,命令如下:

h
python3 main.py libapp.so output

分析后得到的数据如下图所示。

dart 层的数据并没有被混淆,但分析出的数据仅包含类和方法名称以及参数和返回值类型,并没有代码。由于前面已经知道了 sign 参数为 hexstring,所以我们可以直接 hook hex 编码相关的函数。搜索后发现一个 HexEncoder 类,对其进行 hook。

打开 frida,hook 代码如下:

var str_name_so = "libapp.so";    // 要 hook 的 so 名
//var str_name_func = "updateHash";          // 要 hook 的函数名
var n_addr_func = Module.findBaseAddress(str_name_so).add("0x4b9bb0");
console.log("func addr is ---" + n_addr_func);
Interceptor.attach(n_addr_func, {
    // 在 hook 函数之前执行的语句
    onEnter: function(args) 
    {
        console.log("args:------>")
        console.log(hexdump(args[0]))
        //console.log(hexdump(args[1]))
    },
    // 在 hook 函数之后执行的语句
    onLeave:function(retval)
    {
        console.log("**********************************")
        console.log("********************return:------>")
        console.log("**********************************")
        console.log(hexdump(retval, {length:200}))
    }
})

可以看到确实是调用了该函数,但是不知是什么原因,参数并没有被打印出来。

既然参数打印不出,那我们换个思路,去打印结果。在 sign 计算之前,都会对要签名的字符串进行拼接,我们可以去 hook 拼接的函数,输出结果,这样就能看到拼接后的字符串进而推算出签名算法了。

dart 中,使用 Utf8Encoder 类中的 convert 方法去进行字符串的建立和拼接。hook 该函数,得到结果如下:

可以清楚的看到字符串拼接的结果,Body 中的 sign 参数是 data数据 + 时间字符串 + 固定字符串 然后进行了 md5 得到的。Header 中的 sign 参数则是 token + 时间字符串 进行 md5 得到的。至此,加密和签名算法全部搞定。

# 总结

本次逆向的 APP 并没有做什么反逆向措施,只是由于使用了 Flutter 框架,缺乏相关的逆向工具和资料导致难以逆向。对于个人开发者或者小公司来说,flutter 编写的应用确实能够在一定程度上防止逆向。

个人建议 dart 层的函数最好在做一次混淆,这样可以更好的防止逆向。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

HuaYu 微信支付

微信支付

HuaYu 支付宝

支付宝

HuaYu 贝宝

贝宝