shared lib /data/data/factorytest.android.com/lib/libhello-jni.so 0x42117dc0 D/dalvikvm(16064): No JNI_OnLoad 分析: 显然,库没有找到,看似和JNI_OnLoad有关,事实上,这个函数根本没有被调用。这个hello-jni的例子是不需要我们手动去调用JNI_OnLoad的。那么,为什么就是找不到库呢? 3.
最后看到了 MethodChannel 的注册是在 JNI_OnLoad 的方法中。这个方法是在 so 被加载的时候调用的。今天主要从so 的加载看一下 JNI_OnLoad 的调用。 ", nullptr); if (sym == nullptr) { VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]"; 函数 JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym); //调用JNI_OnLoad方法,返回jni版本号 ->SetResult(was_successful); return was_successful; } 上面这个过程我们证明了 so 的加载会调用 JNI_OnLoad 。 方法,我们这次反过来看一下 JNI_OnLoad 加载 native 方法。
NDK学习笔记:线程JNIEnv,JavaVM,JNI_OnLoad 此文章是关于NDK线程的第二篇理论知识笔记。 此时我们引入函数JNI_OnLoad 和 结构体JavaVM,在头文件 jni.h 有它的定义: /* * Prototypes for functions exported by loadable 在虚拟机VM加载c组件的时候(so)就会调用组件加载接口JNI_OnLoad(),在JNI_OnLoad()函数里,就透过VM之指标而取得JNIEnv之指标值,并存入env指标变数里。 换言之这个JavaVM是能全局安全使用的,而且也只能在JNI_OnLoad的回调进行强引用赋值。 理论知识介绍到这里,我们继续测试功能函数,现在代码应该是长这样的: JavaVM *javaVM; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void
JNI方法注册源码分析(JNI_OnLoad|动态注册|静态注册|方法替换) [icon13-png-ed.png] 背景 开发Android应用时,有时候Java层的编码不能满足实际需求,需要通过JNI 此时,程序默认会去载入的.so文件的函数列表中查找JNI_OnLoad函数并执行,然后卸载.so文件。 需要注意的是JNI_OnLoad和JNI_OnUnLoad这两个函数在.so组件中并不是强制要求的,可以不用去实现。 so方法延迟解析 上面的代码说明,JNI_OnLoad是一种更加灵活,而且处理及时的机制。用javah风格的代码,则推迟解析,直到需要调用的时候才会解析。 函数并执行 JNI_OnLoad(JavaVM* vm, void* reserved) { LOGD("JNI_OnLoad"); static JNINativeMethod methodsLog
JNI_OnLoad 方法 III . 被注册的本地 C/C++ 方法参数 IV . JNINativeMethod 结构体 ( 核心重点 ) V . JNI_OnLoad 方法 ---- 1 . JNI_OnLoad 函数原型 : Java 类中调用 System.loadLibrary(“native-lib”) 代码时 , 调用 JNI_OnLoad 方法 ; ① jni.h 中有该函数的声明 JNI_Onload 方法 JNI_Onload 方法在 Java 层执行 System.loadLibrary("native-lib") 代码时调用的方法 主要是执行一些 JNI_Onload 参数说明 : JavaVM *vm : 代表了 Java 虚拟机 void *r : 一般是 NULL JNI_Onload
NDK中Jni函数也是这样,默认会有JNI_OnLoad 一系列函数,我们重写JNI_OnLoad来加载我们自己的逻辑。 当我们调用,System.loadLiberary("xxxxx"); <----> 实际上自动调用了JNI_OnLoad 做动态注册 先看下示例代码 ➜ Java 部分 public native ) (dynamicM01)}, {"dynamicJavaM02", "(Ljava/lang/String;)I", (int *) (dynamicM02)}, }; jint JNI_OnLoad LOGE("动态 注册 dynamic success"); return JNI_VERSION_1_6;// AS的JDK在JNI默认最高1.6 Java的JDKJNI 1.8 } JNI_OnLoad 动态注册核心RegisterNatives ① 重写JNI_OnLoad ② JavaVM 初始化获取JNIEnv,并获取到jclass ③ 注册函数 //RegisterNatives(jclass
动态注册的时机是在加载函数库(.a 或 .so)的时候进行注册,即在 JNI_OnLoad 方法里进行注册。 RegisterNatives(clazz, methods, methodNum) < 0) { return JNI_FALSE; } return JNI_TRUE; } 在 JNI_OnLoad 方法里进行注册: extern "C" jint JNI_OnLoad(JavaVM *jvm, void *p) { LOGCATE("================ JNI_OnLoad
无论哪种方式,最终都会调用到LoadNativeLibrary()方法,该方法主要操作: 通过dlopen打开动态共享库; 通过dlsym获取JNI_OnLoad符号所对应的方法; 调用该加载库中的JNI_OnLoad 看过《理解JNI技术》的应该知道上述代码执行过程中会调用native层的JNI_OnLoad方法,一般用于动态注册native方法。 然后会获取动态库中的JNI_OnLoad方法,如果有的话调用之。 = JNI_VERSION_1_6; } 这也是为什么在JNI_OnLoad函数中必须正确返回的原因。 方法,我们可以在动态库中加一个JNI_OnLoad方法用于动态注册 如果加了JNI_OnLoad方法,其的返回值为JNI_VERSION_1_2 ,JNI_VERSION_1_4, JNI_VERSION
前面总结了静态实现JNI的方法,本文介绍如何动态实现JNI:JNI在加载时,会调用JNI_OnLoad,而卸载时会调用JNI_UnLoad,所以我们可以通过在JNI_OnLoad里面注册我们的native 我们知道,系统初始化JNI在加载时,会调用JNI_OnLoad(),而卸载时会调用JNI_UnLoad();所以,我们可以通过重写JNI_OnLoad(),在JNI_OnLoad()中将函数注册到Android 在本文中,我们就是重写JNI_OnLoad()函数实现ndk_load库。 registerNativeMethods(env, JNIREG_CLASS, method_table, NELEM(method_table)); } JNIEXPORT jint JNI_OnLoad JNI_OnLoad()会在JNI注册时被调用。在JNI_OnLoad()中,调用register_ndk_load()。
packagename_class.h的样式,例如JNIEXPORT void JNICALL Java_android_media_MediaScanner_processFile 动态注册 通过JNI重载JNI_OnLoad System.loadLibrary("nativeffmpeg"); native_init(); } 执行System.loadLibrary()函数时,第一件事是调用JNI_OnLoad ()函数,如果JNI Lib没有定义JNI_OnLoad,则dvm调用dvmResolveNativeMethod进行动态解析. JNI_OnLoad 在jni的文件中重载JNI_OnLoad函数 jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { JNIEnv* env success -- return valid version number */ result = JNI_VERSION_1_4; bail: return result; JNI_OnLoad
bool result = true; void* vonLoad; int version; vonLoad = dlsym(handle, "JNI_OnLoad "); if (vonLoad == NULL) { LOGD("No JNI_OnLoad found in %s %p\n", pathName, classLoader = JNI_VERSION_1_6) { LOGW("JNI_OnLoad returned bad version (%d) in %s %p\n" * * We don't know how far JNI_OnLoad got, so there could ()这个函数的函数指针;执行JNI_OnLoad()函数;
JNI还可以使用动态注册,在JNI源码中,安卓系统(PathClassLoader)加载so文件时,首先会调用一个方法:JNI_OnLoad;这是系统预留给外部使用动态注册使用的,今天来使用动态注册的方式调用 { LOGI("RegisterNatives error"); return JNI_FALSE; } return JNI_TRUE; } 最后写JNI_OnLoad 方法 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){ LOGI("jni_OnLoad begin"); JNIEnv* LOGI("RegisterNatives error"); return JNI_FALSE; } return JNI_TRUE; } JNIEXPORT jint JNI_OnLoad (JavaVM* vm, void* reserved){ LOGI("jni_OnLoad begin"); JNIEnv* env = NULL; if (vm->GetEnv
需要注意的是,JNI_OnLoad与JNI_OnUnload这两个函数在.so组件中并不是强制要求的,用户也可以不去实现,java代码一样可以调用到C组件中的函数,之所以在C组件中去实现这两个函数(特别是 JNI_OnLoad函数),往往是做一个初始化工作或“善后”工作。 由此看来,就不难明白为什么很多jni C组件中会实现JNI_OnLoad这个函数了。 一般情况下,在C组件中的JNI_OnLoad函数用来实现给VM注册接口,以方便VM可以快速的找到Java代码需要调用的C函数。 (此外,JNI_OnLoad函数还有另外一个功能,那就是告诉VM此C组件使用那一个JNI版本,如果未实现JNI_OnLoad函数,则默认是JNI 1.1版本)。
1 JNI_OnLoad Dalvik虚拟机加载C库时,即执行System.loadLibrary()函数时,第一件事是调用JNI_OnLoad()函数。 可以在JNI_OnLoad 去注册方法 JNI_OnLoad --> registerNativeMethods Android系统加载JNI Lib的方式 通过JNI_OnLoad 如果JNI Lib 没有定义JNI_OnLoad,则dvm调用dvmResolveNativeMethod进行动态解析 2 JavaVM JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局只有一个, 即一个进程只有一个
一种可行的方法是基于JNI重载JNI_OnLoad(),在其中对函数进行动态注册。 = nullptr) { jniEnv->DeleteLocalRef(clazz); } } 重载JNI_OnLoad函数,并在其中调用nativeLogUtilsRegisterNatives 函数 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *jniEnv{nullptr}; if
JavaVM *vm ---- JavaVM *vm 获取方法 : 在 JNI_OnLoad() 方法中获取 ; //JNI_OnLoad 中获取的 Java 虚拟机对象放在这里 JavaVM *javaVM ; int JNI_OnLoad(JavaVM *vm, void *r){ javaVM = vm; return JNI_VERSION_1_6; } JNI_OnLoad 参考 : 【Android NDK 开发】JNI 动态注册 ( 动态注册流程 | JNI_OnLoad 方法 | JNINativeMethod 结构体 | GetEnv | RegisterNatives JNI_OnLoad 方法 IV . 局部引用 与 全局引用 分析 ---- 1 . 中获取的 Java 虚拟机对象放在这里 JavaVM *javaVM; int JNI_OnLoad(JavaVM *vm, void *r){ javaVM = vm; return
二、动态注册 1、注册步骤 1)建立java函数和C函数映射的数组(签名必须一致) 2)通过RegisterNatives注册映射数组 3)重写JNI_OnLoad方法,动态库加载时就会调用JNI_OnLoad ,注册映射数组 2、注册原理 动态注册的关键是JNINativeMethod结构体和JNI_OnLoad的实现,JNINativeMethod结构体包含:name-方法名;signature-方法签名( 描述返回值和入参);fnPtr-c中实现的函数指针;JNI_OnLoad作用是绑定JNINativeMethod和class直接的关系并返回JNI的版本号。 在执行JNI_OnLoad完成注册后,当java代码中执行Native方法时根据调用类可以找对应JNINativeMethod再根据方法名和方法签名可以找到对应的C语言函数指针。 gMethods) / sizeof(gMethods[0])); } //动态注册 java通过loadLibrary 调用so库就会触发该函数加载jni库 JNIEXPORT jint JNICALL JNI_OnLoad
4.算法 密钥 都知道了,我们可以写个程序来解密so了,如下图解密前后JNI_OnLoad对比。 DEX 内存dump 1.接着上面so代码解密完成后,反回到这里, libdvm.so:4087B904 MOV R1, R6 这时可以在JNI_OnLoad函数下断点了,如下图: F9来到JNI_OnLoad 到JNI_OnLoad函数后,到下面libprotectClass.so:5B88A22C BLX R4下好断点。
[2]: 官方的解决办法也是在 JNI_OnLoad 中去做 class 缓存,尝试在 JNI_OnLoad 中 FindClass,然后设置全局引用,NewGlobalRef 与 static 都试过 : jobject myClass; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { jclass clazz = env JvmtiHelper"); myClass = (env)->NewGlobalRef(clazz); } static jclass myClass; JNIEXPORT jint JNICALL JNI_OnLoad
. > 3.搜索crackme.so,在jni_onload上下断点 app会停到没有加载的地方 按F8然后Ctrl+s搜索crackme,如果没有就在进行单步调试直到搜索到crackme 记录D7DB06BC然后静态找JNI_ONLoad的地址00001B9C D7DB06BC+00001B9C=D7DB2258 按g跳转到该地址下断点,单步执行到jni_onload 停到 JNI_Onload里了 BLX R7的位置跳了出去,很可疑的位置需要重点注意 静态分析找到R7的位置进行修改 切换hex view 用010打开crackme.so 找到37 FF