ActiveX控件在浏览器中捕获WM_DEVICECHANGE消息
1. WM_DEVICECHANGE消息只发给顶层窗口,子窗口接收不到该消息。
2. HOOK函数不能绑定到进程,而只能绑定到线程。
3. 由于WM_DEVICECHANGE是发给窗口的消息,因此只有UI线程才能捕获到。
4. 消息队列是属于线程,窗口本身没有消息队列。是由线程读出消息并派发给各个窗口。
由于ActiveX控件是在浏览器容器中运行,所以不能捕获到WM_DEVICECHANGE消息。因此只能通过HOOK或者线程注入的方式来捕获容器顶层窗口的消息。
HOOK方式
由前面的2和3可知,要捕获到消息,必须将HOOK函数与一个UI线程挂钩,并且这个UI线程包含顶层窗口,窗口能在浏览器的整个生命周期中都是有效的。一个进程包含一个或多个线程,在线程中又可以创建0个或多个窗体。不是所有的线程,都是UI。只有创建了窗体的线程才属于UI线程。
因此,问题就是如何到浏览器的UI线程?
图1. IE浏览器 图2. UI线程
上图所示,IE浏览器进程包含了多个线程,其中0x00000464线程中创建了很多窗口,因此此线程为UI线程,并且拥有一个消息队列。
消息的派发过程,以鼠标单击某个窗口为例:当鼠标在浏览器单击时,系统监控会出鼠标单击的坐标,所在的窗口等信息封装成一个单击消息,保存在系统消息队列。系统消息队列中的消息会根据消息对应的窗口,查出该窗口所在的进程和线程,然后将消息派发到对应的线程的消息队列。线程循环取出消息,查看该消息是属于哪个窗口,然后发给窗口对应的消息回调函数处理。
远程线程注入方式
远程线程注入,可以将自己写的某一段代码或DLL注入到目标进程,效果和目标进程自己本身的代码和加载DLL一样。
注入代码:将自己写的一段函数写入目标程序。进程只能访问自己地址空间内的变量,因此自己写的函数中需要的变量都需要在目标程序中创建,否则在目标程序中执行该函数时会不到变量或者访问越界,导致程序崩溃。所以,直接注入代码的方式只适合一些很简单的操作。
加载DLL:将复杂的操作写在一个DLL中,然后让目标程序加载。
不论是注入代码或者加载DLL,都需要先在目标进程中创建一个线程来执行你的函数或者LoadLibrary。注意,通过CreateRemoteThread创建的新线程,在执行完你的函数或者加载完DLL后就退出了,没有这个线程了。所以不能在DLL中使用GetCurrentThreadID来获得目标线程,因为这样获得的是创建的新线程的ID,并且该线程也不是UI线程,根本不会收到WM_DEVICECHANGE消息。 加载DLL的方式,也是在DLL中设置钩子函数,来获取目标程序的消息。因此问题又变成了如何获取UI线程的ID。
不能通过DLL来设置全局钩子。设备插入时,每个顶层窗口都会收到同样的WM_DEVICECHANGE消息,而且不止一个。这样,全局钩子会捕获到所有UI线程的重复消息。
方法一:在目标进程中创建一个UI线程。这样需要在DLL中创建一个新的线程,在新线程中创建一个顶层窗口,又需要在DLL中添加对话框资源。这样做很复杂,而且不一定能实现。
方法二:先到浏览器的一个窗口,然后根据窗口到UI线程。但一个UI线程中有那么多窗口,不出那一个是顶层的框架,能够在整个浏览器的生命周期中都可见。而且浏览
器种类繁多,很难统一。
对MFC和操作系统理解的还不深,上面两种方法实现起来有困难。
再回到ActiveX本身。ActiveX控件也是一种窗口,是由浏览器的某一个线程创建,这样,我们可以在线程创建ActiveX控件时,将钩子函数与该线程绑定。下面用SPY++来看一下,是哪个浏览器线程完成的控件加载。
图3. ActiveX控件图4. SPY++到控件
使用SPY++到空间窗口,如图4所示。窗口句柄为0x000404BB。查看窗口属性,就到了控件所在的线程ID为0x00000478。所以能在控件创建时即HOOK该线程最好。
图5. 控件窗口属性
到该线程,查看线程内的所有窗口,如左图所示。可以看到该线程创建了很多窗口,而且不止一个是顶层窗口。在窗口的“属性查看器”中,转到“样式”选项卡,如果样式中包含“WS_POPUP”,则此窗口为顶层窗口,可以接收到WM_DEVICECHANGE消息;若为“WS_CHILDWINDOW”则为子窗口。可以通过查看窗口消息,结合设备插拔来验证上面。
图6. 控件所在线程
下面来实现HOOK该创建ActiveX控件的线程。
intCHelloCtrl::OnCreate(LPCREATESTRUCTlpCreateStruct) { if (activex 控件COleControl::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here SetTimer(1, 1000, NULL); pWnd = this; g_hGetMsg = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, 0, GetCurrentThreadId()) ;// GetCurrentThread获得的是创建该控件的线程ID g_hCall = SetWindowsHookEx(WH_CALLWNDPROC, CallBackProc, 0, GetCurrentThreadId()); CStrings; s.Format("绑定的线程ID:%d", GetCurrentThreadId());// 对话框显示ID MessageBox(s); return 0; } |
在浏览器中运行控件,就会弹出控件HOOK的线程ID,如图7所示。3208转换成十六进制为0xC88。查看该线的WM_DEVICECHANGE消息,如图8所示。
图7. HOOK的线程
设备插拔,在线程0xC88中捕获到的消息。从图8中可以看出Event: DBT_DEVICEREMOVECOMPLETE事件有3次,但是是发给不同的窗口。因为这些窗口都是顶层窗口,都能接收WM_DEVICECHANGE消息。这也就是为什么捕获消息时会获得很多“设备已安装”的消息,就是因为绑定的线程中包含了不止一个的顶层窗口。
图8. 线程捕获WM_DEVICECHANGE消息
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论