`
micheal19840929
  • 浏览: 167207 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Java语言的Hook实现

    博客分类:
  • J2SE
阅读更多

引言:最近在玩完美时空的诛仙Online(不知道这里有没人有共同爱好的),这个游戏每晚七点会出现一个任务“新科试炼”。这个任务简单地说就是做选择题,范围小到柴米油盐,大到世界大千,所以多玩的YY上出现一个频道叫“诛仙答题频道”,这个频道会即时为玩家提供正确答案,所以当大家都上YY的时候,最终出来的成绩的高低并不取决于你的知识面,而是取决你家的网速及你的反应速度(答题越早,所获得的成绩越高)。我家的网速很好,可惜我的反应速度一般,所以从来没上过一次前十,因为每次听说YY上的答案后还要移动鼠标去点击相应的答案,这个过程平均需要0.5秒,这无疑是我成绩不高的根本所在。所以我想到了通过按下键盘上的某些按键实现将鼠标移动到指定位置并模拟鼠标键按下事件(以下简称模拟)。也许你会问:这还不简单,直接加个KeyListener不就完了?但是你想过没有,在模拟的时候,窗口的焦点是在游戏窗口,而不是Java程序的窗口(甚至连窗口都没有),所以我们需要一个监听所有进程的接口,这就是我接下要说的“Hook(钩子)”(了解外挂制作的人应该知道是什么东西,没看过?百度之)。不废话,进入正题:

 

      首先,编写之前,我们要使用到一个第三方类库,它实现的功能就是让你直接调用API,而将对Window的调用交给它处理,下载地址是:http://feeling.sourceforge.net/

 

 

 

 

      将包下载完后解压,并创建项目,结构如左图,lib下的三个dll文件要拷到window/system32下,下面就是编码了:首先我们定义一个抽象类来拦截键盘事件,如下:

import org.sf.feeling.swt.win32.extension.hook.data.HookData;
import org.sf.feeling.swt.win32.extension.hook.data.KeyboardHookData;
import org.sf.feeling.swt.win32.extension.hook.listener.HookEventListener;

public abstract class KeyboardHookEventListener implements HookEventListener{
	
	public void acceptHookData(HookData arg0) {
		KeyboardHookData khd = ((KeyboardHookData) arg0);
		{
			if(khd.getTransitionState())		//处理按下事件
			{
				doPress(khd.getWParam());
			}
			else
			{
				doReleased(khd.getWParam());	//处理释放事件
			}
		}
	}
	public abstract void doPress(int keyNum);
	public abstract void doReleased(int keyNum);
}

 接着再定义一个抽象类到拦截鼠标事件,如下:

import org.sf.feeling.swt.win32.extension.hook.data.HookData;
import org.sf.feeling.swt.win32.extension.hook.data.MouseHookData;
import org.sf.feeling.swt.win32.extension.hook.listener.HookEventListener;

public abstract class MouseHookEventListener implements HookEventListener{
	
	public void acceptHookData(HookData hookData) {
		int x=((MouseHookData) hookData).getPointX();
		int y=((MouseHookData) hookData).getPointY();
		switch(hookData.getWParam())
		{
		case 513:
			doLeftPressed(x,y);
			break;
		case 514:
			doLeftReleased(x,y);
			break;
		case 516:
			doRightPressed(x,y);
			break;
		case 517:
			doRightReleased(x,y);
			break;
		case 519:
			doMiddlePressed(x,y);
			break;
		case 520:
			doMiddleReleased(x,y);
			break;
		default:
		}
	}

	protected abstract void doLeftPressed(int x,int y);
	protected abstract void doLeftReleased(int x,int y);
	protected abstract void doRightPressed(int x,int y);
	protected abstract void doRightReleased(int x,int y);
	protected abstract void doMiddlePressed(int x,int y);
	protected abstract void doMiddleReleased(int x,int y);
}

 

至此,我们的项目底层架构已经完成,下面就是业务流程的控制问题了,在贴上我的代码之前,我觉得有必要先做一下诛仙答题跟项目的介绍(又要废话了,顺便帮老池免费作下广告,遇上我是他的福分,^-^)。

答题是这样的:首先,诛仙这个游戏是支持窗口化(且提供几个固定窗口大小供选择),而其中的答题窗口就是窗口中的窗口了(可拖动的)。7点,答题系统开启,每个玩家可选择进入答题窗口,等下一分钟才真正开始,这一分钟中,页面会显示出3个幸运星,但是没有题目........经过一番分析,可以确定用户要输入的有:当前使用的窗口大小及幸运星的位置(幸运星跟选项的位置不固定的,幸运星一确定,选项的位置也就知道了)。以下是关于这个业务的代码:

定义一个特定的鼠标事件响应,如下:

import java.awt.Dimension;
import java.util.LinkedList;
import java.util.List;

public class MyMouseHookEventListener extends MouseHookEventListener {
	private Dimension zeroDimension;
	private List<Dimension> dimensions=new LinkedList<Dimension>();
	private boolean needFetchZeroDimension=false;
	private String currentOffsetSeries="";
	
	public void resetZeroDimension()
	{
		this.needFetchZeroDimension=true;
	}
	
	public void resetDimensions(String dimensionSeries)
	{
		this.dimensions.clear();
		String[] dimStrs=dimensionSeries.split(",");
		for(int i=0;dimStrs!=null&&i<dimStrs.length/2;i++)
		{
			int width=Integer.parseInt(dimStrs[i*2])+(int)zeroDimension.getWidth();
			int height=Integer.parseInt(dimStrs[i*2+1])+(int)zeroDimension.getHeight();
			dimensions.add(new Dimension(width,height));
		}
	}
	
	public String getDimensionSeries()
	{
		String dimSeries="";
		for(Dimension dim:this.dimensions)
		{
			dimSeries=dimSeries+","+(int)(dim.getWidth()-zeroDimension.getWidth())+","+(int)(dim.getHeight()-zeroDimension.getHeight());
		}
		if(dimSeries.length()>0)
		{
			dimSeries=dimSeries.substring(1);
		}
		return dimSeries;
	}

	@Override
	protected void doLeftPressed(int x, int y) {}

	@Override
	protected void doLeftReleased(int x, int y) {}

	@Override
	protected void doMiddlePressed(int x, int y) {}

	@Override
	protected void doMiddleReleased(int x, int y) {}

	@Override
	protected void doRightPressed(int x, int y) {
		if(this.needFetchZeroDimension)
		{
			this.zeroDimension=new Dimension(x,y);
			resetDimensions(currentOffsetSeries);
			this.needFetchZeroDimension=false;
			System.out.println("幸运星位置已获取,关闭重置模式,\r\n现在你可以使用小键盘上的12345来实现鼠标事件模拟,如果你需要重新选择请按F11");
		}
	}

	@Override
	protected void doRightReleased(int x, int y) {}

	public void setCurrentOffsetSeries(String currentOffsetSeries) {
		this.currentOffsetSeries = currentOffsetSeries;
	}

	public List<Dimension> getDimensions() {
		return dimensions;
	}
	
	

}

 

再定义一个运行类:

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.InputEvent;

import org.sf.feeling.swt.win32.extension.hook.Hook;

public class ZhuXianSwifter {
	
	public static final int NUM_1=97;
	public static final int NUM_2=98;
	public static final int NUM_3=99;
	public static final int NUM_4=100;
	public static final int NUM_5=101;
	public static final int F_11=122;
	public static final int F_12=123;
	
	private static final String OFFSET_SERIES_640_480="-125,84,-125,107,-125,130,-125,152,44,0,20,0,0,0";
	private static final String OFFSET_SERIES_800_600="-156,105,-156,134,-156,163,-156,190,55,0,25,0,0,0";
	private static final String OFFSET_SERIES_1024_768="-200,138,-200,172,-200,211,-200,248,70,0,32,0,0,0";

	/**
	 * 使用说明:
	 * 1、启动后先选择所使用的分辨率,目前只支持640*480,800*600,1024*768;
	 * 2、然后使用鼠标右键点击一下试炼答题窗口的第一个幸运星的中心点即可;
	 * 3、使用小键盘的1234选择答案,使用5点星星(第一个使用完会自动用第二个),
	 * 4、只支持命令行模式
	 * 5、F11为取坐标模式,按F11开始,再次按F11结束,并将零坐标跟之前的偏移坐标复制到系统剪贴板
	 * 6、按F12退出程序
	 * @throws AWTException 
	 */
	public static void main(String[] args) throws AWTException {
		
		/*注册鼠标Hook*/
		final MyMouseHookEventListener mouseListener=new MyMouseHookEventListener();
		Hook.MOUSE.addListener(mouseListener);
		Hook.MOUSE.install();
		
		/*系统剪贴板*/
		final Clipboard systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 
		
		final Robot robot=new Robot();
		
		/*键盘监听器*/
		final KeyboardHookEventListener keyboardListener=new KeyboardHookEventListener(){
			
			private boolean haveChooseMode=false;
			private int count=0;

			@Override
			public void doPress(int keyNum) {
				String mode="";
				if(keyNum==F_12)
				{
					if(!mouseListener.getDimensionSeries().equals(""))
					{
						System.out.println("内容已经复制到系统剪贴板");
						Transferable text = new StringSelection(mouseListener.getDimensionSeries());
						systemClipboard.setContents(text,null);
					}
					System.out.println("------程序退出------");
					System.exit(0);
				}
				else if(keyNum==F_11)
				{
					haveChooseMode=false;
					count=0;
					System.out.println("启动重置模式");
					printChooseMode();
				}
				else
				{
					if(haveChooseMode==false)
					{
						switch(keyNum)
						{
						case NUM_1:
							mode="640*480";
							mouseListener.setCurrentOffsetSeries(OFFSET_SERIES_640_480);
							break;
						case NUM_2:
							mode="800*600";
							mouseListener.setCurrentOffsetSeries(OFFSET_SERIES_800_600);
							break;
						case NUM_3:
							mode="1024*768";
							mouseListener.setCurrentOffsetSeries(OFFSET_SERIES_1024_768);
							break;
						default:
							System.out.println("请重新选择:");
							printChooseMode();
							return;
						}
						System.out.println("您选择了"+mode+"分辨率模式");
						haveChooseMode=true;
						mouseListener.resetZeroDimension();
						printFetchZeroCoordinate();
					}
					else
					{
						switch (keyNum) {
						case NUM_1:
							robot.mouseMove((int)mouseListener.getDimensions().get(0).getWidth(),(int)mouseListener.getDimensions().get(0).getHeight());
							robot.mousePress(InputEvent.BUTTON1_MASK);
							robot.mouseRelease(InputEvent.BUTTON1_MASK);
							break;
						case NUM_2:
							robot.mouseMove((int)mouseListener.getDimensions().get(1).getWidth(),(int)mouseListener.getDimensions().get(1).getHeight());
							robot.mousePress(InputEvent.BUTTON1_MASK);
							robot.mouseRelease(InputEvent.BUTTON1_MASK);
							break;
						case NUM_3:
							robot.mouseMove((int)mouseListener.getDimensions().get(2).getWidth(),(int)mouseListener.getDimensions().get(2).getHeight());
							robot.mousePress(InputEvent.BUTTON1_MASK);
							robot.mouseRelease(InputEvent.BUTTON1_MASK);
							break;
						case NUM_4:
							robot.mouseMove((int)mouseListener.getDimensions().get(3).getWidth(),(int)mouseListener.getDimensions().get(3).getHeight());
							robot.mousePress(InputEvent.BUTTON1_MASK);
							robot.mouseRelease(InputEvent.BUTTON1_MASK);
							break;
						case NUM_5:
							robot.mouseMove((int)mouseListener.getDimensions().get(4+count).getWidth(),(int)mouseListener.getDimensions().get(4+count).getHeight());
							robot.mousePress(InputEvent.BUTTON1_MASK);
							robot.mouseRelease(InputEvent.BUTTON1_MASK);
							count++;
							if(count==3)
							{
								count=0;
							}
							break;
						default:
							break;
						}
					}
				}
			}

			@Override
			public void doReleased(int keyNum) {}
			
		};
		
		Hook.KEYBOARD.addListener(keyboardListener);
		Hook.KEYBOARD.install(); // 註冊事件
		
		printChooseMode();

	}
	
	private static void printChooseMode()
	{
		System.out.println("请选择窗口大小:");
		System.out.println("NUM1:640*480");
		System.out.println("NUM2:800*600");
		System.out.println("NUM3:1024*768");
	}
	
	private static void printFetchZeroCoordinate()
	{
		System.out.println("请在第一个幸运星的中心上点击鼠标右键");
	}

}

 

以上就是本项目的所以代码,运行时要先按小键盘的1/2/3选择使用的窗口大小,然后在第一个幸运星的中心点击下右键鼠标就可以了,之后你就可以用小键盘的1/2/3/4/5(5是幸运星)来选择你的答案了。

7
0
分享到:
评论
2 楼 兰斯洛特1987 2012-03-19  
顶!!!!谢谢分享.最近我也在研究这玩意...
1 楼 playfish 2009-08-27  

哈哈,真是为了游戏,不惜花这么大工夫去折腾啊

相关推荐

    Java语言的Hook实现[参照].pdf

    1. **键盘Hook实现**: - 首先,我们需要创建一个实现了`HookEventListener`的抽象类`KeyboardHookEventListener`。这个类有一个`acceptHookData`方法,用于接收来自键盘Hook的数据。当检测到键盘按键按下(`...

    java hook demo

    首先,Java Hook 是Java语言中的一个概念,它通过Java反射API和动态代理来实现对程序运行时行为的干预。Java反射API允许我们在运行时检查类、接口、字段和方法的信息,甚至可以改变其行为。动态代理则是在运行时创建...

    HOOK实现实例,提供源码希望给大家带来方便。

    本实例提供的源码可能是针对某一特定环境(如Android或Windows)的Hook实现。为了更好地理解和学习,我们需要分析源码中的关键部分,例如: 1. Hook注册:这是实现Hook的第一步,需要找到目标函数的地址,并将其...

    Java 与 jni 部分函数Hook项目

    本项目可能涉及到了针对Java和JNI函数的Hook实现,以便在运行时对这些函数进行拦截和替换。 具体知识点包括: 1. **JNI基础知识**:JNI接口定义了Java和本地代码之间的通信协议。它包括一组头文件和库,使得Java...

    HOOK模板汇编版

    "HOOKtemplate"可能是一个包含预定义的HOOK实现模板的文件,可能包括了一些常见函数的HOOK示例,或者是用于快速生成HOOK代码的工具。使用这样的模板可以大大简化开发过程,减少错误,并提高开发效率。 总的来说,...

    java钩子实现源码

    反射是Java语言的一个重要特性,它允许运行状态中的Java程序对自身进行检查并且可以直接操作程序的内部属性。Java.lang.reflect包提供了类、接口和构造器的反射类,例如Class、Method和Constructor。 Java钩子的...

    Java监听键盘鼠标全局事件[定义].pdf

    Java监听键盘鼠标全局事件是指使用Java语言来监听和处理键盘和鼠标事件的技术。这项技术广泛应用于游戏开发、自动化测试、屏幕阅读器等领域。下面是Java监听键盘鼠标全局事件的详细知识点: 一、Java Native ...

    JVM.rar_java 工作流_java 虚拟机_jvm_jvm hook_虚拟机 Java

    类加载器负责加载类文件,运行时数据区存储程序执行时的各种数据,执行引擎执行字节码,本地方法接口用于调用非Java语言实现的函数,本地库则提供了这些函数的实现。 工作流方面,JVM的启动首先是加载主类,然后...

    dalvik_hook_demo

    JNI是Java平台的标准组成部分,允许Java代码和其他语言写的代码进行交互。在Dalvik Hook中,JNI常用于实现System Call Hook,因为许多系统调用是在C/C++层实现的。 **应用场景:** 1. **调试与日志记录**:Hook可以...

    PC微信聊天机器人HOOK

    标题中的“PC微信聊天机器人HOOK”指的是通过编程技术对微信PC版进行钩子(HOOK)技术的实现,以便能够监听并自动化处理微信的聊天事件。这通常涉及到编程语言中的底层API调用,如Windows API,以及对微信客户端的...

    Java SpEL、Ognl、MVEL2表达式Hook并记录小项目.zip

    SpEL的Hook实现通常涉及到解析表达式并在执行时插入自定义逻辑,如记录日志或验证表达式内容。 接着,我们来看OGNL。OGNL是一种强大的、灵活的表达式语言,它允许开发者以自然的方式访问和修改对象属性。相比于SpEL...

    inlineHook工具类

    这种技术在Java层通常通过修改Dalvik字节码或者 ART的Native方法来实现,在Native层则涉及到汇编语言和指针替换。 **2. inlineHook的实现步骤** - **定位目标函数**:首先需要确定要hook的具体函数地址,这通常...

    wxwork-pc-api 使用HOOK技术将核心功能封装成dll,并提供简易的接口给支持调用dll的语言使用

    wxwork_pc_api 使用HOOK技术将核心功能封装成dll,并提供简易的接口给支持调用dll的语言使用。 你可以通过扩展 wxwork_pc_api 来实现: 监控或收集企业微信消息 自动消息推送 聊天机器人 通过企业微信远程控制你的...

    微信Hook读取二维码源码

    要实现微信Hook读取二维码,我们需要编写一个Xposed模块,该模块会监听微信内部处理二维码扫描的函数,并在触发扫描事件时捕获二维码数据。这需要对Java和Android SDK有深入理解,特别是对反射和Intent机制的运用。 ...

    老外写的Socket hook代码

    为了充分利用这个代码库,你需要了解一些关键概念和编程语言(如C/C++、C#或Java),以及如何在代码中集成和调用这些Hook函数。同时,理解网络协议(如TCP/IP)和Socket编程基础也是必要的。在实际使用中,需要注意...

    NDK编译 JAVA JNI 原生调用

    JNI(Java Native Interface)是Java平台的一个重要特性,它允许Java代码和其他语言写的代码进行交互。在Android开发中,NDK(Native Development Kit)则是一个工具集,它允许开发者使用C/C++编写部分应用程序,以...

    远程键盘映射工具Java

    1. "Java语言的Hook实现 - 当程序爱上游戏 - JavaEye技术网站.mht" 这个文件可能是一个网页存档,包含了关于Java Hook技术的详细讲解,可能涉及到具体的实现细节,如如何使用JNI进行hook,以及在游戏场景下的应用。...

    JAVA_API1.6文档(中文)

    java.lang.instrument 提供允许 Java 编程语言代理检测运行在 JVM 上的程序的服务。 java.lang.management 提供管理接口,用于监视和管理 Java 虚拟机以及 Java 虚拟机在其上运行的操作系统。 java.lang.ref 提供...

Global site tag (gtag.js) - Google Analytics