逆向分析新思路:当Flutter遇上Frida,如何Hook加密函数并自吐算法参数?
Flutter逆向工程进阶:Frida在Dart层的加密算法追踪实战
当移动应用开发进入Flutter时代,逆向工程师们发现传统的Hook技术突然变得力不从心。那些在Java层游刃有余的Frida脚本,面对Flutter编译后的Dart二进制文件时常常束手无策。本文将带你突破这一技术瓶颈,探索Flutter应用逆向分析的全新方法论。
1. Flutter逆向的特殊挑战与突破点
Flutter应用的独特架构给逆向工程带来了三重障碍:首先,业务逻辑被编译为机器码而非Dalvik字节码;其次,Dart语言的运行时环境与Android原生截然不同;最后,跨平台特性使得关键代码可能隐藏在引擎层而非应用层。
关键差异对比表:
| 特性 | Android原生应用 | Flutter应用 |
|---|---|---|
| 代码形式 | Dalvik字节码/Smali | ARM/x86机器码 |
| 加密函数位置 | Java/Kotlin层 | Dart编译后的so/C++引擎层 |
| 通信机制 | JNI调用 | Platform Channel/Dart VM |
| 符号信息 | 保留方法签名 | 可能被Strip |
实践中我们发现三个主要突破口:
- libflutter.so中的Dart VM相关函数
- 通过Hook Skia图形调用逆向UI逻辑
- 拦截Platform Channel的跨语言通信
提示:Flutter 3.0+版本开始默认启用Dart Obfuscation,需要准备对应的符号表文件
2. 构建Flutter逆向分析环境
工欲善其事,必先利其器。针对Flutter逆向的特殊需求,我们需要增强标准工具链:
# 基础工具 pip3 install frida frida-tools --upgrade # Flutter专用扩展 git clone https://github.com/rscloura/Doldrums.git cd Doldrums && make环境配置清单:
- Frida 16.1.3+ (支持最新ARMv8指令集)
- IDA Pro 7.7+ (配备Dart分析插件)
- Dart SDK (用于解析snapshot文件)
- jadx-gui (分析Platform Channel的Java端)
在真机调试时,需要特别注意Flutter引擎的版本匹配:
// 检测Flutter引擎版本 Interceptor.attach(Module.findExportByName("libflutter.so", "_ZN3dart3bin19GetDartVmSnapshotDataEPKc"), { onEnter: function(args) { console.log("[+] Flutter Engine Detected: " + Memory.readUtf8String(args[0])); } });3. Dart层加密函数的定位策略
与传统Android逆向不同,Flutter应用的加密逻辑通常通过以下三种形式实现:
- Dart FFI调用原生库:通过dart:ffi直接调用C/C++加密库
- Platform Channel桥接:在Java/Kotlin层实现加密后回调
- 纯Dart实现:使用Dart的crypto包或自定义算法
实战案例:定位AES加密函数
// 方法1:扫描内存特征 const aesPattern = '61 65 73 28 00'; // 'aes('的16进制 Memory.scan(Module.findBaseAddress('libapp.so'), Module.findBaseAddress('libapp.so').size, aesPattern, { onMatch: function(address, size){ console.log('[+] Potential AES function at:' + address); } }); // 方法2:Hook Dart_Invoke const dartInvoke = Module.findExportByName("libflutter.so", "_ZN3dart6Mirror11InvokeClassENS_6ThreadEPNS_6ObjectERKNS_6ArrayEb"); Interceptor.attach(dartInvoke, { onEnter: function(args) { const funcName = Memory.readUtf8String(args[2]); if(funcName.includes('encrypt')) { console.log(`[+] Dart加密调用: ${funcName}`); this.params = args[3]; } }, onLeave: function(retval) { if(this.params) { console.log(hexdump(this.params)); } } });4. 高级Hook技巧:Dart对象解析
Dart VM的对象模型与JVM差异显著,需要特殊处理才能正确解析参数和返回值。以下是一个完整的Dart String对象提取方案:
function readDartString(address) { const isCompressed = (Memory.readU8(address.add(0x0b)) & 0x01) === 0; const length = Memory.readU32(address.add(0x04)); let result = ''; if(isCompressed) { const chars = Memory.readByteArray(address.add(0x0c), length); result = String.fromCharCode.apply(null, new Uint8Array(chars)); } else { for(let i=0; i<length; i++) { const char = Memory.readU16(address.add(0x0c + i*2)); result += String.fromCharCode(char); } } return result; } // 使用示例 const dartStringPtr = ptr(0x7abc1234); console.log(readDartString(dartStringPtr));对于更复杂的Dart对象,可以借助Doldrums工具库提供的封装:
const DartObject = require('doldrums').DartObject; // 解析Dart Map对象 const mapPtr = ptr(0x7faa5678); const dartMap = new DartObject(mapPtr); dartMap.entries.forEach(([key, value]) => { console.log(`${key.toString()} => ${value.toString()}`); });5. 完整实战:自吐加密参数系统
结合上述技术,我们可以构建一个完整的Flutter加密参数监控系统:
// 监控Dart层的加密调用 const cryptoMethods = ['encrypt', 'decrypt', 'sign', 'verify']; cryptoMethods.forEach(method => { const symbol = DebugSymbol.fromName(`_Dart_${method}`); if(symbol) { Interceptor.attach(symbol.address, { onEnter: function(args) { console.log(`\n[+] ${method} called`); for(let i=0; i<3; i++) { // 前三个参数 try { const obj = new DartObject(args[i]); console.log(`arg${i}: ${obj.toString()}`); } catch(e) {} } }, onLeave: function(retval) { try { const obj = new DartObject(retval); console.log(`retval: ${obj.toString()}`); } catch(e) {} } }); } }); // 监控C层的加密调用 const nativeCrypto = [ 'EVP_EncryptInit', 'EVP_DecryptInit', 'AES_encrypt', 'AES_decrypt' ]; nativeCrypto.forEach(func => { const addr = Module.findExportByName(null, func); if(addr) { Interceptor.attach(addr, { onEnter: function(args) { console.log(`\n[+] Native ${func} called`); console.log(hexdump(args[1], { length: 32 })); } }); } });性能优化技巧:
- 使用
Stalker过滤非关键线程 - 对高频调用设置条件断点
- 缓存已解析的Dart类结构
6. 对抗Flutter逆向防护措施
随着Flutter应用安全意识的提升,越来越多的防护手段被采用:
常见防护方案破解方法:
符号表混淆
- 使用
Doldrums的符号恢复功能 - 基于调用关系重建符号
- 使用
JIT陷阱
// 检测并绕过JIT陷阱 const dartCode = Memory.alloc(0x1000); Interceptor.attach(Module.findExportByName("libflutter.so", "_ZN3dart6kernel14CodeGenerator7VisitCEv"), { onLeave: function(retval) { if(this.context.x1 > 0x10000000) { // 可疑内存区域 Memory.protect(this.context.x1, 0x1000, 'rwx'); } } });反调试措施
- 修改
/proc/self/status的TracerPid字段 - Hook
syscall监控ptrace调用
- 修改
7. 自动化分析框架搭建
为提高分析效率,可以构建自动化分析流水线:
# flutter_analyzer.py import frida from dart_parser import DartParser class FlutterAnalyzer: def __init__(self, package_name): self.session = frida.get_usb_device().attach(package_name) self.dart_parser = DartParser() def hook_crypto(self): script = self.session.create_script(""" // Frida脚本内容 """) script.on('message', self.on_message) script.load() def on_message(self, message, data): if message['type'] == 'send': payload = message['payload'] if payload.get('type') == 'dart_object': parsed = self.dart_parser.parse(payload['data']) print(f"[DART] {parsed}") # 其他消息处理...框架组件:
- Dart对象解析器
- 调用关系图谱构建器
- 加密模式识别模块
- 自动化报告生成器
在逆向Flutter应用的过程中,最令我惊讶的是Dart VM的高效性反而成为了逆向分析的突破口——通过内存模式识别,我们往往可以快速定位关键函数。记得在一次商业App的分析中,正是通过监控Dart_Invoke的调用频率,意外发现了一个隐藏很深的加密通信模块。
