《Windows核心编程》之“Windows挂钩”(二)

    本文接上篇,继续探讨“Windows挂钩”,包括:跨进程的“窗口子类化”,使用Windows Hook注入DLL,跨进程窗口通讯,进程间共享内存和示例程序调试总结。

一、跨进程的“窗口子类化”     subclass a window created by another process

    上一篇最后,我们讲到通过“窗口子类化”来扩展窗口的行为。其关键是将自定义的窗口处理函数(NewWndProc)通过调用user32.dll中的SetWindowLongPtr函数与指定窗口关联起来。当线程调用DispatchMessage时,将取得的是NewWndProc函数的地址。原本发往OldWndProc函数的消息,会转发给NewWndProc。

LONG DispatchMessage(CONST MSG * msg)
{
    LONG lResult;
	WNDPROC lpfnWndProc = (WNDPROC)GetWindowLongPtr(msg.hwnd, msg.message);
	lResult = lpfnWndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
	return(lResult);
}

    当然,这一切的前提是,NewWndProc函数也位于被子类化的窗口所在的进程空间。因为,DispatchMessage同大多数Windows APIs一样,默认只操作调用它的线程所属的进程空间。

    如果我们需要子类化其他进程的窗口,即NewWndProc函数位于我们的进程空间,而不是位于被子类化的窗口所在的进程空间,那么DispatchMessage在引用我们传入的函数地址值时,实际访问的是目标进程的某个地址,根本就不可能调到NewWndProc函数。

    因此,我们要想子类化其他进程的窗口的窗口,第一步就是要把NewWndProc函数注入到目标进程的地址空间。《Windows核心编程》一书中介绍了多种方法来讲NewWndProc函数注入到目标进程地址空间。


二、Windows Hook

1,Windows Hook

    Windows系统提供了一种从进程A将一个函数注入到进程B并截取进程B某个窗口的消息的方法——Windows Hook。

    先来看MSDN怎么说:点击打开链接

A hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.

hook is a mechanism by which an application can intercept events, such as messages, mouse actions, and keystrokes. A function that intercepts a particular type of event is known as a hook procedure. A hook procedure can act on each event it receives, and then modify or discard the event.

The following some example uses for hooks:

  • Monitor messages for debugging purposes
  • Provide support for recording and playback of macros
  • Provide support for a help key (F1)
  • Simulate mouse and keyboard input
  • Implement a computer-based training (CBT) application
Note  Hooks tend to slow down the system because they increase the amount of processing the system must perform for each message. You should install a hook only when necessary, and remove it as soon as possible.
    Windows Hook主要包括:Hook Chains、Hook Procedures和Hook types这么几个概念,在此我就不展开说了,有兴趣的直接看MSDN文档。
    MSDN也给出了两个使用Hook的示例:Installing and Releasing Hook Procedures和Monitoring System Events,点击打开链接。 在此,简单描述一下前一个示例。
    
    
2,Installing and Releasing Hook Procedures

1)SetWindowsHookEx

Installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread.

Syntax

C++
HHOOK WINAPI SetWindowsHookEx(
  _In_ int       idHook,
  _In_ HOOKPROC  lpfn,
  _In_ HINSTANCE hMod,
  _In_ DWORD     dwThreadId
);


    要从我们的进程(进程A)向目标进程(进程B)的某个窗口安装挂钩,就需要在A进程中调用SetWindowsHookEx函数。例如:

Hook hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hInstDll, 0);

    第1个参数WH_MESSGAE表示要安装的挂钩的类型。第2个参数GetMsgProc是一个函数的地址(位于进程A的地址空间),在目标窗口即将处理一条消息的时候,系统应该调用这个函数。第3个参数hInstDll表示一个DLL,这个DLL中包含了GetMsgProc函数。在Windows中,hInstDll的值是进程地址空间中DLL被映射到的虚拟内存地址。最后一个参数0表示要给哪个线程安装挂钩。一个线程可能会调用SetWindowsHookEx并传入系统中另一个线程的线程标识符(一般是线程ID)。通过给这个参数传0,我们告诉系统要给系统中的所有GUI线程安装挂钩。

    从SetWindowsHookEx的原型,我们可以推测:

1)Hook Procedure函数既要载入进程A的地址空间,又要载入进程B的地址空间,且进程A要知道该函数在进程B中的虚拟内存地址。第2个参数表示的就是Hook Procedure函数。

2)Hook Procedure函数要放在一个独立的DLL中。一个函数要同时载入两个进程空间,那么最好的设计是将该函数放到一个DLL中,作为一个导出函数,让进程A和进程B都载入该DLL。第3个参数表示的就是这个DLL。

3)进程A需要自己去加载InstDll,进程B不需要。SetWindowsHookEx会将InstDll加载到指定GUI线程所在进程(进程B)的地址空间。严格来说,SetWindowsHookEx只是注册,真正的加载过程如下:


1)进程B中的一个线程准备向一个窗口派送一条消息(dispatch message)。

2)系统检查该线程是否已经安装了WH_GETMESSAGE挂钩。

3)系统检查GetMsgProc所在的DLL是否已经被映射到进程B的地址空间中。

4)如果DLL尚未被映射,那么系统会强制将该DLL映射到进程B,并将进程B中该DLL的锁计数器(lock count)递增。

5)由于DLL的hInstDll是在进程B中映射的,因此系统会对它进行检查,看它与该DLL在进程A中的位置是否相同。

    如果hInstDLL在进程A和进程B的位置相同,那么GetMsgProc函数在这两个进程空间的虚拟内存地址也就会相同。这种情况,系统就可以直接在进程B中调用GetMsgProc。

    如果hInstDLL不同,系统会通过以下公式换算,来获取GetMsgProc在进程B中的地址

GetMsgProc B = hInstDll B + (GetMsgProc A - hInstDll A)

6)系统在进程B中调用GetMsgProc函数,返回后,系统会递减该DLL在进程B中的锁计数器。


Hook Procedure函数如下:

LRESULT CALLBACK HookProc(
  int nCode, 
  WPARAM wParam, 
  LPARAM lParam
)
{
   // process event
   ...

   return CallNextHookEx(NULL, nCode, wParam, lParam);
}

3,Monitoring System Events

    SetWindowsHookEx函数的第一个参数表示Hook的类型,它对应一种系统事件(event)。Monitoring System Events示例展示了对它支持的7种系统事件的监视和处理。可以查看MSDN文档,了解这7种事件的具体含义。

   《Windows核心编程》中的DIPS示例展示的就是其中的“WM_GETMESSAGE”事件的处理。MSDN说:Installs a hook procedure that monitors messages posted to a message queue.它监视所有发送给被监视线程的消息。


    另一个值得注意的是“WM_KEYBOARD”事件。它配合GetKeyNameText函数,可以监视用户敲击的键盘按键。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    ......
	case WM_KEYDOWN:
		//MessageBox(NULL, TEXT("key down"), NULL, MB_OK);
		WCHAR keyname[128];
		MessageBox(NULL, keyname, NULL, MB_OK);
		break;	
    ......
    case WM_DESTROY:
        PostQuitMessage(0); 
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}


4,Subclass a window created by another process

    注意,SetWindowsHookEx会将整个DLL映射到目标进程,而仅仅是Hook Procedure函数。这就意味着,该DLL内的所有函数都存在进程B中,可以为进程B调用。

    回到之前“跨进程窗口子类化”的话题,我们可以先给创建窗口(目标窗口)的线程设置一个WH_GETMESSAGE挂钩,然后当GetMsgProc被调用的时候,我们可以在它内部调用SetWindowLongPtr来派送子类窗口。当然,子类窗口的过程函数必须和GetMsgProc函数在同一个DLL中。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值