Java安全之JNI
Java安全之JNI
JNI简介
JNI (Java Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。
JNI基础数据类型
Java和JNI定义的类型是需要转换的,不能直接使用Java里的类型,也不能直接将JNI、C/C++的类型直接返回给Java。
参考如下类型对照表:
Java类型 | JNI类型 | C/C++类型 | 大小 |
---|---|---|---|
Boolean | Jblloean | unsigned char | 无符号8位 |
Byte | Jbyte | char | 有符号8位 |
Char | Jchar | unsigned short | 无符号16位 |
Short | Jshort | short | 有符号16位 |
Int | Jint | int | 有符号32位 |
Long | Jlong | long long | 有符号64位 |
Float | Jfloat | float | 32位 |
Double | Jdouble | double | 64位 |
jstring转char*:env->GetStringUTFChars(str, &jsCopy)
char*转jstring: env->NewStringUTF("Hello...")
字符串资源释放: env->ReleaseStringUTFChars(javaString, p);
JNI加载dll
先来波实操
准备Java代码并编写native方法
java代码,底层调用都是通过native
方法实现的
public class JNIHelloWorld {
private native void print();
static
{
System.loadLibrary("JNIHello");
}
public static void main(String[] args) {
new JNIHelloWorld().print();
}
}
也可以使用System.load
指定加载dll的绝对路径,代码示例:System.load("c:\\test\\Hello.dll");
上述代码注明了要访问本地的Hello.dll,调用本地方法print()
javah生成.h头文件
之后通过javac编译并使用javah生成头文件
JDK10移除了javah,需要改为javac
加-h
参数的方式生产头文件,如果您的JDK版本正好>=10
,那么使用如下方式可以同时编译并生成头文件。
javah -jni JNIHelloWorld
Visual Studio需要安装
新建如下项目
c++代码内实现具体功能逻辑
javah生成的头文件中的函数命名方式是有非常强制性的约束的,如
Java_JNIHelloWorld_print
中Java_
是固定的前缀,而JNIHelloWorld_print
也就代表着Java的完整类名称:JNIHelloWorld
,_print
自然是表示的方法名称了。(JNIEnv *, jclass, jstring)
表示分别是JNI环境变量对象
、java调用的类对象
、参数入参类型
。
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include <iostream>
#include"jni.h"
#include"JNIHelloWorld.h"
#include<stdio.h>
JNIEXPORT void JNICALL
Java_JNIHelloWorld_print(JNIEnv* env, jobject obj)
{
printf("Hello jni!\n");
return;
}
添加准备好的头文件
JNIHelloWorld.h是我们刚才生成的
其他两个在jdk目录下
找到调试属性
不使用预编译头 否则会报错
点击生成后会生成x64的dll
其他编译方式
# MACOS
g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/darwin" -shared -o libcmd.jnilib CommandExecute.cpp
# Linux
g++ -fPIC -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libcmd.so CommandExecute.cpp
# Windows
使用min-gw/cygwin安装gcc/g++,如: x86_64-w64-mingw32-g++ -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o cmd.dll CommandExecute.cpp
加载dll
生成的dll放在与class相同目录,执行java JNIHelloWorld
命令
执行命令
执行命令:c++中可以使用如下方法执行系统命令
#include<stdlib.h> //需引入头文件
// 执行命令
system("ipconfig");
system("pause"); //如果没有这一行代码,cmd窗口就会闪退
// 打开文件,空格需要用引号替换
system("E:\\xxx\\xxx.txt");
system("\"D:\\Sublime Text 3\\sublime_text.exe\"");
// 使用vfork()新建子进程,然后调用exec函数族
main()
{
char * argv[ ]={“ls”,”-al”,”/etc/passwd”,(char*) };
if(vfork() = =0)
{
execv(“/bin/ls”,argv);
}else{
printf(“This is the parent process\n”);
}
}
修改c++代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include <iostream>
#include"jni.h"
#include"JNIHelloWorld.h"
#include<stdio.h>
#include<stdlib.h>
JNIEXPORT void JNICALL
Java_JNIHelloWorld_print(JNIEnv* env, jobject obj)
{
printf("Hello jni!\n");
system("ipconfig");
system("pause");
return;
}
优化下代码,参考javasec,主要是不把参数写死
java代码
public class JNICommandExecute {
public static String cmd;
public static String dll_name;
public JNICommandExecute() {
}
private native void exec(String var1);
public static void main(String[] var0) {
System.load(var0[0]);
cmd = var0[1];
new JNICommandExecute().exec(cmd);
}
}
编译
javac -source 1.5 -target 1.5 JNICommandExecute.java
生成头文件
javah -jni JNICommandExecute
添加头文件jni.h
、jni_md.h
、JNICommandExecute.h
c++代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <string>
#include "JNICommandExecute.h"
#include "jni.h"
using namespace std;
JNIEXPORT jstring JNICALL Java_JNICommandExecute_exec
(JNIEnv* env, jobject obj, jstring str) {
if (str != NULL) {
char MsgBuff[1024];
int MsgLen = 1020;
FILE* fp;
jboolean jsCopy;
const char* cmd = env->GetStringUTFChars(str, &jsCopy);
fp = _popen(cmd, "r");
if (fp != NULL) {
string result;
char buf[128];
// 读取popen函数的执行结果
while (fgets(buf, sizeof(buf), fp) != NULL) {
//拼接读取到的结果到result
result += buf;
}
while (fgets(MsgBuff, MsgLen, fp) != NULL)
{
printf("%s\n", MsgBuff);
}
return env->NewStringUTF(result.c_str());;
}
_pclose(fp);
}
else {
return NULL;
}
}
Reference
https://3gstudent.github.io/Java利用技巧-通过JNI加载dll
https://blog.csdn.net/qq_25722767/article/details/52557235 jni与java通信规则