HOOK技术-满足我们程序的偷窥欲(二)
话接上回,这次给个例子讲讲Hook的实例。
这个例子是设置一个全局钩子,捕捉其它程序的键盘消息。
首先,构建DLL,以便给其它程序映射。这个DLL文件名为KeyHook.h / KeyHook.cpp
在KeyHook.h里面,导出两个函数,分别是设置Hook和取消Hook的函数。
//----------------------------------KeyHook.h-----------------------------------//
#ifndef EXPERT
#define EXPERT extern "C" __declspec(dllimport)
#endif
EXPERT BOOL SetHook(HWND hwnd); //设置HOOK
EXPERT BOOL CancelHook(HWND hwnd); //取消HOOK
#define WM_KEY_HOOK WM_USER+102 //自定义消息,设置自定义消息的作用是,当这个DLL截取到其它程序的键盘消息时,给我们的应用程序发送这个消息。
#ifndef EXPERT
#define EXPERT extern "C" __declspec(dllimport)
#endif
EXPERT BOOL SetHook(HWND hwnd); //设置HOOK
EXPERT BOOL CancelHook(HWND hwnd); //取消HOOK
#define WM_KEY_HOOK WM_USER+102 //自定义消息,设置自定义消息的作用是,当这个DLL截取到其它程序的键盘消息时,给我们的应用程序发送这个消息。
//-------------------------KeyHook.cpp---------------------//
#define EXPERT extern "C" __declspec(dllexport)
//定义DLL共享数据段,为的是这个段中的数据可以被本应用程序所改写
#pragma data_seg("HOOKSHARE")
HWND hwndServer=NULL;
#pragma data_seg()
#pragma comment(linker,"/section:HOOKSHARE,rws")
HINSTANCE hInst; //Dll的实例句柄
HHOOK hook; //Hook句柄
LRESULT WINAPI WndHookProc(int nCode,WPARAM wParam,LPARAM lParam); //钩子一旦挂上,这个WndHookProc就会接收其它应用程序的消息了。
BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hInst = (HINSTANCE)hModule;
return TRUE;
//在以后设置钩子时需要用到Dll的实例句柄。所以这里把hModule赋给了全局变量hInst,
case DLL_PROCESS_DETACH:
CancelHook(hwndServer);
return TRUE;
}
return TRUE;
}
EXPERT BOOL SetHook(HWND hwnd)
{
if(hwndServer==NULL) //have't hook
{
hook = ::SetWindowsHookEx(WH_GETMESSAGE,WndHookProc,hInst,0);
//设置全局钩子,这个钩子捕获消息,捕获消息传递到WndHookProc中去处理。hInst是保存了Dll的实例句柄。最后一个参数是线程的ID,设置0表示为全局钩子
if(hook!=NULL)
{
hwndServer = hwnd;
//把我们应用程序的窗口句柄赋给hwndServer.这样,我们在DLL中发送消息就有个目的地(即截取消息后发送到哪个窗口去处理)。
return TRUE;
}
else
return FALSE;
}
else
return FALSE;
}
EXPERT BOOL CancelHook(HWND hwnd)
{
if(hwndServer!=NULL) //is hook
{
if(UnhookWindowsHookEx(hook))
{
hwndServer = NULL;
return TRUE;
}
else
return FALSE;
}
else
return FALSE;
}
LRESULT WINAPI WndHookProc(int nCode,WPARAM wParam,LPARAM lParam)
{
if(nCode<0)
{
::CallNextHookEx(hook,nCode,wParam,lParam);
return 0;
}
LPMSG lpMsg = (LPMSG)lParam;
if(lpMsg->message == WM_KEYDOWN)
{
//如果消息是WM_KEYDOWN消息,则发送我们自定义的消息给我们的应用程序窗口(hwndServer)
::PostMessage(hwndServer,WM_KEY_HOOK,lpMsg->wParam,lpMsg->lParam);
return ::CallNextHookEx(hook,nCode,wParam,lParam);
}
return 0;
}
#define EXPERT extern "C" __declspec(dllexport)
//定义DLL共享数据段,为的是这个段中的数据可以被本应用程序所改写
#pragma data_seg("HOOKSHARE")
HWND hwndServer=NULL;
#pragma data_seg()
#pragma comment(linker,"/section:HOOKSHARE,rws")
HINSTANCE hInst; //Dll的实例句柄
HHOOK hook; //Hook句柄
LRESULT WINAPI WndHookProc(int nCode,WPARAM wParam,LPARAM lParam); //钩子一旦挂上,这个WndHookProc就会接收其它应用程序的消息了。
BOOL APIENTRY DllMain( HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hInst = (HINSTANCE)hModule;
return TRUE;
//在以后设置钩子时需要用到Dll的实例句柄。所以这里把hModule赋给了全局变量hInst,
case DLL_PROCESS_DETACH:
CancelHook(hwndServer);
return TRUE;
}
return TRUE;
}
EXPERT BOOL SetHook(HWND hwnd)
{
if(hwndServer==NULL) //have't hook
{
hook = ::SetWindowsHookEx(WH_GETMESSAGE,WndHookProc,hInst,0);
//设置全局钩子,这个钩子捕获消息,捕获消息传递到WndHookProc中去处理。hInst是保存了Dll的实例句柄。最后一个参数是线程的ID,设置0表示为全局钩子
if(hook!=NULL)
{
hwndServer = hwnd;
//把我们应用程序的窗口句柄赋给hwndServer.这样,我们在DLL中发送消息就有个目的地(即截取消息后发送到哪个窗口去处理)。
return TRUE;
}
else
return FALSE;
}
else
return FALSE;
}
EXPERT BOOL CancelHook(HWND hwnd)
{
if(hwndServer!=NULL) //is hook
{
if(UnhookWindowsHookEx(hook))
{
hwndServer = NULL;
return TRUE;
}
else
return FALSE;
}
else
return FALSE;
}
LRESULT WINAPI WndHookProc(int nCode,WPARAM wParam,LPARAM lParam)
{
if(nCode<0)
{
::CallNextHookEx(hook,nCode,wParam,lParam);
return 0;
}
LPMSG lpMsg = (LPMSG)lParam;
if(lpMsg->message == WM_KEYDOWN)
{
//如果消息是WM_KEYDOWN消息,则发送我们自定义的消息给我们的应用程序窗口(hwndServer)
::PostMessage(hwndServer,WM_KEY_HOOK,lpMsg->wParam,lpMsg->lParam);
return ::CallNextHookEx(hook,nCode,wParam,lParam);
}
return 0;
}
下面,就要写我们的应用程序来处理DLL截获过来的消息了。假定我们的应用程序由一个对话框来处理。
对话框的文件名分别是:SendMsg.h SendMsg.cpp
//------------------SendMsg.h---------------------//
//添加两个Button,分别是Hook / Unhook ,他们的处理程序分别是OnHook和OnUnhook
afx_msg void OnHook();
afx_msg void OnUnhook();
//自定义消息,当窗口接收到WM_KEY_HOOK消息时,就是用onHookMsg函数来处理
afx_msg LRESULT OnHookMsg(WPARAM wParam,LPARAM lParam);
//添加两个Button,分别是Hook / Unhook ,他们的处理程序分别是OnHook和OnUnhook
afx_msg void OnHook();
afx_msg void OnUnhook();
//自定义消息,当窗口接收到WM_KEY_HOOK消息时,就是用onHookMsg函数来处理
afx_msg LRESULT OnHookMsg(WPARAM wParam,LPARAM lParam);
//--------------------SendMsg.cpp-------------------//
#include "SetHook.h" //把Dll的.h文件添加进项目
//自定义消息映射
ON_MESSAGE(WM_KEY_HOOK,OnHookMsg)
//当点击Hook按纽时,设置Hook
void CSendMsg::OnHook()
{
if(!m_IsHook)
{
BOOL isHook = SetHook(m_hWnd); //设置挂钩,让DLL映射进其它进程的地址空间
if(isHook)
{
m_Hook.EnableWindow(FALSE);
m_UnHook.EnableWindow(TRUE);
m_IsHook = TRUE;
}
}
}
//点击UnHook按纽时,取消hook
void CSendMsg::OnUnhook()
{
// TODO: Add your control notification handler code here
if(m_IsHook)
{
BOOL isHook = CancelHook(m_hWnd);
if(isHook)
{
m_Hook.EnableWindow(TRUE);
m_UnHook.EnableWindow(FALSE);
m_IsHook = FALSE;
}
}
}
LRESULT CSendMsg::OnHookMsg(WPARAM wParam,LPARAM lParam)
{
//这里是当在别的进程中收到KeyDown消息后,转到这里来处理。
//可以在这里获得键盘的值,或者把KeyDown更改后转发都可以。
//具体的就不写了,还是比较简单的。
return 0;
}
#include "SetHook.h" //把Dll的.h文件添加进项目
//自定义消息映射
ON_MESSAGE(WM_KEY_HOOK,OnHookMsg)
//当点击Hook按纽时,设置Hook
void CSendMsg::OnHook()
{
if(!m_IsHook)
{
BOOL isHook = SetHook(m_hWnd); //设置挂钩,让DLL映射进其它进程的地址空间
if(isHook)
{
m_Hook.EnableWindow(FALSE);
m_UnHook.EnableWindow(TRUE);
m_IsHook = TRUE;
}
}
}
//点击UnHook按纽时,取消hook
void CSendMsg::OnUnhook()
{
// TODO: Add your control notification handler code here
if(m_IsHook)
{
BOOL isHook = CancelHook(m_hWnd);
if(isHook)
{
m_Hook.EnableWindow(TRUE);
m_UnHook.EnableWindow(FALSE);
m_IsHook = FALSE;
}
}
}
LRESULT CSendMsg::OnHookMsg(WPARAM wParam,LPARAM lParam)
{
//这里是当在别的进程中收到KeyDown消息后,转到这里来处理。
//可以在这里获得键盘的值,或者把KeyDown更改后转发都可以。
//具体的就不写了,还是比较简单的。
return 0;
}
另外,还有其它编译时注意的事项,如果我们要编译本应用程序,需要和DLL一起编译,并且,要告诉编译器,这个项目的依赖项是KeyHook.dll(菜单:项目->项目依赖项)