一.原理
当在系统中调用System.loadLibrary函数时,该函数会找到对应的动态库,
然后首先试图找到"JNI_OnLoad"函数,如果该函数存在,则调用它
JNI_OnLoad可以和JNIEnv的registerNatives函数结合起来,实现动态的函数替换
二. 实战
用ndk学习17的例子继续, 下面演示动态替换TestJni中的sayHellojstring 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.运行结果
字符串被成功替换
![](https://images2015.cnblogs.com/blog/198684/201608/198684-20160810101601449-710043067.png)
三.NDK中使用Log
1.添加链接选项
LOCAL_LDFLAGS += -llog
这些库是在platform/android-xx/xxx/usr/lib目录下的
![](https://images2015.cnblogs.com/blog/198684/201608/198684-20160810101601996-1055417506.png)
包含头文件:
#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函数的在导出表里面抹掉
在动态初始化导出表
这样会增加逆向静态分析的成本