Frida 之 Native Hook
前言
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 : 5args[3] String value: fujie
注意
这里我没有使用代码注入,而是使用了frida-cli进行注入
frida -U com.shark.fridanative -l frida_native.js
如果你使用的是window的话可能会对js脚本进行检测,由于里面的部分代码需要frida的运行环境所以会报错。你可以修改脚本的后缀名称从而绕过检测。
尾言
由于真实的项目基本都使用动态注册还不导出函数,所以直接hook jni的函数还是比较少的,与其hook这还不如hook java层的调用。所以网上的资料基本都是第一种hook系统函数居多。系统函数的常见类型基本就是内存的操作了这里具体可以去看文档。
作者:Sharkchilli
链接:https://www.jianshu.com/p/19d11d65c4f0
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。