版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/
FART简介
ART 环境下基于主动调用的自动化脱壳方案,可以解决函数抽取壳。
关于函数抽取壳的实现原理可以参考:基于 art 下的类加载机制,实现函数抽取壳
FART 的作用就是所有这些被抽空的函数的还原和修复,把加固的 dex 整体 dump 下来。
项目地址:https://github.com/hanbinglengyue/FART
FART 框架

脱壳组件:将内存中的 Dex 数据完整 dump 出来
主动调用组件:构造主动调用链,完成对函数粒度的主动调用并完成 CodeItem 的 dump
修复组件:利用脱壳组件得到的 dex 和主动调用 dump 下来的函数体,完成函数粒度的修复
FART 中的脱壳点
其中 FART 脱壳组件 选择 Execute 作为脱壳点,它是 Interpreter 模式执行所有 Java 方法的统一入口,能够稳定截获和提取所有解释执行的真实方法,从而达到通用脱壳的目的。
ART 下函数在运行时可能是解释执行(Interpreter 模式)或编译执行(Quick 模式)。
为何选择 Execute 作为脱壳点?
dex2oat 编译流程
关于 dex2oat 以及 vdex、cdex、dex 格式转换
dex2oat 编译流程入口函数:
int main(int argc, char** argv) {
int result = static_cast<int>(art::Dex2oat(argc, argv));
// Everything was done, do an explicit exit here to avoid running Runtime destructors that take
// time (bug 10645725) unless we're a debug or instrumented build or running on a memory tool.
// Note: The Dex2Oat class should not destruct the runtime in this case.
if (!art::kIsDebugBuild && !art::kIsPGOInstrumentation && !art::kRunningOnMemoryTool) {
_exit(result);
}
return result;
}
apk 安装时进行的 dex2oat 编译流程,dex2oat 的编译驱动会对函数逐个编译

https://www.processon.com/i/6825c4e0c168ca282c1b9fb4?full_name=PO_iNeoB2
模板函数 CompileDexFile:编译 dex 文件中的所有类和方法,最后会调用 compile_fn 进行函数粒度编译
// 模板函数:编译 dex 文件中的所有类和方法
template <typename CompileFn>
static void CompileDexFile(CompilerDriver* driver,
jobject class_loader,
const DexFile& dex_file,
const std::vector<const DexFile*>& dex_files,
ThreadPool* thread_pool,
size_t thread_count,
TimingLogger* timings,
const char* timing_name,
CompileFn compile_fn) {
// 用于性能分析记录这段编译过程的时间
TimingLogger::ScopedTiming t(timing_name, timings);
// 创建一个用于并行编译的上下文管理器
ParallelCompilationManager context(Runtime::Current()->GetClassLinker(),
class_loader,
driver,
&dex_file,
dex_files,
thread_pool);
// 编译单个类的回调函数
auto compile = [&context, &compile_fn](size_t class_def_index) {
const DexFile& dex_file = *context.GetDexFile();
SCOPED_TRACE << "compile " << dex_file.GetLocation() << "@" << class_def_index;
ClassLinker* class_linker = context.GetClassLinker();
jobject jclass_loader = context.GetClassLoader();
ClassReference ref(&dex_file, class_def_index);
const dex::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
ClassAccessor accessor(dex_file, class_def_index);
CompilerDriver* const driver = context.GetCompiler();
// 跳过验证失败的类(这些类在运行时也会失败)
if (driver->GetCompilerOptions().GetVerificationResults()->IsClassRejected(ref)) {
return;
}
// 进入托管代码环境,访问 Java 对象
ScopedObjectAccess soa(Thread::Current());
StackHandleScope<3> hs(soa.Self());
// 解码 class_loader 对象
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
// 查找类对象
Handle<mirror::Class> klass(
hs.NewHandle(class_linker->FindClass(soa.Self(), accessor.GetDescriptor(), class_loader)));
Handle<mirror::DexCache> dex_cache;
if (klass == nullptr) {
// 类加载失败,清除异常并使用 dex cache
soa.Self()->AssertPendingException();
soa.Self()->ClearException();
dex_cache = hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
} else if (SkipClass(jclass_loader, dex_file, klass.Get())) {
// 判断是否跳过该类(如外部类、系统类等)
return;
} else if (&klass->GetDexFile() != &dex_file) {
// 重复类(已从另一个 dex 文件加载),跳过
return;
} else {
dex_cache = hs.NewHandle(klass->GetDexCache());
}
// 没有方法的类无需编译
if (accessor.NumDirectMethods() + accessor.NumVirtualMethods() == 0) {
return;
}
// 进入 native 状态,避免阻塞 GC
ScopedThreadSuspension sts(soa.Self(), kNative);
// 判断是否启用 dex-to-dex 编译(可能是省略优化过程)
optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level =
GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def);
// 编译类中所有 direct 和 virtual 方法
int64_t previous_method_idx = -1;
for (const ClassAccessor::Method& method : accessor.GetMethods()) {
const uint32_t method_idx = method.GetIndex();
if (method_idx == previous_method_idx) {
// 处理非法 smali 文件:可能多个 method 共用同一个 method_idx(重复定义)
continue;
}
previous_method_idx = method_idx;
// 调用外部传入的 compile_fn 进行实际方法编译
compile_fn(soa.Self(),
driver,
method.GetCodeItem(),
method.GetAccessFlags(),
FART自动化脱壳框架及脱壳点选择

最低0.47元/天 解锁文章
812

被折叠的 条评论
为什么被折叠?



