FART 使用场景和流程分析
FART 简介
FART(First Android Unpack Tool)是由安全研究员归零(GitHub ID:hanbinglengyue/FART)开源的一款基于 ART 虚拟机的自动化脱壳工具。与传统的内存 dump 方案不同,FART 选择在 ART 的类加载流程中主动介入,通过系统级 hook 实现对加密 DEX 的完整还原。
项目开源地址:https://github.com/hanbinglengyue/FART
FART 的定位非常明确:一款针对 ART 虚拟机的自动化脱壳工具,主要解决 DexHunter 等工具在 ART 环境下脱壳不完整的问题,尤其是对抽取壳的修复支持。
FART 的设计理念
传统脱壳方案(如 DexHunter)的核心思路是:在 DEX 加载完成后,从内存中搜索 DEX 的特征(Magic Number dex\n035),然后 dump 整块内存。这种方式在 Dalvik 时代效果不错,但到了 ART 时代存在两个致命问题:
- DEX 在内存中不再连续:ART 会将 DEX 拆分成多个部分存储(
oat_dex_file、CompactDex等),简单搜索特征值容易漏 dump 或重复 dump。 - 方法体被抽取后,原始 DEX 中只剩
return指令:即使 dump 出了完整的 DEX,方法体仍然为空,无法还原业务逻辑。
FART 的设计思路完全不同:
- 主动调用而非被动等待:FART 主动调用
ClassLoader.loadClass()来触发目标 DEX 的加载,确保所有类都被加载到内存中。 - 在类定义阶段介入:FART 在 ART 的
ClassLinker::DefineClass处进行 hook,当每一个类被加载时,FART 会自动记录并处理。 - 内存中直接修复方法体:对于被抽取壳移除的方法体,FART 通过遍历
ArtMethod的entry_point_from_quick_compiled_code_字段,将已经解密到内存中的方法体写回 DEX 文件,完成修复。
简单来说,FART 的核心流程可以概括为:
主动加载 DEX → Hook ClassLinker::DefineClass → 记录已加载类
→ dump 内存 DEX → 修复被抽取的方法体 → 输出完整 DEX
FART 适用的壳类型
| 壳类型 | FART 支持情况 | 说明 |
|---|---|---|
| 第一代 DEX 加密壳 | ✅ 完全支持 | 整体 DEX 加密,运行时解密到内存,FART 直接 dump 即可 |
| 第二代抽取壳 | ✅ 核心优势 | 方法体被抽取到单独的 so 文件,FART 通过修复 entry_point 还原 |
| 第三代壳(部分) | ⚠️ 部分支持 | 如梆梆、爱加密的早期版本,需要配合环境模拟 |
| 第三代壳(VMP) | ❌ 不支持 | 方法体被替换为 native 解释执行,内存中不存在原始字节码 |
FART 最擅长的场景是抽取壳,这也是它相比 DexHunter 最大的优势所在。
FART 的完整工作流程
第一步:部署 FART 环境
FART 提供了两种使用方式:
方式一:刷入 FART 定制 ROM(推荐)
FART 修改了 AOSP 的 art/runtime/class_linker.cc 源码,因此最稳定的方式是使用作者提供的定制 ROM。以 Nexus 5X 为例:
# 1. 下载 FART 定制 ROM
# 从 GitHub Releases 获取对应机型的刷机包
# 2. 解锁 Bootloader 并刷入
adb reboot bootloader
fastboot flashing unlock
fastboot flash boot boot.img
fastboot reboot
提示:FART 目前主要支持 Android 5.0 ~ 7.1 的 AOSP ROM,支持的设备包括 Nexus 5、Nexus 5X、Nexus 6P 等。对于更高版本的 Android,可以使用 FART 的 Xposed 模块方式。
方式二:使用 Xposed 模块(FART-Xposed)
对于不想刷机的用户,FART 提供了基于 Xposed 的实现:
# 1. 安装 Xposed 框架(如 LSPosed)
# 2. 编译或下载 FART-Xposed 模块的 APK
# 3. 安装并激活模块
# 4. 勾选目标 APP,重启设备
第二步:启动目标 APP 并等待脱壳
部署完成后,脱壳过程完全自动化:
# 1. 启动目标 APP
adb shell am start -n com.example.target/.MainActivity
# 2. 等待 APP 完全加载(建议等待 10-30 秒)
# FART 会在后台自动进行类加载和 dump
# 3. 查看 dump 结果
adb shell ls /sdcard/fart/
第三步:FART 内部执行流程
当 APP 启动后,FART 在系统层面执行以下操作:
1. APP 启动,触发 Application.attachBaseContext()
2. 壳代码解密原始 DEX 到内存
3. 壳代码使用 DexClassLoader 加载解密后的 DEX
4. FART 在 ClassLinker::DefineClass 处捕获类加载事件
└── hook 位置:art/runtime/class_linker.cc::DefineClass
5. FART 遍历所有已注册的 ClassLoader
6. 对每个 DEX 文件执行内存 dump
└── dump DEX header + 各个 DEX section
7. 对抽取壳进行方法体修复
└── 遍历所有 ArtMethod
└── 检查 entry_point_from_quick_compiled_code_
└── 如果方法体已被还原到内存,则写回 DEX 的 code_item
8. 将修复后的 DEX 写入 /sdcard/fart/<package_name>/
第四步:查看脱壳结果
# 脱壳文件输出目录结构
/sdcard/fart/
└── com.example.target/
├── com.example.target.dex # 脱壳后的主 DEX
├── classes2.dex # 可能存在的分包 DEX
├── classes3.dex
└── ...
FART 的配置和使用细节
关键配置项
FART 在源码中提供了几个可配置的开关,位于 art/runtime/fart/fart.cc:
// 是否启用主动调用 loadClass
static bool fart_enabled = true;
// 是否启用方法体修复
static bool method_repair_enabled = true;
// dump 输出目录
static const char* kFartOutputDir = "/sdcard/fart/";
// 是否 dump DEX 中的所有方法(包括非抽取方法)
static bool dump_all_methods = false;
主动加载指定 DEX
FART 提供了 fart 命令行工具,可以手动触发对指定包名的脱壳:
# 通过 adb shell 手动触发脱壳
adb shell
su
fart com.example.target
执行后,FART 会主动调用该 APP 的 ClassLoader.loadClass(),遍历加载所有类,确保不遗漏。
过滤不需要的类
在实际使用中,系统类和第三方 SDK 的类通常不需要 dump。可以通过白名单/黑名单来控制:
// 在 fart.cc 中配置类名过滤
static const char* kExcludedPackages[] = {
"android.",
"java.",
"javax.",
"com.google.",
"androidx.",
nullptr // 结束标记
};
脱壳结果分析
使用 jadx 打开脱壳 DEX
# 使用 jadx-gui 打开 dump 出的 DEX
jadx-gui /sdcard/fart/com.example.target/com.example.target.dex
# 或者使用 jadx 命令行反编译
jadx -d output/ /sdcard/fart/com.example.target/com.example.target.dex
验证脱壳质量
打开后需要检查以下几点:
- 类是否完整:检查关键业务类是否存在,对比原 APP 的
AndroidManifest.xml中的四大组件类是否齐全。 - 方法体是否被修复:查看被抽取的方法,确认方法体内有实际代码而非单纯的
return。 - 字符串是否可读:如果字符串仍然加密,说明壳使用了运行时解密字符串的方案,需要额外处理。
// 脱壳成功的方法体示例(修复后)
public String getSecretKey() {
return "a3f8b2c1d4e5"; // ✅ 有实际逻辑
}
// 脱壳失败的方法体示例(未修复)
public String getSecretKey() {
return; // ❌ 方法体被抽空
}
FART 的优缺点分析
优点
- 方法体修复能力强:这是 FART 最大的亮点,能够在 ART 下修复被抽取的方法体,这是 DexHunter 等工具做不到的。
- 自动化程度高:刷入 ROM 后,只需启动 APP 即可自动脱壳,无需手动操作。
- 原理清晰可扩展:基于 ART 源码修改,原理透明,便于二次开发。
- 兼容性较好:支持 Android 5.0 ~ 7.1,覆盖了大量存量设备。
缺点
- 需要特定设备/ROM:定制 ROM 方式需要特定的 Nexus 设备,Xposed 方式稳定性不如 ROM 方式。
- Android 8.0+ 支持不完善:ART 内部结构在 Android 8.0 之后有较大变化,FART 的 hook 点需要适配。
- 无法对抗 VMP:对于使用 VMP(虚拟机保护)的壳,FART 无法还原被解释执行的方法。
- 反检测能力弱:FART 修改了系统源码,部分壳会检测系统完整性,发现异常后拒绝运行。
实际案例:使用 FART 对梆梆加固 APP 进行脱壳
以下以一个使用梆梆加固(早期版本)的 APP 为例,演示完整的脱壳流程。
环境准备
# 设备:Nexus 5X (bullhead)
# 系统:Android 6.0.1 + FART 定制 ROM
# 目标:com.example.bangcle_app(梆梆加固)
# 1. 确认 FART 已就绪
adb shell getprop ro.build.display.id
# 输出包含 FART 标识说明 ROM 正确
# 2. 确认输出目录存在
adb shell mkdir -p /sdcard/fart/
执行脱壳
# 1. 清空之前的脱壳结果
adb shell rm -rf /sdcard/fart/com.example.bangcle_app/
# 2. 启动目标 APP
adb shell am force-stop com.example.bangcle_app
adb shell am start -n com.example.bangcle_app/.MainActivity
# 3. 等待 APP 完全加载(约 15 秒)
sleep 15
# 4. 检查脱壳结果
adb shell ls -la /sdcard/fart/com.example.bangcle_app/
拉取并分析脱壳文件
# 1. 将脱壳文件拉取到本地
adb pull /sdcard/fart/com.example.bangcle_app/ ./fart_output/
# 2. 使用 jadx 分析
jadx-gui ./fart_output/com.example.bangcle_app.dex
# 3. 搜索关键业务逻辑
# 在 jadx 中按 Ctrl+Shift+F 全局搜索 "login" 或关键类名
脱壳结果验证
梆梆加固早期版本对 FART 的防护较弱,脱壳结果通常较好:
- ✅ 主 DEX 完整恢复,包含
Application和MainActivity - ✅ 抽取的方法体大部分被修复,核心业务逻辑可读
- ⚠️ 少量 Native 方法无法还原(正常现象,Native 方法不在 DEX 中)
- ⚠️ 部分字符串仍为加密状态(需要配合 Frida hook
String构造函数解密)
FART 脱壳失败时的排查思路
当 FART 未能成功脱壳时,按以下步骤排查:
1. 检查 dump 目录是否为空
adb shell ls /sdcard/fart/<package_name>/
如果为空,可能原因:
- APP 尚未完成类加载就被杀掉了 → 增加等待时间
- FART 的 hook 被壳反检测了 → 尝试配合 Frida 关闭壳的检测逻辑
- 壳使用了自己的 ClassLoader 而非系统默认 → 需要手动指定 ClassLoader
2. 检查 dump 的 DEX 文件大小
adb shell ls -la /sdcard/fart/<package_name>/*.dex
如果文件很小(几十 KB):
- 可能只 dump 了壳的入口 DEX,原始 DEX 尚未被加载 → 尝试使用
fart命令手动触发 - APP 需要登录或特定操作才能触发 DEX 加载 → 手动操作 APP 到目标页面
如果文件大小正常但内容异常:
- DEX 文件可能损坏 → 检查 DEX 的 Magic Number 和 checksum
- 方法体未修复 → 确认
method_repair_enabled = true
3. 检查 DEX 完整性
# 使用 dexdump 验证 DEX 文件有效性
dexdump -d com.example.target.dex | head -20
# 检查 Magic Number(应该为 dex\n035 或 dex\n037)
xxd com.example.target.dex | head -1
4. 进阶排查
如果以上步骤都无法解决,考虑以下方案:
# 方案一:配合 Frida 使用
# 在 FART 刷机的基础上,使用 Frida 关闭壳的反调试检测
frida -U -f com.example.target -l anti_detect_bypass.js
# 方案二:手动触发 DEX 加载
# 有些壳需要特定操作才触发 DEX 解密
adb shell
su
fart com.example.target
# 方案三:更换脱壳方案
# 如果 FART 不适用,尝试 FDex2、DexDump 或 BlackDex
常见失败场景总结
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| dump 目录为空 | APP 未完成加载 / 反检测 | 增加 wait 时间 / 关闭反检测 |
| DEX 文件很小 | 只 dump 了壳入口 | 使用 fart 命令手动触发 |
| 方法体为空 | 修复失败 / 方法未执行 | 确保方法被调用过再 dump |
| APP 闪退 | 系统检测异常 | 更换 ROM 或使用 Frida 绕过 |
| jadx 打开报错 | DEX 文件损坏 | 检查 checksum,尝试 dex2jar 转换 |
总结
FART 是 Android 逆向工程中一款非常实用的自动化脱壳工具,尤其擅长处理抽取壳的修复问题。它的核心优势在于:
- 基于 ART 源码级别的 hook,相比运行时注入方案更加稳定可靠
- 主动触发类加载,确保所有加密 DEX 都能被 dump
- 方法体修复能力,能还原被抽取壳移除的代码逻辑
在实际工作中,FART 通常作为脱壳的第一选择,配合 jadx 进行静态分析,再结合 Frida 进行动态调试,形成一套完整的逆向分析流程。对于 FART 无法处理的壳(如 VMP 壳),则需要结合其他方案(如内存搜索、系统调用追踪等)进行针对性分析。