游戏插件通常分为三个级别:主要级别是鼠标和键盘模拟,中间级别是Call游戏的内部功能,读写内存,高级级别是数据包捕获,“数据包的“离线挂起”(完全模拟客户端网络数据,无需运行游戏)。用C#编写的插件很少,其中大多数是C ++。主要原因是MS C#当前不支持内联汇编。因此,使用C ++编写基础库,然后使用C#调用它已成为DONET爱好者开发插件的首选。
对于鼠标和键盘模拟的插件,许认为没有技术上的内容,因为它只不过是SendMessage或Key_event,而更高级的一点是Hook进入了程序的内部操作。我还使用了这项技术来开发一些游戏辅助程序。但是最近我在研究XNA时遇到了一些麻烦,这种经过实践检验的方法实际上失败了。游戏根本不“服从”消息命令。我们有一个问题:它如何识别真实键盘的按键?该程序有判断力吗?
如果您忽略上述问题,请换个角度思考。在DOS时代,您还记得经典的Debug命令来删除BIOS密码吗?原理是使用BIOS中断。实际上,对于键盘按键,无论Windows如何打包,都会调用BIOS中断以最后识别按键。但是,消息发送毕竟是Windows系统的事情。无论在Windows级别上如何判断,我们只需要让BIOS下一个“神圣法令”,看看哪个游戏敢于如此傲慢! ! !
在更详细地解释该原理之前,让我们抓住幕后黑手,看看哪个人在支持游戏?让它有胆量违抗Windows message命令。是否判断了实际的键盘信息还是有其他原因?结果,我在DirectX编程中找到了DirectInput API。它绕过Windows的消息机制,其目的是使游戏的实时控制更好,更快。 Windows消息以队列的形式出现,并且传递过程会有所延迟。例如,格斗游戏对实时控制有很高的要求,而窗口消息机制不能满足这一要求。而且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&H8 0)'或扫描码0x50和数据0x80,这意味着释放此键。
当然,DOS时代已经过去了,但是我只是提到了QBasic的强大功能。如果真的使用QBasic编写模拟程序,则可以在Win98下成功,但是在Win2000之后的版本中将无法使用,因为DOS只是进入NT时代后的虚拟机系统。
现在的问题非常清楚,即如何在Windows环境中执行端口操作。由于级别较低,Windows无法让您如此轻松地成功。通常有两种方法:一种是使用驱动程序,而在驱动程序中还有什么不能做的呢?第二种是使用“呼叫门”从Ring3跳到Ring0来执行相关操作。在Internet上进行搜索应该可以找到相关信息。我听说Grid表示有一个名为“击键向导”的VB似乎还不错。下载并研究后,我发现它使用了外国人的WINIO驱动程序的原理。使用WINIO更加容易,因为我的驾驶能力确实是&* ^%$ ^&*。
因此,在WINIO的基础上,我使用C#进行了一个简单的辅助软件包,使其适合在DONET平台上使用驱动程序级按钮模拟。我的DLL中提供了以下方法:
InitSuperKeys()安装WINIO驱动程序,通常用于在Form_Load事件中调用
CloseSuperKeys()卸载通常用于调用Form_Closed事件的WINIO驱动程序
KeyDown(Key)模拟一个普通的Key键的按下。
KeyDownEx(Key)模拟按下扩展的Key键。
KeyUp(Key)模拟普通Key键的弹出窗口。
KeyUpEx(Key)模拟扩展键的向上键。
KeyPress(Key)模拟一次普通Key键的按下和弹出。按下和弹出之间的默认时间间隔为200毫秒
KeyPress(Key,Int3 2)模拟一次普通Key键的按下和弹出操作。两次按下之间的时间间隔是第二个参数,单位为毫秒。
KeyPressEx(Key)模拟扩展的按键并弹出一次。按下和弹出之间的默认时间间隔为200毫秒,写入扩展键信息的间隔为100毫秒
KeyPressEx(Key,Int3 2)模拟一次扩展键的按下和弹出。按下和弹出之间的时间间隔是第二个参数,单位为毫秒,写入扩展键信息的间隔为100毫秒。
KeyPressEx(Key,Int32,Int3 2)模拟一次扩展键Key的按下和弹出。按下和弹出之间的时间间隔是第二个参数,单位为毫秒,以及编写扩展键的间隔时间information是第三个参数,单位为毫秒。
特殊说明:
1、必须先执行InitSuperKeys()才能安装驱动程序,然后才能执行模拟密钥。最好在关闭窗口后卸载驱动程序。
2、上面方法中的参数Key是WinIoSys类中定义的枚举,而不是DONET系统的Key枚举。
3、普通键是指标准键盘键,例如A,B,C和Space。扩展键指的是诸如“方向键”之类的特殊键,在处理此类扩展键时,系统将首先有时间写入扩展键信息。因此,没有Ex结束的方法用于标准普通密钥,而具有Ex结束的方法用于特殊扩展密钥。它们全部超载,用户可以自行设置间隔。至于按钮的详细分类,请到Google自行搜索。
4、模拟按键事件后,必须让程序休眠几毫秒,否则下一个按键将无法正常模拟。
5、似乎USB占用了一条总线,该总线与端口操作无关,因此从理论上讲该方法不支持USB接口键盘。
6、一些防病毒软件会提醒用户安装驱动程序或将WinIo.sys报告为病毒。实际上,这是正常的。如果您从事此类异常活动,防病毒软件肯定会看不起它。请不要怀疑该程序是否包含病毒或木马,最简单的方法是关闭防病毒软件〜。如果您不担心,请不要使用它。
现在我们有了一个如此方便的界面,让我们尝试写一点东西来看看它的功能如何。让我们以“拳皇”为例。该游戏声称对Window消息绝对无效。
让我们创建一个新的EXE项目并引用我的SuperKeys.dll。定义一个全局变量
WinIoSys m_IoSys =新的WinIoSys();
在Load事件中安装驱动程序
m_IoSys.InitSuperKeys();
在Closed事件中卸载驱动程序
m_IoSys.CloseSuperKeys();
当然,我们可以编写一个全局钩子。当游戏运行时,可以通过按下某个键来模拟一系列按键。但这只是一个DEMO,不需要那么正式,只需编写一个计时器即可触发它。为了节省时间,请在每次单击按钮后三秒钟执行模拟按钮操作。请记住在这三秒钟内激活拳皇游戏窗口。
首先统一键盘设置,如图所示:
我们决定模拟最简单的“ Iori”方法:暗杀(即在地面上生火)。如果“ Iori”在左侧,则键盘操作为:↓↘→+ A或C。这是上图中的SDU或SDJ按钮。以SDJ为例,在模拟此键盘时必须非常小心。我开始像这样模拟它:
m_IoSys.KeyPress(WinIoSys.Key.VK_S);
Thread.Sleep(20 0);
m_IoSys.KeyPress(WinIoSys.Key.VK_D);
Thread.Sleep(20 0);
m_IoSys.KeyPress(WinIoSys.Key.VK_J,20 0);
结果不成功。我仔细研究了。实际上,当我们按下SDJ按钮时,我们首先按下S按钮,然后按下D按钮,然后弹起S按钮,然后是D按钮,最后按下J按钮。然后真实的过程应该这样写:
m_IoSys.KeyDown(WinIoSys.Key.VK_S);
Thread.Sleep(10 0);
m_IoSys.KeyDown(WinIoSys.Key.VK_D);
Thread.Sleep(10 0);
m_IoSys.KeyUp(WinIoSys.Key.VK_S);
Thread.Sleep(10 0);
m_IoSys.KeyUp(WinIoSys.Key.VK_D);
Thread.Sleep(1 0); //这个地方不能相隔太远,如果太大,则无法连接整个动作。
m_IoSys.KeyPress(WinIoSys.Key.VK_J,20 0);
测试后,成功模拟我们想要的操作。您可以看到实际的模拟按键效果。
该程序已在VS2008 + WinXP SP3 + WinKawaks 1. 48XP + KOF 2000 + P / S键盘下调试。
使用系统;
使用System.Collections.Generic;
使用System.Text;
使用System.Runtime.InteropServices;
命名空间lizheAionWG
{
公共类WinIo

{
public const int KBC_KEY_CMD = 0x64;
public const int KBC_KEY_DATA = 0x60;
[DllImport(“ winio.dll”)]
公共静态外部布尔InitializeWinIo();
[DllImport(“ winio.dll”)]
公共静态外部布尔GetPortVal(IntPtr wPortAddr,out int pdwPortVal,字节bSize);
[DllImport(“ winio.dll”)]
公共静态外部布尔SetPortVal(uint wPortAddr,IntPtr dwPortVal,字节bSize);
[DllImport(“ winio.dll”)]
公共静态外部字节MapPhysToLin(byte pbPhysAddr,uint dwPhysSize,IntPtr PhysicalMemoryHandle);
[DllImport(“ winio.dll”)]
公共静态外部布尔UnmapPhysicalMemory(IntPtr PhysicalMemoryHandle,字节pbLinAddr);
[DllImport(“ winio.dll”)]
公共静态外部布尔GetPhysLong(IntPtr pbPhysAddr,byte pdwPhysVal);
[DllImport(“ winio.dll”)]
公共静态外部布尔SetPhysLong(IntPtr pbPhysAddr,字节dwPhysVal);
[DllImport(“ winio.dll”)]
公共静态外部无效ShutdownWinIo();
[DllImport(“ user3 2. dll”)]
公共静态外部int MapVirtualKey(uint Ucode,uint uMapType);
public void sendwinio()
{
如果(InitializeWinIo())
{
KBCWait4IBE();
}
}
///等待缓冲区为空
私有无效KBCWait4IBE()
{
int dwVal = 0;
做
{
布尔标志= GetPortVal((IntPtr)0x64,出dwVal,1);
}
while(((dwVal&0x 2)> 0);
}
///按下
public 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);
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/bofangqi/article-362919-1.html
哪个项目有钱赚
魂牵梦萦