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函數的在導出表裏面抹掉
在動態初始化導出表

這樣會增長逆向靜態分析的成本




相關文章
相關標籤/搜索