模拟键盘的终极模拟(二) 终极风暴3键盘设置

(转载)

http://blog.sina.com.cn/s/blog_4abb11b00100082l.html

模拟键盘的终极模拟(二)

(2007-02-23 21:03:53)转载

分类: 网络编程
2.全局级模拟

你会发现,用上面的方法模拟按键并不是对所有程序都有效的,有的程序啊,你向它发了一大堆消息,可是它却一点反应也没有。这是怎么回事呢?这就要看具体的情况了,有些程序(特别是一些游戏)出于某些原因,会禁止用户对它使用模拟按键程序,这个怎么实现呢?比如可以在程序中检查一下,如果发现自己不是活动窗口,就不接受键盘消息。或者仔细检查一下收到的键盘消息,你会发现真实的按键和模拟的按键消息总是有一些小差别,从这些小差别上,目标程序就能判断出:这是假的!是伪造的!!因此,如果用PostMessage发送局部消息模拟按键不成功的话,你可以试一试全局级的键盘消息,看看能不能骗过目标程序。
模拟全局键盘消息常见的可以有以下一些方法:
(1) 用API函数keybd_event,这个函数可以用来模拟一个键盘事件,它的VB声明为:
Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByValbScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo AsLong)
参数bVk表示要模拟的按键的虚拟码,bScan表示该按键的扫描码(一般可以传0),dwFlags表示是按下键还是释放键(按下键为0,释放键为2),dwExtraInfo是扩展标志,一般没有用。比如要模拟按下A键,可以这样:
Const KEYEVENTF_KEYUP = &H2
keybd_event VK_A, 0, 0, 0'按下A键
keybd_event VK_A, 0, KEYEVENTF_KEYUP, 0'释放A键
注意有时候按键的速度不要太快,否则会出问题,可以用API函数Sleep来进行延时,声明如下:
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds AsLong)
参数dwMilliseconds表示延时的时间,以毫秒为单位。
那么如果要模拟按下功能键怎么做呢?比如要按下Ctrl+C实现拷贝这个功能,可以这样:
keybd_event VK_Ctrl, 0, 0, 0'按下Ctrl键
keybd_event VK_C, 0, 0, 0'按下C键
Sleep 500'延时500毫秒
keybd_event VK_C, 0, KEYEVENTF_KEYUP, 0'释放C键
keybd_event VK_Ctrl, 0, KEYEVENTF_KEYUP, 0'释放Ctrl键
好了,现在你可以试试是不是可以骗过目标程序了,这个函数对大部分的窗口程序都有效,可是仍然有一部分游戏对它产生的键盘事件熟视无睹,这时候,你就要用上bScan这个参数了。一般的,bScan都传0,但是如果目标程序是一些DirectX游戏,那么你就需要正确使用这个参数传入扫描码,用了它可以产生正确的硬件事件消息,以被游戏识别。这样的话,就可以写成这样:
keybd_event VK_A, MapVirtualKey(VK_A, 0), 0,0 '按下A键
keybd_event VK_A, MapVirtualKey(VK_A, 0), KEYEVENTF_KEYUP,0 '释放A键
以上就是用keybd_event函数来模拟键盘事件。除了这个函数,SendInput函数也可以模拟全局键盘事件。SendInput可以直接把一条消息插入到消息队列中,算是比较底层的了。它的VB声明如下:
Declare Function SendInput Lib "user32.dll" (ByVal nInputs As Long,pInputs As GENERALINPUT, ByVal cbSize As Long) As Long
参数:
nlnprts:定义plnputs指向的结构的数目。
plnputs:指向INPUT结构数组的指针。每个结构代表插人到键盘或鼠标输入流中的一个事件。
cbSize:定义INPUT结构的大小。若cbSize不是INPUT结构的大小,则函数调用失败。
返回值:函数返回被成功地插人键盘或鼠标输入流中的事件的数目。若要获得更多的错误信息,可以调用GetlastError函数。
备注:Sendlnput函数将INPUT结构中的事件顺序地插入键盘或鼠标的输入流中。这些事件与用户插入的(用鼠标或键盘)或调用keybd_event,mouse_event,或另外的Sendlnput插人的键盘或鼠标的输入流不兼容。
嗯,这个函数用起来蛮复杂的,因为它的参数都是指针一类的东西。要用它来模拟键盘输入,先要构造一组数据结构,把你要模拟的键盘消息装进去,然后传给它。为了方便起见,把它做在一个过程里面,要用的时候直接调用好了,代码如下:
Declare Function SendInput Lib "user32.dll" (ByVal nInputs As Long,pInputs As GENERALINPUT, ByVal cbSize As Long) As Long
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDstAs Any, pSrc As Any, ByVal ByteLen As Long)
Type GENERALINPUT
dwType AsLong
xi(0 To 23) AsByte
End Type

Type KEYBDINPUT
wVk As Integer
wScan As Integer
dwFlags As Long
time As Long
dwExtraInfo As Long
End Type

Const INPUT_KEYBOARD = 1

Sub MySendKey(bkey As Long)
'参数bkey传入要模拟按键的虚拟码即可模拟按下指定键
Dim GInput(0 To 1) As GENERALINPUT
Dim KInput As KEYBDINPUT
KInput.wVk = bkey '你要模拟的按键
KInput.dwFlags = 0 '按下键标志
GInput(0).dwType = INPUT_KEYBOARD
CopyMemory GInput(0).xi(0), KInput, Len(KInput)'这个函数用来把内存中KInput的数据复制到GInput
KInput.wVk = bkey
KInput.dwFlags = KEYEVENTF_KEYUP ' 释放按键
GInput(1).dwType = INPUT_KEYBOARD ' 表示该消息为键盘消息
CopyMemory GInput(1).xi(0), KInput, Len(KInput)
'以上工作把按下键和释放键共2条键盘消息加入到GInput数据结构中
SendInput 2, GInput(0), Len(GInput(0))'把GInput中存放的消息插入到消息列队
End Sub

除了以上这些,用全局钩子也可以模拟键盘消息。如果你对windows中消息钩子的用法已经有所了解,那么你可以通过设置一个全局HOOK来模拟键盘消息,比如,你可以用WH_JOURNALPLAYBACK这个钩子来模拟按键。WH_JOURNALPLAYBACK是一个系统级的全局钩子,它和WH_JOURNALRECORD的功能是相对的,常用它们来记录并回放键盘鼠标操作。WH_JOURNALRECORD钩子用来将键盘鼠标的操作忠实地记录下来,记录下来的信息可以保存到文件中,而WH_JOURNALPLAYBACK则可以重现这些操作。当然亦可以单独使用WH_JOURNALPLAYBACK来模拟键盘操作。你需要首先声明SetWindowsHookEx函数,它可以用来安装消息钩子:
Declare Function SetWindowsHookEx Lib "user32" Alias"SetWindowsHookExA" (ByVal idHook As Long,ByVal lpfn As Long, ByValhmod As Long, ByVal dwThreadId As Long) As Long
先安装WH_JOURNALPLAYBACK这个钩子,然后你需要自己写一个钩子函数,在系统调用它时,把你要模拟的事件传递给钩子参数lParam所指向的EVENTMSG区域,就可以达到模拟按键的效果。不过用这个钩子模拟键盘事件有一个副作用,就是它会锁定真实的鼠标键盘,不过如果你就是想在模拟的时候不会受真实键盘操作的干扰,那么用用它倒是个不错的主意。
3.驱动级模拟

如果上面的方法你都试过了,可是你发现目标程序却仍然顽固的不接受你模拟的消息,寒~~~~~~~~~还好,我还剩下最后一招,这就是驱动级模拟:直接读写键盘的硬件端口!
有一些使用DirectX接口的游戏程序,它们在读取键盘操作时绕过了windows的消息机制,而使用DirectInput.这是因为有些游戏对实时性控制的要求比较高,比如赛车游戏,要求以最快速度响应键盘输入。而windows消息由于是队列形式的,消息在传递时会有不少延迟,有时1秒钟也就传递十几条消息,这个速度达不到游戏的要求。而DirectInput则绕过了windows消息,直接与键盘驱动程序打交道,效率当然提高了不少。因此也就造成,对这样的程序无论用PostMessage或者是keybd_event都不会有反应,因为这些函数都在较高层。对于这样的程序,只好用直接读写键盘端口的方法来模拟硬件事件了。要用这个方法来模拟键盘,需要先了解一下键盘编程的相关知识。
在DOS时代,当用户按下或者放开一个键时,就会产生一个键盘中断(如果键盘中断是允许的),这样程序会跳转到BIOS中的键盘中断处理程序去执行。打开windows的设备管理器,可以查看到键盘控制器由两个端口控制。其中&H60是数据端口,可以读出键盘数据,而&H64是控制端口,用来发出控制信号。也就是,从&H60号端口可以读此键盘的按键信息,当从这个端口读取一个字节,该字节的低7位就是按键的扫描码,而高1位则表示是按下键还是释放键。当按下键时,最高位为0,称为通码,当释放键时,最高位为1,称为断码。既然从这个端口读数据可以获得按键信息,那么向这个端口写入数据就可以模拟按键了!用过QbASIC4.5的朋友可能知道,QB中有个OUT命令可以向指定端口写入数据,而INP函数可以读取指定端口的数据。那我们先看看如果用QB该怎么写代码:
假如你想模拟按下一个键,这个键的扫描码为&H50,那就这样
OUT&H64,&HD2'把数据&HD2发送到&H64端口。这是一个KBC指令,表示将要向键盘写入数据
OUT&H60,&H50'把扫描码&H50发送到&H60端口,表示模拟按下扫描码为&H50的这个键
那么要释放这个键呢?像这样,发送该键的断码:
OUT&H64,&HD2'把数据&HD2发送到&H64端口。这是一个KBC指令,表示将要向键盘写入数据
OUT &H60,(&H50 OR&H80)'把扫描码&H50与数据&H80进行或运算,可以把它的高位置1,得到断码,表示释放这个键
好了,现在的问题就是在VB中如何向端口写入数据了。因为在windows中,普通应用程序是无权操作端口的,于是我们就需要一个驱动程序来帮助我们实现。在这里我们可以使用一个组件WINIO来完成读写端口操作。什么是WINIO?WINIO是一个全免费的、无需注册的、含源程序的WINDOWS2000端口操作驱动程序组件(可以到http://www.internals.com/上去下载)。它不仅可以操作端口,还可以操作内存;不仅能在VB下用,还可以在DELPHI、VC等其它环境下使用,性能特别优异。下载该组件,解压缩后可以看到几个文件夹,其中Release文件夹下的3个文件就是我们需要的,这3个文件是WinIo.sys(用于winxp下的驱动程序),WINIO.VXD(用于win98下的驱动程序),WinIo.dll(封装函数的动态链接库),我们只需要调用WinIo.dll中的函数,然后WinIo.dll就会安装并调用驱动程序来完成相应的功能。值得一提的是这个组件完全是绿色的,无需安装,你只需要把这3个文件复制到与你的程序相同的文件夹下就可以使用了。用法很简单,先用里面的InitializeWinIo函数安装驱动程序,然后就可以用GetPortVal来读取端口或者用SetPortVal来写入端口了。好,让我们来做一个驱动级的键盘模拟吧。先把winio的3个文件拷贝到你的程序的文件夹下,然后在VB中新建一个工程,添加一个模块,在模块中加入下面的winio函数声明:

Declare Function MapPhysToLin Lib "WinIo.dll" (ByVal PhysAddr AsLong, ByVal PhysSize As Long, ByRef PhysMemHandle) As Long
Declare Function UnmapPhysicalMemory Lib "WinIo.dll" (ByValPhysMemHandle, ByVal LinAddr) As Boolean
Declare Function GetPhysLong Lib "WinIo.dll" (ByVal PhysAddr AsLong, ByRef PhysVal As Long) As Boolean
Declare Function SetPhysLong Lib "WinIo.dll" (ByVal PhysAddr AsLong, ByVal PhysVal As Long) As Boolean
Declare Function GetPortVal Lib "WinIo.dll" (ByVal PortAddr AsInteger, ByRef PortVal As Long, ByVal bSize As Byte) AsBoolean
Declare Function SetPortVal Lib "WinIo.dll" (ByVal PortAddr AsInteger, ByVal PortVal As Long, ByVal bSize As Byte) AsBoolean
Declare Function InitializeWinIo Lib "WinIo.dll" () AsBoolean
Declare Function ShutdownWinIo Lib "WinIo.dll" () As Boolean
Declare Function InstallWinIoDriver Lib "WinIo.dll" (ByValDriverPath As String, ByVal Mode As Integer) As Boolean
Declare Function RemoveWinIoDriver Lib "WinIo.dll" () AsBoolean

'------------------------------------以上是WINIO函数声明-------------------------------------------

Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA"(ByVal wCode As Long, ByVal wMapType As Long) As Long

'-----------------------------------以上是WIN32API函数声明-----------------------------------------

再添加下面这个过程:
Sub KBCWait4IBE()'等待键盘缓冲区为空
Dim dwVal As Long
Do
GetPortVal &H64, dwVal, 1
'这句表示从&H64端口读取一个字节并把读出的数据放到变量dwVal中
'GetPortVal函数的用法是GetPortVal 端口号,存放读出数据的变量,读入的长度
Loop While (dwVal And &H2)
End Sub
上面的是一个根据KBC规范写的过程,它的作用是在向键盘端口写入数据前等待一段时间,后面将会用到。
然后再添加如下过程,这2个过程用来模拟按键:

Public Const KBC_KEY_CMD =&H64'键盘命令端口
Public Const KBC_KEY_DATA =&H60'键盘数据端口

Sub MyKeyDown(ByVal vKeyCoad As Long)
'这个用来模拟按下键,参数vKeyCoad传入按键的虚拟码
模拟键盘的终极模拟(二) 终极风暴3键盘设置
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)

KBCWait4IBE'发送数据前应该先等待键盘缓冲区为空
SetPortValKBC_KEY_CMD, &HD2, 1'发送键盘写入命令
'SetPortVal函数用于向端口写入数据,它的用法是SetPortVal 端口号,欲写入的数据,写入数据的长度
KBCWait4IBE
SetPortValKBC_KEY_DATA, btScancode, 1 '写入按键信息,按下键

End Sub

Sub MyKeyUp(ByVal vKeyCoad As Long)
'这个用来模拟释放键,参数vKeyCoad传入按键的虚拟码
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)

KBCWait4IBE'等待键盘缓冲区为空
SetPortValKBC_KEY_CMD, &HD2, 1 '发送键盘写入命令
KBCWait4IBE
SetPortValKBC_KEY_DATA, (btScancode Or &H80), 1'写入按键信息,释放键

End Sub


定义了上面的过程后,就可以用它来模拟键盘输入了。在窗体模块中添加一个定时器控件,然后加入以下代码:


Private Sub Form_Load()

If InitializeWinIo = False Then
'用InitializeWinIo函数加载驱动程序,如果成功会返回true,否则返回false
MsgBox"驱动程序加载失败!"
Unload Me
End If
Timer1.Interval=3000
Timer1.Enabled=True
End Sub

Private Sub Form_Unload(Cancel As Integer)
ShutdownWinIo '程序结束时记得用ShutdownWinIo函数卸载驱动程序
End Sub

Private Sub Timer1_Timer()
Dim VK_A as Long = &H41
MyKeyDown VK_A
MyKeyUp VK_A'模拟按下并释放A键
End Sub
[/quote]
运行上面的程序,就会每隔3秒钟模拟按下一次A键,试试看,怎么样,是不是对所有程序都有效果了?
需要注意的问题:
要在VB的调试模式下使用WINIO,需要把那3个文件拷贝到VB的安装目录中。
键盘上有些键属于扩展键(比如键盘上的方向键就是扩展键),对于扩展键不应该用上面的MyKeyDown和MyKeyUp过程来模拟,可以使用下面的2个过程来准确模拟扩展键:
QUOTE:
Sub MyKeyDownEx(ByVal vKeyCoad As Long)'模拟扩展键按下,参数vKeyCoad是扩展键的虚拟码
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)

KBCWait4IBE'等待键盘缓冲区为空
SetPortValKBC_KEY_CMD, &HD2, 1'发送键盘写入命令
KBCWait4IBE
SetPortValKBC_KEY_DATA, &HE0, 1 '写入扩展键标志信息


KBCWait4IBE'等待键盘缓冲区为空
SetPortValKBC_KEY_CMD, &HD2, 1'发送键盘写入命令
KBCWait4IBE
SetPortValKBC_KEY_DATA, btScancode, 1 '写入按键信息,按下键


End Sub


Sub MyKeyUpEx(ByVal vKeyCoad As Long)'模拟扩展键弹起
Dim btScancode As Long
btScancode = MapVirtualKey(vKeyCoad, 0)

KBCWait4IBE'等待键盘缓冲区为空
SetPortValKBC_KEY_CMD, &HD2, 1'发送键盘写入命令
KBCWait4IBE
SetPortValKBC_KEY_DATA, &HE0, 1 '写入扩展键标志信息


KBCWait4IBE'等待键盘缓冲区为空
SetPortValKBC_KEY_CMD, &HD2, 1'发送键盘写入命令
KBCWait4IBE
SetPortValKBC_KEY_DATA, (btScancode Or &H80), 1'写入按键信息,释放键

End Sub


还应该注意的是,如果要从扩展键转换到普通键,那么普通键的KeyDown事件应该发送两次。也就是说,如果我想模拟先按下一个扩展键,再按下一个普通键,那么就应该向端口发送两次该普通键被按下的信息。比如,我想模拟先按下左方向键,再按下空格键这个事件,由于左方向键是扩展键,空格键是普通键,那么流————程就应该是这样的:
[quote]MyKeyDownEx VK_LEFT'按下左方向键
Sleep 200'延时200毫秒
MyKeyUpEx VK_LEFT'释放左方向键

Sleep 500
MyKeyDown VK_SPACE'按下空格键,注意要发送两次
MyKeyDown VK_SPACE
Sleep 200
MyKeyUp VK_SPACE'释放空格键




好了,相信到这里,你的模拟按键程序也就差不多了,测试一下,是不是很有效呢,嘿嘿~~~~
WINIO组件的下载地址:http://www.114vip.com.cn/download/winio.zip
4.骨灰级模拟
方法3算是很底层的模拟了,我现在还没有发现有它模拟无效的程序。但是如果你用尽上面所有的方法,仍然无效的话,那么还有最后一个方法,绝对对任何程序都会有效,那就是:把键盘拿出来,老老实实地按下去吧。~~~~

文章引用自:http://bbs.mmbest.com/viewthread.php?tid=76122&extra=&page=1

  

爱华网本文地址 » http://www.413yy.cn/a/25101016/318206.html

更多阅读

拯救世界的终极智慧—读王阳明著作有感 王阳明著作

拯救世界的终极智慧—读王阳明著作有感 我原先只知道王阳明是个很厉害的人,并没有阅读过他的文字。最近我在闲暇之余,每天读一段王阳明的文字,不觉间就被他字语行间蕴藏的超凡智慧所震撼,心中崇拜不已。可是他的思想却埋没于现代社会

搜异笔记之黄泉神殿:商纣王雪藏千年的终极秘密!

基本资料搜异笔记之黄泉神殿:商纣王雪藏千年的终极秘密!作者: 谢迅出版社: 武汉出版社出版年:页数:定价: 29.8装帧:ISAN:内容简介一张诡异的青铜面具,牵扯出摸金校尉与发丘天官的隔世恩怨。生性淡泊的小保安无意间卷进了寻找黄泉神殿的惊险旅

音乐是心灵的终极慰藉 心灵上的慰藉

文字---网络音乐是心情的记忆心情是音乐的写真一个人独享音乐听的是心曲走的是心路音乐是心灵的终极慰藉好的音乐是可以感受的哲学 是心灵共同的宗教音乐是上帝给予人类一切苦难的补偿

声明:《模拟键盘的终极模拟(二) 终极风暴3键盘设置》为网友青衫栀拾分享!如侵犯到您的合法权益请联系我们删除