版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/
FART 和 Frida 结合会发生什么?
对 FART 进一步增强:
-
增强 FART 的脱壳能力:解决对抗 FART 的壳、动态加载的 dex 的 dump 和修复;
-
控制 FART 主动调用的范围,让 FART 更精细化,比如按需进行类甚至是函数的修复。
非双亲委派关系下动态加载的 dex 脱壳问题
由于动态加载的 dex 没有取改变 android 中 ClassLoader 双亲委派关系,所以动态加载的 dex 没有自动脱壳。
相关文章:
在 android studio 中创建一个 plugin module 其中包含一个 FartTest 类源码如下:
package com.cyrus.example.plugin
import android.util.Log
class FartTest {
fun test(): String {
Log.d("FartTest", "call FartTest test().")
return "String from FartTest."
}
}
把 plugin-debug.apk push 到 files 目录下
adb push "D:\Projects\AndroidExample\plugin\build\intermediates\apk\debug\plugin-debug.apk" /sdcard/Android/data/com.cyrus.example/files/plugin-debug.apk
ls 一下 files 目录是否存在 plugin-debug.apk
adb shell ls /sdcard/Android/data/com.cyrus.example/files
在 app 动态加载 files 目录下的 plugin-debug.apk 并调用 FartTest 的 test 方法
val apkPath = "/sdcard/Android/data/com.cyrus.example/files/plugin-debug.apk"
// 创建 DexClassLoader 加载 sdcard 上的 apk
val classLoader = DexClassLoader(
apkPath,
null,
this@FartActivity.packageResourcePath,
classLoader // parent 设为当前 context 的类加载器
)
// classLoader 加载 com.cyrus.example.plugin.FartTest 类并通过反射调用 test 方法
val pluginClass = classLoader.loadClass("com.cyrus.example.plugin.FartTest")
val constructor = pluginClass.getDeclaredConstructor()
constructor.isAccessible = true
val instance = constructor.newInstance()
val method = pluginClass.getDeclaredMethod("test")
method.isAccessible = true
val result = method.invoke(instance) as? String
log("动态加载:${apkPath}\n\ncall ${method}\n\nreuslt=${result}")
mClassLoader = classLoader
脱壳完成,但是没有对 plugin-debug.apk 中的目标类 FartTest 发起主动调用

这时候 frida 就派上用场了,因为 frida 本身具有枚举所有 ClassLoader 的能力。
Frida + FART 脱壳动态加载的 dex
枚举出所有 ClassLoader 后,再结合 FART 的 api 就可以实现动态加载 dex 的脱壳。
function invokeAllClassloaders() {
Java.perform(function () {
try {
// 获取 ActivityThread 类
var ActivityThread = Java.use("android.app.ActivityThread");
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
// 过滤掉 BootClassLoader
if (loader.toString().includes("BootClassLoader")) {
console.log("[-] 跳过 BootClassLoader");
return;
}
// 调用 fartWithClassLoader
console.log("[*] 调用 fartwithClassloader -> " + loader);
ActivityThread.fartwithClassloader(loader);
} catch (e) {
console.error("[-] 调用失败: " + e);
}
},
onComplete: function () {
console.log("[*] 枚举并调用完毕");
}
});
} catch (err) {
console.error("[-] 脚本执行异常: " + err);
}
});
}
setImmediate(invokeAllClassloaders)
把 log 导出到 txt
adb logcat -v time > logcat.txt
打开 app 后执行脚本
frida -H 127.0.0.1:1234 -F -l fart_invoke_all_classloaders.js
从输出日志可以看到已经成功对 FartTest 类中方法发起主动调用

局部变量的 ClassLoader 枚举不出来
但还有一个问题呢:局部变量的 ClassLoader 枚举不出来。
因为:
-
enumerateClassLoaders() 只枚举当前 VM 中可访问的、被 GC Root 持有的 ClassLoader;
-
如果 DexClassLoader 作为临时变量创建后,没有被保存,就会被 GC 回收或无法遍历到。
比如,下面的 Kotlin 代码中,当 DexClassLoader 为局部变量时就没有枚举出这个 DexClassLoader 。
/**
* 局部

最低0.47元/天 解锁文章
727

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



