FART中的脱壳点-脱壳组件
上一章对Dalvik和ART下的函数抽取方案进行了分析,本章开始FART框架的解析——本章主要解析FART框架中的脱壳组件
FART框架
框架构成
框架分为三个组件:
脱壳组件:脱壳组件可以把dex的整体结构性内容脱下来,如果是函数抽取类型的壳脱下来的函数体是空的
主动调用组件:通过主动调用让壳自己把抽取的函数代码还原回来,可以用这种方法补完函数体内容
修复组件:将整体结构和函数内容做一个整合,修复完整的dex
FART也是可以脱Dalvik类型的壳的,不过现在Dalvik几乎已经见不到了
脱壳组件部分可以参考我们前面几章分析源码的时候找到的脱壳点:
dex2oat的编译过程最后会调用CompileMethod()将函数编译为二进制代码
- 在编译之前会对dex中的方法进行检查,检查符合编译条件的将其编译为二进制代码。在else中看到用来初始化的方法是不需要进行编译的
初始化方法的执行流程
先暂停捋一下初始化方法的执行流程
在ART下有两种函数执行模式
- interpreter模式:由ART下的解释器解释执行
- quick模式:直接运行dex2oat编译生成的arm指令
不论壳有没有执行dex2oat,它的初始化方法始终是运行在interpreter模式下的。所以它必须经过ART下的解释器去取出code_item中的每一条smali指令,然后解释执行。
ART的解释器在不同的Android版本中有不同的实现:
Android 13.0没有这个解释器了 Android 12~8
- 8~12解释器有两种实现,一种是switch结构另一种是汇编结构的。默认使用汇编结构
Android 7
- 在7.0中还有另外一种通过go-to跳转表的实现
本次我们的测试设备使用的是Android8.0
在ART中,每个Java方法(包括构造函数/初始化函数)都有对应的art method对象。这个method对象包含了method的信息,例如method的字节码指令、参数类型、返回类型等。
ART method对象提供了一个GetDexFile
方法,可以用来获取当前方法所属的DexFile对象。
可以在源码中查看DexFile对象,里面包含有Begin()和Size()两个函数
- 有了这两个函数我们就可以获取到当前dex在内存中的起始地址和大小了
编写脱壳组件
接下来可以通过这个链条,来dump初始化的时候映射在内存中的dex
通过ART method对象 → GetDexFile获取到DexFile → DexFile中的Begin()和Size()获取到dex
ArtMethod* artmethod = shadow_frame.GetMethod();
if(strstr(artmethod->PrettyMethod().c_str(),"<clinit>")){
// inline const DexFile* ArtMethod::GetDexFile()
const DexFile* dexfile = artmethod->GetDexFile();
/*
const uint8_t* Begin() const {
return begin_;
}
size_t Size() const {
return size_;
}
*/
const uint8_t* begin = dexfile->Begin();
size_t size = dexfile->Size();
char dexfilepath[100] = {0};
sprintf(dexfilepath, "/sdcard/%d_%d_Excute.dex", (int)size, getpid());
int fd = open(dexfilepath, O_CREAT|O_RDWR, 0666);
if(fd > 0){
int number = write(fd, begin, size);
if(number > 0){};
close(fd);
}
}
测试脱壳组件
将源码重新编译 > 刷入设备 > 运行目标app脱壳
- 测试货拉拉那个带函数抽取的壳
- 整体框架dump没有问题,但是里面的函数体还是空的
后记
在ART中有很多地方都可以当作脱壳点,可以参考hanbingle大佬在看雪的帖子:
- 标题: FART中的脱壳点-脱壳组件
- 作者: xiaoeryu
- 创建于 : 2023-09-26 11:07:23
- 更新于 : 2023-10-03 11:32:42
- 链接: https://github.com/xiaoeryu/2023/09/26/FART中的脱壳点-脱壳组件/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。