JNI与NDK
1.JNI接口的介绍
jni接口是将java层与c/c++层联系起来,使得它们相互的协调来完成某些任务。 通常在几种情况使用JNI:
(1) 注重处理速度
与本地代码相比,java代码的执行速度更慢,对某段程序执行有较高要求,可以用C/C++来编写,这样往往运行的速度更快。
(2) 硬件控制
硬件控制代码常常由C/C++编写
(3) 既有C/C++代码的复用
编写一些C/C++代码,确保程序的安全性和健壮性,用JNI接口实现。
Android应用开发过程中,使用SDK来开发java程序,使用NDK来开发C/C++。
2.JNI的基本原理
java中调用C库函数的过程
第一步,编写java代码
第二步,编译java代码
编译前看是否设置好JDK,将JDK配置到环境变量中
第三步,生成C语言头文件
创建hellojni.dll运行库文件,具体实现类中声明的两个本地方法
HelloJNI类中声明了printHello()本地方法——>实现相同签名printHello()函数——>System.loadLibrary()方法加载hellojin.dll运行库。
需要创建本地方法的映射C函数,生成头文件。
JNI头文件:
JNI提供了一套与Java数据类型相对应的Java本地类型,使得本地语言可以使用Java数据类型
使用javah命令生成的函数原型的第二个参数是jobject类型或者jclass类型
第四步,编写C/C++代码
在C函数原型生成后,开始编写hellojni.c文件,具体的实现JNI本地函数
第五步,生成C共享库
可以使用VSC++或命令生成hellojni.dll文件库
第六步,运行java程序
java HelloJNI 运行HelloJNI类
3.调用JNI函数
(1)Java层代码
-
JniFuncMain类
-
JniTest类
(2) 分析JNI本地函数代码
1.JniFunMain.h头文件
使用javah生成JniFunMain.h头文件
2.Jnifunc.cpp文件
生成头文件后,我们开始实现函数原型
3.通过JNI,获取成员变量值
程序通过JNI访问Java类/对象的成员变量按如下顺序:
(1)查找含待访问的成员变量的Java类的jclass值 (2)获取该类成员变量的jfieldID值,静态变量:调用名称为GetStaticFieldID()的JNI函数。普通对象:GetFieldID()的JNI函数 (3)使用上文中获得jclass和jfield值,获取或1设置1成员变量值
获取jclass值,调用JNI函数FindClass()即可
可能缺少env参数,这是与C/C++风格有关
生成对象
(1) 首先调用JNI函数FindClass(),查找生成对象的类
(2) 查找类的构造方法ID,保存在jmethodID变量中
(3) 用获得的jclass与构造方法的ID参数,调用JNI函数 NewOject(),生成JniTest类的对象。
局部引用和全局引用
当全局引用使用完毕,应调用名称为DeleteGlobalRef()函数,将全局引用销毁
4.调用Java方法
顺序: (1)获取待调方法的Java类的jclass(若为java对象,则 用该方法获取java类对象的jobject) (2)调用GetMethond函数,获取待调方法的ID(使用jclass与GetMethondID()函数) (3)静态方法:CallStatic
4.通过JNI设置变量成员的值
(3) 编译及运行结果
1.编译.java文件 2.编译.cpp文件 3.运行JniFunMain类
4.在C程序中运行Java类
Invocation API应用示例
执行顺序: (1)主程序InvokeJava.cpp使用Invocation API加载Java虚拟机 (2)加载InvocationTest类至内存中 (3)执行被加载的InvocationTest类的main()方法
分析java代码和C代码
java虚拟机结构体
JavaVMInitArgs结构体的version成员用来指定传递虚拟机的选项的变量的形式 nOptions指定JavaVMOption结构体数组元素的个数,option用来指向JavaVMOption结构体的地址
5.直接注册JNI本地函数
Java虚拟机中运行Java应用程序的步骤: 1.调用System.loadLibrary()方法,将本地方法具体实现的C/C++运行库加载到内存中。 2.Java虚拟机检索库函数,并建立映射关系。
为了解决映射问题,JNI机制提供了RegisterNatives()的JNI函数,建立映射关系。
加载本地库时,注册JNI本地函数
System.loadLibrary()方法的执行过程: 调用System.loadLibrary方法——>java虚拟机会加载其参数指定的共享库——>Java虚拟机检索共享库的函数符号,检查JNI_OnLoad()函数是否被实现(含相关函数,JNI_OnLoad会自动的实现)
开发者若想手工映射本地方法与JNI本地函数,需要在JNI_OnLoad()函数内调用RegisterNatives()函数进行映射匹配
原文始发于微信公众号(安全后厨):Android逆向学习67——JNI与NDK