Frida API示例

Java层

hook普通方法

# -*- coding: utf-8 -*-
import frida
import sys

hook_code = """
Java.perform(function(){

    var utils = Java.use("类名路径");
    utils.方法名.implementation = function(a, b){
        
        a = 123;
        b = 456;

        var retval = this.方法名(a, b);
        console.log(a, b, retval);

        return retval;
    }
});
"""
process = frida.get_usb_device().attach('包名')
script = process.create_script(hook_code)
script.load()
sys.stdin.read()

hook多个重载方法

# -*- coding: utf-8 -*-
import frida
import sys

hook_code = """
Java.perform(function hookTest3(){
    var utils = Java.use("类名路径");
    utils.方法名.overload('重载的参数1').implementation = function(a){
        a = 888;
        var retval = this.方法名();
        console.log(a, retval);
        return retval;
    }
    utils.方法名.overload(无参).implementation = function(){
        var retval = this.方法名();
        console.log(retval);
        return retval;
    }
    utils.方法名.overload('重载的参数2').implementation = function(a){
        var retval = this.方法名(a);
        console.log(retval);
        return retval;
    }
});
"""
process = frida.get_usb_device().attach('包名')
script = process.create_script(hook_code)
script.load()
sys.stdin.read()

hook所有重载方法

与 “多个重载方法” 不同点在于写了参数判断逻辑

# -*- coding: utf-8 -*-
import frida
import sys

hook_code = """
Java.perform(function hookTest3(){
    var utils = Java.use("类名路径");
    //console.log(utils.方法名.overloads.length);
    for(var i = 0; i < utils.方法名.overloads.length; i++){
        utils.方法名.overloads[i].implementation = function(){
            //console.log(JSON.stringify(arguments));

            if(arguments.length == 0){
                return "调用了没有参数的";
            }else if(arguments.length == 1){
                if(JSON.stringify(arguments).indexOf("Money") != -1){
                    return "调用了Money参数的";
                }else{
                    return "调用了int参数的";
                }
            }

            arguments[0] = 1000;
            return this.方法名.apply(this, arguments);
        }
    }
});
"""
process = frida.get_usb_device().attach('包名')
script = process.create_script(hook_code)
script.load()
sys.stdin.read()

hook构造方法

Java.perform(function hookTest4(){
    var money = Java.use("类名路径");
    money.$init.overload('重载的参数1', '重载的参数2').implementation = function(str, num){
        console.log(str, num);
        str = "欧元";
        num = 2000;
        this.$init(str, num);
    }
});

对象实例化

hook_code = """
Java.perform(function hookTest2(){
    var utils = Java.use("utils类路径");
    var money = Java.use("money类路径");
    utils.方法名.overload('重载参数').implementation = function(a){
        a = 888;
        var retval = this.方法名(money.$new("日元", 100000));//对象实例化
        console.log(a, retval);
        return retval;
    }
});
"""

修改类的字段

hook_code = """
Java.perform(function(){
    //静态字段的修改
    var money = Java.use("money类路径");
    //console.log(JSON.stringify(money.字段名));
    money.字段名.value = "xxxxx";
    console.log(money.flag.value);

    //非静态字段的修改
    Java.choose("money类路径", {
        onMatch: function(obj){
            obj._name.value = "ouyuan"; //字段名与函数名相同
            obj.name.value = "ouyuan"; //字段名与函数名不同
            前面加个下划线
            obj.num.value = 150000;
        },
        onComplete: function(){
        }
    });
});
"""

hook内部类与匿名类

hook_code = """
Java.perform(function hookTest6(){
    Java.perform(function(){
        var innerClass = Java.use("money类路径$内部类名");
        console.log(innerClass);
        innerClass.$init.implementation = function(a, b){
            a = "nb";
            b = 888;
            return this.$init(a, b);
        }

        var xxx = Java.use("xxx类路径$smali中查看匿名类数字编号");
        console.log(xxx);
        xxx.getInfo.implementation = function(){
            return "匿名类被Hook了"
        }
    });
});
"""

枚举所有的类与类的所有方法

# -*- coding: utf-8 -*-
hook_code = """
Java.perform(function hookTest7(){
    Java.perform(function(){
        // Java.enumerateLoadedClasses({
        //     onMatch: function(name, handle){
        //         if(name.indexOf("包名") != -1){
        //             console.log(name);
        //             var clazz = Java.use(name);
        //             console.log(clazz);
        //             var methods = clazz.class.getDeclaredMethods();
        //             for(var i = 0; i < methods.length; i++){
        //                 console.log(methods[i]);
        //             }
        //         }
        //     },
        //     onComplete: function(){
        //     }
        // }); 两种写法

        var classes = Java.enumerateLoadedClassesSync();
        for(var i = 0; i < classes.length; i++){
            if(classes[i].indexOf("包名") != -1){
                console.log(classes[i]);
                var clazz = Java.use(classes[i]);
                var methods = clazz.class.getDeclaredMethods();
                for(var j = 0; j < methods.length; j++){
                    console.log(methods[j]);
                }
            }
        }
    });
});
"""

hook类的所有方法

hook_code = """
Java.perform(function hookTest8(){
    Java.perform(function(){
        var md5 = Java.use("md5类路径");
        var methods = md5.class.getDeclaredMethods();
        for(var j = 0; j < methods.length; j++){
            var methodName = methods[j].getName();
            console.log(methodName);

            for(var k = 0; k < md5[methodName].overloads.length; k++){

                md5[methodName].overloads[k].implementation = function(){
                    for(var i = 0; i < arguments.length; i++){
                        console.log(arguments[i]);
                    }
                    return this[methodName].apply(this, arguments);
                }
            }
        }
    });
});
"""

Hook动态加载的dex(Android 7以上)

hook_code = """
Java.perform(function () {
    Java.enumerateClassLoaders({
        onMatch: function (loader) {
            try {
                if (loader.loadClass("com.xiaojianbang.app.Dynamic")) {
                    Java.classFactory.loader = loader;
                    var Dynamic = Java.use("com.xiaojianbang.app.Dynamic");
                    console.log(Dynamic);
                    Dynamic.sayHello.implementation = function () {
                        return "9999999";
                    }
                }
            } catch (error) {
            }
        },
        onComplete: function () {
        }
    });
});
"""

Java特殊类型的遍历与修改

hook_code = """
Java.perform(function () {
    var ShufferMap = Java.use("com.xiaojianbang.app.ShufferMap");
    console.log(ShufferMap);
    ShufferMap.show.implementation = function (map) {
        console.log(JSON.stringify(map));
        //Java map的遍历
        var key = map.keySet();
        var it = key.iterator();
        var result = "";
        while(it.hasNext()){
            var keystr = it.next();
            var valuestr = map.get(keystr);
            result += valuestr;
        }
        console.log(result);
        // return result;

        //Java map的修改
        map.put("pass", "zygx8");
        map.put("guanwang", "www.zygx8.com");

        var retval = this.show(map);
        console.log(retval);
        return retval;
    }
});
"""

打印HashMap

console.log(JSON.stringify(arguments))
var Map = Java.use('java.util.HashMap');
var args_map = Java.cast(arguments[1], Map);
console.log(args_map.toString());

Java层主动调用

hook_code = """
Java.perform(function () {
    //静态方法的主动调用
    var rsa = Java.use("com.xiaojianbang.app.RSA");
    var str = Java.use("java.lang.String");
    var base64 = Java.use("android.util.Base64");
    var bytes = str.$new("xiaojianbang").getBytes();
    console.log(JSON.stringify(bytes));
    var retval = rsa.encrypt(bytes);
    var result = base64.encodeToString(retval, 0);
    console.log(result);
    //非静态方法的主动调用1 (新建一个对象去调用)
    var res = Java.use("com.xiaojianbang.app.Money").$new("日元", 300000).getInfo();
    console.log(res);
    var utils = Java.use("com.xiaojianbang.app.Utils");
    res = utils.$new().myPrint(["xiaojianbang", "is very good", " ", "zygx8", "is very good"]);
    console.log(res);
    //非静态方法的主动调用2 (获取已有的对象调用)
    Java.choose("com.xiaojianbang.app.Money", {
        onMatch: function (obj) {
            if (obj._name.value == "美元") {
                res = obj.getInfo();
                console.log(res);
            }
        },
        onComplete: function () {
        }
    });
});
"""

删除对象引用

$.dispose

获取参数类型

xxx.class.getType()

打印Java层函数堆栈定位关键代码

hook_code = """
Java.perform(function () {
    var MessageDigest = Java.use("java.security.MessageDigest");
    MessageDigest.digest.overload().implementation = function () {
        var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
        console.log(stack);
        return this.digest();
    }

});
"""

用frida注入dex文件

hook_code = """
Java.perform(function () {
    Java.openClassFile("/data/local/tmp/xiaojianbang.dex").load();
    var xiaojianbang = Java.use("com.xiaojianbang.test.xiaojianbang");

    var ShufferMap = Java.use("com.xiaojianbang.app.ShufferMap");
    ShufferMap.show.implementation = function (map) {
        var retval = xiaojianbang.sayHello(map);
        console.log(retval);
        return retval;
    }

});
"""

端口检测解决方案

./data/local/tmp/frida_server_arm64 -l 127.0.0.1:9999
adb forward tcp:9999 tcp:9999

frida启动前注入

frida -H 127.0.0.1:9999 -f com.xjb.cpp -l hook.js --no-pause

frida同时Hook多个设备

获取当前的连接设备
adb devices
指定连接某个设备
adb -s 127.0.0.1:5555 shell
端口转发
adb -s 127.0.0.1:5555 forward tcp:27042 tcp:27042
hook代码
# -*- coding: UTF-8 -*-
import frida, sys

jscode = """
setImmediate(function(){
    Java.perform(function () {
        var utils = Java.use("com.xiaojianbang.app.Utils");
        utils.getCalc.implementation = function(a, b){
            a = 12345;
            b = 54321;
            var retval = this.getCalc(a, b);
            console.log(a, b, retval);
            return retval;
        }
	});
});
"""


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


# rdev = frida.get_device_manager().enumerate_devices()
rdev = frida.enumerate_devices()
print(rdev)
rdev = rdev[int(sys.argv[1])]
process = rdev.attach('com.xiaojianbang.app')  # pid
script = process.create_script(jscode)
script.on('message', message)
script.load()
sys.stdin.read()

so层

枚举导入导出表(ELF即so文件)

枚举导入表
var imports = Module.enumerateImports("libxiaojianbang.so");
for(var i = 0; i < imports.length; i++){
    if(imports[i].name == "strncat"){
        console.log(JSON.stringify(imports[i]));
        console.log(imports[i].address);
    }
}
枚举导出表
var exports = Module.enumerateExports("libxiaojianbang.so");
for(var i = 0; i < exports.length; i++){
    //if(exports[i].name == "strncat"){
        console.log(JSON.stringify(exports[i]));
    //}
}

hook导出函数

hook_code = """
Java.perform(function hookTest2() {
    var helloAddr = Module.findExportByName("libxiaojianbang.so", "Java_com_xiaojianbang_app_NativeHelper_add");
    console.log(helloAddr);
    if (helloAddr != null) {
        Interceptor.attach(helloAddr, {
            onEnter: function (args) {
                //args参数数组
                console.log(args[0]);
                console.log(args[1]);
                console.log(args[2]);
                console.log(args[3]);
                console.log(args[4].toInt32());
            },
            onLeave: function (retval) {
                //retval函数返回值
                console.log(retval);
                console.log("retval", retval.toInt32());
            }
        });
    }
});
"""

函数地址计算

function hookTest14(){
    var soAddr = Module.findBaseAddress("libxiaojianbang.so");
    console.log(soAddr);
    var funcAddr = soAddr.add(0x23F4);
    console.log(funcAddr);
}

Hook未导出函数

function hookTest14(){
    var soAddr = Module.findBaseAddress("libxiaojianbang.so");
    console.log(soAddr);
    var funcAddr = soAddr.add(0x23F4);
    console.log(funcAddr);

    if(funcAddr != null){
        Interceptor.attach(funcAddr,{
            onEnter: function(args){

            },
            onLeave: function(retval){
                console.log(hexdump(retval));
            }
        });
     }
}

获取指针参数返回值

function hookTest5(){
    var soAddr = Module.findBaseAddress("libxiaojianbang.so");
    console.log(soAddr);
    var sub_930 = soAddr.add(0x930); //函数地址计算 thumb+1 ARM不加
    console.log(sub_930);

     var sub_208C = soAddr.add(0x208C); //函数地址计算 thumb+1 ARM不加
     console.log(sub_208C);
     if(sub_208C != null){
        Interceptor.attach(sub_208C,{
            onEnter: function(args){
                this.args1 = args[1];
            },
            onLeave: function(retval){
                console.log(hexdump(this.args1));
            }
        });
     }
}

Hook_dlopen

/data/app/com.xxx.app-l/lib/arm64/xxxxxx.so
function hookTest6(){
    var dlopen = Module.findExportByName(null, "dlopen");
    console.log(dlopen);
    if(dlopen != null){
        Interceptor.attach(dlopen,{
            onEnter: function(args){
                var soName = args[0].readCString();
                console.log(soName);
                if(soName.indexOf("libxiaojianbang.so") != -1){
                    this.hook = true;
                }
            },
            onLeave: function(retval){
                if(this.hook) { hookTest5() };
            }
        });
    }

    var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
    console.log(android_dlopen_ext);
    if(android_dlopen_ext != null){
        Interceptor.attach(android_dlopen_ext,{
            onEnter: function(args){
                var soName = args[0].readCString();
                console.log(soName);
                if(soName.indexOf("libxiaojianbang.so") != -1){
                    this.hook = true;
                }
            },
            onLeave: function(retval){
                if(this.hook) { hookTest5() };
            }
        });
    }

}

内存读写

function hookTest7(){
    var soAddr = Module.findBaseAddress("libxiaojianbang.so");
    console.log(soAddr);
    if(soAddr != null){
        //console.log(soAddr.add(0x2C00).readCString());
        //console.log(hexdump(soAddr.add(0x2C00)));  //读取指定地址的字符串

        //var strByte = soAddr.add(0x2C00).readByteArray(16); //读内存
        //console.log(strByte);
        
        //soAddr.add(0x2C00).writeByteArray(stringToBytes("xiaojianbang")); //写内存
        //console.log(hexdump(soAddr.add(0x2C00)));  //dump指定内存

        //var bytes = Module.readByteArray(soAddr.add(0x2C00), 16);
        //console.log(bytes);

    }
}

主动调用JNI函数

function hookTest8(){
    var funcAddr = Module.findExportByName("libxiaojianbang.so", "Java_com_xiaojianbang_app_NativeHelper_helloFromC");
    console.log(funcAddr);
    if(funcAddr != null){
        Interceptor.attach(funcAddr,{
            onEnter: function(args){

            },
            onLeave: function(retval){
                var env = Java.vm.tryGetEnv();
                var jstr = env.newStringUtf("www.zygx8.com");  //主动调用jni函数 cstr转jstr
                retval.replace(jstr);
                var cstr = env.getStringUtfChars(jstr); //主动调用 jstr转cstr
                console.log(cstr.readCString());
                console.log(hexdump(cstr));
            }
        });
    }
}

jni函数Hook(计算地址方式)

function hookTest9(){
    Java.perform(function(){
        //console.log(JSON.stringify(Java.vm.tryGetEnv()));
        var envAddr = ptr(Java.vm.tryGetEnv().handle).readPointer();
        var newStringUtfAddr = envAddr.add(0x538).readPointer();
        var registerNativesAddr = envAddr.add(1720).readPointer();
        console.log("newStringUtfAddr", newStringUtfAddr);
        console.log("registerNativesAddr", registerNativesAddr)
        if(newStringUtfAddr != null){
            Interceptor.attach(newStringUtfAddr,{
                onEnter: function(args){
                    console.log(args[1].readCString());
                    //args[1] = "xiaojianbang is very good!";
                },
                onLeave: function(retval){

                }
            });
        }
        if(registerNativesAddr != null){     //Hook registerNatives获取动态注册的函数地址
            Interceptor.attach(registerNativesAddr,{
                onEnter: function(args){
                    console.log(args[2].readPointer().readCString());
                    console.log(args[2].add(Process.pointerSize).readPointer().readCString());
                    console.log(args[2].add(Process.pointerSize * 2).readPointer());
                    console.log(hexdump(args[2]));
                    console.log("sub_289C", Module.findBaseAddress("libxiaojianbang.so").add(0x289C));
                },
                onLeave: function(retval){

                }
            });
        }

    });
}

jni函数Hook(libart.so)

function hookTest10(){
    var artSym = Module.enumerateSymbols("libart.so");
    var NewStringUTFAddr = null;
    for(var i = 0; i < artSym.length; i++){
        if(artSym[i].name.indexOf("CheckJNI") == -1 && artSym[i].name.indexOf("NewStringUTF") != -1){
            console.log(JSON.stringify(artSym[i]));
            NewStringUTFAddr = artSym[i].address;
        }
    };

    if(NewStringUTFAddr != null){
        Interceptor.attach(NewStringUTFAddr,{
            onEnter: function(args){
                console.log(args[1].readCString());
            },
            onLeave: function(retval){

            }
        });
    }

}

so层堆栈_快速定位函数注册在哪个so


so层函数主动调用

function hookTest11(){
    Java.perform(function(){
        var funcAddr = Module.findBaseAddress("libxiaojianbang.so").add(0x23F4);
        var func = new NativeFunction(funcAddr, "pointer", ['pointer', 'pointer']);
        var env = Java.vm.tryGetEnv();
        console.log("env: ", JSON.stringify(env));
        if(env != null){
            var jstr = env.newStringUtf("xiaojianbang is very good!!!");
            //console.log("jstr: ", hexdump(jstr));
            var cstr = func(env, jstr);
            console.log(cstr.readCString());
            console.log(hexdump(cstr));
        }
    });
}

frida读写文件

//frida API 读写文件
function hookTest12(){
    var ios = new File("/sdcard/xiaojianbang.txt", "w");
    ios.write("xiaojianbang is very good!!!\n");
    ios.flush();
    ios.close();
}
//Hook libc 读写文件
function hookTest13() {

    var addr_fopen = Module.findExportByName("libc.so", "fopen");
    var addr_fputs = Module.findExportByName("libc.so", "fputs");
    var addr_fclose = Module.findExportByName("libc.so", "fclose");

    console.log("addr_fopen:", addr_fopen, "addr_fputs:", addr_fputs, "addr_fclose:", addr_fclose);
    var fopen = new NativeFunction(addr_fopen, "pointer", ["pointer", "pointer"]);
    var fputs = new NativeFunction(addr_fputs, "int", ["pointer", "pointer"]);
    var fclose = new NativeFunction(addr_fclose, "int", ["pointer"]);

    var filename = Memory.allocUtf8String("/sdcard/xiaojianbang.txt");
    var open_mode = Memory.allocUtf8String("w");
    var file = fopen(filename, open_mode);
    console.log("fopen:", file);

    var buffer = Memory.allocUtf8String("zygxb\n");
    var retval = fputs(buffer, file);
    console.log("fputs:", retval);

    fclose(file);

}