美文网首页
DDCTF2018 Hello Baby Dex

DDCTF2018 Hello Baby Dex

作者: HAPPYers | 来源:发表于2019-07-23 22:14 被阅读0次

    DDCTF2018的Android题目 Hello Baby Dex. 题解有很多,这里用的是frida的hook的方法.

    POINT

    • Robust热修复框架原理
    • Frida的各种数据格式的转换
    • DexClassLoader loadclass动态加载

    LINK

    Description

    具体的分析在上面的两个看雪论坛的链接中有,而且讲得很详细。具体的过程就不赘述了.这里就是在自己复现的时候总结一下两种方法,还有遇到的一些问题吧。
    另外,这道题目可以通过动态调试断在字符串比较判断的地方,也可以获取flag,这里费劲心思用两种方法或许是为了掌握一些hook动态加载的class的方法吧。
    (建议先看完上面两篇的分析再看看下面的PS即可)

    方法1 HOOK Robust中的invokeReflectMethod

    Robust中,通过反射得到类的实例及方法,最终通过invokeReflectMethod代入参数执行方法。也就是说,新动态加载进来的类,它的函数的执行依赖于原本加载好的EnhancedRobustUtils类中的invokeReflectMethod,通过给这个函数传入实例化对象和函数参数数组来执行对应的函数(也就是依附于EnhancedRobustUtils) .
    这里我们发现这个EnhancedRobustUtils 是Robust自带的类,并不是动态加载的。这样我们就能很方便地hook了。
    hookinvokeReflectMethod,对传入的函数名参数检查, hook出Joseph函数的返回结果

    js_code='''
    Java.perform(function(){
            //get EnhancedRobustUtils
            var robust = Java.use("com.meituan.robust.utils.EnhancedRobustUtils");
            //hook invokeReflectMethod of EnhancedRobustUtils
            robust.invokeReflectMethod.implementation = function(v1,v2,v3,v4,v5){
            //get Joseph,equals result
                var result = this.invokeReflectMethod(v1,v2,v3,v4,v5);
                if(v1=="Joseph"){
                    console.log("functionName:"+v1);
                    console.log("functionArg3:"+v3);
                    console.log("functionArg4:"+v4);
                    send(v4);
                    console.log("return:"+result);
                    console.log("-----------------------------------------------------")
                }
     
                else if(v1=="equals"){
                    console.log("functionName:"+v1);
                    console.log("functionArg3:"+v3);
                    console.log("functionArg4:"+v4);
                    send(v4);
                    console.log("return:"+result);
                }
                return result;
            }
    });
    '''
    

    运行:

    F:\Python3\python.exe E:/PythonProject/frida_test.py
    functionName:Joseph
    functionArg3:5,6
    functionArg4:int,int
    [*] [{'$handle': '0x100bce', '$weakRef': 47}, {'$handle': '0x100bd2', '$weakRef': 48}]
    return:29360128
    -----------------------------------------------------
    functionName:Joseph
    functionArg3:7,8
    functionArg4:int,int
    [*] [{'$handle': '0x100f56', '$weakRef': 275}, {'$handle': '0x100f5a', '$weakRef': 276}]
    return:29362176
    -----------------------------------------------------
    functionName:equals
    functionArg3:DDCTF{2936012829362176}
    functionArg4:class java.lang.Object
    [*] [{'$handle': '0x1012f2', '$weakRef': 510}]
    return:false
    

    方法一由于实质上还是hook本身加载好的EnhancedRobustUtils类,所以稳定性很好.

    方法2 HOOK dexclassLoader

    DexClassLoader可以用于加载任意路径的zip,jar或者apk文件,也是进行安卓动态加载的基础.

    通过hook dexclassLoader中的loadClass方法
    我们对传入loadClass的名字参数进行过滤检测,如果是待加载的目标classcn.chaitin.geektan.crackme.MainActivityPatch,我们获取loadclass后的返回值,一个Class<?>类型的变量,我们cast才能获得这个类。
    注意,这里记得cast,因为Class<?>代表所有类型的类,我们使用frida提供的cast的方法Java.cast

    var ClassUse = Java.use("java.lang.Class");
    dexclassLoader.loadClass.overload('java.lang.String').implementation = function(name){
                ...
                var hookClass= this.loadClass(name,false);
                var hookClassCast = Java.cast(hookClass,ClassUse);
    

    然后我们要用到反射机制。(一篇构造器反射机制的笔记)
    总体思路是,我们通过一个已经拿到的对象获取其类的构造器,然后通过这个构造器去实例化出我们想要的对象。这其中,最关键的就是构造器的参数的传递。
    frida中通过java.array()来创建数组。格式如下

    Java.array('type',[value1,value2,....]);
    

    其中type的类型(官方源码)有

    1. Z -- boolean
    2. B -- byte
    3. C -- char
    4. S -- short
    5. I --  int
    6. J -- long
    7. F -- float
    8. D  -- double
    9. V -- void
    

    这里写的语法与smali类似,例如

    var objectclass= Java.use("java.lang.Object");
    var ConstructorParam =Java.array('Ljava.lang.Object;',[objectclass.class]);
    

    首先我们要通过对象获取构造器,这个函数的原型是

    public Constructor<T> getDeclaredtConstructor(Class<?>... parameterTypes)
    

    先构造好参数数组,然后传入给hookClassCast去拿到对应参数的类构造器

    var objectclass= Java.use("java.lang.Object");
    var ConstructorParam =Java.array('Ljava.lang.Object;',[objectclass.class]);
    var Constructor = hookClassCast.getDeclaredConstructor(ConstructorParam);
    

    然后用这个构造器的构造方法(按照之前的构造参数)去创建对象instance

    var instance = Constructor.newInstance([mainAc]);
    

    在后面的invoke中, 我们需要两个参数,一个是执行这个方法的实例对象(此处即instance ),一个是参数数组(辨识调用哪个函数的)。返回值是Object。
    然后我们手动调用Joseph方法即可

    var num1 = Integerclass.$new(5);
    var num2 = Integerclass.$new(6);
    var numArr1 = Java.array('Ljava.lang.Object;',[num1,num2]);
    var rtn1 = hookClassCast.getDeclaredMethods()[0].invoke(instance,numArr1);
    

    PS1

    文中通过

    frida -U -f yourappname
    

    来spawn(即创建进程后挂起)一个app。
    这样每次调试很麻烦,还要在控制台手动%resume。再去开python脚本。
    我们可以把脚本中的attach部分改为如下:

    device = frida.get_usb_device()
    pid = device.spawn(["cn.chaitin.geektan.crackme"])
    session = device.attach(pid)
    script = session.create_script(js_code)
    script.on('message', on_message)
    script.load()
    device.resume(pid)
    sys.stdin.read()
    

    这样就能通过脚本直接启动app并挂起恢复,调试能方便些。

    PS2

    方法二的脚本在某些(不少)Android版本中失去效果,有的Android(测试为5.1.1)中无法hook到dexclassLoader中的loadclass,有的Android(夜神的7.0版本)无法invoke成功(invoke后一直获取不到返回结果)。还没弄清原因😭😭😭.

    相关文章

      网友评论

          本文标题:DDCTF2018 Hello Baby Dex

          本文链接:https://www.haomeiwen.com/subject/dlxnlctx.html