一些安卓app中包含多个apk。app这样做的好处是:方便局部/增量更新。主dex通过DexClassLoader,去加载指定路径下的apk、dex或者jar文件,反射出相应的类,然后根据获得相应的代码和资源。
例如,某app的apk中,在\assets\chimera-modules下面就存在着多个apk。
一些类和方法的源码存在于这些apk中,attach到进程后,使用默认的classloader是不行的(会报错:类不存在)。
例如,某个类org.chromium.net.AndroidNetworkLibrary不存在于apk的某个class.dex下面,而存在于\assets\chimera-modules\CronetDynamite.apk中。
如果直接attach到com.google.android.gms进行hook,会报错:
Error: java.lang.ClassNotFoundException: Didn’t find class “org.chromium.net.AndroidNetworkLibrary” on path: DexPathList …………
需要先检索到对应的loader,再通过设置Java.classFactory.loader 让Frida切换到对应的loader,然后再去hook。
下面是两种检索loader的方案
Java.perform(function () {
// dh.a(java.security.cert.X509Certificate[], java.lang.String, java.lang.String) : java.util.List
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
// the apk contains class: org.chromium.net.AndroidNetworkLibrary
if (loader.findClass("org.chromium.net.AndroidNetworkLibrary")) {
if (loader.toString().indexOf("CronetDynamite") != -1) {
Java.classFactory.loader = loader;
send("loader: " + loader);
// [*] loader: dalvik.system.DelegateLastClassLoader[DexPathList[[zip file "/data/user_de/0/com.google.android.gms/app_chimera/m/00000009/CronetDynamite.apk"],nativeLibraryDirectories=[/data/user_de/0/com.google.android.gms/app_chimera/m/00000009/CronetDynamite.apk!/lib/arm64-v8a, /system/lib64, /system/product/lib64]]]
}
}
} catch (error) {
}
}, onComplete: function () {
}
});
var class_name = 'dh';
var DymClass = Java.use(class_name);
DymClass.a.implementation = function (arg1,arg2,arg3)
{
var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
// if(bt.indexOf("bdpg") != -1)
if (1) {
send('');
console.log("Backtrace:" + bt);
send('arg1 : ' + arg1);
send('arg2 : ' + arg2);
send('arg3 : ' + arg3);
send('ret : ' + this.a(arg1,arg2,arg3));
}
return this.a(arg1,arg2,arg3);
}
});
Java.perform(function () {
// dh.a(java.security.cert.X509Certificate[], java.lang.String, java.lang.String) : java.util.List
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
// [*] loader: dalvik.system.DelegateLastClassLoader[DexPathList[[zip file "/data/user_de/0/com.google.android.gms/app_chimera/m/00000009/CronetDynamite.apk"],nativeLibraryDirectories=[/data/user_de/0/com.google.android.gms/app_chimera/m/00000009/CronetDynamite.apk!/lib/arm64-v8a, /system/lib64, /system/product/lib64]]]
if (loader.toString().startsWith(" dalvik.system.DelegateLastClassLoader")) {
Java.classFactory.loader = loader;
send("loader: " + loader);
}
} catch (error) {
}
}, onComplete: function () {
}
});
var class_name = 'dh';
var DymClass = Java.use(class_name);
DymClass.a.implementation = function (arg1,arg2,arg3)
{
var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
// if(bt.indexOf("bdpg") != -1)
if (1) {
send('');
console.log("Backtrace:" + bt);
send('arg1 : ' + arg1);
send('arg2 : ' + arg2);
send('arg3 : ' + arg3);
send('ret : ' + this.a(arg1,arg2,arg3));
}
return this.a(arg1,arg2,arg3);
}
});
#coding=utf-8
import frida,importlib, sys
importlib.reload(sys)
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.findClass("org.chromium.net.AndroidNetworkLibrary")) {
Java.classFactory.loader = loader;
send("loader: "+ loader);
// console.log(loader);
}
} catch (error) {
}
}, onComplete: function () {
}
});
var DynamicAndroidNetworkLibrary = Java.use("org.chromium.net.AndroidNetworkLibrary");
DynamicAndroidNetworkLibrary.verifyServerCertificates.implementation = function (arg1,arg2,arg3) {
// send('arg1 : ' + arg1);
send('arg2 : ' + arg2);
send('arg3 : ' + arg3);
return this.verifyServerCertificates(arg1,arg2,arg3);
}
});
"""
process = frida.get_usb_device().attach('com.google.android.gms')
script = process.create_script(jscode)
script.on('message', on_message)
print('[*] Running hooking')
script.load()
sys.stdin.read()
关键性语句是Java.classFactory.loader = loader;
这里的Java.classFactory.loader
可以参考:https://frida.re/docs/javascript-api/
Java.ClassFactory: class with the following properties:
loader: read-only property providing a wrapper for the class loader currently being used. For the default class factory this is updated by the first call to Java.perform().
也就是说Java.classFactory.loader是当前的class loader的一个wrapper。修改Java.classFactory.loader就可以修改当前的class loader。
主要参考:https://blog.csdn.net/freeking101/article/details/107720802
其他参考:
新建类对象并调用某方法:http://pollux.cc/2019/11/08/%E4%BD%BF%E7%94%A8frida-Hook%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BDDex/
切换dex环境:
https://blog.csdn.net/lilongsy/article/details/122261331