前言
Frida的native一般用于系统库的拦截hook,这些库的函数都是导出的所以方便hook。jni的类型hook这里官方没有说明,网上也没啥资料。我这里也稍微概述一下.
代码
Hook 系统函数
下面是打开一个文件的操作
extern "C" JNIEXPORT void JNICALL Java_com_shark_fridanative_MainActivity_testOpen
(JNIEnv *, jobject) {
FILE *fp;
char *s = (char*)malloc(30* sizeof(char));
fp = fopen("/data/local/tmp/test.txt", "r");
if (fp == NULL){
LOGI("File open error");
return;
}
fgets(s, 24, fp);
LOGI("open file content is %s", s);
free(s);
fclose(fp);
return;
}
因为fopen最后调用的还是libc.so的open函数。
我们使用下面的代码hook open函数
Java.perform(function () {
Interceptor.attach(Module.findExportByName("libc.so", "open"), {
onEnter: function (args) {
send("open called! args[0]:", Memory.readByteArray(args[0], 24));
},
onLeave: function (retval) {
}
});
});
Interceptor.attach是对指定地址的函数进行拦截操作,它的第一个参数就是拦截的地址,我们可以使用Module.findExportByName获取指定so库的函数地址。
第二个参数是两个回调函数onEnter是函数进入的时候执行onLeave是离开的时候执行。args是一个参数数组.
open的函数声明如下
int open(const char*pathname,intflags);
所以我们想打印这哥文件的文件名称,就需要对char*进行操作。这里frida提供了Memory进行内存字节的操作。使用readByteArray重第一个参数读取指定数量的字节数据。最后打印出来,其结果如下
message: {'type': 'send', 'payload': 'open called! args[0]:'} data: b'/data/local/tmp/test.txt'
Hook JNI函数
定义一个函数
extern "C" JNIEXPORT jint JNICALL Java_com_shark_fridanative_MainActivity_test
(JNIEnv *env, jobject job, jint arg2, jstring arg3) {
LOGI("arg2 is %d", arg2);
const char *charstr = env->GetStringUTFChars(arg3, 0);
LOGI("arg3 is %s", charstr);
env->ReleaseStringUTFChars(arg3, charstr);
return 520;
}
可以看到,我这里就传递了两个参数一个int一个String然后在函数中打印
我们Hook的代码如下
Interceptor.attach(Module.findExportByName("libnative-lib.so", "Java_com_shark_fridanative_MainActivity_test"), {
onEnter: function (args) {
var String_java = Java.use('java.lang.String');
var args_3 = Java.cast(args[3], String_java);
//打印int
console.log("args[2] int value : " + args[2].toInt32());
//修改int
args[2] = ptr(88);
console.log("args[3] String value:", args_3);
},
onLeave: function (retval) {
//修改返回值
retval.replace(999);
}
});
这里区别不大,在onLeave中我使用了replace改变了返回值
在onEnter中jint的值可以直接打印出16进制的数据,也可以使用toInt32打印10进制的数据。
jstring直接打印的话他会显示一个地址,这个地址你使用hexdump函数去打印出来,也是看不到字符串的,因为他不是一个char*。我们需要使用Java.cast将它转化成一个java.lang.String
运行结果如下
args[2] int value : 5
args[3] String value: fujie
注意
这里我没有使用代码注入,而是使用了frida-cli进行注入
frida -U com.shark.fridanative -l frida_native.js
如果你使用的是window的话可能会对js脚本进行检测,由于里面的部分代码需要frida的运行环境所以会报错。你可以修改脚本的后缀名称从而绕过检测。
尾言
由于真实的项目基本都使用动态注册还不导出函数,所以直接hook jni的函数还是比较少的,与其hook这还不如hook java层的调用。所以网上的资料基本都是第一种hook系统函数居多。系统函数的常见类型基本就是内存的操作了这里具体可以去看文档。