Frida-API之Java层Hook

本文详细介绍了如何使用FRIDA在Java中进行动态Hook、方法修改、类操作、枚举和类加载器管理等技术,包括Java.available检测、Android版本获取、Java.perform调用、类实例化与Hook方法、动态加载Dex等关键操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文章

FRIDA-API使用篇:Java、Interceptor、NativePointer(Function/Callback)使用方法及示例-安全客 - 安全资讯平台 (anquanke.com)

JavaScript API | Frida • A world-class dynamic instrumentation toolkit

1、Java.available

判断当前进程是否加载了JavaVM,DalvikART虚拟机

function frida_Java() {
    Java.perform(function () {
        if(Java.available)
        {
            console.log("hello java vm");
        }else{
            console.log("error");
        }
    });
}       
setImmediate(frida_Java);//setImmediate(回调方法, 参数1,参数2,...) 异步任务,立即执行

输出如下:
hello java vm

2、Java.androidVersion

显示android系统版本号

function frida_Java() {
    Java.perform(function () {
        if(Java.available)
        {
            console.log("",Java.androidVersion);
        }else{
            console.log("error");
        }
    });
}       
setImmediate(frida_Java);

输出如下:
10

3、附加调用Java.perform

主要用于当前线程附加到Java VM并且调用fn方法它也有一个好兄弟:Java.performNow(fn)

4、获取类Java.use

  1. 构造实例:$new
  2. 实例的一些属性
    • .$class == .getClass()
    • .$className == .getClass().getName()
    • .$superClass == .getClass().getSuperclass()
    • .$dispose 回收类

5、Hook静态方法和实例方法、修改函数参数和返回值

let str=Java.use("java.lang.String");
str.toString.implementation=function () {
    console.log("HooktoString")
    return "修改值"
}

6、Hook构造方法:$init

Java.use("java.io.File").$init.overload("java.lang.String").implementation = function (path) {
    console.log("",path)
    return this.$init(path);
}

7、Hook重载方法

  1. overload

    Java.use(className).方法名.overload(参数类型1,…).implementation

  2. overloads

    Java.use(className).方法名.overloads返回重载函数数组

8、扫描实例类Java.choose

用法Java.choose(“全类名”,回调对象),在堆上查找实例化的对象

function frida_Java(className) {
    Java.perform(function () {
        Java.choose(className,{
            onMatch:function (instance) {
                console.log("搜索到实例对象:",instance);
                return instance;
            },
            onComplete:function () {
                console.log("内存搜索完毕!");
            }
        });
    });
}
setImmediate(frida_Java,"com.android.settingslib.utils.PowerUtil");

9、主动调用

  1. 静态方法:直接调用
  2. 实例方法
    • 使用$new新建实例调用方法
    • 使用Java.choose在内存中寻找实例,使用找到的实例调用方法

10、获取和修改类的字段

设置/获取字段的值需要"字段.value"

  1. 静态字段:直接获取/修改
  2. 实例字段
    • 使用$new新建实例获取/修改字段
    • 使用Java.choose在内存中寻找实例,使用找到的实例获取/修改字段

11、Hook内部类与匿名类

  1. 内部类
    • Java.use(“全类名$内部类名”)
    • Java.choose(“全类名$内部类名”,JSON对象)
  2. 匿名内部类
    • Java.use(’全类名$1‘)
    • Java.choose(“全类名$1”,JSON对象)

12、Hook枚举类

  1. Java.use(‘枚举类路径’)

    枚举类.values():返回枚举类中所有的值

    let protocol=Java.use('okhttp3.Protolcol')
    console.sole(protocol.values())
    
  2. Java.choose(“枚举类路径”,JSON对象)

    枚举对象.ordinal():返回该枚举常量的索引

    Java.choose("okhttp3.Protolcol", {
        //枚举时调用
        onMatch:function(instance){
            //打印实例
            console.log(instance.ordinal());
        },
        //枚举完成后调用
        onComplete:function() {
            console.log("end")
        }});
    

13、枚举所有类

  1. Java.enumerateLoadedClassesSync()

    枚举当前加载的所有类,并返回string数组

    console.log(Java.enumerateLoadedClassesSync().join("\n"));
    
  2. Java.enumerateLoadedClasses(callbacks)

    枚举当前加载的所有类信息,它有一个回调函数分别是onMatch(每找到一次,执行一次)、onComplete(只执行一次,在搜索完成内存后)函数

    function frida_Java() {
        Java.perform(function () {
            //枚举当前加载的所有类
            Java.enumerateLoadedClasses({
                //每一次回调此函数时其参数className就是类的信息
                onMatch: function (className)
                {
                    //输出类字符串
                    console.log("",className);
                },
                //枚举完毕所有类之后的回调函数
                onComplete: function ()
                {
                    //输出类字符串
                    console.log("输出完毕");
                }
            });
        });
    }
    setImmediate(frida_Java);
    

14、枚举类的所有方法

通过Java反射

function frida_Java(className) {
    Java.perform(function () {
        let cn=Java.use(className);
        let methods=cn.class.getDeclaredMethods();
        for (let i = 0; i < methods.length; i++) {//循环遍历所有方法
            let methodName = methods[i].getName();
            console.log("",methodName)
        }
    });
}
setImmediate(frida_Java,"com.android.settingslib.utils.PowerUtil");

15、Hook类的所有方法

function frida_Java(className) {
    Java.perform(function () {
        let cn=Java.use(className);
        let methods=cn.class.getDeclaredMethods();//cn.class:相当于Object.class   获取所有方法
        for (let i = 0; i < methods.length; i++){//循环遍历所有方法
            let methodName=methods[i].getName();//获取方法名
            let loads=cn[methodName].overloads;//获取该方法的所有重载
            for (let j = 0; j < loads.length; j++) {//遍历该方法的所有重载
                loads[j].implementation=function () {//Hook方法
                    let result=this[methodName].apply(this,arguments);
                    console.log('参数:'+JSON.stringify(arguments));
                    console.log('返回值:'+JSON.stringify(result));
                    return result;
                }
            }
        }
    });
}
setImmediate(frida_Java,"com.android.settingslib.utils.PowerUtil");

16、枚举所有类加载器

  1. Java.enumerateClassLoadersSync()

    枚举所有的ClassLoader,并返回Wrapper数组

    let loaders=Java.enumerateClassLoadersSync();
    for (let i = 0; i < loaders.length; i++) {
        try {
            Java.classFactory.loader = classloader;
            //Hook代码
        }catch (e) {
        }
    }
    
  2. 替换默认ClassLoader

    Java.classFactory.loader = classloader;
    
  3. Java.enumerateClassLoaders(callbacks)

    Java VM中存在的类加载器,它有一个回调函数分别是onMatch(每找到一次,执行一次)、onComplete(只执行一次,在搜索完成内存后)函数

    function frida_Java() {
        Java.perform(function () {
            Java.enumerateClassLoaders({
                onMatch: function (classloader) {
                    console.log(JSON.stringify(classloader));
                    try {
                        //替换默认classLoader
                        Java.classFactory.loader = classloader;
                        //Hook代码
                    } catch (e) {
                    }
                },
                onComplete: function () {
                    console.log("内存搜索完毕!");
                }
            });
        });
    }
    setImmediate(frida_Java);
    

17、类型转换器Java.cast

Java.cast(handle, klass),handle即对象实例,klass是要强转成的句柄,从Java.use获取。

//此类包装器还具有用于获取其类的包装器的类属性,以及用于获取其类名的字符串表示的$className属性,通常在拦截so层时会使用此函数将jstring、jarray等等转换之后查看其值。
Java.cast(map,Java.use("java.util.HashMap"))//将map实例对象强转成HashMap对象

18、定义任意数组类型Java.array

使用Java.array(“类型”,[实例对象,实例对象])

function frida_Java() {
    Java.perform(function () {
        let name=Java.use('java.lang.String').$new('Jack')
        let age=Java.use('java.lang.Integer').$new(18)
        Java.array('java.lang.Object',[name,age])
        /**
         * 可省略描述符 [name,age] == Java.array('java.lang.Object',[name,age])
         * 可变参数本质上就是数组,按数组处理即可
         * 基本数据类型数据要使用其包装类构造 Java.use('java.lang.Integer').$new(18)
         * 类型可以是"java.lang.Object",也可以是"Ljava/lang/Object;"
         */
    });
}
setImmediate(frida_Java);

19、注册类Java.registerClass(spec)

  1. Java.registerClass:创建一个新的Java类并返回一个包装器

    • name:指定类名称的字符串。
    • superClass:(可选)父类。要从 java.lang.Object 继承的省略。
    • implements:(可选)由此类实现的接口数组。
    • fields:(可选)对象,指定要公开的每个字段的名称和类型。
    • methods:(可选)对象,指定要实现的方法。
    function frida_Java() {
        Java.perform(function () {
            //注册一个目标进程中的类,返回的是一个类对象
            var r0ysue = Java.registerClass({
                name: 'com.roysue.roysueapplication',
                implements:[Java.use('java.lang.Runnable')],
                methods:{
                    run:function () {
                        console.log('Thread Starting...')
                    }
                }
            });
            Java.use('java.lang.Thread').$new(r0ysue.$new()).start()//开启一个线程
        });
    }
    
    setImmediate(frida_Java);
    

20、注入dex

使用Java.openClassFile(“dex路径”).load()

//加载Dex文件,相当于new DexClassLoader(...)
Java.openClassFile("/data/local/tmp/classes.dex").load()

21、写文件

function writeToFile(filePath,data) {
    Java.perform(function () {
        let ios=new File(filePath,'w');
        ios.write(data);
        ios.flush();
        ios.close();
    });
}

22、全局上下文环境获取

function getApplicationContext() {
    //获取Application实例对象
    let activityThread=Java.use("android.app.ActivityThread");
    let application=activityThread.currentApplication();
    //获取context
    // let context=application.getBaseContext();
    let context=application.getApplicationContext();
    return context;
}

23、打印函数堆栈

function showStacks() {
    console.log(
        Java.use("android.util.Log")
            .getStackTraceString(
                Java.use("java.lang.Throwable").$new()
            )
    );
}

24、Hook只在指定函数内生效

  1. 步骤

    1. Hook指定函数

    2. 在指定函数内Hook要生效的函数

    3. 在指定函数结束前,将要生效的函数Hook赋值null

      类名.方法名.implementation=null

  2. 代码

    function makeHookMethodInSpecifiedFunction(specifiedClass,specifiedMethod,className,methodName) {
        Java.perform(function () {
            //Hook指定函数
            let specifiedClazz=Java.use(specifiedClass);
            specifiedClazz[specifiedMethod].implementation=function () {
                console.log("指定的Hook函数生效");
                //Hook 要Hook的函数
                let clazz=Java.use(className);
                clazz[methodName].implementation=function () {
                    console.log("要Hook的函数生效");
                    return this[methodName](arguments);
                }
                //关闭要Hook的函数
                clazz[methodName].implementation=null;
                return this[specifiedMethod](arguments);
            }
        });
    }
    

25、Hook定位接口/抽象类的实现类

  1. 定位接口的实现类

    1. 枚举所有类
    2. 获取类的所有接口
    3. 遍历接口过滤符合条件的类
  2. 定位抽象类的实现类

    1. 枚举所有类
    2. 获取类的父类
    3. 遍历父类过滤符合条件的类
  3. 代码

    //Hook定位接口/抽象类的实现类:className是接口全路径或抽象类全路径   type:0表示接口、1表示抽象类
    function HookLocateInterfaceOrAbstract(superClassName,type) {
        Java.perform(function () {
            if (type===0){//接口
                try {
                    //1.遍历类
                    let classes=Java.enumerateLoadedClassesSync();
                    for (let i = 0; i < classes.length; i++) {
                        //2.获取类的所有接口
                        let name=classes[i];
                        let clazz=Java.use(name);
                        let interfaces=clazz.class.getInterfaces();
                        //3.遍历接口
                        for (let j = 0; j < interfaces.length; j++) {
                            //4.获取接口类名
                            let interClass=interfaces[j];
                            let interName=interClass.getName();
                            //5.过滤
                            if (interName==superClassName){
                                //输出实现类名
                                console.log(clazz.class.getName());
                            }
                        }
                    }
                }catch (e) {
                }
            }else if (type===1){//抽象类
                //1.遍历类
                Java.enumerateLoadedClasses({
                    onMatch:function (className) {
                        try {
                            //2.获取类的父类
                            let clazz=Java.use(className);
                            let superClass=clazz.class.getSuperclass();
                            //3.获取父类名
                            let superName=superClass.getName();
                            //4.过滤
                            if (superName==superClassName){
                                //输出实现类名
                                console.log(className);
                            }
                        }catch (e){
                        }
                    },
                    onComplete:function () {
                        console.log("内存搜索完毕!");
                    }
                });
            }
        });
    }
    

26、Hook动态加载的Dex

  1. Java.enumerateClassLoaders

    onMatch:function (classloader) {
        try {
            Java.classFactory.loader=classloader;
            //Hook 代码
        }catch (e) {
        }
    }
    
  2. Java.enumerateClassLoadersSync()

    for (let i = 0; i < loaders.length; i++) {
        try {
            Java.classFactory.loader = classloader;
            //Hook代码
        }catch (e) {
        }
    }
    
  3. Hook DexClassLoader.loadClass

    function HookDexClassLoaderLoadClass() {
        Java.perform(function () {
            let loader=Java.use("dalvik.system.DexClassLoader");
            loader.loadClass.overload("java.lang.String").implementation=function (className) {
                try {
                    Java.classFactory.loader=this;
                    //Hook代码   
                }catch (e) {
                }
                return this.loadClass(className);
            }
        });
    }
    

27、注意事项

  • Java类型对象调用Java方法,js类型对象调用js方法

  • frida会自动处理Java和js字符串的相互转换

  • Hook属性字段时,设置/获取字段的值需要"字段.value"

  • 定义变量时,尽量使用let而非var

    var是全局变量,在遍历Hook函数时,很容易导致最后返回方法时,方法名与变量名对不上,导致Hook出错

  • Hook函数中的"this"指的是Java.use获取的对象

  • this.方法名.apply(this,arguments)

    遍历重载函数时,可用此返回

  • Java.use获得的只是句柄,若需要对象,则使用句柄.$new()。若需要class字节码,则使用句柄.class

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值