动态注册基本

首先 javah产生头文件
拷贝到头文件里的方法到C文件中进行映射调用
由于方法生成的映射名不太规则也比较长,且调用数据较慢
可以动态注册解决这个问题

以下代码阐释了动态注册的过程

#include <...>


jstring native_hello(JNIEnv* env, jobject thiz){
    return (*env) -> NewStringUTF(env, "动态注册JNI");
}


// 方法对应表,应该是需要动态注册的函数,需要添加进这个数组里
static JNINativeMethod gMethods[] = {
    {"stringFromJNI", "()Ljava/lang/String;", (void*)native_hello},
};

// 为某一个类注册本地方法
static int registerNativeMethods(JNIEnv* env, const char* className,JNINativeMethod* gMethods, int numMethods){
    jclass clazz;
    clazz = (*env) -> FindClass(env, className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if ((*env) -> RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


// 为所有类注册本地方法
static int registerNatives(JNIEnv* env){
    const char* kClassName = "com/example/your_project_name/your_class_which_invokes_JNI";
    return registerNativeMethods(env, kClassName, gMethods, sizeof(gMethods) /     sizeof(gMethods[0]));
}


// JNI_OnLoad
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv* env = NULL;
    jint result = -1;
    
    if ((*vm) -> GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);

    if (!registerNatives(env)) {  // Registration
        return -1;
    }
    
    // Success
    result = JNI_VERSION_1_4;
    return result;
}

这样就注册好了,大体流程就是,

  1. 获取调用JNI的类,(*env) -> findClass
  2. 进行注册,(*env) -> RegisterNatives(env, clazz, gMethods, numMethods)
  3. JNI_OnLoad中获取JNIEnv,(vm) -> GetEnv(vm, (void**) &env, JNI_VERSION_1_4)
  4. 然后直接注册即可,传入env

逆向动态注册

了解动态注册主要是这次看雪CTF的题就是动态注册,下面记录一下

方法是看姜维大牛的博客学来的,在这里放上链接
http://www.520monkey.com/archives/1310
我啥时候也能变成这种大佬啊_(:з)∠)_

首先看function window,没有类似Java_包名_函数名这样的函数,应该就是在JNI_OnLoad里动态注册的了;
然后找到JNI_OnLoad的第一行F2插上断点;

配置好Debugger,然后Attach to process
选择same,即将我们在本地打开的so,load进手机内存里,这样我们提前插的断点就生效了

差不多看到这种场面,就是那篇博客里说的效果了
p1

如果看到类似于vXX + YY这样的,选中vXX,然后按Y键,替换成JNIEnv*即可

之后会变成这样,选中出现的方法
p2

右键,然后Force call type,之后会变成这样
p3

这样看起来就舒服多了,我们双击那个JNINativeMethod,会跳转到这个结构体的数据部分

结构体是这样的:

typedef struct {
    const char* name;                 // 方法名
    const char* signature;            // 方法签名
    void* fnPtr;                      // 对应的native函数地址
}JNINativeMethod;

所以我们需要跳到第三个数据的位置去找这个被隐藏起来的函数

p4

也就是这里

双击进去就是那个被注册的函数了,就酱,后面的分析实在是搞不来,那些大佬的wp也真的是简洁到原地爆炸,哦不是,是让我原地爆炸,改天再啃这道题。