JNI的简介
JNI 是本地语言编程接口(Java Native Interface)。它允许运行在 JVM 中的 Java 代码和用C、C++ 或 汇编 写的本地代码相互操作。
JNI存在的意义:
【开发】一些系统底层功能]ava无法直接调用,只能借助C、C++编程来实现。
【开发】对于一些性能要求比较高的功能,用C、C++更加高效。
【爬虫】将核心算法用C、C++ 增加逆向破解的难度。
JNI安装
安装好NDK后,就可以基于Android Studio进行JNI开发。
新建Android项目时,会用C去开发核心代码。【Native C++模版】
Native C++模版(Empty模版+C基本设置)
静态注册
如果想要在Java中结合JNI实现调用C代码。
创建Java类,不需要实现具体的逻辑,只定义类和方法。
package com.nb.fucker;
// com.nb.fucker.encryptUtils
public class encryptUtils {
// 加载C文件
static {
System.loadLibrary("encrypt");
}
public static native int add(int v1, int v2);
public static native String sign(String origin);
}
创建C/C++文件并编写代码,具体实现功能的函数。
命令自动生成 头文件(进入类的路径)
javac -h ./ encryptUtils.java
C语言的函数名,取名规则->Java_包名_类名_方法名称
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_nb_fucker_encryptUtils */
#ifndef _Included_com_nb_fucker_encryptUtils
#define _Included_com_nb_fucker_encryptUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_nb_fucker_encryptUtils
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint
JNICALL Java_com_nb_fucker_encryptUtils_add(JNIEnv *env, jclass obj, jint v1, jint v2) {
// 写 C 语言代码 + env是JNI对象 + obj当前类 encryptUtils + v1、v2 是参数
return v1 + v2;
}
/*
* Class: com_nb_fucker_encryptUtils
* Method: sign
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring
JNICALL Java_com_nb_fucker_encryptUtils_sign(JNIEnv *env, jclass obj, jstring origin) {
char data[] = "wupeiqi";
return (*env)->NewStringUTF(env, data); // 将 c中的字符串 转换成 java中的String
}
#ifdef __cplusplus
}
#endif
#endif
配置CMakeLists.txt
add_library( # Sets the name of the library.
encrypt
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
encrypt.c)
target_link_libraries( # Specifies the target library.
fucker encrypt
# Links the target library to the log library
# included in the NDK.
${log-lib})
调用
int v1 = encryptUtils.add(1, 2);
String v2 = encryptUtils.sign("xxxxx");
价值
逆向别人网站的so文件时,知道他们文件名之间的对应关系。
扫描二维码关注公众号,回复:
14904695 查看本文章
动态注册
动态注册的步骤:
Java的类:
public class DynamicUtils {
static {
System.loadLibrary("dynamic");
}
public static native int add(int v1, int v2);
}
C中的函数:
JNI_OnLoad
-- 动态找到add
jint plus(JNIEnv *env, jclass obj, jint v1, jint v2) {
return v1 + v2;
}
static JNINativeMethod gMethod[] = {
//Java函数 ----> C语言中的函数
{"add", "(II)I", (void *) plus},
};
/*
* System.loadLibrary("包")时会自动调用
*/
JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
// 在Java虚拟机中获取 env
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
// 找到 Java 中的类,env=JNI
jclass clazz = (*env)->FindClass(env, "com/nb/fucker/DynamicUtils");
// 将类中的方法注册到JNI中
int res = (*env)->RegisterNatives(env, clazz, gMethod, 1);
if(res < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
配置CMakeLists.txt
add_library( # Sets the name of the library.
dynamic
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
dynamic.c)
target_link_libraries( # Specifies the target library.
fucker dynamic
# Links the target library to the log library
# included in the NDK.
${log-lib})
调用
int v3 = DynamicUtils.add(3, 4);
Log.e("DynamicUtils.add===>", String.valueOf(v3));
JNI调用Java中的成员
在JNI中如果想要调用Java中的成员,是使用:
示例1:(静态方法: int+int->int)
// Java
package com.nb.dk;
class Query{
public static int getData(int v1, int v2) {
return v1 + v2;
}
}
// JNI
jclass cls = (*env) -> FindClass(env, "com/nb/dk/Query");
jmethodID mid = (*env) -> GetStaticMethodID(env, cls, "getData", "(II)I");
// "(II)I":
// JNI中 I 对应 Java中的 int 数据类型;
// (II)-->代表参数是2个 int;
// 括号外的 I ---> 代表返回值是 int;
int res = (*env) -> CallStaticIntMethod(env, cls, mid, 1, 2);
示例2:(静态方法: int+int->String)
// Java
package com.nb.dk;
class Query{
public static String getData(int v1, int v2) {
return v1 + v2;
}
}
// JNI
jclass cls = (*env) -> FindClass(env, "com/nb/dk/Query");
jmethodID mid = (*env) -> GetStaticMethodID(env, cls, "getData", "(II)Ljava/lang/String;");
// "(II)Ljava/lang/String;":
// JNI中 I 对应 Java中的 int 数据类型;
// (II)-->代表参数是2个 int;
// 括号外的 Ljava/lang/String; ---> 代表返回值是 String;
int res = (*env) -> CallStaticIntMethod(env, cls, mid, 1, 2);
示例3:(静态方法: int+String->String)
// Java
package com.nb.dk;
class Query{
public static String getData(String v1, int v2) {
return String.valueOf(v1 + v2);
}
}
// JNI, C语言
jclass cls = (*env) -> FindClass(env, "com/nb/dk/Query");
jmethodID mid = (*env) -> GetStaticMethodID(env, cls, "getData", "(Ljava/lang/String;I)Ljava/lang/String;");
// (Ljava/lang/String;I)Ljava/lang/String;":
// JNI中 I 对应 Java中的 int 数据类型;
// (Ljava/lang/String;I)-->代表参数是第一个数据类型是String; 第二个数据类型是int;
// 括号外的 Ljava/lang/String; ---> 代表返回值是 String;
jstring arg1 = (*env)->NewStringUTF(env, "哈哈哈"); // 将C语言的字符串转化成 java的字符串
int res = (*env) -> CallStaticIntMethod(env, cls, mid, arg1, 2);
示例4:(动态方法: int+String->String)
// Java
package com.nb.dk;
class Query{
// 构造方法
public Query(int arg1, int arg2, String arg3) {
}
// getData
public static String getData(String v1, int v2) {
return String.valueOf(v1 + v2);
}
}
// JNI
jclass cls = (*env) -> FindClass(env, "com/nb/dk/Query");
// "<init>" 找类中的构造方法
// v --> void
jmethodID init = (*env) -> GetStaticMethodID(env, cls, "<init>", "(IILjava/lang/String;)v");
// 创建对象
jobject cls_object = (*env)->NewObject(env, cls, init, 1, 2, (*env)->NewStringUTF(env, "哈哈哈"));
jmethodID mid = (*env) -> GetMethodID(env, cls, "getData", "(II)Ljava/lang/String;");
jstring arg1 = (*env)->NewStringUTF(env, "字符串啊");
int res = (*env) -> CallStaticIntMethod(env, cls_object, mid, arg1, 2);