Loading

Java安全之JNI

Java安全之JNI

JNI简介

JNIJava Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(CC++汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。

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_printJava_是固定的前缀,而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.hjni_md.hJNICommandExecute.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通信规则

https://javasec.org/javase/JNI/

posted @ 2022-08-26 16:05  Zh1z3ven  阅读(348)  评论(0编辑  收藏  举报