ndk学习20: jni之OnLoad动态注册函数


一.原理
当在系统中调用System.loadLibrary函数时,该函数会找到对应的动态库,
然后首先试图找到"JNI_OnLoad"函数,如果该函数存在,则调用它

JNI_OnLoad可以和JNIEnv的registerNatives函数结合起来,实现动态的函数替换


二. 实战
用ndk学习17的例子继续, 下面演示动态替换TestJni中的sayHello
jstring JNICALL Java_org_bing_testjni_MainActivity_sayHello
(JNIEnvenvjobject objjstring name)
{
    const charpname = env->GetStringUTFChars(nameNULL);
    string str_info = "Hello World:";
    str_info += pname;
    jstring ret_str = env->NewStringUTF(str_info.c_str());
    // C文件使用(*env)->Fun(env,xxx,...)的方式传递
    // (*env)->NewStringUTF(env, "Hello World");
    return ret_str;
}  

1.jni目录下新建一个cpp文件jni_replace.cpp
Android.mk中添加选项:
include $(CLEAR_VARS)
LOCAL_MODULE    := jni_replace
LOCAL_SRC_FILES := jni_replace.cpp
LOCAL_LDFLAGS += -llog
include $(BUILD_SHARED_LIBRARY)  


2. 编写代码如下
#include "org_bing_testjni_MainActivity.h"
#include <stdio.h>
#include <string>
#include <android/log.h>
using namespace std;
__attribute__((visibility("hidden")))  jstring JNICALL sayHello (JNIEnv *, jobjectjstring);
__attribute__((visibility("hidden")))  JNINativeMethod g_Methods[] = {
        "sayHello",
        "(Ljava/lang/String;)Ljava/lang/String;",
        (void*) sayHello
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVMvmvoidreserved) {
    __android_log_print(ANDROID_LOG_DEBUG"__BING__""JNI_OnLoad");
    JNIEnv *pEnv = NULL;
    //获取环境
    jint ret = vm->GetEnv((void**) &pEnv, JNI_VERSION_1_6);
    if (ret != JNI_OK) {
        __android_log_print(ANDROID_LOG_DEBUG"__BING__""jni_replace JVM ERROR:GetEnv");
        return -1;
    }
    jclass cls = pEnv->FindClass("org/bing/testjni/MainActivity");
    if (cls == NULL) {
        __android_log_print(ANDROID_LOG_DEBUG"__BING__""jni_replace:FindClass Error");
        return -1;
    }
    //动态注册本地方法
    ret = pEnv->RegisterNatives(cls, g_Methods,sizeof(g_Methods) / sizeof(g_Methods[0]));
    if (ret != JNI_OK) {
        __android_log_print(ANDROID_LOG_DEBUG"__BING__""jni_replace:FindClass Error");
        return -1;
    }
    //返回java版本
    return JNI_VERSION_1_6;
}
__attribute__((visibility("hidden")))  jstring JNICALL sayHello(JNIEnvenv,jobject objjstring name) {
    const charpname = env->GetStringUTFChars(nameNULL);
    string str_info = "replace say hello function:";
    str_info += pname;
    jstring ret_str = env->NewStringUTF(str_info.c_str());
    return ret_str;
}

3.运行结果
字符串被成功替换


三.NDK中使用Log
1.添加链接选项
LOCAL_LDFLAGS += -llog

这些库是在platform/android-xx/xxx/usr/lib目录下的


包含头文件:
#include <android/log.h>  

函数:
__android_log_print(ANDROID_LOG_DEBUG"TAG""Hello");  

日志输出选项是一个枚举
typedef enum android_LogPriority {
    ANDROID_LOG_UNKNOWN = 0,
    ANDROID_LOG_DEFAULT,    /* only for SetMinPriority() */
    ANDROID_LOG_VERBOSE,
    ANDROID_LOG_DEBUG,
    ANDROID_LOG_INFO,
    ANDROID_LOG_WARN,
    ANDROID_LOG_ERROR,
    ANDROID_LOG_FATAL,
    ANDROID_LOG_SILENT,     /* only for SetMinPriority(); must be last */
android_LogPriority;  


四.总结
这种JNI Onload的方法,在把Onload函数的在导出表里面抹掉
在动态初始化导出表

这样会增加逆向静态分析的成本






posted @ 2016-08-10 10:17  Bingghost  阅读(4010)  评论(0编辑  收藏  举报