frida一般用法

景承教
2023-12-01

被hook的内容,需要放在Java.perform中

function hook_java() {
    Java.perform(function () {

    });
}

一般被hook的内容

var LoginActivity = Java.use("类名");
// hook指定的重载方法
LoginActivity.a.overload('java.lang.String', 'java.lang.String').implementation = function (str, str2) {
	//调用原来的函数
    var result = this.a(str, str2);     
    return result; // 返回结果
};

主动调用

//主动调用静态方法
var clazz = Java.use("类名");
 //设置静态成员变量
clazz.static_var.value = something;        
// 调用静态方法
clazz.static_func();        

// 主动调用非静态方法
Java.choose("类名", {
    onMatch: function (instance) {
        instance.nonStaticFunc(arg);
    },
    onComplete: function () {

    }
});

hook内部类

var InnerClasses = Java.use("com.abc.abc$InnerClasses");

hook 类的多个函数

var clazz = Java.use(class_name);
var all_methods = clazz.class.getDeclaredMethods();
for (var i = 0; i < all_methods.length; i++) {
    var method = (all_methods[i]);
    var methodStr = method.toString();
    var substring = methodStr.substr(methodStr.indexOf(class_name) + class_name.length + 1);
    var methodname = substring.substr(0, substring.indexOf("("));

    InnerClasses[methodname].implementation = function () {

    }

}

设置有相同函数名的成员变量的值

// 需要在变量名前加下划线
instance._same_name_bool_var.value = true;  

hook 动态加载的dex

Java.enumerateClassLoaders({
    onMatch: function (loader) {
        try {
            if (loader.findClass("类名")) {
                console.log(loader);
                Java.classFactory.loader = loader;      //切换classloader
            }
        } catch (error) {

        }

    }, onComplete: function () {

    }
});

枚举Class

Java.enumerateLoadedClasses({
    onMatch: function (name) {
        if (name.indexOf("类名") >= 0) {
            console.log(name);
        }
    }, onComplete: function () {

    }
})

frida启动

// spawn 启动app
frida -U --no-pause -f com.package.abc --l hook.js

hook构造方法

var a = Java.use("类名");
//hook 构造函数
a.$init.implementation = function (a,b,c) {
    this.$init(a,b,c);
};

打印堆栈

console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));

动态加载dex

Java.openClassFile("/data/local/tmp/dex.dex").load();

class转jar,转dex

jar -cvf something.jar something.class
dx --dex --output=something.dex something.jar
android_ndk 下的 d8 命令,直接class ——> dex

hook native func

// 知道函数名,函数符号导出
var v_funcName = Module.findExportByName("libnative.so", "funcName");
//hook模块的导出函数
Interceptor.attach(v_funcName, {
    onEnter: function (args) {
		// TODO
    }, onLeave: function (retval) {

    }
});


// 函数未导出
var base_libnative = Module.findBaseAddress("libnative.so.so");
var sub_func = base_libnative.add(funcAddr_In_so); // 加函数的偏移地址 thumb需要实际地址加1
Interceptor.attach(sub_func, {
    onEnter: function (args) {
		// TODO
    }, onLeave: function (retval) {

    }
});


// 批量hook
var module_libart = Process.findModuleByName("libart.so");
var symbols = module_libart.enumerateSymbols();     //枚举模块的符号

var addr_GetStringUTFChars = null;
var addr_FindClass = null;
var addr_GetStaticFieldID = null;
var addr_SetStaticIntField = null;

for (var i = 0; i < symbols.length; i++) {
    var name = symbols[i].name;
    if (name.indexOf("art") >= 0) {
        if ((name.indexOf("CheckJNI") == -1) && (name.indexOf("JNI") >= 0)) {
            if (name.indexOf("GetStringUTFChars") >= 0) {
                console.log(name);
                addr_GetStringUTFChars = symbols[i].address;
            } else if (name.indexOf("FindClass") >= 0) {
                console.log(name);
                addr_FindClass = symbols[i].address;
            } else if (name.indexOf("GetStaticFieldID") >= 0) {
                console.log(name);
                addr_GetStaticFieldID = symbols[i].address;
            } else if (name.indexOf("SetStaticIntField") >= 0) {
                console.log(name);
                addr_SetStaticIntField = symbols[i].address;
            }
        }
    }
}

// 将c函数转成js可以调用的样子
var addr_fopen = Module.findExportByName("libc.so", "fopen");
var addr_fputs = Module.findExportByName("libc.so", "fputs");
var addr_fclose = Module.findExportByName("libc.so", "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/reg.dat");
var open_mode = Memory.allocUtf8String("w+");
var file = fopen(filename, open_mode); // 这个

从地址获取字符串

var c_String = ptr(0x1234).readCString()

一些字符串处理

TODO

frida 的api来写文件

var file = new File("/sdcard/txt.txt", "w");
file.write("12345abcd");
file.flush();
file.close();

打印Java对象的内容

/*
打印Java对象的内容,使用gson包,使用方法如下  
参考 https://github.com/qiang/AndroidSecurityStudy
	https://bbs.pediy.com/thread-259186.htm
*/
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use("com.r0ysue.gson.Gson");
console.log(gson.$new().toJson(XXX));

// 或者
JSON.stringify()

frida中构造Java数组

var values = Java.array('int',  [100, 101, 102]);

接口interface、Java.register

// 被hook的apk中存在一个接口,frida可以使用这个接口动态的创建一个类

var beer = Java.registerClass({
    name : 'com.android.beer', // 动态创建的类的类名
    implements : [Java.use('com.android.water')], // apk中接口类的类名
    methods : {
        funcA : function(arg1,arg2){

        },
        funcB : function(arg1,arg2){
            return arg1 + arg2;
        },
    }
});

// 如何使用
console.log("beer.funcA :" , beer.$new().funcA());

Remote Procedure Call 远程调用

function invoke(){
  Java.perform(function(){
    Java.choose("",{
      onMatch:function(instance){
        instance.secret();
      },onComplete:function(){}
    })
  })
}

source = """
    rpc.exports = {
    add: function (a, b) {
        return a + b;
    },
    sub: function (a, b) {
        return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(a - b);
        }, 100);
        });
    }
    invokeFunc:invoke
    };
"""

script = session.create_script(source)  # 加载脚本
script.on('message', on_message)
script.load()
print(script.exports.add(2, 3)) # 远程调用
print(script.exports.sub(5, 3))
session.detach()

动态修改

Java.perform(function () {
    var tv_class = Java.use("android.widget.TextView");
    tv_class.setText.overload("java.lang.CharSequence").implementation = function (x) {
        var string_to_send = x.toString();
        var string_to_recv;
        send(string_to_send); // 将数据发送给kali主机的python代码
        recv(function (received_json_object) {
            string_to_recv = received_json_object.my_data
            console.log("string_to_recv: " + string_to_recv);
        }).wait(); //收到数据之后,再执行下去
        return this.setText(string_to_recv);
    }
});
import time
import frida

def my_message_handler(message, payload):
    print message
    print payload
    if message["type"] == "send":
        print message["payload"]
        data = message["payload"].split(":")[1].strip()
        print 'message:', message
        data = data.decode("base64")
        user, pw = data.split(":")
        data = ("admin" + ":" + pw).encode("base64")
        print "encoded data:", data
        script.post({"my_data": data})  # 将JSON对象发送回去
        print "Modified data sent"

device = frida.get_usb_device()
pid = device.spawn(["com.roysue.demo04"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
with open("s4.js") as f:
    script = session.create_script(f.read())
script.on("message", my_message_handler)  # 注册消息处理函数
script.load()
raw_input()

TODO
 类似资料: