`
micheal19840929
  • 浏览: 161138 次
  • 性别: 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

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

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

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

    JAVA_API1.6文档(中文)

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

    Xposed的一个Hook模块,用于java加密算法破解.zip

    Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems公司于1995年5月正式发布。它的设计目标是“一次编写,到处运行(Write Once, Run Anywhere)”,这意味着开发者可以使用Java编写应用程序,并在支持...

    Java 1.6 API 中文 New

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

    java api最新7.0

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

    JavaAPI1.6中文chm文档 part1

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

    JavaAPI中文chm文档 part2

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

    [Java参考文档]

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

    DaenWxHook+千寻VX框架

    免费的微信HOOK和框架,提供HTTPAPI接口和封装好的框架,微信版本为3.6.0.18,内置HTTPAPI,方便您使用自己喜欢的编程语言快速开发。 支持文本代码,多人艾特,emoji表情,微信表情等,全面解决收发消息、昵称内的...

    利用Xposed框架hook系统方法 改变GPS定位 实现Android模拟定位.zip

    软件开发设计:PHP、QT、应用软件开发、系统软件开发、移动应用开发、网站开发C++、Java、python、web、C#等语言的项目开发与学习资料 硬件与设备:单片机、EDA、proteus、RTOS、包括计算机硬件、服务器、网络设备、...

    [Java参考文档].JDK_API 1.6

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

    JDK_1_6 API

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

    wechat_pc_api:微信机器人,微信HOOK,微信PC版挂钩

    您可以通过扩展wechat_pc_api来实现:监控或收集微信消息自动消息推聊天机器人通过微信远程控制你的设备测试可以使用语言有C / C ++,C#,易语言,Python,Java,Go,NodeJs,PHP,VB,Delphi。目前支持的微信PC...

    基于Laravel开发的一款跨境电商系统,主要面向外贸,跨境行业提供的商品管理、订单管理、会员管理、支付、物流、系统管理等功能

    BeikeShop商城 Beike多语言商城 多货币商城B2C商城系统 代码100%开源 PHP商城系统 H5/PC商城 跨境电商系统 跨境商城系统 电商商城... Event 机制实现Hook功能,自定义可视化装修。BeikeShop一款开源好用的跨境电商系统

    nodejs_wx_aipay_api:微信支付宝个人免签收款Api系统,有了它对接再也不用担心我的业务不能支付了

    特点:支付通知单,0程序费实时到账(不经过任何第三方,直接到账微信/支付宝余额),提供无依赖服务端源代码运行在自己的服务器上,支持php / java / python等任意开发语言监听,监听方式非xp框架HOOK的方式,无需...

Global site tag (gtag.js) - Google Analytics