Java语言的Hook实现

Wesley13
• 阅读 608

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

 

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

 

 

 

 

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

Java代码  Java语言的Hook实现 Java语言的Hook实现
  1. import org.sf.feeling.swt.win32.extension.hook.data.HookData;  
  2. import org.sf.feeling.swt.win32.extension.hook.data.KeyboardHookData;  
  3. import org.sf.feeling.swt.win32.extension.hook.listener.HookEventListener;  
  4.   
  5. public abstract class KeyboardHookEventListener implements HookEventListener{  
  6.       
  7.     public void acceptHookData(HookData arg0) {  
  8.         KeyboardHookData khd = ((KeyboardHookData) arg0);  
  9.         {  
  10.             if(khd.getTransitionState())        //处理按下事件  
  11.             {  
  12.                 doPress(khd.getWParam());  
  13.             }  
  14.             else  
  15.             {  
  16.                 doReleased(khd.getWParam());    //处理释放事件  
  17.             }  
  18.         }  
  19.     }  
  20.     public abstract void doPress(int keyNum);  
  21.     public abstract void doReleased(int keyNum);  
  22. }  

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

Java代码  Java语言的Hook实现 Java语言的Hook实现
  1. import org.sf.feeling.swt.win32.extension.hook.data.HookData;  
  2. import org.sf.feeling.swt.win32.extension.hook.data.MouseHookData;  
  3. import org.sf.feeling.swt.win32.extension.hook.listener.HookEventListener;  
  4.   
  5. public abstract class MouseHookEventListener implements HookEventListener{  
  6.       
  7.     public void acceptHookData(HookData hookData) {  
  8.         int x=((MouseHookData) hookData).getPointX();  
  9.         int y=((MouseHookData) hookData).getPointY();  
  10.         switch(hookData.getWParam())  
  11.         {  
  12.         case 513:  
  13.             doLeftPressed(x,y);  
  14.             break;  
  15.         case 514:  
  16.             doLeftReleased(x,y);  
  17.             break;  
  18.         case 516:  
  19.             doRightPressed(x,y);  
  20.             break;  
  21.         case 517:  
  22.             doRightReleased(x,y);  
  23.             break;  
  24.         case 519:  
  25.             doMiddlePressed(x,y);  
  26.             break;  
  27.         case 520:  
  28.             doMiddleReleased(x,y);  
  29.             break;  
  30.         default:  
  31.         }  
  32.     }  
  33.   
  34.     protected abstract void doLeftPressed(int x,int y);  
  35.     protected abstract void doLeftReleased(int x,int y);  
  36.     protected abstract void doRightPressed(int x,int y);  
  37.     protected abstract void doRightReleased(int x,int y);  
  38.     protected abstract void doMiddlePressed(int x,int y);  
  39.     protected abstract void doMiddleReleased(int x,int y);  
  40. }  

 

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

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

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

Java代码  Java语言的Hook实现 Java语言的Hook实现
  1. import java.awt.Dimension;  
  2. import java.util.LinkedList;  
  3. import java.util.List;  
  4.   
  5. public class MyMouseHookEventListener extends MouseHookEventListener {  
  6.     private Dimension zeroDimension;  
  7.     private List<Dimension> dimensions=new LinkedList<Dimension>();  
  8.     private boolean needFetchZeroDimension=false;  
  9.     private String currentOffsetSeries="";  
  10.       
  11.     public void resetZeroDimension()  
  12.     {  
  13.         this.needFetchZeroDimension=true;  
  14.     }  
  15.       
  16.     public void resetDimensions(String dimensionSeries)  
  17.     {  
  18.         this.dimensions.clear();  
  19.         String\[\] dimStrs=dimensionSeries.split(",");  
  20.         for(int i=0;dimStrs!=null&&i<dimStrs.length/2;i++)  
  21.         {  
  22.             int width=Integer.parseInt(dimStrs\[i\*2\])+(int)zeroDimension.getWidth();  
  23.             int height=Integer.parseInt(dimStrs\[i\*2+1\])+(int)zeroDimension.getHeight();  
  24.             dimensions.add(new Dimension(width,height));  
  25.         }  
  26.     }  
  27.       
  28.     public String getDimensionSeries()  
  29.     {  
  30.         String dimSeries="";  
  31.         for(Dimension dim:this.dimensions)  
  32.         {  
  33.             dimSeries=dimSeries+","+(int)(dim.getWidth()-zeroDimension.getWidth())+","+(int)(dim.getHeight()-zeroDimension.getHeight());  
  34.         }  
  35.         if(dimSeries.length()>0)  
  36.         {  
  37.             dimSeries=dimSeries.substring(1);  
  38.         }  
  39.         return dimSeries;  
  40.     }  
  41.   
  42.     @Override  
  43.     protected void doLeftPressed(int x, int y) {}  
  44.   
  45.     @Override  
  46.     protected void doLeftReleased(int x, int y) {}  
  47.   
  48.     @Override  
  49.     protected void doMiddlePressed(int x, int y) {}  
  50.   
  51.     @Override  
  52.     protected void doMiddleReleased(int x, int y) {}  
  53.   
  54.     @Override  
  55.     protected void doRightPressed(int x, int y) {  
  56.         if(this.needFetchZeroDimension)  
  57.         {  
  58.             this.zeroDimension=new Dimension(x,y);  
  59.             resetDimensions(currentOffsetSeries);  
  60.             this.needFetchZeroDimension=false;  
  61.             System.out.println("幸运星位置已获取,关闭重置模式,\\r\\n现在你可以使用小键盘上的12345来实现鼠标事件模拟,如果你需要重新选择请按F11");  
  62.         }  
  63.     }  
  64.   
  65.     @Override  
  66.     protected void doRightReleased(int x, int y) {}  
  67.   
  68.     public void setCurrentOffsetSeries(String currentOffsetSeries) {  
  69.         this.currentOffsetSeries = currentOffsetSeries;  
  70.     }  
  71.   
  72.     public List<Dimension> getDimensions() {  
  73.         return dimensions;  
  74.     }  
  75.       
  76.       
  77.   
  78. }  

 

再定义一个运行类:

Java代码  Java语言的Hook实现 Java语言的Hook实现
  1. import java.awt.AWTException;  
  2. import java.awt.Robot;  
  3. import java.awt.Toolkit;  
  4. import java.awt.datatransfer.Clipboard;  
  5. import java.awt.datatransfer.StringSelection;  
  6. import java.awt.datatransfer.Transferable;  
  7. import java.awt.event.InputEvent;  
  8.   
  9. import org.sf.feeling.swt.win32.extension.hook.Hook;  
  10.   
  11. public class ZhuXianSwifter {  
  12.       
  13.     public static final int NUM\_1=97;  
  14.     public static final int NUM\_2=98;  
  15.     public static final int NUM\_3=99;  
  16.     public static final int NUM\_4=100;  
  17.     public static final int NUM\_5=101;  
  18.     public static final int F\_11=122;  
  19.     public static final int F\_12=123;  
  20.       
  21.     private static final String OFFSET\_SERIES\_640\_480="-125,84,-125,107,-125,130,-125,152,44,0,20,0,0,0";  
  22.     private static final String OFFSET\_SERIES\_800\_600="-156,105,-156,134,-156,163,-156,190,55,0,25,0,0,0";  
  23.     private static final String OFFSET\_SERIES\_1024\_768="-200,138,-200,172,-200,211,-200,248,70,0,32,0,0,0";  
  24.   
  25.     /\*\* 
  26.      \* 使用说明: 
  27.      \* 1、启动后先选择所使用的分辨率,目前只支持640\*480,800\*600,1024\*768; 
  28.      \* 2、然后使用鼠标右键点击一下试炼答题窗口的第一个幸运星的中心点即可; 
  29.      \* 3、使用小键盘的1234选择答案,使用5点星星(第一个使用完会自动用第二个), 
  30.      \* 4、只支持命令行模式 
  31.      \* 5、F11为取坐标模式,按F11开始,再次按F11结束,并将零坐标跟之前的偏移坐标复制到系统剪贴板 
  32.      \* 6、按F12退出程序 
  33.      \* @throws AWTException  
  34.      \*/  
  35.     public static void main(String\[\] args) throws AWTException {  
  36.           
  37.         /\*注册鼠标Hook\*/  
  38.         final MyMouseHookEventListener mouseListener=new MyMouseHookEventListener();  
  39.         Hook.MOUSE.addListener(mouseListener);  
  40.         Hook.MOUSE.install();  
  41.           
  42.         /\*系统剪贴板\*/  
  43.         final Clipboard systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();   
  44.           
  45.         final Robot robot=new Robot();  
  46.           
  47.         /\*键盘监听器\*/  
  48.         final KeyboardHookEventListener keyboardListener=new KeyboardHookEventListener(){  
  49.               
  50.             private boolean haveChooseMode=false;  
  51.             private int count=0;  
  52.   
  53.             @Override  
  54.             public void doPress(int keyNum) {  
  55.                 String mode="";  
  56.                 if(keyNum==F\_12)  
  57.                 {  
  58.                     if(!mouseListener.getDimensionSeries().equals(""))  
  59.                     {  
  60.                         System.out.println("内容已经复制到系统剪贴板");  
  61.                         Transferable text = new StringSelection(mouseListener.getDimensionSeries());  
  62.                         systemClipboard.setContents(text,null);  
  63.                     }  
  64.                     System.out.println("------程序退出------");  
  65.                     System.exit(0);  
  66.                 }  
  67.                 else if(keyNum==F\_11)  
  68.                 {  
  69.                     haveChooseMode=false;  
  70.                     count=0;  
  71.                     System.out.println("启动重置模式");  
  72.                     printChooseMode();  
  73.                 }  
  74.                 else  
  75.                 {  
  76.                     if(haveChooseMode==false)  
  77.                     {  
  78.                         switch(keyNum)  
  79.                         {  
  80.                         case NUM\_1:  
  81.                             mode="640\*480";  
  82.                             mouseListener.setCurrentOffsetSeries(OFFSET\_SERIES\_640\_480);  
  83.                             break;  
  84.                         case NUM\_2:  
  85.                             mode="800\*600";  
  86.                             mouseListener.setCurrentOffsetSeries(OFFSET\_SERIES\_800\_600);  
  87.                             break;  
  88.                         case NUM\_3:  
  89.                             mode="1024\*768";  
  90.                             mouseListener.setCurrentOffsetSeries(OFFSET\_SERIES\_1024\_768);  
  91.                             break;  
  92.                         default:  
  93.                             System.out.println("请重新选择:");  
  94.                             printChooseMode();  
  95.                             return;  
  96.                         }  
  97.                         System.out.println("您选择了"+mode+"分辨率模式");  
  98.                         haveChooseMode=true;  
  99.                         mouseListener.resetZeroDimension();  
  100.                         printFetchZeroCoordinate();  
  101.                     }  
  102.                     else  
  103.                     {  
  104.                         switch (keyNum) {  
  105.                         case NUM\_1:  
  106.                             robot.mouseMove((int)mouseListener.getDimensions().get(0).getWidth(),(int)mouseListener.getDimensions().get(0).getHeight());  
  107.                             robot.mousePress(InputEvent.BUTTON1\_MASK);  
  108.                             robot.mouseRelease(InputEvent.BUTTON1\_MASK);  
  109.                             break;  
  110.                         case NUM\_2:  
  111.                             robot.mouseMove((int)mouseListener.getDimensions().get(1).getWidth(),(int)mouseListener.getDimensions().get(1).getHeight());  
  112.                             robot.mousePress(InputEvent.BUTTON1\_MASK);  
  113.                             robot.mouseRelease(InputEvent.BUTTON1\_MASK);  
  114.                             break;  
  115.                         case NUM\_3:  
  116.                             robot.mouseMove((int)mouseListener.getDimensions().get(2).getWidth(),(int)mouseListener.getDimensions().get(2).getHeight());  
  117.                             robot.mousePress(InputEvent.BUTTON1\_MASK);  
  118.                             robot.mouseRelease(InputEvent.BUTTON1\_MASK);  
  119.                             break;  
  120.                         case NUM\_4:  
  121.                             robot.mouseMove((int)mouseListener.getDimensions().get(3).getWidth(),(int)mouseListener.getDimensions().get(3).getHeight());  
  122.                             robot.mousePress(InputEvent.BUTTON1\_MASK);  
  123.                             robot.mouseRelease(InputEvent.BUTTON1\_MASK);  
  124.                             break;  
  125.                         case NUM\_5:  
  126.                             robot.mouseMove((int)mouseListener.getDimensions().get(4+count).getWidth(),(int)mouseListener.getDimensions().get(4+count).getHeight());  
  127.                             robot.mousePress(InputEvent.BUTTON1\_MASK);  
  128.                             robot.mouseRelease(InputEvent.BUTTON1\_MASK);  
  129.                             count++;  
  130.                             if(count==3)  
  131.                             {  
  132.                                 count=0;  
  133.                             }  
  134.                             break;  
  135.                         default:  
  136.                             break;  
  137.                         }  
  138.                     }  
  139.                 }  
  140.             }  
  141.   
  142.             @Override  
  143.             public void doReleased(int keyNum) {}  
  144.               
  145.         };  
  146.           
  147.         Hook.KEYBOARD.addListener(keyboardListener);  
  148.         Hook.KEYBOARD.install(); // 註冊事件  
  149.           
  150.         printChooseMode();  
  151.   
  152.     }  
  153.       
  154.     private static void printChooseMode()  
  155.     {  
  156.         System.out.println("请选择窗口大小:");  
  157.         System.out.println("NUM1:640\*480");  
  158.         System.out.println("NUM2:800\*600");  
  159.         System.out.println("NUM3:1024\*768");  
  160.     }  
  161.       
  162.     private static void printFetchZeroCoordinate()  
  163.     {  
  164.         System.out.println("请在第一个幸运星的中心上点击鼠标右键");  
  165.     }  
  166.   
  167. }  

 

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

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java反射, 不看你可别后悔
<divid"content\_views"class"markdown\_views"<!flowchart箭头图标勿删<svgxmlns"http://www.w3.org/2000/svg"style"display:none;"<pathstrokelinecap"round"d"M5,00,
Wesley13 Wesley13
3年前
QQ玩一玩好友排行榜与世界排行榜
QQ玩一玩好友排行榜与世界排行榜1、开发环境CocosCreatorV2.0.5手Q版本V7.9.0.3820(目前市场中最新版本)qqPlayCore.jsbuildTime:'FriNov09201813:20:45GMT0800(GMT08:00)'上出现,
Stella981 Stella981
3年前
Skynet 通过组播(Multicast)实现一个简单的世界频道
什么是世界频道?  "世界频道"这个概念就是在一个游戏内经常见到。简单来说,世界频道就是在游戏内的一个大区中所有玩家可以接收、发布的消息的一个玩家间的统称。  玩家在游戏中进行体验的同时,通常需要发布一些特殊的消息,包含:"买卖装备、组队等";通常游戏中的各种频道的实现都依赖于消息队列的广播方式。Skynet的组播解决
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
JOptionPane修改图标
1.在Linux平台下.JOptionPane会显示Java默认的图标,在window平台不显示图标,如何替换这个图标了?2JOptionPane.setIcon(Icon)修改的是内容区域的icon,而不是左上角的Icon.所以需要通过修改Jdialog/Frame的图标来达到修改默认图标的问题.3.代码:if(JOptio
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这