Android逆向学习67——JNI与NDK

JNI与NDK

    1.JNI接口的介绍

    jni接口是将java层与c/c++层联系起来,使得它们相互的协调来完成某些任务。 通常在几种情况使用JNI:

    (1) 注重处理速度

    与本地代码相比,java代码的执行速度更慢,对某段程序执行有较高要求,可以用C/C++来编写,这样往往运行的速度更快。
    Android逆向学习67——JNI与NDK

    (2) 硬件控制

    硬件控制代码常常由C/C++编写

    (3) 既有C/C++代码的复用

    编写一些C/C++代码,确保程序的安全性和健壮性,用JNI接口实现。
    Android应用开发过程中,使用SDK来开发java程序,使用NDK来开发C/C++。

    2.JNI的基本原理

    java中调用C库函数的过程

    Android逆向学习67——JNI与NDK

    第一步,编写java代码

    Android逆向学习67——JNI与NDK

    第二步,编译java代码

    Android逆向学习67——JNI与NDK

    编译前看是否设置好JDK,将JDK配置到环境变量中

    Android逆向学习67——JNI与NDK

    第三步,生成C语言头文件

    创建hellojni.dll运行库文件,具体实现类中声明的两个本地方法
    HelloJNI类中声明了printHello()本地方法——>实现相同签名printHello()函数——>System.loadLibrary()方法加载hellojin.dll运行库。Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK

    需要创建本地方法的映射C函数,生成头文件。

    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK

    JNI头文件:

    Android逆向学习67——JNI与NDK

    JNI提供了一套与Java数据类型相对应的Java本地类型,使得本地语言可以使用Java数据类型
    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK

    使用javah命令生成的函数原型的第二个参数是jobject类型或者jclass类型

    第四步,编写C/C++代码

    在C函数原型生成后,开始编写hellojni.c文件,具体的实现JNI本地函数

    Android逆向学习67——JNI与NDK
    第五步,生成C共享库

    可以使用VSC++或命令生成hellojni.dll文件库

    Android逆向学习67——JNI与NDK

    第六步,运行java程序

    java HelloJNI  运行HelloJNI类

    Android逆向学习67——JNI与NDK

    3.调用JNI函数

    Android逆向学习67——JNI与NDK

    (1)Java层代码

    1. JniFuncMain类
    Android逆向学习67——JNI与NDK
    1. JniTest类

    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK

    (2) 分析JNI本地函数代码

    1.JniFunMain.h头文件

    使用javah生成JniFunMain.h头文件

    Android逆向学习67——JNI与NDK

    2.Jnifunc.cpp文件

    生成头文件后,我们开始实现函数原型

    Android逆向学习67——JNI与NDK

    3.通过JNI,获取成员变量值

    Android逆向学习67——JNI与NDK

    程序通过JNI访问Java类/对象的成员变量按如下顺序:
    (1)查找含待访问的成员变量的Java类的jclass值 (2)获取该类成员变量的jfieldID值,静态变量:调用名称为GetStaticFieldID()的JNI函数。普通对象:GetFieldID()的JNI函数 (3)使用上文中获得jclass和jfield值,获取或1设置1成员变量值

    获取jclass值,调用JNI函数FindClass()即可

    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK
    可能缺少env参数,这是与C/C++风格有关

    Android逆向学习67——JNI与NDK

    生成对象

    (1) 首先调用JNI函数FindClass(),查找生成对象的类

    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK

    (2) 查找类的构造方法ID,保存在jmethodID变量中

    Android逆向学习67——JNI与NDK

    (3) 用获得的jclass与构造方法的ID参数,调用JNI函数 NewOject(),生成JniTest类的对象。

    Android逆向学习67——JNI与NDK

    局部引用和全局引用

    Android逆向学习67——JNI与NDK
    当全局引用使用完毕,应调用名称为DeleteGlobalRef()函数,将全局引用销毁

    4.调用Java方法

    顺序: (1)获取待调方法的Java类的jclass(若为java对象,则  用该方法获取java类对象的jobject) (2)调用GetMethond函数,获取待调方法的ID(使用jclass与GetMethondID()函数) (3)静态方法:CallStaticMethond() 类对象:CallMethond()

    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK

    4.通过JNI设置变量成员的值

    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK

    (3) 编译及运行结果

    1.编译.java文件 2.编译.cpp文件 3.运行JniFunMain类

    4.在C程序中运行Java类

    Invocation API应用示例

    Android逆向学习67——JNI与NDK
    执行顺序: (1)主程序InvokeJava.cpp使用Invocation API加载Java虚拟机 (2)加载InvocationTest类至内存中 (3)执行被加载的InvocationTest类的main()方法

    分析java代码和C代码

    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK

    java虚拟机结构体

    Android逆向学习67——JNI与NDK
    JavaVMInitArgs结构体的version成员用来指定传递虚拟机的选项的变量的形式 nOptions指定JavaVMOption结构体数组元素的个数,option用来指向JavaVMOption结构体的地址

    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK

    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
    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK
    Android逆向学习67——JNI与NDK


    原文始发于微信公众号(安全后厨):Android逆向学习67——JNI与NDK

    版权声明:admin 发表于 2024年5月14日 上午9:51。
    转载请注明:Android逆向学习67——JNI与NDK | CTF导航

    相关文章