此文解决的是使用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提示如下:
根据项目具体路径。输入命令如例:
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项目即可获取相应的结果。