JNI_OnLoad函数不存在的问题

今天分析一个app的老版本时,无意发现JNI_OnLoad不存在,但是so的确是java层load加载,各native函数也有声明和调用,以为又遇到什么黑科技。查找发现最早期的ndk开发版本中的确是没有这个函数的。

现期各版本的ndk中JNI_OnLoad函数都是load时自动调用的,如果未发现则去调用dvmResolveNativeMethod。以下时一份详细的流程解释。

以下摘自 http://yanbober.github.io/2015/02/25/android_studio_jni_3/

Android OS加载JNI Lib的方法有两种:

  • 通过JNI_OnLoad。
  • 如果JNI Lib实现中没有定义JNI_OnLoad,则dvm调用dvmResolveNativeMethod进行动态解析。

PS:咱们上面第一部分就是dvm调用dvmResolveNativeMethod进行动态解析,所以log打印No JNI_OnLoad found。

从网上查到的深入解析(此解析模块代码引用自网络)

JNI_OnLoad机制分析

System.loadLibrary调用流程如下所示:

System.loadLibrary->Runtime.loadLibrary->(Java)nativeLoad->(C: java_lang_Runtime.cpp)Dalvik_java_lang_Runtime_nativeLoad->dvmLoadNativeCode->(dalvik/vm/Native.cpp)

接着如下:

  • dlopen(pathName, RTLD_LAZY) (把.so mmap到进程空间,并把func等相关信息填充到soinfo中)
  • dlsym(handle, “JNI_OnLoad”)
  • JNI_OnLoad->RegisterNatives->dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName, const char* signature, void* fnPtr)->dvmUseJNIBridge(method, fnPtr)->(method->nativeFunc = func)

JNI函数在进程空间中的起始地址被保存在ClassObject->directMethods中。

struct ClassObject : Object {       /* static, private, and <init> methods */       int             directMethodCount;       Method*         directMethods;          /* virtual methods defined in this class; invoked through vtable */       int             virtualMethodCount;       Method*         virtualMethods;   }

此ClassObject通过gDvm.jniGlobalRefTable或gDvm.jniWeakGlobalRefLock获取。

dvmResolveNativeMethod延迟解析机制

如果JNI Lib中没有JNI_OnLoad,即在执行System.loadLibrary时,无法把此JNI Lib实现的函数在进程中的地址增加到ClassObject->directMethods。则直到需要调用的时候才会解析这些javah风格的函数 。这样的函数dvmResolveNativeMethod(dalvik/vm/Native.cpp)来进行解析,其执行流程如下所示:

void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, Thread* self)->(Resolve a native method and invoke it.)

接着如下:

  • void* func = lookupSharedLibMethod(method)(根据signature在所有已经打开的.so中寻找此函数实现)dvmHashForeach(gDvm.nativeLibs, findMethodInLib,(void*) method)->findMethodInLib(void* vlib, void* vmethod)->dlsym(pLib->handle, mangleCM)
  • dvmUseJNIBridge((Method*) method, func)
  • (*method->nativeFunc)(args, pResult, method, self);(调用执行)

说完蛋疼Load基础后该准么办?

答案其实就是推荐Android OS加载JNI Lib的方法的通过JNI_OnLoad。因为通过它你可以干许多自定义的事,譬如实现自己的本地注册等。 因为在上面的解析中已经看到了JNI_OnLoad->RegisterNatives->…这两个关键方法。具体细节咱们现在再说说。

先来看JNI_OnLoad函数

JNI_OnLoad()函数主要的用途有两点:

  • 通知VM此C组件使用的JNI版本。如果你的.so文件没有提供JNI_OnLoad()函数,VM会默认该.so使用最老的JNI 1.1版本。 而新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer, 就必须藉由JNI_OnLoad()函数来告知VM。
  • 因为VM执行到System.loadLibrary()函数时,会立即先调运JNI_OnLoad(),所以C组件的开发者可以由JNI_OnLoad()来进行C组件内的初期值之设定(Initialization)。

既然有JNI_OnLoad(),那就有相呼应的函数,那就是JNI_OnUnload(),当VM释放JNI组件时会呼叫它,因此在该方法中进行善后清理,资源释放的动作最为合适。

再来看RegisterNatives函数

在上面第一部分时我们看见通过javah命令生成的io_github_yanbober_ndkapplication_NdkJniUtils.h里函数的名字好长,看着就蛋疼。你肯定也想过怎么这么长, 而且当有时候项目需求原因导致类名变了的时候,函数名必须一个一个的改,更加蛋疼。我第一次接触时那时候自己经验不足,就遇上了这个蛋疼问题。泪奔啊!

既然这样那就有解决办法的,那就是RegisterNatives大招。接下来来看下这个大招:

App的Java程序寻找c本地方法的过程一般是依赖VM去寻找*.so里的本地函数,如果需要连续调运很多次,每次都要寻找一遍, 会多花许多时间。因此为了解决这个问题我们可以自行将本地函数向VM进行登记,然后让VM自行调registerNativeMethods()函数。

VM自行调registerNativeMethods()函数的作用主要有两点:  

  • 更加有效率去找到C语言的函数  
  • 可以在执行期间进行抽换,因为自定义的JNINativeMethod类型的methods[]数组是一个名称-函数指针对照表,在程序执行时, 可以多次调运registerNativeMethods()函数来更换本地函数指针,从而达到弹性抽换本地函数的效果。

上面提到的JNINativeMethod结构是c/c++方法和Java方法之间映射关系的关键结构,该结构定义在jni.h中,具体定义如下:

typedef struct {    const char* name;//java方法名称    const char* signature; //java方法签名   void*       fnPtr;//c/c++的函数指针   } JNINativeMethod;

所谓自定义的JNINativeMethod类型的methods[]数组自然也就类似长下面这样了:

static JNINativeMethod methods[] = {   {"generateKey", "(Ljava/lang/String;)Ljava/lang/String;", (void*)generateKey},   };

以上也就是所谓的动态注册JNI了。

时间: 2024-08-11 09:46:46

JNI_OnLoad函数不存在的问题的相关文章

IDA远程调试so库JNI_Onload函数

JNI_OnLoad函数大概功能就是在程序加载so的时候,会执行JNI_OnLoad函数,做一系列的准备工作.很多时候,程序猿们会将一些重要信息放在此函数中,而不是通过某种事件来重复触发.包括说将反调试函数放置在此函数中.因此,调试手段发生了改变,上述调试方法基本上被淘汰. 1.静态分析,找到JNI_OnLoad函数的偏移 2.执行android_server 3.端口转发 4. 以调试模式启动程序adb shell am start -D -n com.example.mytestcm/.Ma

Android逆向之动态调试so库JNI_Onload函数-----基于IDA实现

之前看过吾爱破解论坛一个关于Android'逆向动态调试的经验总结帖,那个帖子写的很好,对Android的脱壳和破解很有帮助,之前我们老师在上课的时候也讲过集中调试的方法,但是现在不太实用.对吾爱破解论坛的该贴,我也是看了很多遍,自己也查了不少资料,但是自己动手的时候总觉比较繁琐,并且很多细节的地方没有注意到,按照那个帖子尝试了几遍但是却出现了错误(后面会提到),今天周末重新拾起来试了试,终于把遇到的问题给解决了,顺便做个记录以免忘记了,其中的一些细节我也不是太明白,忘知道的人给指出. 第一步.

android在JNI_OnLoad入口函数下断点动态调试so库

一般来说,很多APK的校验代码,都会在程序运行的时候自动加载一些动态so库,然后执行这些库中的校验代码.所以为了能够通过程序的校验,我们必须在执行这些函数之前下断点--理想的方法就是在JNI_OnLoad入口函数下断点. 在2.3.3模拟器中详细步骤如下: ①在控制台输入adb shell 进入手机,然后使用  am start -D -n 包名/类名,以等待调试的模式启动APK应用: 这里需要说明的是 "包名/类名"的书写方法: # am start -n {包(package)名}

【转】深入了解android平台的jni---注册native函数

原文网址:http://my.oschina.net/u/157503/blog/169041 注册native函数有两种方法:静态注册和动态注册. 1.静态注册方法 根据函数名找到对应的JNI函数:Java层调用函数时,会从对应的JNI中寻找该函数,如果没有就会报错,如果存在则会建立一个关联联系,以后在调用时会直接使用这个函数,这部分的操作由虚拟机完成. 静态方法就是根据函数名来遍历java和jni函数之间的关联,而且要求jni层函数的名字必须遵循 特定的格式,其缺点在于: 1)javah生成

Android使用JNI(从java调用本地函数)

当编写一个混合有本地C代码和Java的应用程序时,需要使用Java本地接口(JNI)作为连接桥梁.JNI作为一个软件层和API,允许使用本地代码调用Java对象的方法,同时也允许在Java方法中调用本地函数. 在Java端,开发者所需要做的仅仅是在连接本地函数的方法之前加上native关键字.这样VM就会去寻找这个本地函数. 1.从Java调用本地函数 从Java调用本地函数时,需要在类中定义一个带有native关键字的特有方法,作为连接本地代码的桥梁.通过这个定义,尝试调用本地方法时JVM会找

深入了解android平台的jni---注册native函数

注册native函数有两种方法:静态注册和动态注册. 1.静态注册方法 根据函数名找到对应的JNI函数:Java层调用函数时,会从对应的JNI中寻找该函数,如果没有就会报错,如果存在则会建立一个关联联系,以后在调用时会直接使用这个函数,这部分的操作由虚拟机完成. 静态方法就是根据函数名来遍历java和jni函数之间的关联,而且要求jni层函数的名字必须遵循 特定的格式,其缺点在于: 1)javah生成的jni层函数特别长: 2)初次调用native函数时要根据名字搜索对应的jni层函数来建立关联

Oracle官网JNI简介和接口函数分析

第一章 概述 本章主要介绍JNI(Java Native Interface),JNI是一种本地编程接口.它允许运行在JAVA虚拟机中的JAVA代码和用其他编程语言,诸如C语言.C++.汇编,写的应用和库之间的交互操作. JNI的最大优势在于没有强加任何限制在JAVA虚拟机的下层实现上,因此,JAVA虚拟机供应商能够提供JNI的支持而不影响虚拟机的其他部分,程序员只需写出一个版本的本地应用和库,就可使之运行在一切支持JNI的JAVA虚拟机上. 本章包含了以下的要点: ? JNI概述 ? 目标 ?

浅析JNI函数的注册过程

我们在java中调用Native code的时候,一般是通过JNI来实现的,我们只需要在java类中加载本地.so库文件,并声明native方法,然后在需要调用的地方调用即可,至于java中native方法的具体实现,全部交给了Native层.我们要在java中正确地调用到本地代码中对应函数的前提是什么呢?答案就是通过一定的机制建立java中native方法和本地代码中函数的一一对应关系,那么这种机制是什么呢?就是JNI函数的注册机制. JNI函数的注册有两种方式,一种是静态注册方式,另一种是动

JNI通过动态注册实现native函数

一.概述 上一篇写的是通过javah工具将java代码中的native声明的函数生成标准的C/C++函数头,每个函数的名字都很长(Java_包名_类名_函数名),这样C/C++函数的函数名就是定死的,不能修改,否则java找不到函数.这里还有种方式,通过注册的方式将C/C++的函数与java中的native函数进行一一对应的,函数名可以任意书写. 二.代码实现 SimpleJni.java package com.bt.jni; public class SimpleJni {    stati