帕格尼尼的钟为何出名:用跨进程子类化技术实现对其它进程消息的拦载

来源:百度文库 编辑:中财网 时间:2024/04/28 20:56:16

用跨进程子类化技术实现对其它进程消息的拦载

分类: Delphi 2008-10-03 13:25 189人阅读 评论(0) 收藏 举报

  大家都知道每个窗口都有默认的窗口函数来进行对窗口消息的处理.
  而子类化技术就是替换窗口的窗口函数为自己定义的函数的技术.例如下面的代码:
var
  Form1: TForm1;
  OldWndProc: Pointer;
implementation

{$R *.dfm}
function NewWndProc(hHwnd, Msg, wParam, lParam: LongWORD): Longint; stdcall;
begin
  if Msg=WM_CLOSE then
    exit;
  Result := CallWindowProc(OldWndProc, hHwnd, Msg, wParam, lParam);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  {保存旧的窗口函数地址}
  OldWndProc := Pointer(GetWindowLong(Self.Handle, GWL_WNDPROC));
  {设置新的窗口函数为自定义函数}
  SetWindowLong(Self.Handle, GWL_WNDPROC, Longint(@NewWndProc));
end;
  这样在窗口建立时就对窗口实现了子类化,这时按下窗口的关闭按钮就会发现关不了窗口,因为新的窗口处理函数把WM_CLOSE消息过滤掉了,要取消子类化,只需要简单的把以前的窗口函数恢复过来就可以了.SetWindowLong(Self.Handle, GWL_WNDPROC, Longint(OldWndProc));

  现在看来似乎很简单,只要对其它进程中的目标窗口进行子类化就可以实现对其消息的拦载监视了.但是在WIN32下,每一个进程都有自己独立的内存空间,新的窗口函数必须和目标窗口在同一个进程内,直接使用SetWindowLong(其它进程中窗口的句柄, GWL_WNDPROC, 新窗口函数)就会失败,所以就要想办法把我们的窗口函数代码放到目标进程内,这儿有二个办法,一是使用CreateRemoteThread在目标进程内建立线程,但这函数只在NT及以上操作系统实现,而且还要涉及到API地址重定位等问题,很麻烦(请参考http://www.csdn.net/develop/Read_Article.asp?Id=21079).另一个方法就是使用HOOK技术(SetWindowsHookEx,如果不知道,请先参考HOOK技术方面的文章),大家都知道,对其它进程进行HOOK时,此进程会自动加载HOOK过程所在的DLL,如果我们把窗口函数也放在DLL中,那窗口函数就相当于加载到了目标进程的地址空间中了,这方法简单易行.在这里我们就采用HOOK技术来实现跨进程子类化.

  最后一个问题是如何在DLL中实现全局变量,因为DLL中的变量在每个进程加载这个DLL时都申请新的空间来存放变量,所以DLL中的变量在各个进程内不一样,可以利用内存文件映射,WM_COPYDATA等方法来实现全局变量.这儿采用内存文件映射.

  现在需要的知识都已了解了,就让我们来看具体的代码吧(这儿是把所有函数放在一个DLL中):

  1. library Hook;     
  2. uses
  3.   SysUtils,windows, Messages;
  4. const
  5.   WM_UNSUBCLASS = WM_USER + 1001;  {卸载子类化消息}
  6.   WM_NEWMESSAGE = WM_USER + 1002;  {通知监视窗口拦到了新消息}
  7.   HOOK_EVENT_NAME = 'MyHook';
  8. type
  9.   PMyDLLVar = ^TMyDLLVar;
  10.   TMyDLLVar = record
  11.     SubClass: Boolean;                 {是否已经子类化}
  12.     HookWindow, SpyWindow: LongWORD;   {要安装HOOK的窗口及用于接收消息的窗口}
  13.     hHook: LongWORD;                   {HOOK句柄}
  14.     OldWndProc: pointer;               {旧的窗口过程}
  15.     MsgHwnd: LongWORD;
  16.     Msg: TMessage;
  17.   end;
  18. var
  19.   DLLData: PMyDLLVar;
  20. {---------------------------------------}
  21. {函数名:NewWndProc
  22. {函数功能:新的窗口过程
  23. {函数参数:hHwnd:窗口句柄 Msg:消息ID
  24. {         wParam, lParam:消息参数
  25. {函数返回值:下一个窗口过程的返回值
  26. {---------------------------------------}
  27. function NewWndProc(hHwnd, Msg, wParam, lParam: LongWORD): Longint; stdcall;
  28. begin
  29.   if Msg = WM_UNSUBCLASS then   {如果收到卸载子类化消息就恢复以前的WndProc}
  30.   begin
  31.     SetWindowLong(DLLData^.HookWindow, GWL_WNDPROC, longint(DLLData^.OldWndProc));
  32.     exit;
  33.   end;
  34.   {这儿是把收到的消息放在映射的内存中,我们自己的程序可以通过读这个内存来得到监视到的消息.}
  35.   DLLData^.Msg.Msg := Msg;            
  36.   DLLData^.Msg.WParam := wParam;
  37.   DLLData^.Msg.LParam := lParam;
  38.   DLLData^.MsgHwnd := hHwnd;
  39.   {给监视窗口发送拦载新消息的消息}
  40.     SendMessage(DLLData^.SpyWindow, WM_NEWMESSAGE, wParam, lParam);
  41.   {这儿可以添加自己对目标进程消息处理的代码,因为己经是在目标进程的地址空间内,现在可以为所
  42.   欲为 ^_^}
  43.   Result := CallWindowProc(DLLData^.OldWndProc, hHwnd, Msg, wParam, lParam);
  44. end;
  45. {------------------------------------}
  46. {过程名:HookProc
  47. {过程功能:HOOK过程
  48. {过程参数:nCode, wParam, lParam消息的相
  49. {         关参数
  50. {------------------------------------}
  51. procedure HookProc(nCode, wParam, lParam: LongWORD);stdcall;
  52. var
  53.   hEvent: THandle;
  54. begin
  55.   if not DllData^.SubClass then  {如果此窗口未子类化}
  56.   begin    {保存窗口过程地址并子类化}
  57.   hEvent := OpenEvent(Synchronize, False, HOOK_EVENT_NAME);
  58.     if hEvent <> 0 then
  59.     begin
  60.       WaitForSingleObject(hEvent, INFINITE);
  61.       CloseHandle(hEvent); 
  62.     end;
  63.     DLLData^.OldWndProc := pointer(GetWindowLong(DLLData^.HookWindow, GWL_WNDPROC));
  64.     SetWindowLong(DLLData^.HookWindow, GWL_WNDPROC, integer(@NewWndProc));
  65.     DLLData^.SubClass := True;
  66.     //hEvent := OpenEvent(Synchronize, False, HOOK_EVENT_NAME);
  67.   end;
  68.   {调用下一个Hook}
  69.   CallNextHookEx(DLLData^.hHook, nCode, wParam, lParam);
  70. end;
  71. {------------------------------------}
  72. {函数名:InstallHook
  73. {函数功能:在指定窗口上安装HOOK
  74. {函数参数:HWindow:要安装HOOK的窗口
  75. {         SWindow:用于接收消息的窗口
  76. {返回值:成功返回TRUE,失败返回FALSE
  77. {------------------------------------}
  78. function InstallHook(HWindow, SWindow: LongWORD):Boolean;stdcall;
  79. var
  80.   ThreadID: LongWORD;
  81.   hEvent: THandle;
  82. begin
  83.   Result := False;
  84.   DLLData^.hHook := 0;
  85.   DLLData^.HookWindow := HWindow;
  86.   DLLData^.SpyWindow := SWindow;
  87.   {得到指定窗口的线程ID}
  88.   ThreadID := GetWindowThreadProcessId(HWindow, nil);
  89.   {给指定窗口挂上钩子}
  90.   hEvent := CreateEvent(nil, True, False, HOOK_EVENT_NAME);
  91.   DLLData^.hHook := SetWindowsHookEx(WH_MOUSE, @HookProc, Hinstance, ThreadID);
  92.   SetEvent(hEvent);
  93.   CloseHandle(hEvent);
  94.   if DLLData^.hHook > 0 then Result := True;  {是否成功HOOK}
  95. end;
  96. {------------------------------------}
  97. {过程名:UnHook
  98. {过程功能:卸载HOOK
  99. {过程参数:无
  100. {------------------------------------}
  101. procedure UnHook;stdcall;
  102. begin
  103.   {发送卸载子类化消息给指定窗口}
  104.   SendMessage(DLLData^.HookWindow, WM_UNSUBCLASS, 0, 0);
  105.   DLLData^.SubClass := False;
  106.   {卸载Hook}
  107.   UnhookWindowsHookEx(DLLData^.hHook);
  108. end;
  109. {------------------------------------}
  110. {过程名:DLL入口函数
  111. {过程功能:进行DLL初始化,释放等
  112. {过程参数:DLL状态
  113. {------------------------------------}
  114. procedure MyDLLHandler(Reason: Integer);
  115. var
  116.   FHandle: LongWORD;
  117. begin
  118.   case Reason of
  119.     DLL_PROCESS_ATTACH:
  120.     begin            {建立文件映射,以实现DLL中的全局变量}
  121.       FHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, $ff, 'MYDLLDATA');
  122.       if FHandle = 0 then
  123.       if GetLastError = ERROR_ALREADY_EXISTS then
  124.       begin
  125.         FHandle := OpenFileMapping(FILE_MAP_ALL_ACCESS, False, 'MYDLLDATA');
  126.         if FHandle = 0 then Exit;
  127.       end else Exit;
  128.       DLLData := MapViewOfFile(FHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
  129.       if DLLData = nil then
  130.         CloseHandle(FHandle);
  131.     end;
  132.     DLL_PROCESS_DETACH:
  133.       if Assigned(DLLData) then
  134.       begin
  135.         UnmapViewOfFile(DLLData);
  136.         DLLData := nil;
  137.       end;
  138.     DLL_THREAD_ATTACH:;
  139.     DLL_THREAD_DETACH:;
  140.   end;
  141. end;
  142. {$R *.res}
  143. exports
  144.   InstallHook, UnHook, HookProc;
  145. begin
  146.   DLLProc := @MyDLLHandler;
  147.   MyDLLhandler(DLL_PROCESS_ATTACH);
  148. end.

编译这个DLL,然后在我们的程序中加载这个DLL,并调用InstallHook(目标窗口句柄, 自己窗口句柄)就可  以实现对目标窗口消息的监视了(在接收到WM_NEWMESSAGE消息时读映射的内存),调用UnHook则可以卸载掉子类化和HOOK.具休的代码还请读者自行编写.

  1. unit Unit2;
  2. interface
  3. uses
  4.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  5.   Dialogs, StdCtrls;
  6. const WM_NEWMESSAGE = WM_USER + 1002;
  7. type
  8.   TForm2 = class(TForm)
  9.     btn1: TButton;
  10.     btn2: TButton;
  11.     lst1: TListBox;
  12.     procedure btn1Click(Sender: TObject);
  13.     procedure btn2Click(Sender: TObject);
  14.   private
  15.     procedure ShowMsg(var Message:TMessage);message WM_NEWMESSAGE;
  16.   public
  17.     { Public declarations }
  18.   end;
  19.   function InstallHook(HWindow, SWindow: LongWORD):Boolean;stdcall;external 'Hook.dll';
  20.   procedure UnHook;stdcall;external 'Hook.dll';
  21. var
  22.   Form2: TForm2;
  23.   ListCount:Integer;
  24. implementation
  25. {$R *.dfm}
  26. procedure TForm2.ShowMsg(var Message:TMessage);
  27. begin
  28.   if Message.Msg = WM_MOUSEHOVER then
  29.   begin
  30.     ListCount:=ListCount+1;
  31.     Form2.lst1.AddItem(IntToStr(ListCount)+'.'+IntToStr(Message.WParam)+','+IntToStr(Message.lParam),nil);
  32.   end;
  33. end;
  34. function GetWindowHwnd:THandle;
  35. begin
  36.   Result:=FindWindow(nil,'ABCDE.txt - 记事本');
  37. end;
  38. procedure TForm2.btn1Click(Sender: TObject);
  39. var
  40.   TmpWndHandle: THandle;
  41. begin
  42.   TmpWndHandle := 0;
  43.   TmpWndHandle := GetWindowHwnd;
  44.   if not isWindow(TmpWndHandle) then
  45.   begin
  46.     MessageBox(self.Handle, '没有找到指定窗口', '!!!', MB_OK);
  47.     exit;
  48.   end;
  49.   InstallHook(TmpWndHandle,Handle);
  50. end;
  51. procedure TForm2.btn2Click(Sender: TObject);
  52. begin
  53.   UnHook;  //下message钩子
  54. end; 
  55. end.