Flutter逆向系列--实战技术

背景

  由于Flutter本身开发标准一致,降低了RD人员适配多端的工作量,且本身具有很好的安全性,因此越来越多的应用尝试使用Flutter。而逆向Flutter应用却不像Java一样简单,常用的IDA、JEB等工具完全失效。
   本文主要通过一个实际例子,讲解逆向分析Flutter的具体步骤和方法论

逆向步骤简述

  1. 确定APP是否为Flutter应用
    • Android: lib/xxx/libapp.so lib/xxx/libflutter.so
    • IOS: Payload/Frameworks/Flutter.framework
  2. 获取逆向关键信息
  3. 将libapp.so或app等相关Flutter代码拖入IDA进行分析
    • [可选] 重命名函数(混淆时使用)
  4. 获取_kDartIsolateSnapshotInstructions偏移
  5. 利用frida动静结合进行算法分析

例子

以某🐟为例子

  1. 确认是否为Flutter应用
    Flutter
  2. reFlutter
  • 安装 pip3 install reflutter
  • 使用 reflutter example.apk/reflutter example.ipa
  • 重签名后在真机上安装并运行应用
  • 获取
    • Android adb -d shell "cat /data/data/<PACKAGE_NAME>/dump.dart" > dump.dart
    • IOS scp /private/var/mobile/Containers/Data/Application/<UUID>/dump.dart .
Library:'package:anyapp/navigation/DeepLinkImpl.dart' Class: Navigation extends Object {  
String* DeepUrl = anyapp://evil.com/ ;
 Function 'Navigation.': constructor. (dynamic, dynamic, dynamic, dynamic) => NavigationInteractor { 
    Code Offset: _kDartIsolateSnapshotInstructions + 0x0000000000009270
}
    

Library:'package:anyapp/auth/navigation/AuthAccount.dart' Class: AuthAccount extends Account {
PlainNotificationToken* _instance = sentinel;
 
Function 'getAuthToken':. (dynamic, dynamic, dynamic, dynamic) => Future<AccessToken*>* { 
	Code Offset: _kDartIsolateSnapshotInstructions + 0x00000000003ee548
}
  1. IDA分析
    • IDA运行以下脚本,重命名函数 [遇到混淆时使用]
import sys, os, json
import idaapi, ida_kernwin
try:
    import flure
except ImportError:
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))

idaapi.require("flure.code_info")
idaapi.require("flure.ida.patch_names")
from flure.code_info import CodeInfo
from flure.ida.patch_names import create_ida_folders

if __name__ == "__main__":
    function_info_file = ida_kernwin.ask_file(False, f"*.json", "Flutter snapshot function name filename")
    if function_info_file is not None:
        with open(function_info_file, 'r') as fp:
            code_info = CodeInfo.load(json.load(fp))
        create_ida_folders(code_info)

如果混淆相对严重,也可使用BinDiff或者Diaphora进行新旧版本比对

  1. 获取_kDartIsolateSnapshotInstructions
    • readelf -Ws libapp.so
    • let kDartIsolateSnapshotInstructions = Module.findExportByName("Flutter", "kDartIsolateSnapshotInstructions")
  2. Frida分析
/**
* 利用第二步dump出来的信息,查找关键的函数偏移地址之后进行HOOK,并追踪调用链路
**/
function hook_flutter_function() {
    let kDartIsolateSnapshotInstructions = Module.findExportByName("App", "kDartIsolateSnapshotInstructions")
    let sign = kDartIsolateSnapshotInstructions.add(0x0000000000xxxxx); //从dump.dart中获取对应地址
    Interceptor.attach(sign, {
        onEnter(args) {
            console.log("sign:", Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
        }
    })
}

利用上述Frida打印的调用链路,进入IDA进行分析,获取对应的函数地址并进行下一步Hook分析

/**
* Precompiled_xxxx_xxxx 由IDA解析获取,前提条件是函数名没有混淆
**/
function hook_flutter_function_ida() {
    let flutter_fun = DebugSymbol.fromName("Precompiled_xxxx_xxxx")
    Interceptor.attach(flutter_fun.address, {
        onEnter(args) {
            this.log = []
            this.log.push(flutter_fun.name + " onEnter:\r\n")
            for(let i = 0; i < 8; i++) {
                try {
                    this.log.push(hexdump(args[i]), "\r\n");  
                } catch (error) {
                    this.log.push((args[i]), "\r\n");
                }
            }
        }, onLeave(retval) {
            this.log.push(flutter_fun.name + " onLeave:\r\n")
            try {
                this.log.push(hexdump(retval), "\r\n");  
            } catch (error) {
                this.log.push((retval), "\r\n");
            }
            this.log.push("=======================")
            console.log(this.log);
        }
    })
}

参考

The Current State & Future of Reversing Flutter™ Apps
Guardsquare/flutter-re-demo

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值