DDCTF2018 Hello Baby Dex

2019-07-23  本文已影响0人  HAPPYers

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

POINT

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后一直获取不到返回结果)。还没弄清原因😭😭😭.

上一篇 下一篇

猜你喜欢

热点阅读