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 时代存在两个致命问题:

  1. DEX 在内存中不再连续:ART 会将 DEX 拆分成多个部分存储( oat_dex_fileCompactDex 等),简单搜索特征值容易漏 dump 或重复 dump。
  2. 方法体被抽取后,原始 DEX 中只剩 return 指令:即使 dump 出了完整的 DEX,方法体仍然为空,无法还原业务逻辑。

FART 的设计思路完全不同:

  • 主动调用而非被动等待:FART 主动调用 ClassLoader.loadClass() 来触发目标 DEX 的加载,确保所有类都被加载到内存中。
  • 在类定义阶段介入:FART 在 ART 的 ClassLinker::DefineClass 处进行 hook,当每一个类被加载时,FART 会自动记录并处理。
  • 内存中直接修复方法体:对于被抽取壳移除的方法体,FART 通过遍历 ArtMethodentry_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

验证脱壳质量

打开后需要检查以下几点:

  1. 类是否完整:检查关键业务类是否存在,对比原 APP 的 AndroidManifest.xml 中的四大组件类是否齐全。
  2. 方法体是否被修复:查看被抽取的方法,确认方法体内有实际代码而非单纯的 return
  3. 字符串是否可读:如果字符串仍然加密,说明壳使用了运行时解密字符串的方案,需要额外处理。
// 脱壳成功的方法体示例(修复后)
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 完整恢复,包含 ApplicationMainActivity
  • ✅ 抽取的方法体大部分被修复,核心业务逻辑可读
  • ⚠️ 少量 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 逆向工程中一款非常实用的自动化脱壳工具,尤其擅长处理抽取壳的修复问题。它的核心优势在于:

  1. 基于 ART 源码级别的 hook,相比运行时注入方案更加稳定可靠
  2. 主动触发类加载,确保所有加密 DEX 都能被 dump
  3. 方法体修复能力,能还原被抽取壳移除的代码逻辑

在实际工作中,FART 通常作为脱壳的第一选择,配合 jadx 进行静态分析,再结合 Frida 进行动态调试,形成一套完整的逆向分析流程。对于 FART 无法处理的壳(如 VMP 壳),则需要结合其他方案(如内存搜索、系统调用追踪等)进行针对性分析。