什么是fart
ART环境下基于主动调用的自动化脱壳方案
fart 脱壳流程总结
1 获取当前应用的类加载器 的 (类列表)反射使函数可以调用
2 通过反射获取class的类名字
3 加载指定的类,并反射执行构造函数和成员函数
4 通过调用DexFile自定义的函数从而主动调用实现类dump
具体代码分析
先反射修改函数为可访问
获取DexFile 的mCookie 拿到class的类名
mCookie 是个啥?
直接看源码 其实就是保存的一个dex 信息
加载类和方法
加载方法又分两种方式
1 先加载类中构造函数
2 加载类中声明的函数
至于为什么 fart 能执行某个函数 我们继续往下看
Fart 在 DexFile 自定义 dumpMethodCode 函数
该函数是一个native 函数 参数为一个 method 对象
具体实现在 art\runtime\native\dalvik_system_DexFile.cc
伪造系统的ArtMethod Invoke 调用
在art\runtime\art_method.cc中myfartInvoke实现虚拟调用
这里传null 是有讲究的 过滤如果传进来的是我们伪造的invoke调用 直接return 不执行 但是这时候其实已经触发壳的函数解密 直接保存即可
dumpArtMethod fart 的核心函数
dumpArtMethod 代码解释:
extern "C" void dumpArtMethod(ArtMethod * artmethod)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
char *dexfilepath = (char *) malloc(sizeof(char) * 2000); //开启dump 的空间
if (dexfilepath == nullptr) {
LOG(INFO) <<
"ArtMethod::dumpArtMethodinvoked,methodname:"
<< PrettyMethod(artmethod).
c_str() << "malloc 2000 byte failed";
return;
}
int fcmdline = -1;
char szCmdline[64] = { 0 };
char szProcName[256] = { 0 };
int procid = getpid();
//获取当前进程的名字
sprintf(szCmdline, "/proc/%d/cmdline", procid);
fcmdline = open(szCmdline, O_RDONLY, 0644);
if (fcmdline > 0) {
read(fcmdline, szProcName, 256);
close(fcmdline);
}
if (szProcName[0]) {
//获取dex
const DexFile *dex_file = artmethod->GetDexFile();
const char *methodname =
PrettyMethod(artmethod).c_str();、
//dex 的起始地址
const uint8_t *begin_ = dex_file->Begin();
//dex 的大小
size_t size_ = dex_file->Size();
memset(dexfilepath, 0, 2000);
int size_int_ = (int) size_;
//创建dump文件夹 并加权限
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "%s", "/sdcard/fart");
mkdir(dexfilepath, 0777);
memset(dexfilepath, 0, 2000);
sprintf(dexfilepath, "/sdcard/fart/%s",
szProcName);
mkdir(dexfilepath, 0777);
memset(dexfilepath, 0, 2000);
//拼接dex路径
sprintf(dexfilepath,
"/sdcard/fart/%s/%d_dexfile.dex",
szProcName, size_int_);
int dexfilefp = open(dexfilepath, O_RDONLY, 0666);
if (dexfilefp > 0) {
close(dexfilefp);
dexfilefp = 0;
} else {
dexfilefp =
open(dexfilepath, O_CREAT | O_RDWR,
0666);
if (dexfilefp > 0) {
write(dexfilefp, (void *) begin_,
size_);
fsync(dexfilefp);
close(dexfilefp);
}
}
//获取codeitem 即被抽取的函数指令 什么是 见下图 Codeitem
const DexFile::CodeItem * code_item =
artmethod->GetCodeItem();
if (LIKELY(code_item != nullptr)) {
int code_item_len = 0;
uint8_t *item = (uint8_t *) code_item;
if (code_item->tries_size_ > 0) {
const uint8_t *handler_data =
(const uint8_t *) (DexFile::
GetTryItems
(*code_item,
code_item->
tries_size_));
uint8_t *tail =
codeitem_end(&handler_data);
code_item_len =
(int) (tail - item);
} else {
code_item_len =
16 +
code_item->
insns_size_in_code_units_ * 2;
}
memset(dexfilepath, 0, 2000);
int size_int = (int) dex_file->Size(); // Length of data
uint32_t method_idx =
artmethod->get_method_idx();
sprintf(dexfilepath,
"/sdcard/fart/%s/%d_%ld.bin",
szProcName, size_int, gettidv1());
int fp2 =
open(dexfilepath,
O_CREAT | O_APPEND | O_RDWR,
0666);
if (fp2 > 0) {
lseek(fp2, 0, SEEK_END);
memset(dexfilepath, 0, 2000);
int offset = (int) (item - begin_);
//拼接方法的基本信息,名称、id、偏移、大小
sprintf(dexfilepath,
"{name:%s,method_idx:%d,offset:%d,code_item_len:%d,ins:",
methodname, method_idx,
offset, code_item_len);
int contentlength = 0;
while (dexfilepath[contentlength]
!= 0)
contentlength++;
write(fp2, (void *) dexfilepath,
contentlength);
long outlen = 0;
char *base64result =
base64_encode((char *) item,
(long)
code_item_len,
&outlen);
write(fp2, base64result, outlen);
write(fp2, "};", 2);
fsync(fp2);
close(fp2);
if (base64result != nullptr) {
free(base64result);
base64result = nullptr;
}
}
}
}
if (dexfilepath != nullptr) {
free(dexfilepath);
dexfilepath = nullptr;
}
}
Codeitem
好了 至此 fart 分析就算完毕了 总之一句话 frat 作者牛逼 !
frat 地址