首页
社区
课程
招聘
[原创]进阶Frida--Android逆向之动态加载dex Hook(三)(下篇)
发表于: 2018-7-8 14:35 38513

[原创]进阶Frida--Android逆向之动态加载dex Hook(三)(下篇)

2018-7-8 14:35
38513

上篇花了很多篇幅讲了Robust的原理,并以做题的思路去求解了这个示例ctf,其实这是一种思路的启示,当我们在不知道怎么hook动态加载的dex,jar时候,找找是否存在能够操作动态加载出来的类的方法。当然这不是重点,这篇我会重点给大家分享如何使用frida去hook DexclassLoader,怎么用反射直接调用类的方法,达到跟hook一般类一样的效果。

经过上篇文章我们对Robust的原理学习和对app的分析,我们知道Robust的其实就是在正常的使用DexClassLoader去动态加载文件,随后通过反射的方式去调用类方法或成员变量。

同时在上篇文章中,我们也知道Robust调用DexClassLoader的类是在PatchExecutor中,而调用PatchExecutor类是在一个叫runRobust的方法中,这个方法就在MainActivity中,并且在onCreate方法中调用。

现在我们明白了一点是,app动态加载dex的地方是在onCreate中,也就是说app一启动就执行了动态加载,并不是在我们点击按钮的时候。所以这个地方,我们要执行py脚本的话,需要在app执行onCreate方法之前。frida有一个功能可以为我们生成一个进程而不是将它注入到运行中的进程中,它注入到Zygote中,生成我们的进程并且等待输入。

我们可以通过-f参数选项来实现。


从上面可以看到,通过-f参数,frida会Spawned这个应用,在这个时候启动python脚本,再执行%resume命令,我们就可以在app执行onCreate方法前完成脚本的启动,这时候就能hook住onCreate中执行的一些方法。

好,我们已经知道怎么hook住onCreate中执行的方法了,现在我们就来试试,第一个目标是能够获取到动态加载的dex中的类。在这之前我们来看看,直接去hook 动态加载的类会出现什么情况。
测试js代码如下,我们尝试获取dex中的MainActivityPatch类。

完整代码:(后面的代码就只贴js_code =中的javascript代码了,因为这里面只有js_code的代码变化了)

可以清晰的看到错误信息,未找到类。

果不其然,这样去直接hook类肯定是不行的,但我们知道只要是从外部资源文件中动态加载dex,一般都是采用DexClassLoader动态加载的。学习过Java的同学应该知道,DexClassLoader动态加载的主要方法就是loadClass()。我们从Java源码上去分析一下,这里给大家一个java开发文档的查看地址:访问

我们看到DexClassLoader的构造函数有4个参数,这里没有loadClass(),我们继续查看它的父类BaseDexClassLoader。

同样BaseDexClassLoader也没有loadClass(),最终在它的父类ClassLoader中找到了loadClass()方法。

可以看到DexClassLoader加载的逻辑其实就是ClassLoader中的loadClass(),它的机制简单的了解到这里,现在我们来试试,通过这样的方式能不能hook我们想要的类。

我们先hook DexClassLoader的构造函数,看看传递进的参数值是什么。

执行看看:

我们获取到了构造函数的参数,简单看一下。

接下来,就来尝试一下获取动态加载的类。

执行看看结果。

可以看到frida报错了,从报错信息我们可以看到loadClass()有2个重载方法,我们这里需要通过overload指定我们需要Hook的重载方法才行,如果你不知道该用哪个重载方法,可以先让frida报错,然后它会把所有的重载方法抛出在错误信息中,像下面一样,这是个小技巧。

我们在来看看ClassLoader类中的两个loadClass重载方法。
loadClass(String name);

loadClass(String name, boolean resolve);

可以看到真正执行loadClass的方法是loadClass(String name, boolean resolve);,而loadClass(String name);只是简单的调用它。那我们hook哪个呢?当然是选择第一个,因为我们hook第一个,然后在其调用第二个重载方法,这不就是它原本的逻辑吗,这样理解更容易一些。下面,我们来重新构造一下js代码。

执行看看:

可以看到打印出的hookClass变量值即为我们想要hook的类,说明我们已经通过hook loadClass拿到了MainActivityPatch类。

那接下来怎么调用类方法呢,这里我们是不能直接通过(.)运算符直接调用方法的,可以看到loadClass()返回类型的是一个泛型,其中?代表任何类型,因为loadClass()也不知道要加载的类的类型,所以返回类型就采用Class<?>代表所有类型的类,所以最后返回的是一个类型为指向MainActivityPatch的Class对象

理论是这样的,但实际上却不是,我们还需要进行类型转换。这里Frida提供的一个方法处理泛型的方法Java.cast
构造代码如下:

执行看看结果:

可以清晰的看到,cast后才能调用getMethods(),没有cast则会报未定义不能调用的错误。

现在我们得到了一个类型为MainActivityPatch的Class对象,我们接下来就来看看怎么调用Joseph方法。在这之前,你需要对反射的用法有一定的了解。至于怎么用,就针对实际情况选取你认为最好的办法就行了。
当然在我多次踩坑之后,比如:

我认为还是有必要给大家提供一种比较通用的方法。
1.利用getDeclaredConstructor()获取具有指定参数列表构造函数的Constructor。

可以看到,参数是一个Class<?>...,也就是说这是一个[Ljava.lang.Object;类型的数组。我们现在要得到MainActivityPatch构造函数对象,从代码中可以知道参数是Object类型。

我们怎么来构造并传入这个数组呢。

这里要特别提出的是java.array()的用法格式

支持什么type,大家可以参看frida-java源码。在class-factory.js中就可以找到了。基本类型如下:

话题回过来,我们getDeclaredConstructor()得到了构造函数Constructor,我们现在要将它实例化,再来看看MainActivityPatch的构造函数,传递一个object对象,并将它强制转换成MainActivity类型。
那我们实例化的参数就是MainActivity对象了。

代码如下:

看看结果:

可以看到,得到的结果为:

2.我们得到了实例化的类后,第二步就是利用getDeclaredMethods(),获取本类中的所有方法,包括私有的(private、protected、默认以及public)的方法,并通过数组下标的方式获取我们想要的方法。

从getDeclaredMethods(),我们知道它返回的是一个Method数组。

看看一个完整的示例,和上面的一样,仅仅调用了getDeclaredMethods()方法。

执行看看。

可以看到,我们已经拿到了我们想要的方法。

3.接下来就是调用Method.invoke()去执行方法了,invoke方法的第一个参数是执行这个方法的对象实例,第二个参数是带入的实际值数组,返回值是Object,也既是该方法执行后的返回值。

那现在就有一个问题,第二个值是一个数据,我们需要带入实际值的数组,那这么来构造数组呢,很简单刚刚我们已经学习了Frida 中Java.array的用法。现在我们就来构造2个实际值的Integer数组。

接下来我们就可以愉快的调用Joseph方法了。

来看看最终代码,这里就不在写注释了,大家应该都能看懂了。
最终代码脚本:下载

执行后发现成功 :D

得到最终答案:

通过对这个例子的一个完整过程的学习,可以说算是掌握了Frida java中一些比较高级的用法

同时也对frida怎么hook动态加载dex的方法有了一个清晰的思路,当然frida学习路还很长......

 
 
 
 
 
 
 
 
 
 
 
 
frida -U -f app完整名
Java.perform(function(){
        console.log("test");
        Java.use("cn.chaitin.geektan.crackme.MainActivityPatch");
        console.log("test over");

});
# -*- coding: UTF-8 -*-
import frida,sys

def on_message(message, data):
    if message['type'] == 'send':
        print("[*] {0}".format(message['payload']))
    else:
        print(message)


js_code = '''
    Java.perform(function(){
        console.log("test");
        Java.use("cn.chaitin.geektan.crackme.MainActivityPatch");
        console.log("test over");

});
'''


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

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2018-7-8 14:36 被ghostmazeW编辑 ,原因:
收藏
免费 13
支持
分享
打赏 + 2.00雪花
打赏次数 1 雪花 + 2.00
 
赞赏  junkboy   +2.00 2018/07/08
最新回复 (31)
雪    币: 36
活跃值: (1061)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
mark 好文
2018-7-8 14:40
0
雪    币: 578
活跃值: (870)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
3
Mark
2018-7-8 16:29
0
雪    币: 102
活跃值: (2050)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
mark
2018-7-8 16:51
0
雪    币: 0
活跃值: (275)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
楼主用的操作系统和模拟器分别是什么?
2018-7-8 19:55
0
雪    币: 930
活跃值: (731)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
6
天路 楼主用的操作系统和模拟器分别是什么?
我写了的呢,天天模拟器,操作系统win 
2018-7-8 20:39
0
雪    币: 226
活跃值: (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
谢谢楼主无私分享,前段时间我在frida构造数组这里卡了很久
2018-7-9 09:44
0
雪    币: 215
活跃值: (372)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
文章写的很好了, 楼主Map的泛型知道怎么处理么?? 
2018-7-9 10:40
0
雪    币: 215
活跃值: (372)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
Java.openClassFile("").load() 这个也可以加载插件.
2018-7-9 11:58
0
雪    币: 297
活跃值: (265)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
10
好文支持一个
2018-7-10 09:26
0
雪    币: 54
活跃值: (705)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
学习了。。。。。。。
2018-7-10 14:01
0
雪    币: 41
活跃值: (88)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
好文好文! 楼主可以加个qq吗,我qq 294627719
2018-7-23 15:07
0
雪    币: 2735
活跃值: (2171)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
13
假如想要hook cn.chaitin.geektan.crackme.MainActivityPatch里面的方法该怎么弄?楼组有解决方案么
2018-8-15 15:27
0
雪    币: 930
活跃值: (731)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
14
Lakitu 假如想要hook cn.chaitin.geektan.crackme.MainActivityPatch里面的方法该怎么弄?楼组有解决方案么
文章举例就是hook的MainActivityPatch哦,hook它里面的joseph(int,int)方法
2018-8-20 22:47
0
雪    币: 2735
活跃值: (2171)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
15
你这个是使用invoke方法主动调用joseph,并不是hook joseph方法
2018-8-21 13:18
1
雪    币: 49
活跃值: (136)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
赞,支持一个。
2018-8-22 11:09
0
雪    币: 81
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
写得不错
2019-2-20 16:32
0
雪    币: 294
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
大佬,请教个关于frida里面反射调用的问题,package com.example.cryptutils.util;里面有个类以及它的构造函数以及获取实例是这样的:
public class CryptoUtils {
public static String TAG = "MainActivity";
private static final String STORE_FILE_NAME = "crypto";
private static final String DEFAULT_SECRETKEY_NAME = "default_secretkey_name";
private static final String KEYSTORETYPE="AndroidKeyStore";
private KeyStore keyStore;
private byte[] iv;
private static Context context;
private static CryptoUtils cryptoUtils;
private CryptoUtils(KeyStore keyStore){
this.keyStore=keyStore;
}

public static CryptoUtils getInstance(Context context){
    KeyStore keyStore;
    File file=new File(context.getFilesDir(),STORE_FILE_NAME);
    if(cryptoUtils==null){
        synchronized (CryptoUtils.class){
            if(cryptoUtils==null){
                CryptoUtils.context=context;
                keyStore=createKeyStore(file);
                initKey(keyStore,file);
                cryptoUtils=new CryptoUtils(keyStore);
            }
        }
    }

    return cryptoUtils;
}
CryptoUtils 这个类里面有一个public byte[] aesEncrypt(String plaintext)方法,我想在frida里面反射调用这个方法,主要代码如下:
var class_name1 = "com.example.cryptutils.util.CryptoUtils";
var Platform1 = Java.use(class_name1);
var clz = Java.use("java.lang.Class");
var objectclass= Java.use("java.lang.Object");
var string=Java.use("java.lang.String");
var b = Java.use("[B");
var method_aes = Platform1.class.getDeclaredMethod("aesEncrypt",Java.array('java.lang.Class', [string.class]) );

//var mainAc = Platform1.$new();

//console.log("orinin:"+mainAc);
var ConstructorParam =Java.array('java.lang.Class',[Java.use("java.security.KeyStore").class]);
var Constructor = Platform1.class.getDeclaredConstructor(ConstructorParam);
//var instance = Constructor.newInstance([Platform1.class]);
//var a = Platform1.class.getDeclaredConstructor([objectclass.class]);
//var ret = method_aes.invoke(instance,Java.array("java.lang.Object",[string.$init("hahaha").class]));
我现在就是在最后一个invoke里面的一个参数instance,构造这个instance这里卡壳了,请问一下这种情况怎么构造这个instance的呢,最好来个示例代码。
2019-3-27 20:50
0
雪    币: 294
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
在Class的getMethod方法中,怎么用js构造int.class,float.class,以及构造Integer.TYPE数组出现莫名错误。
大佬,请问你是怎么解决这个问题的呢,就这些基础变量咋个构造xxx.class
2019-4-11 19:18
0
雪    币: 1640
活跃值: (1714)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
console.log("----------------------------Methods---------------------------------");
                var func1 = hookClassCast.getDeclaredMethods();
getDeclaredMethods()运行错误怎么办?是我的frida的问题么?
2019-8-29 17:44
0
雪    币: 201
活跃值: (145)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
大佬请问一下 frida 能否实现 hook native方法 做成接口主动调用 不需要去解密so了  不知道 后面会不会更新此类文章
2019-9-5 15:06
0
雪    币: 266
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
gqm
22
您好,可以找您定做一些东西吗?
2019-9-23 23:42
0
雪    币: 758
活跃值: (78)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
tql
2019-12-12 15:22
0
雪    币: 4883
活跃值: (18890)
能力值: ( LV13,RANK:317 )
在线值:
发帖
回帖
粉丝
24
支持支持
2020-3-23 14:19
0
雪    币: 30
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
mark
2020-3-23 14:53
0
游客
登录 | 注册 方可回帖
返回
//