首页
论坛
课程
招聘
[原创]记录向:单纯使用Frida书写类抽取脱壳工具的一些心路历程和实践
2020-8-3 13:12 6631

[原创]记录向:单纯使用Frida书写类抽取脱壳工具的一些心路历程和实践

2020-8-3 13:12
6631

开篇要求:

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下的所有的类】:

//加载当前ClassLoader下的所有的类
function load_all_class() {
    if (Java.available) {
        Java.perform(function () {
            var DexFileclass = Java.use("dalvik.system.DexFile");
            var BaseDexClassLoaderclass = Java.use("dalvik.system.BaseDexClassLoader");
            var DexPathListclass = Java.use("dalvik.system.DexPathList");
            Java.enumerateClassLoaders({
                onMatch: function (loader) {
                    console.log("loader:",loader);
                    //为了排除BootClassLoader和不相关的ClassLoader
                    if((loader.toString().indexOf("java.lang.BootClassLoader")==-1)&&(loader.toString().indexOf("zip")==-1) ){
                        try {
                            var BaseDexClassLoader_obj = Java.cast(loader, BaseDexClassLoaderclass);
                            var pathList = BaseDexClassLoader_obj.pathList.value;
                            var pathList_obj = Java.cast(pathList, DexPathListclass)
                            var ElementsArray = pathList_obj.dexElements.value;
                            console.log("ElementsArray---->",ElementsArray);
                            //下面三行是为了验证appClassLoader是哪一个
                            //var ActivityThreaeclass = Java.use("android.app.ActivityThread");
                            //var classloader = ActivityThreaeclass.getClassloader();
                            //console.log("appClassLoader is :",classloader );
                            for (var index in ElementsArray) {
                                var element = ElementsArray[index];
                                try {
                                        try{var dexfile = element.dexFile.value;}catch(e){}
                                    
                                         var dexfileobj = Java.cast(dexfile, DexFileclass);
                                         const classNames = [];
                                         const enumeratorClassNames = dexfileobj.entries();
                                         while (enumeratorClassNames.hasMoreElements()) {
                                            var className = enumeratorClassNames.nextElement().toString();
                                            classNames.push(className);
                                            //指定加载某个类
                                            if(className.indexOf("com.aipao.hanmoveschool.activity.LoginActivity")>=0){
                                                console.log("ClassName : " ,className);
                                                loader.loadClass(className);
                                            }  
                                        }     
                                }catch(e){
                                    console.log(e)
                                }
                            }
                        } catch (e) {
                            console.log(e);
                        }
                    }
                },
                onComplete: function () {
                    console.log("[*]load_all_class success!");
                }
            })
        });
    }
}

通过每个类获取到该类下的所有方法【包括构造函数和普通函数】

2.接下来完成的功能

//把上面的核心加载处进行修改,调用load_all_methods方法
                                            
                                            //指定加载某个类
                                            if(className.indexOf("com.aipao.hanmoveschool.activity.LoginActivity")>=0){
                                                console.log("ClassName : " ,className);
                                                //loader.loadClass(className);
                                                //每个类都要去把自己的所有函数加载进来
                                                load_all_methods(loader,className);
                                            }
//加载每个类下的所有函数
function load_all_methods(loader,classname){
    if (Java.available) {
        Java.perform(function () {
            //获取当前类的投射,即xxxxx.class这个方法,hookClassCast这个就是返回值
            var clazz = loader.loadClass(classname);
            var ClassUse = Java.use("java.lang.Class");
            var hookClassCast = Java.cast(clazz,ClassUse);
            console.log("hookClassCast is :",hookClassCast);
            console.log("------------------------------Constructors-------------------------------");
            var Constructors = hookClassCast.getDeclaredConstructors();
            console.log(Constructors);
            console.log("---------------------------------Methods---------------------------------");
            var Methods = hookClassCast.getDeclaredMethods();
            console.log(Methods,"\n");
        });
    }
}


增加构造函数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它。

代码如下:

function hook_native() {
    
    var module_libart = Process.findModuleByName("libart.so");
    var symbols = module_libart.enumerateSymbols();
    var ArtMethod_Invoke = null;
    
    for (var i = 0; i < symbols.length; i++) {
        var symbol = symbols[i];
        var address = symbol.address;
        var name = symbol.name;
        //var indexArt = name.indexOf("Art");
        var indexInvoke = name.indexOf("InvokeMethodERKNS");
        if ( indexInvoke >= 0) {
            console.log(name);
            ArtMethod_Invoke = address;
            console.log(ArtMethod_Invoke);
        }
    }
    if (ArtMethod_Invoke) {
        Interceptor.attach(ArtMethod_Invoke, {
            onEnter: function (args) {
                try {   
                    console.log("soa is :",args[0],"javaMethod is :",args[1]);//观察结果,打印第一个(soa)和第二个参数(javamethod)
                } catch (error) {
                    console.log(error);
                }
            }, onLeave: function (retval) {
            }
        });
    }   
}

并注意函数method的调用

function load_all_methods(loader,classname){
    if (Java.available) {
        Java.perform(function () {
            //获取当前类的cast,即xxxxx.class这个方法,hookClassCast这个就是返回值
            var clazz = loader.loadClass(classname);
            var ClassUse = Java.use("java.lang.Class");
            var hookClassCast = Java.cast(clazz,ClassUse);
            console.log("hookClassCast is :",hookClassCast);
            console.log("------------------------------Constructors-------------------------------");
            var Constructors = hookClassCast.getDeclaredConstructors();
            console.log(Constructors);
            console.log("---------------------------------Methods---------------------------------");
            var Methods = hookClassCast.getDeclaredMethods();
            for(var i =0; i<Methods.length;i++){
                var method = Methods[i];
                method.setAccessible(true);
                try{method.invoke(null,null);
                    console.log("method:",method,"is invoke ok!")}catch(e){console.log(e)}
            }
            console.log(Methods,"\n");
        });
    }
}

可以发现每一个调用都来到了InvokeMethod这个函数来,我们的Hook是成功的!

而且通过后面打印的内容发现soa的值是固定的。


接下来我们尝试打印一下每一个Method一一对应的ArtMethod对象指针,看看到底是不是这样?

不过hook什么函数才能拿到ArtMethod指针呢?我们继续来看源码中InvokeMethod函数,如下图框框所示:

关键词来了,GetArtMethod!因此我们可以写下如下脚本:

    //我们来hook GetArtMethod的返回值
    if (addr_GetArtMethod) {
        Interceptor.attach(addr_GetArtMethod, {
            onEnter: function (args) {
                    //console.log("hook GetArtMethod!");
            }, onLeave: function (retval) {
                console.log("java_ArtMethod_ptr:",retval);
            }
        });
    }
}

验证,我想取得每个Method对应的ArtMethod的想法得以成真:

我们都知道,ArtMethod指针和ArtMethod对象是两个不同的概念,

我们首先来通过FART的源码内容,尝试一下我们拿到的这个ArtMethod指针可否进行主动调用?

//主要是送给myfartInvoke
//我们来hook GetArtMethod的返回值
    if (addr_GetArtMethod) {
        Interceptor.attach(addr_GetArtMethod, {
            onEnter: function (args) {
                    //console.log("hook GetArtMethod!");
            }, onLeave: function (retval) {
                console.log("java_ArtMethod_ptr:",retval);//拿到ArtMethod指针
               
                var artmethod_ptr = retval;
                var myAutoDInvoke = new NativeFunction(addr_myfartInvoke, "void", ["pointer"]);
                myfartInvoke(artmethod_ptr);
             
            }
        });
    }

可以发现,已经成功使用frida调用myfartInvoke并产生了对应的bin文件

因此我们的这个ArtMethod指针是正确的!

当时偷了个懒,在这里替换成源码中的dumpArtMethod发现也是可以的

 //我们来hook GetArtMethod的返回值
            if (addr_GetArtMethod) {
                Interceptor.attach(addr_GetArtMethod, {
                    onEnter: function (args) {
                            //console.log("hook GetArtMethod!");
                    }, onLeave: function (retval) {
                        console.log("java_ArtMethod_ptr:",retval);
        
                        var artmethod_ptr = retval;
                        if(artmethod_maps.length==0){
                            artmethod_maps[0]=ptr(artmethod_ptr);
                        }else if(artmethod_maps[file]==undefined){
                                    artmethod_maps[file]=ptr(artmethod_ptr);
                        }
                        file++;
                        var dumpArtMethod = new NativeFunction(addr_dumpArtMethod, "void", ["pointer"]);
                        console.log("dumpArtMethod_in :",dumpArtMethod );
                        //dumpArtMethod(artmethod_ptr);
                        dumpArtMethod_out(artmethod_ptr);
                    }
                });
            }


虽然没有达成要求的"纯用frida"的条件,因此思路写在这里纪念一下算是一种->只调用FART的一个API而达成的目标,逃


另外一种解决方法:

去遍历所有的类并且在Java层加载,能够修复那种执行的时候把代码填充回dex的,也就是占坑型。

我是用Java层写了个仿照fart的loadAllClass的流程【由于可以照抄源码,因此这里具体我就省略了】,然后把dex使用base64加密(创建dex加载前会解密),到时候使用DexClassLoader将其加载,并通过参数类型new Object了method的参数,这样一来函数就可被我们调用。【这里我可能绕弯了,不过多一种思路,不亏!】

Dex:

   public static void loadAllClass(ClassLoader classLoader) {
        int i;
        Method[] methods;
        Constructor<?> cons[];
        Class clazz;
        try {
            Iterator Ir = EnumerateClass.getClassNameList(classLoader).iterator();
            while(true) {
                if(!Ir.hasNext()) {
                    return;
                }
                Object obj = Ir.next();
                clazz = classLoader.loadClass(((String)obj));
                methods = clazz.getDeclaredMethods();
                cons = clazz.getDeclaredConstructors();
                Log.d("Frida_UNPACK", "load class: " + clazz.getName());
                i = 0;
                while(i < methods.length){
                    Method method = methods[i];
                  Object[] objs = new Object[method.getParameterTypes().length];
                  Log.d("FRiDA_UNPACK", "try to load method: " + clazz.getName() + "-->" + method.getName());
                   method.invoke(null, objs);//其实只调用方法就好,构造函数用处不是很大
                  Log.d("FRiDA_UNPACK", "success");
                  ++i;
                }
       }catch(Throwable e){}
    }

然后使用base64在线工具对其Hex内容加密以备后续使用,另外注意:我这里的loadAllClass是静态方法

除开省略的base64之外的Frida完整脚本如下:

var dex_maps = {};
//脱壳点是DefineClass,当然大家也可以换成别的
function hook_dex(){
    var openmemory = Module.findExportByName("libart.so", "_ZN3art11ClassLinker11DefineClassEPNS_6ThreadEPKcmNS_6HandleINS_6mirror11ClassLoaderEEERKNS_7DexFileERKNS9_8ClassDefE");
    if(openmemory != undefined) {
        console.log("DefineClass at" + openmemory);
        Interceptor.attach(openmemory, {
            onEnter: function (args) {
                var dex_file= args[5];
                var base = ptr(dex_file).add(Process.pointerSize).readPointer();
                var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt();
    
                if (dex_maps[base] == undefined) {
                    dex_maps[base] = size;
                    console.log("hook_dex:", base, size);
                }
            },
            onLeave: function (retval) {
    
            }
        });
    }
}

function get_self_process_name() {
    var openPtr = Module.getExportByName('libc.so', 'open');
    var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);

    var readPtr = Module.getExportByName("libc.so", "read");
    var read = new NativeFunction(readPtr, "int", ["int", "pointer", "int"]);

    var closePtr = Module.getExportByName('libc.so', 'close');
    var close = new NativeFunction(closePtr, 'int', ['int']);

    var path = Memory.allocUtf8String("/proc/self/cmdline");
    var fd = open(path, 0);
    if (fd != -1) {
        var buffer = Memory.alloc(0x1000);

        var result = read(fd, buffer, 0x1000);
        close(fd);
        result = ptr(buffer).readCString();
        return result;
    }

    return "-1";
}

function loadClassAndDump(){
    if(Java.available) {
        Java.perform(function(){
            var dexBase64 = "(这里放置调用所有方法的dex文件的base64加密格式,篇幅问题,我就省点地方)";
            var application = Java.use("android.app.Application");
            var BaseDexClassLoader = Java.use("dalvik.system.BaseDexClassLoader");
            var Base64 = Java.use("android.util.Base64");
            var FileOutputStream = Java.use("java.io.FileOutputStream");
            var DexClassLoader = Java.use("dalvik.system.DexClassLoader");
 
            var reflectClass = Java.use("java.lang.Class");

            if(application != undefined) {
                
                var Context_class = Java.use("android.content.Context");
                var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
                var context = currentApplication.getApplicationContext();
                var castedContext = Java.cast(context,Context_class);
                var classloader = castedContext.getClassLoader();
                var filesDir = castedContext.getFilesDir();
                var codeCacheDir = castedContext.getCodeCacheDir();
                    if(classloader != undefined) {
                        var dexbytes = Base64.decode(dexBase64, 0);
                        var dexpath = filesDir + "/emmm.dex";
                        var fout = FileOutputStream.$new(dexpath);
                        fout.write(dexbytes, 0, dexbytes.length);
                        fout.close();
                        console.log("write dex to " + dexpath);
    
                        var dexstr = dexpath.toString();
                        var cachestr = codeCacheDir.toString();
    
                        var dyndex = DexClassLoader.$new(dexstr, cachestr, cachestr, classloader);
                        console.log(dyndex.toString());
                        var EnumerateClass = dyndex.loadClass("com.Youzi.EnumerateClass");
                        var castedEnumerateClass = Java.cast(EnumerateClass, reflectClass);
                        var methods = castedEnumerateClass.getDeclaredMethods();
                        // loadAllClass
                        var loadAllClass = undefined;
                        for(var i =0; i<methods.length;i++){
                            var method = methods[i];
                            method.setAccessible(true);
                            if(method.getName()=="loadAllClass"){
                                loadAllClass = method;
                            }
                        }
                                                //找到loadAllClass方法
                        if(loadAllClass != undefined) {
                            console.log("loadAllClass: " + loadAllClass.toString());
                            var args = Java.array('Ljava.lang.Object;',[classloader]);
                            var classlist = loadAllClass.invoke(null , args);//之前说过这个函数是静态方法,因此直接调用
                            console.log("start dump dex ");
                            //开始dump
                            for(var base in dex_maps) {
                                var size = dex_maps[base];
                                var magic = ptr(base).readCString();
                                if(magic.indexOf("dex") == 0) {
                                    var process_name = get_self_process_name();
                                    if (process_name != "-1") {
                                         var dex_path = "/data/data/" + process_name + "/files/" + base.toString(16) + "_" + size.toString(16) + ".dex";
                                         console.log("[find dex]:", dex_path);
                                         var fd = new File(dex_path, "wb");
                                         if (fd && fd != null) {
                                            var dex_buffer = ptr(base).readByteArray(size);
                                                 fd.write(dex_buffer);
                                                 fd.flush();
                                                 fd.close();

                                        }
                                    }
                                    console.log("write file to " + dex_path);
                                }
                            }
                        }
                    } else {
                        console.error("unable get classloader");
                    }
               }
        });
    }
}
function main() {
    hook_dex();
}

setImmediate(main);

开始调用loadClassAndDump(),这个函数是开始主动调用+dump

一系列已经修复好的dex生成,不需要另外使用修复脚本(算个小优点吧2333)。


可以看见,面对占坑型抽取还是可以完美解决的(偏移型则需要使用C++指针辅助这是Java的软肋)

总结起来就是使用了dex来辅助(这一步其实可以写进Frida里头,但是我想介绍动态加载dex这种思路供大家学习),但是整体均在Frida环境中完成【基本上是纯Java和JavaScript环境】,没有跨平台,符合开篇要求。

最后,Thank you for using Frida! 



[公告]第4届看雪技术峰会2020年10月23日上海浦东喜来登由由大酒店召开!

上传的附件:
收藏
点赞3
打赏
分享
打赏 + 2.00
打赏次数 1 金额 + 2.00
 
赞赏  雪衫   +2.00 2020/08/05 感谢分享~
最新回复 (21)
雪    币: 239
活跃值: 活跃值 (6712)
能力值: ( LV9,RANK:166 )
在线值:
发帖
回帖
粉丝
0x指纹 活跃值 3 2020-8-3 13:22
2
0
感谢分享!
雪    币: 4734
活跃值: 活跃值 (122)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
cqzhou 活跃值 2020-8-3 13:24
3
0
感谢分享!
雪    币: 6255
活跃值: 活跃值 (194)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tDasm 活跃值 2020-8-3 13:59
4
0

...

最后于 2020-8-3 14:25 被tDasm编辑 ,原因:
雪    币: 6255
活跃值: 活跃值 (194)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tDasm 活跃值 2020-8-3 14:10
5
0
谢谢分享!
咨询一下,你有art模式下动态加、解密方法的app?(动态加解密方法是指执行方法前解密,执行完又加密)。
我发现只有VM模式下才有动态加、解密方法,同一app到art模式下运行就变成占坑型抽取。
雪    币: 573
活跃值: 活跃值 (1026)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
supperlitt 活跃值 2020-8-3 15:52
6
0
楼主,最后实现了没有。。纯frida,还原类抽取。。。
雪    币: 1959
活跃值: 活跃值 (1031)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
一颗金柚子 活跃值 1 2020-8-3 16:07
7
0
supperlitt 楼主,最后实现了没有。。纯frida,还原类抽取。。。
实现了,就结合我最后的这段代码加上导出一些源码函数(也就是java和C++配合)完成对占坑型和偏移型的对抗。
雪    币: 1959
活跃值: 活跃值 (1031)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
一颗金柚子 活跃值 1 2020-8-3 16:09
8
0
tDasm 谢谢分享! 咨询一下,你有art模式下动态加、解密方法的app?(动态加解密方法是指执行方法前解密,执行完又加密)。 我发现只有VM模式下才有动态加、解密方法,同一app到art模式下运行就变成占 ...
这个我以前搜集过,但是电脑最近刚换,没备份到目前的电脑上来。你可以去找找那种助手类似的App,它们比较用的多好像。
雪    币: 6255
活跃值: 活跃值 (194)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tDasm 活跃值 2020-8-3 16:16
9
0
一颗金柚子 这个我以前搜集过,但是电脑最近刚换,没备份到目前的电脑上来。你可以去找找那种助手类似的App,它们比较用的多好像。
能否具体指出APP名?
另外,对于占坑型抽取不用你文中那么复杂,因为方法还原后就不会再加密,一直在DEX原位置。
雪    币: 1959
活跃值: 活跃值 (1031)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
一颗金柚子 活跃值 1 2020-8-3 16:24
10
0
tDasm 能否具体指出APP名? 另外,对于占坑型抽取不用你文中那么复杂,因为方法还原后就不会再加密,一直在DEX原位置。
具体的我是有点想不起来了,是叫什么太郎助手还是什么的。
嗯是的,这是我4月末做的内容,时间已经过去的有点多了,只是发出来进一下我的论坛档案~你的回答也有利于大家学习,很棒
雪    币: 573
活跃值: 活跃值 (1026)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
supperlitt 活跃值 2020-8-3 16:38
11
0
大佬,ins_的bin文件,,没看到哪里生成。。。
雪    币: 1959
活跃值: 活跃值 (1031)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
一颗金柚子 活跃值 1 2020-8-3 16:53
12
0
supperlitt 大佬,ins_的bin文件,,没看到哪里生成。。。
这篇文章主要叙述的是占坑型,我的脚本不需要生成bin文件就可以恢复函数。bin文件主要是用来针对偏移型的,不在本文的讨论范围~偏移型的需要使用so库调用或者是利用我在文中写到的“使用FART脱壳机的API”
雪    币: 4630
活跃值: 活跃值 (268)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
windy_ll 活跃值 2020-8-3 22:20
13
0
感谢分享
雪    币: 6255
活跃值: 活跃值 (194)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tDasm 活跃值 2020-8-4 08:32
14
0
 i = 0;
                while(i < methods.length){
                    Method method = methods[i];
                  Object[] objs = new Object[method.getParameterTypes().length];
                  Log.d("FRiDA_UNPACK", "try to load method: " + clazz.getName() + "-->" + method.getName());
                   method.invoke(null, objs);//其实只调用方法就好,构造函数用处不是很大
                  Log.d("FRiDA_UNPACK", "success");
                  ++i;
                }
你这个循环是不能完成的,因为method.invoke没有实际参数导致执行异常,建议在 在method.invoke前用try。
雪    币: 41
活跃值: 活跃值 (67)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
雨昏灰 活跃值 2020-8-12 15:45
15
0

楼主你好 我对脱壳也很感兴趣  看了你的文章感觉思路很清晰  于是就想自己试一试 没想到一开始就挂在了第一步  不晓得楼主用的环境是啥环境  我用的安卓9.0  magisk   和7.0  xposed   这两个环境下都不行  frida是最新版本的12.11.7

最后于 2020-8-12 16:49 被雨昏灰编辑 ,原因: 自己失误 现在找到原因啦
雪    币: 41
活跃值: 活跃值 (67)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
雨昏灰 活跃值 2020-8-12 16:47
16
0
找到原因了 类都是在最后一个PathClassLoader里面  你那个.zip的判断要改一下   手机环境不同的原因吧 
雪    币: 41
活跃值: 活跃值 (67)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
雨昏灰 活跃值 2020-8-14 15:47
17
0
java层调应用所有的方法  程序崩溃了  咋整  try catch也不行
雪    币: 6255
活跃值: 活跃值 (194)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tDasm 活跃值 2020-8-15 13:27
18
0
雨昏灰 java层调应用所有的方法 程序崩溃了 咋整 try catch也不行
文中标红部分:当前很多抽取壳在类的加载流程当中就已经完成了被抽取函数的修复。
没必要调用所有方法。
雪    币: 41
活跃值: 活跃值 (67)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
雨昏灰 活跃值 2020-8-17 08:32
19
0
tDasm 文中标红部分:当前很多抽取壳在类的加载流程当中就已经完成了被抽取函数的修复。 没必要调用所有方法。
明白了  谢谢~~
雪    币: 99
活跃值: 活跃值 (90)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tianhaoday 活跃值 2020-8-19 17:13
20
0
supperlitt 楼主,最后实现了没有。。纯frida,还原类抽取。。。
我也想知道结果 最终完成了没有
雪    币: 99
活跃值: 活跃值 (90)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tianhaoday 活跃值 2020-8-19 17:15
21
0
雨昏灰 找到原因了 类都是在最后一个PathClassLoader里面 你那个.zip的判断要改一下 手机环境不同的原因吧
有没有修复好的完整脚本 来耍耍
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
cecini 活跃值 2020-9-15 12:36
22
0
主动调用链的构造,这里构造是啥意思? 与深度啥关系
游客
登录 | 注册 方可回帖
返回