此文解决的是使用 Eclipse ,通过 JNI ,调用 DLL 文件。再通过该 DLL 文件,使用 GetProcAddress 调用 EXE 文件的导出函数。
目前只能做到调用单一的函数。
下一步可扩展的:
1. 连续调用多个函数,并组合使用。
2. 在 EXE 运行过程中。动态的调用导出函数并获取实时的数据。
3. 通过强制破解,调用非导出的函数。
A. 在 Eclipse 下创建 java 项目。创建包: com 。创建 java 类: Helloworld 。
代码如下:
//包名在生成头文件时容易出问题。应当注意java头文件全名包括包名。
package com;
public class Helloworld {
/**
* 此处载入DLL。DLL中必须实现所有native方法。否则会出现错误。
* load和loadLibrary不同处在于:load是指向具体文件全名,包括路径和后缀名。这对跨平台有影响。
* loadLibrary是指向资源文件。针对不同平台(windows是DLL)。java会搜寻环境里所有的路径,查找符合条件的资源文件。
* 具体路径列表包括系统环境配置里的路径 > 项目所在路径。
*/
static {
System.out.println(System.getProperty("java.library.path"));//打印系统环境变量
System.loadLibrary("Helloworld");
}
/**
* 声明native的displayHelloworld方法。以备DLL中实现。
* 须注意传入参数和返回参数的类型。
*/
public native int displayHelloWorld(int testInt);
public static void main(String[] args) {
try {
Helloworld hw = new Helloworld();
System.out.println("测试开始");
int j = hw.displayHelloWorld(99);//调用native方法。此时java会追踪本地资源的具体实现方法。
System.out.println(j);
} catch (Exception e) {
e.printStackTrace();
}
}
}
B. 编译该 java 文件,生成 Helloworld.class 文件。
C. 在命令行使用 javah 命令调用 Helloworld.class 生成头文件。 Javah 提示如下:
用法:javah [选项] <类>
其中 [选项] 包括:
-help 输出此帮助消息并退出
-classpath <路径> 用于装入类的路径
-bootclasspath <路径> 用于装入引导类的路径
-d <目录> 输出目录
-o <文件> 输出文件(只能使用 -d 或 -o 中的一个)
-jni 生成 JNI样式的头文件(默认)
-version 输出版本信息
-verbose 启用详细输出
-force 始终写入输出文件
使用全限定名称指定 <类>(例
如,java.lang.Object)。
根据项目具体路径。输入命令如例:
com.Helloworld 为包名加“ . ”加 class 文件名。
运行后目标文件 com_Helloworld.h 会在指定目录生成。
用编辑工具打开后,代码如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_Helloworld */
#ifndef _Included_com_Helloworld
#define _Included_com_Helloworld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_Helloworld
* Method: displayHelloWorld
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_com_Helloworld_displayHelloWorld
(JNIEnv *, jobject, jint);
#ifdef __cplusplus
}
#endif
#endif
此头文件的作用是连接 java 与 C++ 。文件路径与 DLL 的文件路径保持一致,与 Java 项目路径无关。
它将作为中转 DLL 的头文件,中转 DLL 将实现头文件中的 JNICALL Java_com_Helloworld_displayHelloWorld 方法。
D. 创建基于 C++ 的 EXE 项目。 VC2008 中的创建方法如下:
1. 文件 > 新建 > 项目。
2. 选择类型 Win32>Win32 项目。并输入项目名如: Hello 。位置随意。
3. 点击确定成功后,在新窗口点击下一步选择 windows 应用程序,选中空项目。
4. 创建主 CPP 文件: Hello.cpp 。代码如下:
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;
int i = 20;
extern "C" __declspec(dllexport) int returnAdd(int j) //__declspec(dllexport)代表此函数为导出函数。
{
return ++j;
}
int main()
{
cout << returnAdd(i) << endl;
system("pause");
return 0;
}
5. 编译并测试,结果如下图:
名为 Hello.exe 文件就创建完成。此文件作为被测试的 EXE 文件。文件位置随意。
E. 创建基于 C++ 的 DLL 项目。 VC2008 中的创建方法如下:
1. 文件 > 新建 > 项目。
2. 按下图选择类型 Win32>Win32 项目。并输入项目名如: Helloworld 。位置随意。
3. 点击确定成功后,在新窗口点击下一步选择 DLL ,选中空项目后如图:
4. 点击完成。生成空的 DLL 项目。配置该 DLL 项目环境。如下图:在项目上点击右键,选择属性。
选择配置属性 > 常规 > 输出目录。如下图:单击下拉按钮,选择 java 项目路径。
选择配置属性 > 常规 >C/C++ 。如下图:单击下拉按钮,选择 java 项目路径, JDK 目录下的 Include 文件夹和 win32 文件夹。如下下图。
在资源文件上点右键 > 添加 > 现有项。选择 java 生成的头文件。
5. 依次创建头文件: stdafx.h :
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // 从Windows 头中排除极少使用的资料
// Windows 头文件:
#include <windows.h>
// TODO: 在此处引用程序需要的其他头文件
targetver.h :
#pragma once
// 以下宏定义要求的最低平台。要求的最低平台
// 是具有运行应用程序所需功能的Windows、Internet Explorer 等产品的
// 最早版本。通过在指定版本及更低版本的平台上启用所有可用的功能,宏可以
// 正常工作。
// 如果必须要针对低于以下指定版本的平台,请修改下列定义。
// 有关不同平台对应值的最新信息,请参考MSDN。
#ifndef WINVER // 指定要求的最低平台是Windows Vista。
#define WINVER 0x0600 // 将此值更改为相应的值,以适用于Windows 的其他版本。
#endif
#ifndef _WIN32_WINNT // 指定要求的最低平台是Windows Vista。
#define _WIN32_WINNT 0x0600 // 将此值更改为相应的值,以适用于Windows 的其他版本。
#endif
#ifndef _WIN32_WINDOWS // 指定要求的最低平台是Windows 98。
#define _WIN32_WINDOWS 0x0410 // 将此值更改为适当的值,以适用于Windows Me 或更高版本。
#endif
#ifndef _WIN32_IE // 指定要求的最低平台是Internet Explorer 7.0。
#define _WIN32_IE 0x0700 // 将此值更改为相应的值,以适用于IE 的其他版本。
#endif
创建 CPP 主文件: Helloworld.cpp
#include "stdio.h"
#include "jni.h"//导入JNI头文件。该文件在JDK目录下的include文件里。
#include "com_Helloworld.h"//导入java生成的头文件。
#include "stdafx.h"
JNIEXPORT jint JNICALL Java_com_Helloworld_displayHelloWorld(JNIEnv *, jobject, jint j)
{
typedef DWORD (CALLBACK* LPFNEXEFUNC)(DWORD);//定义exe文件中导出函数的指针类型。返回值为DWORD,参数为DWORD。
int i = j;//定义一个返回变量并赋初值。
printf("尝试获取Instance\n");
HINSTANCE hInstance =::LoadLibrary(TEXT("D:\\work\\VS2008\\Projects\\Hello\\Debug\\Hello.exe"));//导入目标EXE文件。导入方法与导入DLL一致。注意路径需要转换格式。
printf("获取Instance完毕\n");
if (hInstance != NULL)
{
printf("尝试获取方法指针\n");
LPFNEXEFUNC returnadd = (LPFNEXEFUNC)::GetProcAddress(hInstance, "returnAdd");
if (!returnadd)
{
printf("获取方法指针失败\n");
// handle the error
FreeLibrary(hInstance);
i = 0;
}
else
{
printf("获取方法指针成功\n");
i = (int)returnadd(i);
printf("方法执行完毕\n");
FreeProcInstance(returnadd);
FreeLibrary(hInstance);
printf("Handle释放完毕\n");
}
}
else
{
printf("Instance为空\n");
}
return i;
}
//$(SolutionDir)$(ConfigurationName) 输出目录
6. 单击 F7 编辑。如果成功,在 java 项目的路径下会生成 5 个文件。其中包括一个 DLL 文件。如下图:
F. 在 Eclipse 下执行 java 项目(如果遇到错误点击忽略)。可以看到如下结果:
改变传入的值。重新运行 java 项目即可获取相应的结果。

