首页
社区
课程
招聘
[原创]Frida Hook JNI 基本应用
发表于: 2023-12-10 23:38 10608

[原创]Frida Hook JNI 基本应用

2023-12-10 23:38
10608

JNI(Java Native Interface)是 Java 提供的一种机制,用于实现 Java 程序与本地(Native)代码之间的交互。通过 JNI,Java 程序可以调用本地代码,而本地代码也可以调用 Java 程序中的方法。
不管APP应用程序自身的so文件如何混淆,系统的函数都是不变的,通常可以hook一些系统函数来定位关键代码。linker、libc.so、libdl.so、libart.so都有很多被hook的系统函数,其中libart.so中的jni函数在so文件的分析中发挥重要的作用。通过hook JNI函数,可以大体上知道so代码的逻辑。

要hook或者调用这些JNI函数,都需要先获取JNIENV * 指针变量的内存地址。

handle属性记录的就是原始的JNIEnv*的指针变量的内存地址。

比如:在so文件分析中,我们hook GetStringUTFChars ,打印他的返回值,可以验证我们的猜想。
图片描述

要得到原始的JNIEnv * 指针变量的内存地址,通过hook一些函数,他的第一个参数就是他的地址(var env = args[0];)。

先通过Java.vm.tryGetEnv()获取到frida包装后的JNIEnv对象,接着再调用frida提供的API来调用JNI函数。

图片描述

通过hook某些系统函数并打印函数栈是快捷定位关键代码的方法之一。可以通过Thread.backTrace来获取函数栈。

new NativeFunction 创建函数指针,有了这个函数指针,即可再代码中主动调用so函数,传入自定义的实参。

dlsym 函数返回值地址有可能为0。静态注册的native函数首次被调用才会经过dlsym函数,之后不再触发。JNI_OnLoad 不属于静态注册函数,只是系统在so文件加载完毕后,去获取JNI_OnLoad函数地址,使用的也是dlsym。

图片描述

再来看下IDA里面的代码:

图片描述

initSN : 0x13b0
n1 : 000013B0
n1 = initSN 可以快速锁定对应的函数

Java.vm.getEnv()
Java.vm.tryGetEnv()
 
 
function hook_jni_1(){
    Java.perform(function () {
        var getEnv = Java.vm.tryGetEnv();
        console.log(JSON.stringify(getEnv)); //{"handle":"0xd802bc00","vm":{}}
    })
}
 
// JNIEnv 的结构体地址:
getEnv.handle.readPointer()
ptr(getEnv).readPointer()
Java.vm.getEnv()
Java.vm.tryGetEnv()
 
 
function hook_jni_1(){
    Java.perform(function () {
        var getEnv = Java.vm.tryGetEnv();
        console.log(JSON.stringify(getEnv)); //{"handle":"0xd802bc00","vm":{}}
    })
}
 
// JNIEnv 的结构体地址:
getEnv.handle.readPointer()
ptr(getEnv).readPointer()
function hook_jni_2(){
    Java.perform(function () {
       var symbols = Process.getModuleByName("libart.so").enumerateSymbols();
       var addr_GetStringUTFChars = NULL;
       for (var index = 0; index < symbols.length; index++) {
        const symbols_one = symbols[index];
        if (symbols_one.name.indexOf("art") >= 0){
            if (symbols_one.name.indexOf("checkJNI") == -1 && symbols_one.name.indexOf("GetStringUTFChar")>= 0){
                console.log("GetStringUTFChar ",JSON.stringify(symbols_one));
                addr_GetStringUTFChars = symbols_one.address;
                break
            }
        }    
       }
       Interceptor.attach(addr_GetStringUTFChars,{
        onEnter:function(args){
            var env = args[0];
            var param1 = args[1];
            console.log("env :",env,"param1 ",ptr(param1).readAnsiString);
        },onLeave:function (retval) {
            console.log("addr_GetStringUTFChars retval :",ptr(retval).readCString())
        }
       })
    })
}
function hook_jni_2(){
    Java.perform(function () {
       var symbols = Process.getModuleByName("libart.so").enumerateSymbols();
       var addr_GetStringUTFChars = NULL;
       for (var index = 0; index < symbols.length; index++) {
        const symbols_one = symbols[index];
        if (symbols_one.name.indexOf("art") >= 0){
            if (symbols_one.name.indexOf("checkJNI") == -1 && symbols_one.name.indexOf("GetStringUTFChar")>= 0){
                console.log("GetStringUTFChar ",JSON.stringify(symbols_one));
                addr_GetStringUTFChars = symbols_one.address;
                break
            }
        }    
       }
       Interceptor.attach(addr_GetStringUTFChars,{
        onEnter:function(args){
            var env = args[0];
            var param1 = args[1];
            console.log("env :",env,"param1 ",ptr(param1).readAnsiString);
        },onLeave:function (retval) {
            console.log("addr_GetStringUTFChars retval :",ptr(retval).readCString())
        }
       })
    })
}
function hook_jni_3(){
    Java.perform(function () {
        var jstr = "kanxue and xibei";
        var env = Java.vm.getEnv();
        var cstr = env.newStringUtf(jstr);
        console.log("=============================================")
        var ss = env.getStringUtfChars(cstr);
        console.log("---------------------------------------------")
        console.log("ss : ",hexdump(ss))
    });
}
function hook_jni_3(){
    Java.perform(function () {
        var jstr = "kanxue and xibei";
        var env = Java.vm.getEnv();
        var cstr = env.newStringUtf(jstr);
        console.log("=============================================")
        var ss = env.getStringUtfChars(cstr);
        console.log("---------------------------------------------")
        console.log("ss : ",hexdump(ss))
    });
}
var backInfo = Thread.backtrace(this.context,Backtracer.ACCURATE);
console.log(backInfo.map(DebugSymbol.fromAddress).join("\n")+"\n");
var backInfo = Thread.backtrace(this.context,Backtracer.ACCURATE);
console.log(backInfo.map(DebugSymbol.fromAddress).join("\n")+"\n");
function write_reg_dat2() {
 
    //把C函数定义为NativeFunction来写文件
    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/reg.dat");
    var open_mode = Memory.allocUtf8String("w+");
    var file = fopen(filename, open_mode);
    console.log("fopen file:", file);
 
    var buffer = Memory.allocUtf8String("EoPAoY62@ElRD");
    var ret = fputs(buffer, file);
    console.log("fputs ret:", ret);
 
    fclose(file);
}
function write_reg_dat2() {
 
    //把C函数定义为NativeFunction来写文件
    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/reg.dat");
    var open_mode = Memory.allocUtf8String("w+");
    var file = fopen(filename, open_mode);
    console.log("fopen file:", file);
 
    var buffer = Memory.allocUtf8String("EoPAoY62@ElRD");
    var ret = fputs(buffer, file);
    console.log("fputs ret:", ret);
 
    fclose(file);
}
function hook_jni_func(){
    Java.perform(function(){
        var symbols = Process.getModuleByName("libart.so").enumerateSymbols();
        var NewStringUTF_addr = NULL;
        var GetStringUTFChars_addr = NULL;
        for (var index = 0; index < symbols.length; index++) {
            const symbol = symbols[index];
            if (symbol.name.indexOf("CheckJNI")==-1 &&  symbol.name.indexOf("NewStringUTF") >=0){
                NewStringUTF_addr = symbol.address;
            }
 
            if (symbol.name.indexOf("CheckJNI")==-1 &&  symbol.name.indexOf("GetStringUTFChars") >=0){
                GetStringUTFChars_addr = symbol.address;
            }
             
        }
 
        console.log("NewStringUTF_addr :",NewStringUTF_addr,"GetStringUTFChars_addr :",GetStringUTFChars_addr);
 
        var NewStringUTF = new NativeFunction(NewStringUTF_addr,'pointer',['pointer','pointer'])
 
        var GetStringUTFChars = new NativeFunction(GetStringUTFChars_addr,'pointer',['pointer','pointer','pointer']);
 
        var env = Java.vm.tryGetEnv().handle;
        console.log("Java.vm.tryGetEnv() ",JSON.stringify(Java.vm.tryGetEnv()));
        var string_eg_addr = Memory.allocUtf8String("xibei");
        console.log("string_ex_addr :",string_eg_addr);
        var jString = NewStringUTF(env,string_eg_addr);
        console.log("jString :",jString);
 
        var cStr = GetStringUTFChars(env,jString,ptr(0));
        console.log("cStr :",cStr.readCString());
 
    });
}
 
打印值:
                                                                                                                     
NewStringUTF_addr : 0xeff56c71 GetStringUTFChars_addr : 0xeff573e1
Java.vm.tryGetEnv()  {"handle":"0xea01bb00","vm":{}}
string_ex_addr : 0xd38e4a80
jString : 0x1
cStr : xibei
function hook_jni_func(){
    Java.perform(function(){
        var symbols = Process.getModuleByName("libart.so").enumerateSymbols();
        var NewStringUTF_addr = NULL;
        var GetStringUTFChars_addr = NULL;
        for (var index = 0; index < symbols.length; index++) {
            const symbol = symbols[index];
            if (symbol.name.indexOf("CheckJNI")==-1 &&  symbol.name.indexOf("NewStringUTF") >=0){
                NewStringUTF_addr = symbol.address;
            }
 
            if (symbol.name.indexOf("CheckJNI")==-1 &&  symbol.name.indexOf("GetStringUTFChars") >=0){

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

最后于 2023-12-11 08:33 被西贝巴巴编辑 ,原因: 快速定位中,initSN 修正地址,看错了 写成其他函数地址了。
收藏
免费 5
支持
分享
最新回复 (1)
雪    币: 3535
活跃值: (31011)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-12-11 09:39
1
游客
登录 | 注册 方可回帖
返回
//