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 : 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系统函数居多。系统函数的常见类型基本就是内存的操作了这里具体可以去看文档。

你可能感兴趣的:(Frida 之 Native Hook)