游戏外挂一般分为三个级别:初级是鼠标、键盘模拟,中级是Call游戏内部函数,读写内存,高级是抓包,封包的“脱机挂”(完全模拟客户端网络数据,不用运行游戏)。用C#写外挂的不是很多,大部分是C++,主要原因是MS的C#目前不支持内联汇编功能。因此用C++写底层库,然后用C#调用成为DONET爱好者开发外挂的首选。

对于鼠标键盘模拟的外挂而言,很多人认为没有什么技术含量,因为无非就是SendMessage或者Key_event,再高级点就是Hook进入程序内部操作。我也曾用这种技术开发过一些游戏辅助程序。但最近我在研究XNA的时候遇到了一点麻烦,这种屡试不爽方法居然失效了。游戏对于消息命令一点也不“服从”,我们有这样的疑问:它是如何识别真实键盘的按键的呢?难道是程序中有判断吗?

如果抛开上面的疑问,换另外一个角度思考。在DOS时代,还记得那个经典的去除BIOS密码的Debug命令吗?其原理就是利用了BIOS中断,其实对于键盘按键,不管Windows怎样封装,到最后都会调用BIOS中断来识别按键。然而消息发送毕竟是Windows系统的玩意儿,不管在Windows层面上怎样判断,我们只要让BIOS下一道“圣旨”,看哪个游戏还敢如此嚣张!!!

在解释更详细的原理之前,我们先来抓出幕后黑手,看看是哪个给游戏撑腰?让它有胆子违抗Windows消息命令。究竟是判断了真实键盘信息,还是有其他原因。结果在DirectX编程中发现了DirectInput这个API。就是它绕过了Windows的消息机制,它的目的是为了让游戏的实时性控制更好、更快。Windows消息是队列形式的,在传递过程中会有延时,比如格斗类游戏对实时性控制要求是非常高的,Window消息机制不能满足这个需求。而DirectInput直接和键盘驱动程序打交道,效率当然要高出一大截。我认为大部分游戏不响应消息的真正的原因在这里,而不是故意写了反作弊系统。

既然知道了是DirectInput在搞鬼,可是我们该怎么办呢?前面我们提到了一点思路,因此最实际的办法就是直接读写键盘端口的方法来模拟硬件事件。在DOS时代每按下一个键就会产生一个键盘中断,这样程序会跳到BIOS中的键盘中断去处理程序执行。在曾经的那个年代我最牛X的是QBasic程序了。QBasic中有一个OUT函数可以向指定端口写入数据,用INP函数可以从指定端口读取数据。因此用QBasic来做硬件级别的键盘模拟非常简单。

假如这个键的扫描码为0x51,先模拟按下这个键:
OUT &H64,&HD2 '把数据0xD2发送到0x64端口。
OUT &H60,&H51 '把扫描码0x51发送到0x60端口,表示模拟按下扫描码为0x51的这个键
下面再模拟释放这个按键:
OUT &H64,&HD2 '把数据0xD2发送到0x64端口。
OUT &H60,(&H50 OR &H80) '把扫描码0x50与数据0x80进行或运算,表示释放这个键。

当然DOS时代早已经一去不复返,只是刚才提到QBasic时候让我感慨了一下其曾经的强大而已。如果真的用QBasic来写模拟程序,在Win98下可以成功,但是在Win2000以后的版本就不行了,因为进入NT时代后DOS只是一个虚拟机系统了。

现在的问题很清晰了,就是如何在Windows环境下来进行端口操作。由于比较底层,Windows不会那么轻松让你得逞。一般会有两种方法:其一就是利用驱动,在驱动里面还有什么事情不能做?其二就是利用“调用门”从Ring3跳到Ring0去进行相关操作。在网上搜一搜应该都会找到相关的资料。我听格格说过有个VB做的叫做“按键精灵”的貌似比较牛X,下载下来研究后发现其使用了老外的WINIO驱动的原理。用WINIO就容易多了,因为我在驱动方面的能力实在是&*^%$^&*。

于是在WINIO的基础上,我使用C#进行了简单的二次封装,使其适用于DONET平台上使用驱动级别的按键模拟。在我的DLL中提供了以下几个方法:

InitSuperKeys() 安装WINIO驱动,一般用于Form_Load事件中调用

CloseSuperKeys() 卸载WINIO驱动,一般用于Form_Closed事件中调用

KeyDown(Key) 模拟普通Key键按下。

KeyDownEx(Key)模拟扩展Key键按下。

KeyUp(Key)模拟普通Key键弹起。

KeyUpEx(Key)模拟扩展Key键弹起。

KeyPress(Key)模拟普通Key键按下并弹起一次。其中按下和弹起的默认时间间隔是200毫秒

KeyPress(Key,Int32)模拟普通Key键按下并弹起一次。其中按下和弹起的时间间隔是第二个参数,单位为毫秒。

KeyPressEx(Key)模拟扩展按键Key按下并弹起一次。其中按下和弹起的默认时间间隔是200毫秒,写入扩展按键信息间隔时间为100毫秒

KeyPressEx(Key,Int32)模拟扩展按键Key按下并弹起一次。其中按下和弹起的时间间隔是第二个参数,单位为毫秒,写入扩展按键信息间隔时间为100毫秒。

KeyPressEx(Key,Int32,Int32)模拟扩展按键Key按下并弹起一次。其中按下和弹起的时间间隔是第二个参数,单位为毫秒,写入扩展按键信息间隔时间是第三个参数,单位为毫秒。

特别说明:
1、 在执行模拟按键之前必须先执行InitSuperKeys()进行驱动的安装,在窗体关闭之后最好可以卸载驱动。

2、 以上方法中的参数Key为我在WinIoSys类中定义的一个枚举,并非DONET系统的Key枚举。

3、 普通Key是指A,B,C,Space这种标准键盘按键。而扩展按键是指“方向键”等特殊按键,系统在处理这种扩展键的时候会先有一个写扩展按键信息的时间。因此没有Ex结尾的方法都是用于标准普通按键的,有Ex结尾的方法是用于特殊的扩展按键的。其中他们都有重载,用户可以自己设置间隔时间。至于按键详细分类,请自己上Google搜索。

4、 模拟一次按键事件后,一定要让程序Sleep一些毫秒,否则下一个按键是无法正常模拟出的。

5、 貌似USB走的是总线,和端口操作无关,因此该方法理论上不支持USB接口的键盘。

6、 部分杀毒软件会提醒用户安装驱动,或者将WinIo.sys报为病毒,其实这是正常现象。如果搞这种非正常的事情,杀毒软件当然看不过去。请不要质疑本程序有病毒或木马,最简单的方法就是关闭杀毒软件~。不放心者,请勿使用。

既然有了这么方便的接口,我们来尝试写个小东西看看威力如何。我们就拿《拳皇》来举例子吧。这款游戏就号称针对Window消息绝对无效。

我们先新建一个EXE工程,并引用我的SuperKeys.dll。定义一个全局变量
WinIoSys m_IoSys = new WinIoSys();
在Load事件中安装驱动
m_IoSys.InitSuperKeys();
在Closed事件中卸载驱动
m_IoSys.CloseSuperKeys();

我们当然可以写一个全局钩子,在游戏执行的时候,按下某键就可以进行一系列的按键模拟。但是这仅仅是个DEMO,不用搞那么正式,直接写一个计时器来触发好了。为了节约时间,在每次点击button三秒后执行模拟按键操作,在这三秒中要记得把拳皇游戏窗口激活哦。
我们先来统一下键盘设置,如图所示:

我们决定来模拟“八神”的最简单一招: 暗杀炎(就是从地上放把火过去),如果“八神”在左侧,其键盘操作为::↓↘→+A或C。就是上图中的SDU或SDJ按键。以SDJ为例子,在模拟这个键盘时要非常细心才行,我一开始这样模拟:

m_IoSys.KeyPress(WinIoSys.Key.VK_S);
Thread.Sleep(200);
m_IoSys.KeyPress(WinIoSys.Key.VK_D);
Thread.Sleep(200);
m_IoSys.KeyPress(WinIoSys.Key.VK_J,200);

结果没有成功。仔细研究了一下,其实我们按SDJ按键的时候,是先将S 按下,再将D键按下,然后弹起S键,再弹起D键,最后再按下J键的。那么真正的过程应该这样写:

m_IoSys.KeyDown(WinIoSys.Key.VK_S);
Thread.Sleep(100);
m_IoSys.KeyDown(WinIoSys.Key.VK_D);
Thread.Sleep(100);
m_IoSys.KeyUp(WinIoSys.Key.VK_S);
Thread.Sleep(100);
m_IoSys.KeyUp(WinIoSys.Key.VK_D);
Thread.Sleep(10); //这个地方不能间隔太大,太大就整个动作连不起来了。
m_IoSys.KeyPress(WinIoSys.Key.VK_J, 200);

经过测试,成功模拟我们想要的操作。大家可以看到实际的模拟按键效果。

代码下载地址:

https://download.csdn.net/download/dragoonnet/10919497

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace lizheAionWG
{public class WinIo{public const int KBC_KEY_CMD = 0x64;public const int KBC_KEY_DATA = 0x60;[DllImport("winio.dll")]public static extern bool InitializeWinIo();[DllImport("winio.dll")]public static extern bool GetPortVal(IntPtr wPortAddr, out int pdwPortVal, byte bSize);[DllImport("winio.dll")]public static extern bool SetPortVal(uint wPortAddr, IntPtr dwPortVal, byte bSize);[DllImport("winio.dll")]public static extern byte MapPhysToLin(byte pbPhysAddr, uint dwPhysSize, IntPtr PhysicalMemoryHandle);[DllImport("winio.dll")]public static extern bool UnmapPhysicalMemory(IntPtr PhysicalMemoryHandle, byte pbLinAddr);[DllImport("winio.dll")]public static extern bool GetPhysLong(IntPtr pbPhysAddr, byte pdwPhysVal);[DllImport("winio.dll")]public static extern bool SetPhysLong(IntPtr pbPhysAddr, byte dwPhysVal);[DllImport("winio.dll")]public static extern void ShutdownWinIo();[DllImport("user32.dll")]public static extern int MapVirtualKey(uint Ucode, uint uMapType);public void sendwinio(){if (InitializeWinIo()){KBCWait4IBE();}}///Wait for Buffer gets emptyprivate void KBCWait4IBE(){int dwVal = 0;do{bool flag = GetPortVal((IntPtr)0x64, out dwVal, 1);}while ((dwVal & 0x2) > 0);}/// key downpublic void MykeyDown(int vKeyCoad){int btScancode = 0;btScancode = MapVirtualKey((byte)vKeyCoad, 0);KBCWait4IBE();SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)0xe2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)btScancode, 1);}/// Key uppublic void MykeyUp(int vKeyCoad){int btScancode = 0;btScancode = MapVirtualKey((byte)vKeyCoad, 0);KBCWait4IBE();SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)0xe0, 1);KBCWait4IBE();SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1);KBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)btScancode, 1);}/// Simulate mouse downpublic void MyMouseDown(int vKeyCoad){int btScancode = 0;btScancode = MapVirtualKey((byte)vKeyCoad, 0);KBCWait4IBE(); // 'wait for buffer gets emptySetPortVal(KBC_KEY_CMD, (IntPtr)0xD3, 1);// 'send write commandKBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1);// 'write in io}/// Simulate mouse uppublic void MyMouseUp(int vKeyCoad){int btScancode = 0;btScancode = MapVirtualKey((byte)vKeyCoad, 0);KBCWait4IBE(); // 'wait for buffer gets emptySetPortVal(KBC_KEY_CMD, (IntPtr)0xD3, 1); //'send write commandKBCWait4IBE();SetPortVal(KBC_KEY_DATA, (IntPtr)(btScancode | 0x80), 1);// 'write in io}//----------------------------------------------------------------------------------//VK codes//----------------------------------------------------------------------------------public enum Key{// mouse movementsmove = 0x0001,leftdown = 0x0002,leftup = 0x0004,rightdown = 0x0008,rightup = 0x0010,middledown = 0x0020,//keyboard stuffVK_LBUTTON = 1,VK_RBUTTON = 2,VK_CANCEL = 3,VK_MBUTTON = 4,VK_BACK = 8,VK_TAB = 9,VK_CLEAR = 12,VK_RETURN = 13,VK_SHIFT = 16,VK_CONTROL = 17,VK_MENU = 18,VK_PAUSE = 19,VK_CAPITAL = 20,VK_ESCAPE = 27,VK_SPACE = 32,VK_PRIOR = 33,VK_NEXT = 34,VK_END = 35,VK_HOME = 36,VK_LEFT = 37,VK_UP = 38,VK_RIGHT = 39,VK_DOWN = 40,VK_SELECT = 41,VK_PRINT = 42,VK_EXECUTE = 43,VK_SNAPSHOT = 44,VK_INSERT = 45,VK_DELETE = 46,VK_HELP = 47,VK_NUM0 = 48, //0VK_NUM1 = 49, //1VK_NUM2 = 50, //2VK_NUM3 = 51, //3VK_NUM4 = 52, //4VK_NUM5 = 53, //5VK_NUM6 = 54, //6VK_NUM7 = 55, //7VK_NUM8 = 56, //8VK_NUM9 = 57, //9VK_A = 65, //AVK_B = 66, //BVK_C = 67, //CVK_D = 68, //DVK_E = 69, //EVK_F = 70, //FVK_G = 71, //GVK_H = 72, //HVK_I = 73, //IVK_J = 74, //JVK_K = 75, //KVK_L = 76, //LVK_M = 77, //MVK_N = 78, //NVK_O = 79, //OVK_P = 80, //PVK_Q = 81, //QVK_R = 82, //RVK_S = 83, //SVK_T = 84, //TVK_U = 85, //UVK_V = 86, //VVK_W = 87, //WVK_X = 88, //XVK_Y = 89, //YVK_Z = 90, //ZVK_NUMPAD0 = 96, //0VK_NUMPAD1 = 97, //1VK_NUMPAD2 = 98, //2VK_NUMPAD3 = 99, //3VK_NUMPAD4 = 100, //4VK_NUMPAD5 = 101, //5VK_NUMPAD6 = 102, //6VK_NUMPAD7 = 103, //7VK_NUMPAD8 = 104, //8VK_NUMPAD9 = 105, //9VK_NULTIPLY = 106,VK_ADD = 107,VK_SEPARATOR = 108,VK_SUBTRACT = 109,VK_DECIMAL = 110,VK_DIVIDE = 111,VK_F1 = 112,VK_F2 = 113,VK_F3 = 114,VK_F4 = 115,VK_F5 = 116,VK_F6 = 117,VK_F7 = 118,VK_F8 = 119,VK_F9 = 120,VK_F10 = 121,VK_F11 = 122,VK_F12 = 123,VK_NUMLOCK = 144,VK_SCROLL = 145,middleup = 0x0040,xdown = 0x0080,xup = 0x0100,wheel = 0x0800,virtualdesk = 0x4000,absolute = 0x8000}}
}
    InitSuperKeys() 安装WINIO驱动,一般用于Form_Load事件中调用CloseSuperKeys() 卸载WINIO驱动,一般用于Form_Closed事件中调用KeyDown(Key) 模拟普通Key键按下。KeyDownEx(Key)模拟扩展Key键按下。KeyUp(Key)模拟普通Key键弹起。KeyUpEx(Key)模拟扩展Key键弹起。KeyPress(Key)模拟普通Key键按下并弹起一次。其中按下和弹起的默认时间间隔是200毫秒KeyPress(Key,Int32)模拟普通Key键按下并弹起一次。其中按下和弹起的时间间隔是第二个参数,单位为毫秒。KeyPressEx(Key)模拟扩展按键Key按下并弹起一次。其中按下和弹起的默认时间间隔是200毫秒,写入扩展按键信息间隔时间为100毫秒KeyPressEx(Key,Int32)模拟扩展按键Key按下并弹起一次。其中按下和弹起的时间间隔是第二个参数,单位为毫秒,写入扩展按键信息间隔时间为100毫秒。KeyPressEx(Key,Int32,Int32)模拟扩展按键Key按下并弹起一次。其中按下和弹起的时间间隔是第二个参数,单位为毫秒,写入扩展按键信息间隔时间是第三个参数,单位为毫秒。

驱动级键盘模拟(C#)修复源码下载(转)相关推荐

  1. Java可以hook微信吗,Hook实现Android 微信、陌陌 、探探位置模拟(附源码下载)

    Hook实现Android 微信.陌陌 .探探位置模拟 最近需要对微信,陌陌等程序进行位置模拟 实现世界各地发朋友圈,搜索附近人的功能,本着站在巨人肩膀上的原则 爱网上搜索一番. 也找到一些 代码和文 ...

  2. 小程序源码:修复登录大河盲盒小程序源码,实现运营“玩法自由”,超多功能的盲盒型抽奖挖矿程序源码下载

    程序介绍 应用支持哪些类型的商品? 1.实物需邮寄商品,用户领取时填写收货信息,后台发货. 2.虚拟商品,如:教程.课程.图文.图片.下载链接等等. 3.卡密商品,后台添加卡密商品,填写使用方法.批量 ...

  3. 修复版动态视频壁纸微信小程序源码下载,支持多种类型流量主收益

    相信各位小伙伴应该也知道动态视频小程序吧 小编之前也发过几款壁纸小程序 不过最近有小伙伴反应说接口使用了一年左右好像失效了 ​ 编辑 功能基本和之前的差不多吧,内有 动态视频壁纸功能,静态壁纸功能,头 ...

  4. 修复版拼团商城前端+后端微信小程序源码下载

    1.怎么玩? 多人拼团,部分人获得得商品,没获得商品的人退款并瓜分红包,红包佣金从获得商品的人的订单利润里返. 2.参与拼团的人需要支付多少钱? 每个人需要支付商品规定的卖价金额,一元购模式的大家分担 ...

  5. 修复登录答题微信小程序源码下载

    功能简介: 1.答题有奖 3.后台含有区间余额区间奖励配置: 4.签到邀请好友获取答题卡, 更多功能具体自己研究,支持用户提现等 后台系统设置: 基础设置.奖励管理.分享配置 题目管理: 题目增加.题 ...

  6. 求职招聘微信小程序源码下载v4.1.78 修复首页授权空白问题

    1.多城市招聘平台2.职位版块 3.人才版块 4.招聘会(支持企业在线报名参加招聘会) 5.职场资讯6.企业登录(在手机端可操作企业信息编辑.发布职位.查收简历.通知面试) 7.企业VIP套餐购买功能 ...

  7. 小程序源码:修复登录接口版最新知识付费变现小程序源码下载-独立后台版本

    这个小程序是干什么的? 资源分享小程序为网络资源分享类自媒体解决变现与传播问题,小小的资源,要钱不合适,不要钱又不能用爰发电,让用户分享又没有效果,那么我们就可以让用户打开小程序,看一段广告,直接获取 ...

  8. 网盘修复版新增qq支付仿城通网盘115网盘源码下载

    网盘修复版新增qq支付仿城通网盘115网盘源码下载这套源码出现的时机,其实挺巧妙的.因为就在今天我还在二开它,谁知道就有朋友放出来了,虽然二开的方向不对.不过还是在这里描述一下吧.本套源码,是需要用户 ...

  9. 小程序源码:登录已修复零象垃圾废品回收微信小程序源码下载,V2.8.2完整全开源前端+后端

    零象垃圾废品回收小程序源码,V2.8.2完整全开源安装包+小程序前端, 是一款专注于垃圾回收小程序源码,支持协议定期企业废品回收,垃圾分类小区物业定期回收. 关于程序 运行环境:微擎+PHP+MYSQ ...

最新文章

  1. 【Ex_BSGSBSGS算法模板】poj2417 poj3243
  2. 09 ORA系列:ORA-06553 PLS-306
  3. mysql blob 字段_MySQL中TEXT与BLOB字段类型的区别
  4. imgageJ开发【Java】
  5. 修复2008r2 rpc服务器,windows server 2008R2 复制问题(RPC服务不可用)
  6. php助理工作内容,生产助理的工作职责
  7. Tensorflow:模型调参
  8. 兼容西门子 CPU226IE量产方案
  9. Word前几页是罗马数字,后面是阿拉伯数字怎么设置
  10. 思维导图的10种类型有哪些?思维导图结构详解
  11. 我的世界java出生蘑菇岛,我的世界:有出生蘑菇岛和要塞的超大村庄?这超富有种子满足你!...
  12. Meatycake,51nod2117,树状数组
  13. 文献记录(part104)--Distance-Based Outlier Detection: Consolidation and Renewed Bearing
  14. 英菲克I5M_I6M_I7M_I10M-晶晨S805处理器-当贝纯净桌面-线刷固件包
  15. anthony1314的小笔记
  16. EasyTouch API接口简介
  17. duobango-tinySDP,rfc 2327
  18. ubuntu10.04安装有线网卡驱动
  19. 创建一个 Spring Boot 项目的4种方法,你会几种?- 第396篇
  20. 自动化生产中直线度测量仪起到的作用

热门文章

  1. Spring Boot配置属性(567个,丁雪峰 译 《Spring Boot 实战》 附录)
  2. Halcon-图像视频采集
  3. python私有方法应用场景_Python 私有属性和私有方法应用场景分析
  4. PS一键搞定 照片图片转手绘 全程演示简单
  5. GPS 有源天线无源天线
  6. Filter过滤词汇
  7. 一次m2eclipse的安装大坑经历之http://m2eclipse.sonatype.org/sites/m2e
  8. Swift基于ARKit的仿抖音潜水艇小游戏
  9. Python每日练习 01 图像右上角添加数字
  10. 预算技术(EAC,BAC,ETC)