一.原理
當在系統中調用System.loadLibrary函數時,該函數會找到對應的動態庫,
然后首先試圖找到"JNI_OnLoad"函數,如果該函數存在,則調用它
JNI_OnLoad可以和JNIEnv的registerNatives函數結合起來,實現動態的函數替換
二. 實戰
用ndk學習17的例子繼續, 下面演示動態替換TestJni中的sayHello
jstring JNICALL Java_org_bing_testjni_MainActivity_sayHello
(JNIEnv* env, jobject obj, jstring name)
{
const char* pname = env->GetStringUTFChars(name, NULL);
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 *, jobject, jstring);
__attribute__((visibility("hidden"))) JNINativeMethod g_Methods[] = {
"sayHello",
"(Ljava/lang/String;)Ljava/lang/String;",
(void*) sayHello
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
__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(JNIEnv* env,jobject obj, jstring name) {
const char* pname = env->GetStringUTFChars(name, NULL);
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函數的在導出表里面抹掉
在動態初始化導出表
這樣會增加逆向靜態分析的成本