版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/
FART 源码
FART 是一种基于主动调用的自动化脱壳方案,能够在 ART 虚拟机的执行流程中精准捕获并还原被抽空的函数,实现对被加固的 dex 文件整体 dump。
FART 相关链接:
-
FART 开源地址:https://github.com/hanbinglengyue/FART
目前 FART 是基于 Android 6.0 实现,源码文件结构如下:

这里以把 FART 源码移植到 LineageOS 17.1(Android 10)为例。
LineageOS 源码的下载、编译、签名参考:
移植 FART 到 Android 10
对比 android 6.0 中的源码和 FART 的源码找到修改的地方并移植到 Android 10 源码中

interpreter.cc
路径:art/runtime/interpreter/interpreter.cc
Android 6.0 源码:https://cs.android.com/android/platform/superproject/+/android-6.0.0_r1:art/runtime/interpreter/interpreter.cc
namespace art 中增加 dumpDexFileByExecute 函数声明
//add
extern "C" void dumpDexFileByExecute(ArtMethod* artmethod);
在 Execute 函数头部增加 dumpDexFileByExecute 调用
static inline JValue Execute(
Thread* self,
const CodeItemDataAccessor& accessor,
ShadowFrame& shadow_frame,
JValue result_register,
bool stay_in_interpreter = false,
bool from_deoptimize = false) REQUIRES_SHARED(Locks::mutator_lock_) {
//add
LOG(INFO) << "[Execute]" << shadow_frame.GetMethod()->PrettyMethod();
if(strstr(PrettyMethod(shadow_frame.GetMethod()).c_str(),"<clinit>")!=nullptr) {
dumpDexFileByExecute(shadow_frame.GetMethod());
}
...
}
dalvik_system_DexFile.cc
路径:art/runtime/native/dalvik_system_DexFile.cc
Android 6.0 源码:https://cs.android.com/android/platform/superproject/+/android-6.0.0_r1:art/runtime/native/dalvik_system_DexFile.cc
导入头文件:
//add
#include "scoped_fast_native_object_access-inl.h"
namespace art 中增加 myfartInvoke 函数声明
//add
extern "C" void myfartInvoke(ArtMethod* artmethod);
//add
extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod);
添加 DexFile_dumpMethodCode 函数实现(注意:要在 gMethods 之前)
//add
static void DexFile_dumpMethodCode(JNIEnv* env, jclass,jobject method) {
if(method!=nullptr) {
ArtMethod* proxy_method = jobject2ArtMethod(env, method);
myfartInvoke(proxy_method);
}
return;
}
在 gMethods 中增加 dumpMethodCode 的函数签名
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile, dumpMethodCode, "(Ljava/lang/Object;)V"),
};
java_lang_reflect_Method.cc
在 namespace art 中增加 jobject2ArtMethod 方法实现
//add
extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
return method;
}
art_method.cc
路径:art/runtime/art_method.cc
Android 6.0 源码:https://cs.android.com/android/platform/superproject/+/android-6.0.0_r1:art/runtime/art_method.cc
添加头文件
//add
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "runtime.h"
#include <android/log.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
增加宏定义
//add
#define gettidv1() syscall(__NR_gettid)
#define LOG_TAG "ActivityThread"
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
namespace art 中增加以下函数
//add
uint8_t* codeitem_end(const uint8_t **pData){
uint32_t num_of_list = DecodeUnsignedLeb128(pData);
for (;num_of_list>0;num_of_list--) {
int32_t num_of_handlers=DecodeSignedLeb128(pData);
int num=num_of_handlers;
if (num_of_handlers<=0) {
num=-num_of_handlers;
}
for (; num > 0; num--) {
DecodeUnsignedLeb128(pData);
DecodeUnsignedLeb128(pData);
}
if (num_of_handlers<=0) {
DecodeUnsignedLeb128(pData);
}
}
return (uint8_t*)(*pData);
}
//add
extern "C" char *base64_encode(char *str,long str_len,long* outlen){
long len;
char *res;
int i,j;
const char *base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
if(str_len % 3 == 0)
len=str_len/3*4;
else
len=(str_len/3+1)*4;
res=(char*)malloc(sizeof(char)*(len+1));
res[len]='\0';
*outlen=len;
for(i=0,j=0;i<len-2;j+=3,i+=4){
res[i]=base64_table[str[j]>>2];
res[i+1]=base64_table[(str[j]&0x3)<<4 | (str[j+1]>>4)];
res[i+2]=base64_table[(str[j+1]&0xf)<<2 | (str[j+2]>>6)];
res[i+3]=base64_table[str[j+2]&0x3f];
}
switch(str_len % 3){
case 1:
res[i-2]='=';
res[i-1]='=';
break;
case 2:
res[i-1]='=';
break;
}
return res;
}
//add
extern "C" void dumpDexFileByExecute(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
char szCmdline[64] = {0};
char szProcName[256] = {0};
int procid = getpid();
snprintf(szCmdline, sizeof(szCmdline), "/proc/%d/cmdline", procid);
int fcmdline = open(szCmdline, O_RDONLY);
if (fcmdline > 0) {
ssize_t result = read(fcmdline, szProcName, sizeof(szProcName) - 1);
if (result < 0) {
LOG(ERROR) << "dumpDexFileByExecute: Failed to read cmdline";
}
close(fcmdline);
}
if (szProcName[0] == '\0') return;
const DexFile* dex_file = artmethod->GetDexFile();
const uint8_t* begin_ = dex_file->Begin();
size_t size_ = dex_file->Size();
int size_int = static_cast<int>(size_);
std::string base_dir = "/sdcard/fart/";
std::string app_dir = base_dir + szProcName;
std::string dex_path = app_dir + "/" + std::to_string(size_int) + "_dexfile_execute.dex";
std::string classlist_path = app_dir + "/" + std::to_string(size_int) + "_classlist_execute.txt";
mkdir(base_dir.c_str(), 0777);
mkdir(app_dir.c_str(), 0777);
int dexfilefp = open(dex_path.c_str(), O_RDONLY);
if (dexfilefp > 0) {
close(dexfilefp);
} else {
int fp = open(dex_path.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
if (fp > 0) {
ssize_t w1 = write(fp, begin_, size_);
if (w1 < 0) {
LOG(ERROR) << "dumpDexFileByExecute: Failed to write dex file";
}
fsync(fp);
close(fp);
int classlistfile = open(classlist_path.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
if (classlistfile > 0) {
for (size_t ii = 0; ii < dex_file->NumClassDefs(); ++ii) {
const dex::ClassDef& class_def = dex_file->GetClassDef(ii);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
ssize_t w2 = write(classlistfile, descriptor, strlen(descriptor));
if (w2 < 0) {
LOG(ERROR) << "dumpDexFileByExecute: Failed to write class descriptor";
}
ssize_t w3 = write(classlistfile, "\n", 1);
if (w3 < 0) {
LOG(ERROR) << "dumpDexFileByExecute: Failed to write newline";
}
}
fsync(classlistfile);
close(classlistfile);
}
}
}
}
//add
extern "C" void dumpArtMethod(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
char szProcName[256] = {0};
int procid = getpid();
// 获取进程名
char szCmdline[64] = {0};
snprintf(szCmdline, sizeof(szCmdline), "/proc/%d/cmdline", procid);
int fcmdline = open(szCmdline, O_RDONLY);
if (fcmdline > 0) {
ssize_t result = read(fcmdline, szProcName, sizeof(szProcName) - 1);
if (result < 0) {
LOG(ERROR) << "ArtMethod::dumpArtMethod: read cmdline failed.";
}
close(fcmdline);
}
if (szProcName[0] == '\0') return;
const DexFile* dex_file = artmethod->GetDexFile();
const uint8_t* begin_ = dex_file->Begin();
size_t size_ = dex_file->Size();
int size_int = static_cast<int>(size_);
// 路径拼接
std::string baseDir = "/sdcard/fart/";
std::string processDir = baseDir + szProcName;
mkdir(baseDir.c_str(), 0777);
mkdir(processDir.c_str(), 0777);
// 保存 dex 文件
std::string dexPath = processDir + "/" + std::to_string(size_int) + "_dexfile.dex";
int dexfilefp = open(dexPath.c_str(), O_RDONLY);
if (dexfilefp > 0) {
close(dexfilefp);
} else {
int fp = open(dexPath.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
if (fp > 0) {
ssize_t w = write(fp, begin_, size_);
if (w < 0) {
LOG(ERROR) << "ArtMethod::dumpArtMethod: write dexfile failed -> " << dexPath;
}
fsync(fp);
close(fp);
// 保存 class 列表
std::string classListPath = processDir + "/" + std::to_string(size_int) + "_classlist.txt";
int classlistfile = open(classListPath.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
if (classlistfile > 0) {
for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const dex::ClassDef& class_def = dex_file->GetClassDef(i);
const char* descriptor = dex_file->GetClassDescriptor(class_def);
ssize_t w1 = write(classlistfile, descriptor, strlen(descriptor));
if (w1 < 0) {
LOG(ERROR) << "ArtMethod::dumpArtMethod: write class descriptor failed";
}
ssize_t w2 = write(classlistfile, "\n", 1);
if (w2 < 0) {
LOG(ERROR) << "ArtMethod::dumpArtMethod: write newline failed";
}
}
fsync(classlistfile);
close(classlistfile);
}
}
}
// 保存指令码
const dex::CodeItem* code_item = artmethod->GetCodeItem();
if (LIKELY(code_item != nullptr)) {
uint8_t* item = (uint8_t*)code_item;
int code_item_len = 0;
CodeItemDataAccessor accessor(*dex_file, code_item);
if (accessor.TriesSize() > 0) {
const uint8_t* handler_data = accessor.GetCatchHandlerData();
uint8_t* tail = codeitem_end(&handler_data);
code_item_len = static_cast<int>(tail - item);
} else {
code_item_len = 16 + accessor.InsnsSizeInCodeUnits() * 2;
}
uint32_t method_idx = artmethod->GetDexMethodIndex();
int offset = static_cast<int>(item - begin_);
pid_t tid = gettidv1();
std::string insPath = processDir + "/" + std::to_string(size_int) + "_ins_" + std::to_string(tid) + ".bin";
int fp2 = open(insPath.c_str(), O_CREAT | O_APPEND | O_RDWR, 0666);
if (fp2 > 0) {
lseek(fp2, 0, SEEK_END);
std::string header = "{name:" + artmethod->PrettyMethod() +
",method_idx:" + std::to_string(method_idx) +
",offset:" + std::to_string(offset) +
",code_item_len:" + std::to_string(code_item_len) +
",ins:";
ssize_t w3 = write(fp2, header.c_str(), header.length());
if (w3 < 0) {
LOG(ERROR) << "ArtMethod::dumpArtMethod: write header failed";
}
long outlen = 0;
char* base64result = base64_encode((char*)item, (long)code_item_len, &outlen);
if (base64result != nullptr) {
ssize_t w4 = write(fp2, base64result, outlen);
if (w4 < 0) {
LOG(ERROR) << "ArtMethod::dumpArtMethod: write base64 ins failed";
}
free(base64result);
}
ssize_t w5 = write(fp2, "};", 2);
if (w5 < 0) {
LOG(ERROR) << "ArtMethod::dumpArtMethod: write tail failed";
}
fsync(fp2);
close(fp2);
}
}
}
//add
extern "C" void myfartInvoke(ArtMethod* artmethod) REQUIRES_SHARED(Locks::mutator_lock_) {
JValue *result=nullptr;
Thread *self=nullptr;
uint32_t temp=6;
uint32_t* args=&temp;
uint32_t args_size=6;
artmethod->Invoke(self, args, args_size, result, "fart");
}
ArtMethod::Invoke 中添加判断如果是 fart 的主动调用就 dump
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
//add
if (self == nullptr) {
dumpArtMethod(this);
return;
}

最低0.47元/天 解锁文章
1万+

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



