Fart脱壳-源码分析
参阅:
ART中类的真实形态
源码分析
首先看java代码,在APP启动时候反射了DexFile.java中三个方法,分别是
-
getClassNameList
-
defineClassNative
-
dumpMethodCode
// frameworks/base/core/java/android/app/ActivityThread.java
public static void fart() {
...
Class DexFileClazz = null;
try {
DexFileClazz = appClassloader.loadClass("dalvik.system.DexFile");
} catch (Exception e) {
e.printStackTrace();
}
Method getClassNameList_method = null;
Method defineClass_method = null;
Method dumpDexFile_method = null;
Method dumpMethodCode_method = null;
for (Method field : DexFileClazz.getDeclaredMethods()) {
if (field.getName().equals("getClassNameList")) {
getClassNameList_method = field;
getClassNameList_method.setAccessible(true);
}
if (field.getName().equals("defineClassNative")) {
defineClass_method = field;
defineClass_method.setAccessible(true);
}
if (field.getName().equals("dumpMethodCode")) {
dumpMethodCode_method = field;
dumpMethodCode_method.setAccessible(true);
}
}
}
其中前面两个方法是android自带的,dumpMethodCode
方法是fart自己添加的,最终走到native,执行dumpArtMethod
BaseDexClassLoader#findClass
最终会到defineClassNative
, 调用链如下
BaseDexClassLoader#findClass --> DexPathList#findClass --> Element#findCLass --> DexFile#loadClassBinaryName --> DexFile#defineClass --> DexFile#defineClassNative
(native) --> DexFile_defineClassNative --> ClassLinker#DefineClass
重点: ArtMethod::FromReflectedMethod
获取 ArtMethod 指针
static void DexFile_dumpMethodCode(JNIEnv* env, jclass,jobject method) {
ScopedFastNativeObjectAccess soa(env);
if(method!=nullptr)
{
ArtMethod* artmethod = ArtMethod::FromReflectedMethod(soa, method);
myfartInvoke(artmethod);
}
return;
}
extern "C" void myfartInvoke(ArtMethod * artmethod)
SHARED_LOCKS_REQUIRED(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");
}
注意第一个参数self,这里为null,会进入到dumpArtMethod
中,也是fart添加的
void ArtMethod::Invoke(Thread * self, uint32_t * args,
uint32_t args_size, JValue * result,
const char *shorty) {
if (self == nullptr) {
dumpArtMethod(this);
return;
}
....
}
重点
artmethod->GetDexFile()
获取 DexFile
dex_file->Begin()
对应dex在内存中的起点
dex_file->size()
对应dex文件大小
DexFile::CodeItem
是什么目前还不清楚
extern "C" void dumpArtMethod(ArtMethod * artmethod)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
char *dexfilepath = (char *) malloc(sizeof(char) * 2000);
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]) {
const DexFile *dex_file = artmethod->GetDexFile();
const char *methodname = PrettyMethod(artmethod).c_str();
const uint8_t *begin_ = dex_file->Begin();
size_t size_ = dex_file->Size();
memset(dexfilepath, 0, 2000);
int size_int_ = (int) size_;
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);
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);
}
}
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_);
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;
}
}
dumpDexFileByExecute
是在Execute
中被调用, 也就是是执行指令的时候
// art/runtime/interpreter/interpreter.cc
static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register) {
if(strstr(PrettyMethod(shadow_frame.GetMethod()).c_str(),"<clinit>") != nullptr)
{
dumpDexFileByExecute(shadow_frame.GetMethod());
}
...
extern "C" void dumpDexFileByExecute(ArtMethod * artmethod)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
char *dexfilepath = (char *) malloc(sizeof(char) * 2000);
if (dexfilepath == nullptr) {
LOG(INFO) <<
"ArtMethod::dumpDexFileByExecute,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]) {
const DexFile *dex_file = artmethod->GetDexFile();
const uint8_t *begin_ = dex_file->Begin(); // Start of data.
size_t size_ = dex_file->Size(); // Length of data.
memset(dexfilepath, 0, 2000);
int size_int_ = (int) size_;
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);
sprintf(dexfilepath,
"/sdcard/fart/%s/%d_dexfile_execute.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);
}
}
}
if (dexfilepath != nullptr) {
free(dexfilepath);
dexfilepath = nullptr;
}
}
测试实际效果
某平台加固后
fart之后
总结
Hook点有2个:
- 是app启动时候,在ActivityThread中利用反射,拿到
ArtMethod
,再通过ArtMethod->GetDexFile()
拿到DexFile
- 在安卓虚拟机执行指令时候
Execute
中进行dump出DexFile,相较于早期的DexFile::OpenCommon(const uint8_t* base, size_t size, ...)
中进行脱壳更加底层
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】