第 31 章 跨平台框架逆向(Flutter / React Native / Electron)
现代应用大量使用跨平台框架:Flutter(Dart 编译到原生)、React Native(JS 桥接)、Electron(Chromium + Node)、Tauri(系统 WebView + Rust)。框架决定逆向路线——认框架比认语言更重要。
📍 知识点地图 | 主题:跨平台框架 | 前置:第10-13章 | 后续:— | 核心概念:Flutter/RN/Electron/Tauri识别与路线
31.1 跨平台框架分类学(先认框架)
| 框架 | 技术栈 | 产物特征 | 逆向路线 |
|---|---|---|---|
| Flutter | Dart 编译成 AOT 原生 | libapp.so + libflutter.so | Blutter 还原符号 |
| React Native | JS 运行在 Hermes/JSC | index.android.bundle / main.jsbundle | 直接读 JS |
| Electron | Chromium + Node | app.asar + 大量 .js | 解 asar 读源码 |
| Tauri | 系统 WebView + Rust 后端 | 小体积 + 二进制 | 前端直接读 + Rust 逆 native |
| Kotlin Multiplatform | Kotlin 原生 | 平台二进制 | 按原生处理 |
| .NET MAUI | C# | 平台二进制 | dnSpy(第 12 章) |
第一条原则:先用文件特征识别框架,再选对应工具——框架认错会浪费大量时间。
31.2 Flutter 逆向(Dart AOT)
识别
APK/IPA 内存在 libapp.so + libflutter.so
assets/flutter_assets/ 目录
lib/arm64-v8a/libapp.so(业务逻辑全在这一个 so 里)
为什么难
Dart AOT 编译成原生代码 → 无 JVM/无 IL 元数据
函数名被编译为符号(snapshot hash)
普通 IDA/Ghidra 看到的是"天书"级的编译产物
Blutter(目前唯一正道)
# Blutter: Dart 符号 + 类结构 + Frida 脚本 全自动恢复
python3 blutter.py path/to/app/lib/arm64-v8a out_dir
# 输出:
# dart 符号表(函数名/类名恢复)
# 反编译的类结构
# 可直接用的 Frida 脚本
Flutter 逆向工作流
1. 识别: 找 libapp.so + libflutter.so
2. Blutter 恢复符号 → 类/函数名直读
3. 定位业务逻辑(登录/签名/加密类)
4. 静态: IDA/Ghidra 按 Blutter 符号分析
5. 动态: Frida Hook(Blutter 生成的脚本基础上)
6. 特殊: 字符串加密(Flutter 常用自定义加密字符串)→ 找解密函数批量解
常见坑
□ 混淆: --obfuscate 编译 → 符号变成随机名 → 靠行为/字符串定位
□ 字符串加密: 用 Flutter 加密字符串库 → 先找解密函数
□ 代码重定位: 每次启动随机 → 分析时用基址偏移
□ Dart 版本差异: Blutter 对新版本支持滞后 → 检查兼容性
31.3 React Native 逆向(最容易)
识别
APK 内存在 index.android.bundle / main.jsbundle
libhermes.so(Hermes 引擎)或 libjsc.so
assets/index.android.bundle 是 JS bundle
逆向流程(等于读源码)
1. 解包 APK → 找到 bundle 文件
2. bundle 是 JS → 直接可读(可能被混淆)
3. Hermes 字节码(.hbc)→ 用 hermes-dec 等工具还原为 JS
4. 分析业务逻辑 = 分析 JS(第 13 章方法)
5. 本地存储/AsyncStorage 通常明文 → 直接看
为什么最容易:React Native 业务逻辑 100% 在 JS 层,而 JS 基本等于源码。
31.4 Electron 逆向(桌面 App)
识别
目录结构: resources/app.asar + 一堆 .js/.node
进程: 有 electron 主进程 + 渲染进程
大体积(内置 Chromium + Node)
逆向流程
1. 找到 app.asar
2. 解包: npx asar extract app.asar app/ (或 asar 命令行)
3. 直接读 JS 源码(主进程 main.js + 渲染进程代码)
4. 混淆 JS → AST 去混淆(第 13 章)
5. 核心逻辑可能在 .node 原生模块 → 按原生二进制逆向
6. 注意: child_process.spawn / ffi-napi 调用 → 业务可能交给原生
常见情况
□ 登录/授权: 可能在 JS(好办)或原生模块(难一点)
□ 反调试: 检测 DevTools/远程调试端口
□ 代码保护: asar 加密(少见)→ 运行时 dump
□ 更新机制: 检查更新 URL/签名 → 可伪造更新(攻击视角)
31.5 Tauri 逆向
识别: 体积小 + Rust 二进制 + 前端资源在 assets/
结构: 前端(HTML/JS,直接读)+ 后端(Rust 编译原生)
逆向: 前端=读代码;后端=按 Rust 逆向(第 14 章)
注意: 关键逻辑常在后端 Rust(比 Electron 难,无 JS 可读)
31.6 框架逆向决策树
拿到 App/程序
├─ 有 libapp.so+libflutter.so? → Flutter → Blutter
├─ 有 .bundle/.jsbundle? → React Native → 直接读 JS
├─ 有 app.asar? → Electron → asar extract
├─ 小体积+前端资源? → Tauri → 前端直读+Rust 原生
├─ 有 DLL/程序集? → .NET MAUI → dnSpy
└─ 纯原生? → 走传统逆向(第 3-8 章)
动手练习
- 找一个 Flutter 开源应用(GitHub 有源码),用 Blutter 跑一遍,对比恢复的符号与源码。
- 找一个 React Native APK,解包读 bundle,看业务逻辑如何暴露。
- 用 asar extract 解一个 Electron 应用(如任何开源 Electron 工具),分析它的主进程代码。
- 对比同一应用的 Flutter/RN/Electron 三个版本,体会框架对逆向难度的影响。
深入阅读
- 仓库:
skills/reverse-engineering/field-notes.md(Flutter APK 章节:Blutter 用法) - 仓库:
skills/reverse-engineering/tools.md(Flutter APK 分析章节) - 仓库:
skills/reverse-engineering/languages-platforms.md(Electron ASAR + native 二进制案例) - 仓库:
skills/reverse-engineering/languages-compiled.md(Kotlin/Native 等)