开篇要求:
FART通过对系统源码修改编译,生成脱壳rom,完成对dex的函数粒度的修复;其中FART的主动调用链构造的深度可以结合壳的修复时机进行定制。当前很多抽取壳在类的加载流程当中就已经完成了被抽取函数的修复。
试想一下,如果不考虑FART当中主动调用链的构造,能否单纯用Frida这个hook框架实现一个能够在函数粒度上解决抽取壳的脱壳脚本?
关于Fart的脱壳点:
一个是Execute函数,另一个就是送到主动调用链的时候
<clinit> - Execute => dumpDexFileByExecute
其他正常函数 - DexFile_dumpMethodCode => myfartInvoke => Invoke => dumpArtMethod
关于主动调用链:
启动fart-(getClassloader来获取ClassLoader)>fartwithClassLoader-(反射获取mCookie)>loadClassAndInvoke-(dumpMethodCode将各种函数转化成ArtMethod类型并送入我们的fake_Invoke参数包装)->送入系统的Invoke-(调用dumpArtMethod实现第二个脱壳点)
在这里我们看出Fart主动调用前提:
获取appClassLoader,通过ClassLoader加载到所有类 ,通过每个类获取到该类下的所有方法【包括构造函数和普通函数】
++++++++++++++++++++++++++++++++++++++++动手环节++++++++++++++++++++++++++++++++++++++++++
获取appClassLoader,通过ClassLoader加载到所有类
1.首先实现和的功能【加载App最后依附的ClassLoader下的所有的类】:
通过每个类获取到该类下的所有方法【包括构造函数和普通函数】
2.接下来完成的功能
增加构造函数Constructors
现在Constructor和Method都可以打印出来了,接下来就是将它们送给ArtMethod::Invoke中,进行一种“虚拟的”调用。
此时来了第一个疑惑点:能把我们的这些方法“送”到ArtMethod::Invoke去吗?
================================分析FART中的主动调用链===================================
DexFile_dumpMethodCode中使用了jobject2ArtMethod函数【这个很重要】
static void DexFile_dumpMethodCode(JNIEnv* env, jclass, jobject method) {
if(method!=nullptr)
{
ArtMethod* proxy_method = jobject2ArtMethod(env, method);
myfartInvoke(proxy_method);
}
return;
}
而这里其实只是传递给Invoke然后再在Invoke中dumpArtMethod,可以说直到dumpArtMethod之前是没有执行任何具有功能性的代码。
可以发现Fart使用的主动调用链的深度其实是:jobject2ArtMethod这个函数决定的。
extern "C" ArtMethod* jobject2ArtMethod(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
return method;
}
通过 mirror::ArtMethod::FromReflectedMethod 获取了Java对象的在native层的 ArtMethod指针
ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_method) {
ObjPtr<mirror::Executable> executable = soa.Decode<mirror::Executable>(jlr_method);
DCHECK(executable != nullptr);
return executable->GetArtMethod();
}
这里,我想到了一个不错的点子,我通过遍历method方法并对每一个方法进行一个调用,由于缺少参数,或者其他原因,最后肯定不能成功执行。
但是我们可以使用hook_native方式提前在load_all_class之前执行等待Java层的.invoke方法(注意是小写的invoke,这是java方法)到来,由于Java层的.invoke方法最终会调用InvokeMethod,因此我们hook它。
代码如下:
并注意函数method的调用
可以发现每一个调用都来到了InvokeMethod这个函数来,我们的Hook是成功的!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)