保姆级frida的各种api的使用教程

学习目标:熟练使用frida对Java层代码进行hook

静态方法和实例方法的hook

  • 不需要管修饰符,不需要区分静态和实例方法,都是使用Java.use
Java.perform(function(){
    var money = Java.use("com.xiaojianbang.hook.Money");
    money.getInfo.implementation = function () {
        var result = this.getInfo();
        console.log("money.getInfo result: ", result)
        return result;
    }
    money.setFlag.implementation = function (a) {
        console.log("money.setFlag param: ", a);
        return this.setFlag(a);
    }
});

函数参数和返回值的修改

Java.perform(function(){
    var money = Java.use("com.xiaojianbang.hook.Money");
    money.getInfo.implementation = function () {
        var result = this.getInfo();
        console.log("money.getInfo result: ", result)
        return "这是修改后的返回值";
    }
    money.setFlag.implementation = function (a) {
        console.log("money.setFlag param: ", a);
        return this.setFlag("这是我新设置的参数");
    }
});

构造方法的hook $init

Java.perform(function(){
    var money = Java.use("com.xiaojianbang.hook.Money");
    money.$init.implementation = function (a, b) {
        console.log("money.$init param: ", a, b);
        return this.$init("美元", 200);
    }
});

对象参数的修改 $new

Java.perform(function(){
    var wallet = Java.use("com.xiaojianbang.hook.Wallet");
    var money = Java.use("com.xiaojianbang.hook.Money");
    wallet.deposit.implementation = function (a) {
        console.log("wallet.deposit param: ", a.getInfo());
        return this.deposit(money.$new("美元", 200));
    }
});

HashMap的打印

这里很奇怪,使用spawn启动时,不会输出结果,不知道为什么

Java.perform(function(){
    var utils = Java.use("com.xiaojianbang.hook.Utils");
    var stringBuilder = Java.use("java.lang.StringBuilder");
    utils.shufferMap.implementation = function (a) {
        var key = a.keySet();
        var it = key.iterator();
        var result = stringBuilder.$new();
        while(it.hasNext()){
            var keystr = it.next();
            var valuestr = a.get(keystr);
            result.append(valuestr);
        }
        console.log("utils.shufferMap param: ", result.toString());
        var result = this.shufferMap(a);
        console.log("utils.shufferMap result: ", result);
        return result;
    }
});

重载方法的hook

Java.perform(function(){
    var utils = Java.use("com.xiaojianbang.hook.Utils");
    utils.getCalc.overload('int', 'int').implementation = function (a, b) {
        console.log("utils.getCalc param: ", a, b);
        return this.getCalc(a, b);
    }
    utils.getCalc.overload('int', 'int', 'int').implementation = function (a, b, c) {
        console.log("utils.getCalc param: ", a, b, c);
        return this.getCalc(a, b, c);
    }
    utils.getCalc.overload('int', 'int', 'int', 'int').implementation = function (a, b, c, d) {
        console.log("utils.getCalc param: ", a, b, c, d);
        return this.getCalc(a, b, c, d);
    }
});

hook方法的所有重载

Java.perform(function(){
    var utils = Java.use("com.xiaojianbang.hook.Utils");
    var overloadsArr = utils.getCalc.overloads;
    for(var i =0; i<overloadsArr.length; i++){
        var overload = overloadsArr[i];
        overload.implementation = function (){
            var param ="";
            for(var j=0; j<arguments.length; j++){
                param+=arguments[j]+" ";
            }
            console.log(param);
            return this.getCalc.apply(this, arguments);
        }
    }
});

主动调用

function activeCall(){
    Java.perform(function(){
        var money = Java.use("com.xiaojianbang.hook.Money");
        //主动调用静态方法
        money.setFlag("hyq");
        //主动调用实例方法,通过创建一个对象
        var moneyObj = money.$new("hyq", 222);
        console.log(moneyObj.getInfo());
        //主动调用实例方法,通过使用Java.choose
        Java.choose("com.xiaojianbang.hook.Money",{
            onMatch:function(obj){
                console.log(obj.getInfo());
            },onComplete:function(){
                console.log("搜索完毕");
            }
        });
    });
}

函数堆栈的打印

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

获取和修改类的字段

function test(){
    Java.perform(function () {
        //静态字段,记得加上.value
        var money = Java.use("com.xiaojianbang.hook.Money");
        console.log(money.flag.value);
        money.flag.value = "wys";
        console.log(money.flag.value);
        //实例字段,通过创建新对象
        var moneyObj = money.$new("欧元", 200);
        console.log(moneyObj.currency.value);
        moneyObj.currency.value = "hyq currency";
        console.log(moneyObj.currency.value);
        //实例字段,通过使用Java.choose()
        Java.choose("com.xiaojianbang.hook.Money",{
            onMatch: function(obj){
                console.log("Java.choose: ", obj.currency.value);
                obj.currency.value = "wys currency";
                console.log("Java.choose: ", obj.currency.value);
            },onComplete: function(){
                console.log("搜索完毕");
            }
        });
        //如果字段名和方法名一样,字段名需要加下划线前缀
        Java.choose("com.xiaojianbang.hook.BankCard", {
            onMatch: function (obj) {
                console.log("Java.choose BankCard: ", obj._accountName.value);
            }, onComplete: function () {

            }
        });
    });
}

Hook内部类与匿名类

function test(){
    Java.perform(function () {
        //Hook内部类使用$
        Java.choose("com.xiaojianbang.hook.Wallet$InnerStructure", {
           onMatch: function (obj) {
               console.log(obj.bankCardsList.value);
           },onComplete:function () {
               console.log("搜索完毕");
            }
        });
        //Hook匿名类使用$1
        var money$1 = Java.use("com.xiaojianbang.app.MainActivity$1");
        money$1.getInfo.implementation = function (){
            var result = this.getInfo();
            console.log("moeny.getInfo result: ", result);
            return result;
        }
    });
}

枚举所有已加载的类

function test(){
    Java.perform(function () {
        console.log(Java.enumerateLoadedClassesSync());
    });
}

枚举类的所有方法

function test(){
    Java.perform(function () {
        var wallet = Java.use("com.xiaojianbang.hook.Wallet");
        var methods = wallet.class.getDeclaredMethods();
        var constructors = wallet.class.getDeclaredConstructors();
        var fields = wallet.class.getDeclaredFields();
        var classes = wallet.class.getDeclaredClasses();

        for(let i=0; i<methods.length; i++) {
            console.log(methods[i].getName());
        }
        console.log("=============================================");
        for(let i=0; i<constructors.length; i++) {
            console.log(constructors[i].getName());
        }
        console.log("=============================================");
        for(let i=0; i<fields.length; i++) {
            console.log(fields[i].getName());
        }
        console.log("=============================================");
        for(let i=0 ; i<classes.length; i++) {
            console.log(classes[i].getName());
            //classes[i] 这里得到的已经是类的字节码,不需要再.calss
            var Wallet$InnerStructure = classes[i].getDeclaredFields();
            for(let j=0 ; j<Wallet$InnerStructure.length;j++){
                console.log(Wallet$InnerStructure[j].getName());
            }

        }
    });
}

Hook类的所有方法

function test(){
    Java.perform(function () {
        var utils = Java.use("com.xiaojianbang.hook.Utils");
        var methods = utils.class.getDeclaredMethods();
        for(let i =0; i < methods.length; i++){
            var methodName = methods[i].getName();
            console.log(methodName);
            var overloadsArr = utils[methodName].overloads;
            for(let j =0;j < overloadsArr.length; j++){
                overloadsArr[j].implementation = function (){
                    var params = "";
                    for(let k=0; k<arguments.length; k++){
                        params += arguments[k] + " ";
                    }
                    console.log("utils." + methodName + " is called! params is: ", params);
                    return this[methodName].apply(this, arguments);
                }
            }
        }
    });
}

这一段代码这样写会报错,为什么呢?因为这样写相当于在内存中开辟了一整块空间,这些空间中的每一段分别存放了一个函数的hook代码写法,在第一遍注入时已将每一个函数的hook代码给写好放在数组里面了,而如果使用var 来定义methodName,这相当于在每一次循环时,都将之前写好的函数的hook代码又写了一遍,修改了它们原本应该使用的返回值名称,而是用let就相当于在每一次循环时,这个methodName都是独立的,不会修改到之前已经写好的函数的hook代码

所以这样写就好了

function test(){
    Java.perform(function () {
        var utils = Java.use("com.xiaojianbang.hook.Utils");
        let methods = utils.class.getDeclaredMethods();
        for(let i =0; i < methods.length; i++){
            var methodName = methods[i].getName();
            console.log(methodName);
            var overloadsArr = utils[methodName].overloads;
            for(let j =0;j < overloadsArr.length; j++){
                overloadsArr[j].implementation = function (){
                    var params = "";
                    for(let k=0; k<arguments.length; k++){
                        params += arguments[k] + " ";
                    }
                    console.log("utils." + methodName + " is called! params is: ", params);
                    return this[methodName].apply(this, arguments);
                }
            }
        }
    });
}

或者是封装成函数

Java.perform(function () {
    function hookFunc(methodName){
        console.log(methodName);
        var ovarloadsArr = utils[methodName].overloads;
        for(let i=0; i<ovarloadsArr.length; i++){
            ovarloadsArr[i].implementation = function (){
                var params = "";
                for(let j=0; j<arguments.length; j++){
                    params+=arguments[j]+" ";
                }
                console.log(params);
                return this[methodName].apply(this, arguments);
            }
        }

    }
   var utils = Java.use("com.xiaojianbang.hook.Utils");
   var methods = utils.class.getDeclaredMethods();
   for(let i=0; i<methods.length; i++) {
       var methodName = methods[i].getName();
       hookFunc(methodName);
   }
});

bksmali与smali的使用

源码:https://github.com/JesusFreke/smali

下载:https://bitbucket.org/JesusFreke/smali/downloads/

反编译dex

java -jar baksmali-2.5.2.jar d clesses.dex

回编译dex

java -jar smali-2.5.2.jar a smali(smali要改成目录名字)

frida注入dex文件

Java.openClassFile("/data/local/tmp/hyq.dex").load()
Java.perform(function () {
    Java.openClassFile("/data/local/tmp/patch.dex").load();
    var test = Java.use("com.xiaojianbang.myapplication.Test");
    var utils = Java.use("com.xiaojianbang.hook.Utils");
    utils.shufferMap.implementation = function (map) {
        var result = test.print(map);
        console.log(result);
        return result;
    }
});

frida写文件

SD卡的一些知识
在 Android 设备的 shell 环境中执行df -h命令后得到的文件系统磁盘空间使用情况的输出结果
df -h

SD卡权限需要app申请

没有申请权限,也可以通过设置给他权限

Environment.getRootDirectory().toString();
Environment.getDataDirectory().toString();
Environment.getDownloadCacheDirectory().toString();
Environment.getExternalStorageDirectory().toString();
Environment.getExternalStorageState();
getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();

SD卡也分为共有存储空间与私有存储空间

以下两个目录app都有写入权限

/data/data/xxx.xxx	给app的私有目录
/sdcard/Android/data/xxx.xxx	给app的私有目录,空间较大

得到context,为什么要得到context,因为调用getExternalFilesDir需要实例一个对象,实例这个对象需要用到context参数

var current_application = Java.use('android.app.ActivityThread').currentApplication();
var context = current_application.getApplicationContext();
Java.perform(function () {
    var current_application = Java.use('android.app.ActivityThread').currentApplication();
    var context = current_application.getApplicationContext();
    var path = Java.use("android.content.ContextWrapper").$new(context).getExternalFilesDir("Download").toString();
    console.log(path);
    var ios = new File(path + "/hyq.txt", "w");
    ios.write("hyq is very very very good!!!\n");
    ios.flush();
    ios.close();
});

Java.cast

向上转型的,不能用toString直接得到结果,比如Map、List类型的打印

Java.perform(function () {
    var utils = Java.use("com.xiaojianbang.hook.Utils");
    utils.shufferMap.implementation = function (hashMap){
        console.log("shufferMap: ", hashMap);
        return this.shufferMap(hashMap);
    }
    utils.shufferMap2.implementation = function(map){
        console.log("map: ", map);
        var result = Java.cast(map, Java.use("java.util.HashMap"));
        console.log("result: ", result);
        return this.shufferMap2(map);
    }
});

Java.array

有两个参数,第一个参数传数组元素的类型,第二个参数传数组

Java.perform(function () {
    var utils = Java.use("com.xiaojianbang.hook.Utils");
    //Stirng类型的数组
    var strArr = Java.array("Ljava.lang.String;", ["xiaojianbang", "QQ:24358757", "VX:xiaojianbang8888", "公众号:非攻code"]);
    console.log(utils.myPrint(strArr));
    //也可以直接使用
    console.log(utils.myPrint(["xiaojianbang", "QQ:24358757", "VX:xiaojianbang8888", "公众号:非攻code"]));
    //Object类型的数组,有些类型frida不会帮助我们变成对象,比如int需要我们自己处理
    var utils = Java.use("com.xiaojianbang.hook.Utils");
    var bankCard = Java.use("com.xiaojianbang.hook.BankCard");
    var bankCardObj = bankCard.$new("xiaojianbang", "123456789", "CBDA", 1, "15900000000");
    var integer = Java.use("java.lang.Integer");
    var boolean = Java.use("java.lang.Boolean");
    var objArr = Java.array("Ljava.lang.Object;", ["xiaojianbang", integer.$new(30), boolean.$new(true), bankCardObj]);
    console.log(utils.myPrint(objArr));
    //也可以直接使用
    console.log(utils.myPrint(["xiaojianbang", integer.$new(30), boolean.$new(true), bankCardObj]));
    
});

ArrayList的主动调用

Java.perform(function () {
    var arrayList = Java.use("java.util.ArrayList").$new();
    var integer = Java.use("java.lang.Integer");
    var boolean = Java.use("java.lang.Boolean");
    var bankCard = Java.use("com.xiaojianbang.hook.BankCard");
    var bankCardObj = bankCard.$new("xiaojianbang", "123456789", "CBDA", 1, "15900000000");
    arrayList.add("xiaojianbang");
    arrayList.add(integer.$new(30));
    arrayList.add(boolean.$new(true));
    arrayList.add(bankCardObj);
    var utils = Java.use("com.xiaojianbang.hook.Utils");
    console.log(utils.myPrint(arrayList));
});

让hook只在某一个指定函数内生效

Java.perform(function () {
    var mainActivity = Java.use("com.xiaojianbang.app.MainActivity");
    var stringBuilder = Java.use("java.lang.StringBuilder");
    mainActivity.generateAESKey.implementation = function (){
        console.log("mainActivity.generateAESKey is called!");
        stringBuilder.toString.implementation = function (){
            var result = this.toString();
            console.log(result);
            return result;
        }
        var result = this.generateAESKey.apply(this, arguments);
        stringBuilder.toString.implementation = null;
        return result;
    }
});

hook定位接口的实现类

Java.perform(function () {
    var classes = Java.enumerateLoadedClassesSync();
    for (var index in classes) {
        let className = classes[index];
        if(className.indexOf("com.xiaojianbang") === -1) continue;
        let clazz = Java.use(className);
        let resultArr = clazz.class.getInterfaces();
        if(resultArr.length === 0) continue;
        for (let i = 0; i < resultArr.length; i++) {
            if(resultArr[i].toString().indexOf("com.xiaojianbang.app.TestRegisterClass") !== -1){
                console.log("className: ", className);
                console.log("resultArr: ", resultArr[i]);
            }
        }
    }
});

hook定位抽象类的实现类

Java.perform(function () {
    var classes = Java.enumerateLoadedClassesSync();
    for (const index in classes) {
        let className = classes[index];
        if(className.indexOf("com.xiaojianbang") === -1) continue;
        let clazz = Java.use(className);
        let resultClass = clazz.class.getSuperclass();
        if(resultClass == null) continue;
        if(resultClass.toString().indexOf("com.xiaojianbang.app.TestAbstract") !== -1){
            console.log(className, resultClass);
        }
    }
});

frida的各种选项介绍

frida为什么会打印两次

frida检测到js文件修改,注入了两次

退出,重新注入,没有动态修改的的时候就没有问题

frida.exe的使用

frida attach

  • 包名、pid、前端进程注入

  • frida -U -n xxx -l hyq.js
    frida -U -p xxx -l hyq.js
    frida -U -F -l hyq.js
    

frida spawn

frida.exe的选项介绍

  • -U 连接远程的USB设备(-U和-H只能选择一个)
  • -H 通过ip和端口连接,可以连接多台设备(netstat -tulnp查看端口)
  • -F 附加到最前这个app
  • -l 后面指明需要加载的JS脚本
  • -o 把信息输出到指定文件中

frida连接多设备

指定frida-server监听的端口

./fsarm64 -l 0.0.0.0:9000

连接时使用-H

frida -H 10.194.0.241:9000 -f com.xiaojianbang.app -l api.js
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值