hook入门[网络整理]

来源:互联网 发布:tcl通讯宁波知乎 编辑:程序博客网 时间:2024/04/30 13:48

下面文档资料的VC6程序源代码请在我的资源里下载

-----------------OK BEGIN鼠标--------------------------------

http://www.programbbs.com/doc/1580.htm

在VC++ 6.0下应用Win32系统钩子技术
所属类别:VC++
推荐指数:★★☆
文档人气:334
本周人气:1
发布日期:2007-5-8

信息产业部电子第二十二研究所 郎锐

一、引言
钩子的本质是一段用以处理系统消息的程序,通过系统调用,把它挂入系统。钩子的种类很多,每种钩子可以截获并处理相应的消息,每当特定的消息发出,在到达目的窗口之前,钩子程序先行截获该消息、得到对此消息的控制权。此时钩子函数可以对截获的消息进行加工处理,甚至可以强制结束消息的传递。这有点类似与MFC中的PreTranslateMessage函数,所不同的是该函数只能用于拦截本进程中的消息,而对系统消息则无能为力。
二、Win32系统钩子的实现
每种类型的钩子均由系统来维护一个钩子链,最近安装的钩子位于链的开始,拥有最高的优先级,而最先安装的钩子则处在链的末尾。要实现Win32的系统钩子,首先要调用SDK中的API函数SetWindowsHookEx来安装这个钩子函数,其原型是:
HHOOK SetWindowsHookEx(int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId);
其中,第一个参数是钩子的类型,常用的有WH_MOUSE、WH_KEYBOARD、WH_GETMESSAGE等;第二个参数是钩子函数的地址,当钩子钩到任何消息后便调用这个函数;第三个参数是钩子函数所在模块的句柄;第四个参数是钩子相关函数的ID用以指定想让钩子去钩哪个线程,为0时则拦截整个系统的消息此时为全局钩子。如果指定确定的线程,即为线程专用钩子。
全局钩子函数必须包含在DLL(动态链接库)中,而线程专用钩子则可包含在可执行文件中。得到控制权的钩子函数在处理完消息后,可以调用另外一个SDK中的API函数CallNextHookEx来继续传递该消息。也可以通过直接返回TRUE来丢弃该消息,阻止该消息的传递。
使用全局钩子函数时需要以DLL为载体,VC6中有三种形式的MFC DLL可供选择,即Regular statically linked to MFC DLL(标准静态链接MFC DLL)、Regular using the shared MFC DLL(标准动态链接MFC DLL)以及Extension MFC DLL(扩展MFC DLL)。第一种DLL在编译时把使用的MFC代码链接到DLL中,执行程序时不需要其他MFC动态链接类库的支持,但体积较大;第二种DLL在运行时动态链接到MFC类库,因而体积较小,但却依赖于MFC动态链接类库的支持;这两种DLL均可被MFC程序和Win32程序使用。第三种DLL的也是动态连接,但做为MFC类库的扩展,只能被MFC程序使用。
三、Win32 DLL
Win32 DLL的入口和出口函数都是DLLMain这同Win16 DLL是有区别的。只要有进程或线程载入和卸载DLL时,都会调用该函数,其原型是:
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason, LPVOID lpvReserved);其中,第一个参数表示DLL的实例句柄;第三个参数系统保留;第二个参数指明了当前调用该动态连接库的状态,它有四个可能的值:DLL_PROCESS_ATTACH(进程载入)、DLL_THREAD_ATTACH(线程载入)、DLL_THREAD_DETACH(线程卸载)、DLL_PROCESS_DETACH(进程卸载)。在DLLMain函数中可以通过对传递进来的这个参数的值进行判别,根据不同的参数值对DLL进行必要的初始化或清理工作。由于在Win32环境下,所有进程的空间都是相互独立的,这减少了应用程序间的相互影响,但大大增加了编程的难度。当进程在动态加载DLL时,系统自动把DLL地址映射到该进程的私有空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间,每个进程所拥有的相同的DLL的全局数据其值却并不一定是相同的。当DLL内存被映射到进程空间中,每个进程都有自己的全局内存拷贝,加载DLL的每一个新的进程都重新初始化这一内存区域,也就是说进程不能再共享DLL。因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。一种方法便是把这些需要共享的数据单独分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享,建立一个内存共享的DLL。

四、全局共享数据的实现
可以用#pragma data_seg建立一个新的数据段并定义共享数据,其具体格式为:
#pragma data_seg (shareddata)
HWND sharedwnd=NULL;共享数据
#pragma data_seg()
所有在data_seg pragmas语句之间声明的变量都将在shareddata段中。仅定义一个数据段还不能达到共享数据的目的,还要告诉编译器该段的属性,有两种方法可以实现该目的(其效果是相同的),一种方法是在.DEF文件中加入如下语句:
SETCTIONS
shareddata READ WRITE SHARED
另一种方法是在项目设置链接选项中加入如下语句:
SECTIONshareddata,rws
五、鼠标钩子程序示例
本示例程序用到全局钩子函数,程序分两部分:可执行程序MouseDemo和动态连接库MouseHook。首先编制MFC扩展动态连接库MouseHook.dll
(一)选择MFC AppWizard(DLL)创建项目Mousehook;
(二)选择MFC Extension DLL(MFC扩展DLL)类型;
(三)通过Project菜单的AddToProject子菜单的New…添加头文件MouseHook.h。
(四)在头文件中建立钩子类:
  class AFX_EXT_CLASS CMouseHookpublic CObject
  {
  public
  CMouseHook();  钩子类的构造函数
  ~CMouseHook();  钩子类的析构函数
  BOOL StartHook(HWND hWnd);   安装钩子函数
  BOOL StopHook();    卸载钩子函数
};
(五)在MouseHook.cpp文件中加入#includeMouseHook.h语句;
(六)加入全局共享数据变量:
  #pragma data_seg(mydata)
  HWND glhPrevTarWnd=NULL;  上次鼠标所指的窗口句柄
  HWND glhDisplayWnd=NULL;  显示目标窗口标题编辑框的句柄
  HHOOK glhHook=NULL;   安装的鼠标勾子句柄
  HINSTANCE glhInstance=NULL; DLL实例句柄
#pragma data_seg()
(七)在DEF文件中定义段属性:
  SECTIONS
  mydata READ WRITE SHARED
(八)在主文件MouseHook.cpp的DllMain函数中加入保存DLL实例句柄的语句:
extern C int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
if (!AfxInitExtensionModule(MouseHookDLL, hInstance))
return 0;
new CDynLinkLibrary(MouseHookDLL);
glhInstance=hInstance;    插入保存DLL实例句柄
}
else if (dwReason == DLL_PROCESS_DETACH)
{
AfxTermExtensionModule(MouseHookDLL);
}
return 1;  ok
}
这个函数最重要的部分是调用AfxInitExtensionModule(),它初始化DLL使它在MFC框架中正确的工作。它需要传递给DllMain()的DLL实例句柄和AFX_EXTENSION_MODULE结构,结构中存在着对MFC有用的信息。
(九) 类CMouseHook的成员函数的具体实现:
CmousehookCmousehook() 类构造函数
{
}
Cmousehook~Cmousehook() 类析构函数
{
  stophook();
}
BOOL Cmousehookstarthook(HWND hWnd)  安装钩子并设定接收显示窗口句柄
{
BOOL bResult=FALSE;
glhHook=SetWindowsHookEx(WH_MOUSE,MouseProc,glhInstance,0);
if(glhHook!=NULL)
bResult=TRUE;
glhDisplayWnd=hWnd;  设置显示目标窗口标题编辑框的句柄
return bResult;
}
BOOL Cmousehookstophook()  卸载钩子
{
BOOL bResult=FALSE;
if(glhHook)
{
bResult= UnhookWindowsHookEx(glhHook);
if(bResult)
{
glhPrevTarWnd=NULL;
glhDisplayWnd=NULL;清变量
glhHook=NULL;
}
}
return bResult;
}
(十) 钩子函数的实现
LRESULT WINAPI MouseProc(int nCode,WPARAM wparam,LPARAM lparam)
{
LPMOUSEHOOKSTRUCT pMouseHook=(MOUSEHOOKSTRUCT FAR ) lparam;
if (nCode=0)
{
HWND glhTargetWnd=pMouseHook-hwnd;  取目标窗口句柄
HWND ParentWnd=glhTargetWnd;
while (ParentWnd !=NULL)
{
glhTargetWnd=ParentWnd;
ParentWnd=GetParent(glhTargetWnd); 取应用程序主窗口句柄
}
if(glhTargetWnd!=glhPrevTarWnd)
{
char szCaption[100];
GetWindowText(glhTargetWnd,szCaption,100); 取目标窗口标题
if(IsWindow(glhDisplayWnd))
SendMessage(glhDisplayWnd,WM_SETTEXT,0,(LPARAM)(LPCTSTR)szCaption);
glhPrevTarWnd=glhTargetWnd;   保存目标窗口
}
}
return CallNextHookEx(glhHook,nCode,wparam,lparam); 继续传递消息
}
编译完成便可得到运行时所需的鼠标钩子的动态连接库MouseHook.dll和链接时用到的MouseHook.lib。
六、集成
下面新建一调用鼠标钩子动态连接库的钩子可执行程序:
(一) 用MFC的AppWizard(EXE)创建项目MouseDemo;
(二) 选择基于对话应用,其余几步均为确省;
(三) 在对话框上加入一个编辑框IDC_EDIT1;
(四) 在MouseDemo.h中加入对Mousehook.h的包含语句:#IncludeMousehook.h;
(五) 在CMouseDemoDlg.h的CMouseDemoDlg类定义中添加私有数据成员:CMouseHook m_hook;
(六) 在OnInitDialog函数的TODO注释后添加:
CWnd  pwnd=GetDlgItem(IDC_EDIT1);  取得编辑框的类指针
m_hook.StartHook(pwnd-GetSafeHwnd()); 取得编辑框的窗口句柄并安装钩子
(七)链接DLL库,即把Mousehook.lib加入到项目设置链接标签中;
(八)把MouseHook.h和MouseHook.lib复制到MouseDemo工程目录中,MouseHook.dll复制到Debug目录下。编译执行程序即可。当鼠标滑过窗口时便会在编辑框中将此窗口的标题显示出来。
结论:
系统钩子具有相当强大的功能,通过这种技术可以对几乎所有的Windows
系统消息进行拦截、监视、处理。这种技术可以广泛应用于各种软件,尤其是需要
有监控、自动记录等对系统进行监测功能的软件。本程序只对鼠标消息进行拦截,
相应的也可以在Win32环境下对键盘、端口等应用此技术完成特定的功能。


摘自:计算机世界网

 

---------------------------------------------------------------------------------------------------


全局钩子详解
http://www.programbbs.com/doc/3164.htm
所属类别:VC++
推荐指数:★★★☆
文档人气:3330
本周人气:32
发布日期:2008-5-22
监控程序的实现
     我们发现一些木马或其他病毒程序常常会将我们的键盘或鼠标的操作消息记录下来然后再将它发到他们指定的地方以实现监听.这种功能其他是利用了全局钩子将鼠标或键盘消息进行了截取,从而获得了操作的消息.要得到鼠标和键盘的控制权,我们要用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动态链接库的空工程,新建一个头文件为了动态链接库本身和使用动态链接库的应用程序也能用,我们定义好导入导出宏和自定义消息以及要导入和导出的函数的定义
HookDll.h
 定义函数修饰宏,方便引用本DLL工程的导出函数
#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;
}
最后再在工程目录下建一个HookDll.def模块定义文件.写上以下代码
LIBRARY HookDll
EXPORTS         指明导出函数名称
   SetKeyHook
   SetMouseHook
SECTIONS       指明共享字段
   YCIShared   Read Write Shared
用了模块定义文件时,在使用动态链接库的时间就可以直接用函数名调用函数了,否则将无法找到函数.其实用模块定义文件是为了不让动态链接库发生名字改编.

有了动态链接库后我们还需要用一个应用程序来设置和记录我们的鼠标和键盘记录.
我们新建一个基于对话框的MFC应用程序工程HookApp.我们首先为我们的自定义消息添加所需消息响应的实现代码.
在对话框类的头文件的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 CHookAppDlgOnHookKey(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,
   CFilemodeRead  CFilemodeWrite))
{
   MyFile.Open(this-MyDocumentDir,
   CFilemodeCreate);
   return 0;
}
MyFile.SeekToEnd();                 移动记录指针到末尾
pListCtrl-GetText(pListCtrl-GetCount()-1,strItem);
content=strItem.GetBuffer(MAX_PATH);
MyFile.Write(content,strItem.GetLength());
CTime today=CTimeGetCurrentTime();
CString str=today.Format(tt%Y年%m月%d日 %H%M%Srn);
MyFile.Write(str.GetBuffer(str.GetLength()),str.GetLength());
MyFile.Close();
return 0;
}
long CHookAppDlgOnHookMouse(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=CWndGetForegroundWindow();
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 CHookAppDlgSaveToFile(CString strText,CListBox pListCtrl)
{
char content;
     CFile MyFile;
if(!MyFile.Open(this-MyDocumentDir,
   CFilemodeRead  CFilemodeWrite))
{
   MyFile.Open(this-MyDocumentDir,
   CFilemodeCreate);
   pListCtrl-InsertString(-1,失败);
   return;
}
MyFile.SeekToEnd();
content=strText.GetBuffer(strText.GetLength());
MyFile.Write(content,strText.GetLength());
CTime today=CTimeGetCurrentTime();
CString strTime=today.Format(tt%Y年%m月%d日 %H%M%Srn);
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 CHookAppDlgOnDestroy()
{
SetKeyHook(FASLE);取消安装钩子
SetMouseHook(FALSE);取消安装钩子
}

这是鼠标和键盘消息的监听代码,你也可以为应用程序安装其他类型的钩子.

 

原创粉丝点击