程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 用跨進程子類化技術實現對其它進程消息的攔載

用跨進程子類化技術實現對其它進程消息的攔載

編輯:Delphi
      大家都知道每個窗口都有默認的窗口函數來進行對窗口消息的處理.
    而子類化技術就是替換窗口的窗口函數為自己定義的函數的技術.例如下面的代碼:
  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中):
  library Hook;

  uses
    SysUtils,Windows, Messages;

  const
    WM_UNSUBCLASS = WM_USER + 1001;  {卸載子類化消息}
    WM_NEWMESSAGE = WM_USER + 1002;  {通知監視窗口攔到了新消息}
    HOOK_EVENT_NAME = 'MyHook';

  type
    PMyDLLVar = ^TMyDLLVar;
    TMyDLLVar = record
      SubClass: Boolean;                 {是否已經子類化}
      HookWindow, SpyWindow: LongWord;   {要安裝HOOK的窗口及用於接收消息的窗口}
      hHook: LongWord;                   {HOOK句柄}
      OldWndProc: pointer;               {舊的窗口過程}
      MsgHwnd: LongWord;
      Msg: TMessage;
    end;

  var
    DLLData: PMyDLLVar;

  {---------------------------------------}
  {函數名:NewWndProc
  {函數功能:新的窗口過程
  {函數參數:hHwnd:窗口句柄 Msg:消息ID
  {         wParam, lParam:消息參數
  {函數返回值:下一個窗口過程的返回值
  {---------------------------------------}
  function NewWndProc(hHwnd, Msg, wParam, lParam: LongWord): Longint; stdcall;
  begin
    if Msg = WM_UNSUBCLASS then   {如果收到卸載子類化消息就恢復以前的WndProc}
    begin
      SetWindowLong(DLLData^.HookWindow, GWL_WNDPROC, longint(DLLData^.OldWndProc));
      exit;
    end;
    {這兒是把收到的消息放在映射的內存中,我們自己的程序可以通過讀這個內存來得到監視到的消息.}
    DLLData^.Msg.Msg := Msg;           
    DLLData^.Msg.WParam := wParam;
    DLLData^.Msg.LParam := lParam;
    DLLData^.MsgHwnd := hHwnd;
    {給監視窗口發送攔載新消息的消息}
    SendMessage(DLLData^.SpyWindow, WM_NEWMESSAGE, 0, 0);
    {這兒可以添加自己對目標進程消息處理的代碼,因為己經是在目標進程的地址空間內,現在可以為所
    欲為 ^_^)
    Result := CallWindowProc(DLLData^.OldWndProc, hHwnd, Msg, wParam, lParam);
  end;

  {------------------------------------}
  {過程名:HookProc
  {過程功能:HOOK過程
  {過程參數:nCode, wParam, lParam消息的相
  {         關參數
  {------------------------------------}
  procedure HookProc(nCode, wParam, lParam: LongWord);stdcall;
  var
    hEvent: THandle;
  begin
    if not DllData^.SubClass then  {如果此窗口未子類化}
    begin                          {保存窗口過程地址並子類化}
      if hEvent <> 0 then
      begin
        WaitForSingleObject(hEvent, INFINITE);
        CloseHandle(hEvent);
      end;
      DLLData^.OldWndProc := pointer(GetWindowLong(DLLData^.HookWindow, GWL_WNDPROC));
      SetWindowLong(DLLData^.HookWindow, GWL_WNDPROC, integer(@NewWndProc));
      DLLData^.SubClass := True;
      hEvent := OpenEvent(Synchronize, False, HOOK_EVENT_NAME);
    end;
    {調用下一個Hook}
    CallNextHookEx(DLLData^.hHook, nCode, wParam, lParam);
  end;

  
  {------------------------------------}
  {函數名:InstallHook
  {函數功能:在指定窗口上安裝HOOK
  {函數參數:HWindow:要安裝HOOK的窗口
  {         SWindow:用於接收消息的窗口
  {返回值:成功返回TRUE,失敗返回FALSE
  {------------------------------------}
  function InstallHook(HWindow, SWindow: LongWord):Boolean;stdcall;
  var
    ThreadID: LongWord;
    hEvent: THandle;
  begin
    Result := False;
    DLLData^.hHook := 0;
    DLLData^.HookWindow := HWindow;
    DLLData^.SpyWindow := SWindow;
    {得到指定窗口的線程ID}
    ThreadID := GetWindowThreadProcessId(HWindow, nil);
    {給指定窗口掛上鉤子}
    hEvent := CreateEvent(nil, True, False, HOOK_EVENT_NAME);
    DLLData^.hHook := SetWindowsHookEx(WH_GETMESSAGE, @HookProc, Hinstance, ThreadID);
    SetEvent(hEvent);
    CloseHandle(hEvent);
    if DLLData^.hHook > 0 then Result := True;  {是否成功HOOK}
  end;

  {------------------------------------}
  {過程名:UnHook
  {過程功能:卸載HOOK
  {過程參數:無
  {------------------------------------}
  procedure UnHook;stdcall;
  begin
    {發送卸載子類化消息給指定窗口}
    SendMessage(DLLData^.HookWindow, WM_UNSUBCLASS, 0, 0);
    DLLData^.SubClass := False;
    {卸載Hook}
    UnhookWindowsHookEx(DLLData^.hHook);
  end;

  {------------------------------------}
  {過程名:DLL入口函數
  {過程功能:進行DLL初始化,釋放等
  {過程參數:DLL狀態
  {------------------------------------}
  procedure MyDLLHandler(Reason: Integer);
  var
    FHandle: LongWord;
  begin
    case Reason of
      DLL_PROCESS_ATTACH:
      begin            {建立文件映射,以實現DLL中的全局變量}
        FHandle := CreateFileMapping($FFFFFFFF, nil, PAGE_READWRITE, 0, $ff, 'MYDLLDATA');
        if FHandle = 0 then
        if GetLastError = ERROR_ALREADY_EXISTS then
        begin
          FHandle := OpenFileMapping(FILE_MAP_ALL_Access, False, 'MYDLLDATA');
          if FHandle = 0 then Exit;
        end else Exit;
        DLLData := MapVIEwOfFile(FHandle, FILE_MAP_ALL_Access, 0, 0, 0);
        if DLLData = nil then
          CloseHandle(FHandle);
      end;
      DLL_PROCESS_DETACH:
        if Assigned(DLLData) then
        begin
          UnmapVIEwOfFile(DLLData);
          DLLData := nil;
        end;
      DLL_THREAD_ATTACH:;
      DLL_THREAD_DETACH:;
    end;
  end;

  {$R *.res}
  exports
    InstallHook, UnHook, HookProc;

  begin
    DLLProc := @MyDLLHandler;
    MyDLLhandler(DLL_PROCESS_ATTACH);
  end.

    編譯這個DLL,然後在我們的程序中加載這個DLL,並調用InstallHook(目標窗口句柄, 自己窗口句柄)就可  以實現對目標窗口消息的監視了(在接收到WM_NEWMESSAGE消息時讀映射的內存),調用UnHook則可以卸載掉子類化和HOOK.具休的代碼還請讀者自行編寫.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved