首页  编辑  

一个键盘Hook控件

Tags: /超级猛料/Hardware.硬件相关/键盘和鼠标/   Date Created:

summernightrain(夏夜雨) 

MapVirtualKey是个API函数。关于它的用法可以在 API函数的帮助中查到,我想就不用多说了。

我只是想解释一下,我为什么要这样做的原因好了。

 是这样的,我要做一个监视键盘的程序(就是木马中常用的那种功能),我找到了一个自称为不用Hook方法就可以实现拦截键盘的控件,我研究了半天。因为它用来拦截键盘有问题,有顺序错误(这还得了,作为一个木马出这样的错误,不是成了废物??当然我编的不是一个木马了!!呵呵),而且其中一处汇编调用导致了还不用在Win2000使用。所以我决定自己做了。

 我开始用DLL,SetWindowsHookEx(WH_KEYBOARD.....)结果不好 ,还要带一个动态连接库,真麻烦,上面的控件(叫KeySpy)就可以不用DLL。后来我发现SetWindowsHookEx(WH_KEYBOARD.....)这个函数不用放在DLL中也可以工作,于是高兴坏了。马上动手,等我编好以后,又发现了一个致命问题焦点不再自己身上的时候就得不到,还不如在窗体中用OnKeyDown呢!!浪费时间!!!(关于这个问题我没有仔细研究,如果那位大侠知道的话,请赐教,就是不用DLL的SetWindowsHookEx(WH_KEYBOARD....也能做键盘监视吗?),

 后来我又研究了一个例子,决定利用这例子的原理写一个控件(主要原理代码付后),也是用的SetWindowsHookEx,不过变成了这样的调用SetWindowsHookex(WH_JOURNALRECORD, KeyBoardHook, Hinstance, 0);就是WH_JOURNALRECORD这种类型的钩子,是用来监视系统事件的,而function KeyBoardHook(iCode: Integer; wParam: wParam;

 lParam: lParam): LRESULT; stdcall;中 lParam就是事件结构的指针了,哈哈。得到时间结构的指针不久可以判断了吗?

 于是就有:

 pMsg := pEventMsg(lParam); //pEventMsg是一个自定义的事件结构

           if ((pMsg.Message = WM_KEYDOWN) or (pMsg.Message = WM_SYSKEYDOWN)) then

             PostMessage(OwnerHandle, WM_KEYSPY, pMsg.paramL, pMsg.paramH)

   问题似乎解决了,我长出了一个口气,上面的pMsg.ParamL就是VirtualKey Code虚拟键码,就等于OnKeyDown中的Key那个参数。

 得到虚拟键码就好办了,然后我为了监视工作跟加一目了然,当然是把这个键码转换成字符串,比如键码91就得到WIN这个字符串(这个 WIN 让我吃尽苦头,后面会详细解释原因的),27就得到ESC....等等。工作太繁琐,那以前的那个KeySpy来看,哈哈,它定义了长的常量数组,都写好了,照搬过来。成了,这里我说明一下,先开始不知道那段汇编是干吗的,于是照搬。

   这段汇编  

 {asm

   in al, 60h

   mov Key, al

   end; }

   实际上是KeySpy用来监视键盘的核心,它用一个Timer去查询当前键盘中的扫描码,就是当前被按下的那些键,而我呢?弄巧成拙了,把它当成将虚拟键码转换成扫描码的过程,当然也可以这么用,因为当时这个键确实被按下了。后来我把KeySpy中的这段汇编给去掉了,改成我的MapVirtualKey函数,看它的定义--MapVirtualKey(虚拟键码,标志):扫描码; 一看就知道返回值是以虚拟键码为前提的。KeySpy一被这样改就变成了DoNothing控件。而我的控件可不是用这原理哦,嘿嘿,因为我在调用MapVirtualKey之前已经得到正确的虚拟键码了!!

 关于Win的烦恼篇:

 (抱歉,有点灌水嫌疑,请正在看的朋友耐心点),用了SetWindowsHookex(WH_JOURNALRECORD, KeyBoardHook, Hinstance, 0);和MapVirtualKey蛮以为可以高枕无忧乐,98和2000都可以用,哈哈!!完事咯!!我马上编了一个例子来测试它,它工作的非常好。突然,我莫名其妙的按下了WIN键,再按其它的键,发现它已经失效了,就是只要我按下了WIN,这个钩子就不起作用了。我开始查错。。。。调试。。。查错。。。。。(叮叮光光,修理它),还是不行,不论怎样避免都不行,看来是机制问题。没办法查帮助,现在看来帮助真是个好东西啊,打开帮助看WH_JOURNALRECORD这种HOOK的说明,下面引用引起这问题的一段帮助中的原文说明:

   If the user presses CTRL+ESC or CTRL+ALT+DEL during journal playback, the system stops the playback, unhooks the journal playback procedure, and posts a WM_CANCELJOURNAL message to the journaling application.

   看明白了吧?只要用户按了CTRL+ESC or CTRL+ALT+DEL ,所有的journal playback 都会被UnHook掉,CTRL+ESC =WIN键,气死我了!!!

   哎,没办法,看来我在这条路上是死定了。。。。。(后记:这个问题也已经被我解决,不过方法吗,不值得提倡和推广,所以就不拿出来丢丑了)

付的源码:

(请在这方面有经验的朋友一定要无私的奉献一下,共同进步嘛!!)

{*************************************************************}

{            TExKeySpy Component for Delphi 5.0              }

{ Version:  1.0                                              }

{ Author:    Xuxing                                          }

{            ?                                              }

{ E-Mail:    www@juzorn.com.cn;ukyo521xu@sina.com            }

{ Created:  August, 22, 2001                                }

{ Modified:  August, 22, 2001                                }

{ Legal:    Copyright (c) 1999 by Xuxing from Wuhan China    }

{*************************************************************}  

unit ExKeySpy;

interface

uses

{$IFDEF WIN32}Windows, {$ELSE}WinTypes, WinProcs, {$ENDIF}

 SysUtils, Controls, Classes, Messages, Forms, Dialogs;

const

 WM_KEYSPY = WM_USER + 20;

 LowButtonName: array[1..88] of PChar = ('--Esc', '1', '2', '3', '4', '5', '6', '7', '8', '9',

   '0', '-', '=', '--BkSp', '--Tab', 'q', 'w', 'e', 'r', 't',

   'y', 'u', 'i', 'o', 'p', '[', ']', '--Enter', '--Ctrl', 'a',

   's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '''', '`',

   '--LShift Down', '\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',',

   '.', '/', '--RShift Down', '--Gray*', '--Alt', '--Space',

   '--CapsLock', '--F1', '--F2', '--F3', '--F4', '--F5',

   '--F6', '--F7', '--F8', '--F9', '--F10',

   '--NumLock', '--ScrollLock', '--Home', '--Up',

   '--PgUp', '--Gray-', '--Left', '5', '--Right',

   '--Gray+', '--End', '--Down', '--PgDown', '--Ins',

   '--Del', '--LShift Up', '--RShift Up',

   '--Unknown', '--F11', '--F12');

 HiButtonName: array[1..88] of PChar = ('--Esc', '!', '@', '#', '$', '%', '^', '&', '*', '(',

   ')', '_', '+', '--BkSp', '--Tab', 'Q', 'W', 'E', 'R', 'T',

   'Y', 'U', 'I', 'O', 'P', '{', '}', '--Enter', '--Ctrl', 'A',

   'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',

   '--LShift Down', ' , 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<',

   '>', '?', '--RShift Down', '--Gray*', '--Alt', '--Space',

   '--CapsLock', '--F1', '--F2', '--F3', '--F4', '--F5',

   '--F6', '--F7', '--F8', '--F9', '--F10',

   '--NumLock', '--ScrollLock', '--HomeX', '--Up',

   '--PgUp', '--Gray-', '--Left', '5', '--Right',

   '--Gray+', '--End', '--Down', '--PgDown', '--Ins',

   '--Del', '--LShift Up', '--RShift Up',

   '--Unknown', '--F11', '--F12');

type

 TEventMsg = packed record

   Message: UINT;

   paramL: UINT;

   paramH: UINT;

   time: DWORD;

   hwnd: HWND;

 end;

 pEventMsg = ^TEventMsg;

type

 TOnGetKey = procedure(Sender: TObject; Key: Byte; KeyStr: string) of object;

 TExKeySpy = class(TComponent)

 private

   FEnabled: Boolean;

   FOwnerWin: THandle;

   FOnGetKey: TOnGetKey;

   FForm: TForm;

   FOldFormMethod: TWndMethod;

   procedure WM_GETKEY(var Message: TMessage);

   procedure Win_Proc(var Message: TMessage);

   procedure SetEnabled(Value: Boolean);

   { Private declarations }

 protected

   { Protected declarations }

 public

   constructor Create(AOwner: TComponent); override;

   destructor Destroy; override;

   property Active: Boolean read FEnabled write SetEnabled;

   { Public declarations }

 published

   property OnGetKey: TOnGetKey read FOnGetKey write FOnGetKey;

   { Published declarations }

 end;

var

 hNextHookProc: hHook;

 OwnerHandle: Thandle;

 Count: Cardinal;

procedure Register;

implementation

procedure Register;

begin

 RegisterComponents('WalkSlowly', [TExKeySpy]);

end;

{ TExKeySpy }

constructor TExKeySpy.Create(AOwner: TComponent);

begin

 inherited Create(AOwner);

 FEnabled := False;

 FOwnerWin := 0;

 FForm := TForm(AOwner);

 if FForm <> nil then

   begin

     FOldFormMethod := FForm.WindowProc;

     OwnerHandle := FForm.Handle;

     FForm.WindowProc := Win_Proc;

   end;

end;

destructor TExKeySpy.Destroy;

begin

 inherited Destroy;

 if FEnabled then

   begin

     UnHookWindowsHookEx(hNextHookProc);

     hNextHookProc := 0;

   end;

end;

function KeyBoardHook(iCode: Integer; wParam: wParam;

 lParam: lParam): LRESULT; stdcall;

const _KeyPressMask = $80000000;

var

 pMsg: PEventMsg;

 recOK: Integer;

begin

 recOK := 1;

 Result := 0;

 if iCode < 0 then

   Result := CallNextHookEx(hNextHookProc, iCode, wParam, lParam)

 else

   if iCode = HC_SYSMODALON then

     recOK := 0

   else

     if iCode = HC_SYSMODALOFF then

       recOK := 1

     else

       if ((recOK > 0) and (iCode = HC_ACTION)) then

         begin

           pMsg := pEventMsg(lParam);

           if ((pMsg.Message = WM_KEYDOWN) or (pMsg.Message = WM_SYSKEYDOWN)) then

             PostMessage(OwnerHandle, WM_KEYSPY, pMsg.paramL, pMsg.paramH)

         end;

end;

procedure DisableKeyHook;

begin

 UnHookWindowsHookEx(hNextHookProc);

 hNextHookProc := 0;

end;

function EnableKeyHook: Boolean;

begin

 Result := False;

 if hNextHookProc <> 0 then Exit;

 hNextHookProc := SetWindowsHookex(WH_JOURNALRECORD, KeyBoardHook, Hinstance, 0);

 Result := hNextHookProc <> 0;

end;

procedure TExKeySpy.SetEnabled(Value: Boolean);

begin

 if Value <> FEnabled then

   begin

     FEnabled := Value;

     if FEnabled then

       EnableKeyHook

     else

       DisableKeyHook;

   end;

end;

procedure TExKeySpy.Win_Proc(var Message: TMessage);

begin

 if (csDesigning in ComponentState) then

   FOldFormMethod(Message)

 else

   case Message.Msg of

     WM_KEYSPY: WM_GETKEY(Message);

   else

     FOldFormMethod(Message);

   end;

end;

procedure TExKeySpy.WM_GETKEY(var Message: TMessage);

var

 Key, OldKey: Byte;

 KeyStr: string;

 Point: TPoint;

begin

 GetCursorPos(Point);

 Key := Message.WParam;

 OldKey := Key;

 

{asm

   in al, 60h

   mov Key, al  //这就是那一段让人头疼了一段时间的汇编

 end; }

 Key := MapVirtualKey(Key, 0);

 if Key = 91 then //处理WIN健

   begin

     //省略若干字

   end;

 if Key = 170 then

   Key := 84;

 if Key = 182 then

   Key := 85;

 if Key = 250 then

   Key := 186;

 if Key <= 88 then

   begin

     if GetKeyState(VK_Shift) < 0 then

       KeyStr := StrPas(HiButtonName[Key])

     else

       KeyStr := StrPas(LowButtonName[Key]);

     if (GetKeyState(vk_Capital) = 1) and (Length(KeyStr) = 1) then

       KeyStr := UpperCase(KeyStr);

   end

 else

   begin

     if (Key - 128 > 0) and (Key - 128 < 88) then

       begin

         if GetKeyState(VK_Shift) < 0 then

           KeyStr := StrPas(HiButtonName[Key - 128])

         else

           KeyStr := StrPas(LowButtonName[Key - 128]);

         if (GetKeyState(vk_Capital) = 1) and (Length(KeyStr) = 1) then

           KeyStr := UpperCase(KeyStr);

       end;

   end;

 if (GetKeyState(VK_NUMLOCK) = 1) and (OldKey >= 96) and (OldKey <= 105) then

   KeyStr := Inttostr(OldKey - 96);

 FOnGetKey(Self, Key, KeyStr);

end;

end.