通过主动调用脱函数抽取类壳

被动调用

以自实现的抽取壳为例,对Test01.proc1函数进行抽空,但这次不同的是此Test01类在apk加载的过程中并不会被加载。通过之前被动调用的方式dump dex发现此函数还是被抽空的状态。

总结:单单通过在LoadMethod调用后进行dump dex是一种被动调用的脱壳手段。因为APK在执行过程中只有显式/隐式加载此类时才会调用LoadMethod,然后壳进行抽取函数的回填,在调用LoadMethod函数后我们进行dump dex就可以得到回填后的dex文件。但是如果还没有显式/隐式加载此类的话就不会调用LoadMethod,壳也就暂时不会对抽取的函数进行回填,这样我们dump的dex中此函数就还是被抽空的状态。

主动调用的优点

由于只有一个类在被显式/隐式加载的时候才会调用LoadMethod进行函数的加载,这样壳代码才会对抽取的函数进行回填,我们的dump代码才有执行的时机。所以就可以通过遍历加载apk的所有类来实现主动调用,从而让我们的dump代码得到执行时机,这里使用寒冰大佬的frida_fart中的脚本。熟悉android dex热修复的对这段代码肯定很了解,因为每一个classloader类加载器都有其对应的DexPathList类,DexPathList类对应一个Element内部类数组,Element内部类数组的每一项都对应此classloader加载的dex文件信息,其中每一个dex文件信息都用一个DexFile类描述,其中包含了此dex文件中所有的类名称等信息,通过调用DexFile类的entries方法得到类名后就可以利用classloader对其进行主动加载(显式加载)。

function dealwithClassLoader(classloaderobj) {
    if (Java.available) {
        Java.perform(function () {
            try {
                var dexfileclass = Java.use("dalvik.system.DexFile");
                var BaseDexClassLoaderclass = Java.use("dalvik.system.BaseDexClassLoader");
                var DexPathListclass = Java.use("dalvik.system.DexPathList");
                var Elementclass = Java.use("dalvik.system.DexPathList$Element");
                var basedexclassloaderobj = Java.cast(classloaderobj, BaseDexClassLoaderclass);
                var tmpobj = basedexclassloaderobj.pathList.value;
                var pathlistobj = Java.cast(tmpobj, DexPathListclass);
                var dexElementsobj = pathlistobj.dexElements.value;
                if (dexElementsobj != null) {
                    for (var i in dexElementsobj) {
                        var obj = dexElementsobj[i];
                        var elementobj = Java.cast(obj, Elementclass);
                        tmpobj = elementobj.dexFile.value;
                        var dexfileobj = Java.cast(tmpobj, dexfileclass);
                        const enumeratorClassNames = dexfileobj.entries();
                        while (enumeratorClassNames.hasMoreElements()) {
                            var className = enumeratorClassNames.nextElement().toString();
                            if(-1 != className.indexOf("com.reverccqin")){
                                console.log("start loadclass->", className);
                                var loadclass = classloaderobj.loadClass(className);
                                console.log("after loadclass->", loadclass);
                            }
                        }
                    }
                }
            } catch (e) {
                console.log(e)
            }
        });
    }
}

通过执行此脚本可以发现被抽空的Test01.proc1函数对应的Test01类已经被主动加载了。

然后再次查看dump文件就可以发现Test01.proc1函数已经被成功回填。

posted @ 2022-10-29 15:33  怎么可以吃突突  阅读(296)  评论(0编辑  收藏  举报