我们知道Windows中的窗口程序是基于消息,由事件驱动的,在某些情况下可能需要捕获或者修改消息,从而完成一些特殊的功能(MFC框架就利用Windows钩子对消息进行引导)。对于捕获消息而言,无法使用IAT或Inline Hook之类的方式去进行捕获,这就要用到接下来要介绍的Windows提供的专门用于处理消息的钩子函数。

1. 挂钩原理

Windows下的应用程序大部分都是基于消息机制的,它们都会有一个消息过程函数,根据不同的消息完成不同的功能。Windows操作系统提供的钩子机制的作用就是用来截获和监视这些系统中的消息。Windows钩子琳琅满目,可以用来应对各种不同的消息。

按照钩子作用的范围不同,又可以分为局部钩子和全局钩子。其中,全局钩子具有相当大的功能,几乎可以实现对所有Windows消息的拦截、处理和监控。局部钩子是针对某个线程的;而全局钩子则是作用于整个系统中基于消息的应用。全局钩子需要使用DLL文件,在DLL中实现相应的钩子函数。系统钩子,系统就必须把钩子函数插入到其它进程的地址空间,要做到这一点要求钩子函数必须在一个动态链接库中,所以如果您想要使用系统钩子,就必须把该钩子函数放到动态链接库中去。在操作系统中安装全局钩子后,只要进程接收到可以发出钩子的消息,全局钩子的DLL文件就会被操作系统自动或强行地加载到该进程中。因此,设置消息钩子,也可以达到DLL注入的目的。

般来说,HOOK API由两个组成部分,即实现HOOK API的DLL文件,和启动注入的主调程序。本文采用HOOK API 技术对剪切板相关的API 函数进行拦截,从而实现对剪切板内容的监控功能,同样使用该技术实现进程防终止功能。其中DLL文件支持HOOK API的实现,而主调客户端程序将在初始化时把带有HOOK API功能的DLL随着鼠标钩子的加载注入到目标进程中,这里的鼠标钩子属于系统钩子。

几点需要说明的地方:

  (1) 如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。

(2) 对同一事件消息可安装多个钩子处理过程,这些钩子处理过程形成了钩子链。当前钩子处理结束后应把钩子信息传递给下一个钩子函数。而且最近安装的钩子放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。

(3) 钩子特别是系统钩子会消耗消息处理时间,降低系统性能。只有在必要的时候才安装钩子,在使用完毕后要及时卸载。

钩子的类型

1、按事件分类

有如下的几种常用类型

(1) 键盘钩子和低级键盘钩子可以监视各种键盘消息。

(2) 鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。

(3) 外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。

(4) 日志钩子可以记录从系统消息队列中取出的各种事件消息。

(5) 窗口过程钩子监视所有从系统消息队列发往目标窗口的消息。

此外,还有一些特定事件的钩子提供给我们使用,不一一列举。

2、按使用范围分类

主要有线程钩子和系统钩子:

(1) 线程钩子监视指定线程的事件消息。

(2) 系统钩子监视系统中的所有线程的事件消息。因为系统钩子会影响系统中所有的应用程序,所以钩子函数必须放在独立的动态链接库(DLL)中。这是系统钩子和线程钩子很大的不同之处。

2. 钩子函数

HHOOK SetWindowsHookEx(

int idHook,

HOOKPROC lpfn,

HINSTANCE hMod,

DWORD dwThreadId

);

该函数的返回值是一个钩子句柄。参数介绍如下:

lpfn:指定Hook函数的地址。如果dwThreadId参数被赋值为0,或者被设置为一个其他进程中的线程ID(远程钩子/全局钩子),那么lpfn属于DLL中的函数过程。如果dwThreadId为当前进程中的一个线程ID,那么lpfn可以使指向当前进程模块中的函数,当然,也可以使DLL模块中的函数(局部钩子/本地钩子)。

hMod:该参数指定钩子函数所在模块的模块句柄。即lpfn所在的模块句柄。如果dwThreadId为当前进程中的线程ID,若lpfn所指函数在当前进程中,则该参数被设置为NULL。

dwThreadId:指定需要被挂钩的线程ID号。如果设置为0,表示在所有基于所有的线程中挂钩;如果设置了具体的线程ID,表示在指定线程中挂钩。该参数影响上面两个参数的取值,同时也决定了该钩子是全局钩子还是局部钩子。注意是否全局钩子还是局部钩子与下面具体的钩子种类无关仅由本参数控制。

idHook:该参数表示钩子的类型。常用的几种如下:

  • WH_GETMESSAGE

按照该钩子的作用是监视被投递到消息队列中的消息。也就是当调用GetMessage或PeekMessage函数时,函数从程序的消息队列中获取一个消息后调用该钩子。

WH_GETMESSAGEG的钩子函数如下:

LRESULT CALLBACK GetMsgProc(

int code, //hook code

WPARAM wParam, //removal option

LPARAM lParam        //message

);

  • WH_MOUSE

该钩子用于监视鼠标消息。钩子函数如下:

LRESULT CALLBACK MouseProc(

int nCode, //hook code

WPARAM wParam, //message identifier

LPARAM lParam //mouse coordinates

);

  • WH_KEYBOARD

该钩子用于监视键盘消息。钩子函数如下:

LRESULT CALLBACK KeyboardProc(

int code, //hook code

WPARAM wParam, //virtual-key code

LPARAM lParam //keystroke-message information

}

  • WH_DEBUG

用于调试其它钩子。钩子函数如下:

LRESULT CALLBACK DebugProc(

int nCode, //hook code

WPARAM wParam, //hook type

LPARAM lParam //debugging information

);

对于以上钩子函数的详情还请各位看客老爷们自行挪步到MSDN了。

移除先前用SetWindowsHookEx安装的钩子:

BOOL UnhookWindowsHookEx(

HHOOK hhk

);

唯一的参数是待移除的钩子句柄。

在实际应用中,可以多次调用SetWindowsHookEx函数来安装钩子,而且可以安装多个同样类型的钩子。这样,钩子就会形成一条钩子链,最后安装的钩子会首先截获到消息。当该钩子对消息处理完毕后,可以选择返回或者把消息继续传递下去。如果是为了屏蔽某消息,可以在安装的钩子函数中直接返回非零值。如果希望我们的钩子函数处理完消息后可以继续传递给目标窗口,则必须选择将消息继续传递。继续传递消息的函数定义如下:

LRESULT CallNextHookEx(

HHOOK hhk, //handle to current hook

int nCode, //hook code passed to hook procedure

WPARAM wParam, //value passed to hook procedure

LPARAM lParam //value passed to hook procedure

);

第一个参数是钩子句柄,就是调用SetWindowsHookEx函数的返回值;后面3个参数是钩子的参数,直接一次copy即可。例如:

HHOOK g_Hook = SetWindowsHookEx(…);

LRESULT CALLBACK GetMsgProc(

int code, //hook code

WPARAM wParam, //removal option

LPARAM lParam        //message

)

{

return CallNextHookEx(g_Hook, code, wParam, lParam);

}

3. 钩子实例

Windows钩子的使用场景比较广泛,我们就几种比较常见的情况做一个应用示例。

参看上一小结可知,编写钩子程序的三个步奏是:

定义钩子函数:

LRESULT CALLBACK HookProc(int nCode ,WPARAM wParam,LPARAM lParam)

安装钩子:

HHOOK SetWindowsHookEx( int idHook,HOOKPROC lpfn, INSTANCE hMod,DWORD dwThreadId )

卸载钩子:

BOOL UnhookWindowsHookEx( HHOOK hhk)

还需要注意一点:系统钩子必须放在独立的动态链接库中。由此,程序分为两个部分:一个是钩子程序动态链接库,实现了鼠标钩子程序;另一个是MFC操作窗体,对DLL进行加载和卸载,即对DLL进行测试。//我在win7下测试没有使用dll也成功了why?不想在DLL实现SetWindowsHookEx时第三个参数传入GetModuleHandle(NULL)即可?

3.1全局键盘钩子

先新建一个DLL程序(这个不会可以看我以前的博客,这里就不重复了),我们在头文件中增加两个导出函数和两个全局。

#define MY_API __declspec(dllexport)

extern "C" MY_API VOID SetHookOn();

extern "C" MY_API VOID SetHookOff();

HHOOK g_Hook = NULL; //钩子句柄

HINSTANCE g_Inst = NULL; //DLL模块句柄

在DllMain中保存该DLL模块的句柄,以方便安装全局钩子。

BOOL APIENTRY DllMain( HANDLE hModule,

DWORD  ul_reason_for_call,

LPVOID lpReserved

)

{

//保存DLL模块句柄

g_Inst = (HINSTANCE)hModule;

return TRUE;

}

安装与卸载钩子的函数如下:

VOID SetHookOn()

{

//安装钩子

g_Hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_Inst, 0);

}

VOID SetHookOff()

{

//卸载钩子

UnhookWindowsHookEx(g_Hook);

}

钩子函数的实现如下:

//钩子函数

LRESULT CALLBACK KeyboardProc(int code, WPARAM wParam, LPARAM lParam)

{

if(code < 0)

{

//如果code小于0,必须调用CallNextHookEx传递消息,不处理该消息,并返回CallNextHookEx的返回值。

return CallNextHookEx(g_Hook, code, wParam, lParam);

}

if(code == HC_ACTION && lParam > 0)

{

//code等于HC_ACTION,表示消息中包含按键消息

//如果为WM_KEYDOWN,则显示按键对应的文本

char szBuf[MAXBYTE] = {0};

GetKeyNameText(lParam, szBuf, MAXBYTE);

MessageBox(NULL, szBuf, "提示", MB_OK);

}

return CallNextHookEx(g_Hook, code, wParam, lParam);

}

编译链接后产生我们需要的.dll和.lib文件,然后新建一个项目来导入动态库内容调用相关函数。

新建项目如下:

首先导入库:

#pragma comment (lib, "全局钩子.lib")

声明将要调用的函数(不声明链接时将报错):

extern "C" VOID SetHookOn();

extern "C" VOID SetHookOff();

在按钮事件中调用导出函数:

void CHookDebugDlg::OnHookon()

{

SetHookOn();

}

void CHookDebugDlg::OnHookoff()

{

SetHookOff();

}

3.2低级键盘钩子

数据防泄漏软件通常会精致PrintScreen键,防止通过截屏将数据保存为图片而导致数据泄密。下面我们也可以模仿一下,简单的实现该功能。这里需要注意的是,普通的键盘钩子(WH_KEYBOARD)是无法过滤一些系统按键的,得通过安装低级键盘钩子(WH_KEYBOARD_LL)来达到目的。

在低级键盘钩子的回调函数中,判断是否为PrintScreen键,如果是,则直接返回TRUE;如果不是,则传递给下一个钩子处理。

实现过程与上面相同。

可能在编译时会报错,说WH_KEYBOARD_LL和KBDLLHOOKSTRUCT未定义,此时可以在文件开头加上如下代码:

#define WH_KEYBOARD_LL 13

typedef struct tagKBDLLHOOKSTRUCT {

DWORD vkCode;

DWORD scanCode;

DWORD flags;

DWORD time;

DWORD dwExtraInfo;

} KBDLLHOOKSTRUCT, FAR *LPKBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;

其实在winuser.h中已有定义,但是可能是兼容的缘故用不了。

3.3钩子注入DLL

利用WH_GETMESSAGE钩子,可以方便地将DLL文件注入到所有基于消息机制的程序中。因为有时候可能需要DLL文件完成一些工作,但是工作时需要DLL在目标进程的空间中。这个时候,就可以将DLL注入目标进程来完成相关的功能。

主要的代码如下:

BOOL APIENTRY DllMain( HANDLE hModule,

DWORD  ul_reason_for_call,

LPVOID lpReserved

)

{

//保存DLL模块句柄

g_Inst = (HINSTANCE)hModule;

switch (ul_reason_for_call)

{

case DLL_PROCESS_ATTACH:

{

DoSomething();

break;

}

case DLL_THREAD_ATTACH:

case DLL_THREAD_DETACH:

case DLL_PROCESS_DETACH:

break;

}

return TRUE;

}

VOID SetHookOn()

{

g_Hook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, g_Inst, 0);

}

VOID SetHookOff()

{

UnhookWindowsHookEx(g_Hook);

}

LRESULT CALLBACK GetMsgProc(int code, WPARAM wParam, LPARAM lParam)

{

return CallNextHookEx(g_Hook, code, wParam, lParam);

}

VOID DoSomething()

{

MessageBox(NULL, "Hello,我被执行了!", "提示", MB_OK);

}

windows下钩子的使用相关推荐

  1. Windows下钩子

    一.钩子     为了监视或控制windows的系统事件,windows提供钩子技术.Windows应用可以安装一个子进程来监控windows中消息的往来,还能把消息到达目的窗口过程之前处理某种类型的 ...

  2. 转:Windows下WSH/JS实现SVN服务器钩子脚本阻止提交空日志信息和垃圾文件

    http://blog.csdn.net/caikanxp/article/details/8279921 如何强制用户在提交SVN时填写日志信息? 如果用户使用的都是TortoiseSVN客户端,可 ...

  3. Windows下的钩子

    Windows下的钩子 2011-9-9 API钩子应用程序将kernel32.dll加载到自己的私有空间0x0001000~0x7FFE0000之间,所以本地进程只要能访问目标进程的地址空间,就可以 ...

  4. windows下c语言钩子,Windows的钩子机制详解

    一.概述: 了解windows程序设计的人都知道,Windows系统程序的运行是建立在消息传递机制的基础之上的,几乎所有的程序活动都由消息来驱动.钩子机制可以看作是一个消息的中转站,控制系统发出消息的 ...

  5. 基于svnserve的SVN服务器(windows下安装与配置)

    基于svnserve的SVN服务器(windows下安装与配置) 关键字: svn 安装SVNserve 从http://subversion.tigris.org/servlets/ProjectD ...

  6. Windows 下的 7 种 DLL 劫持技术

    本文讲的是Windows 下的 7 种 DLL 劫持技术,在本文中,我将列出半打可以在Windows运行用户模式的进程中使用DLL注入技术.也许可能会有更多类似的技术,但我正在和你分享的是我所拥有的第 ...

  7. windows 下执行mysql脚本_Windows下批处理执行MySQL脚本文件

    一. @echo off Setlocal enabledelayedexpansion ::CODER BY Mark_Li POWERD BY iBAT 1.6 cd "C:\Progr ...

  8. windows下安装subversion

    前言: 最近在写windows版本下svn hooks(钩子)  post-commit的实现.所以会需要在windows下安装相应的subversion.经过一番查询后,决定使用VisualSVN ...

  9. Windows下资源泄漏检测

    前言 内存泄露和资源泄露是C\C++程序员不得不面对的一个问题,随着程序越来越大,稍不留神就可能在程序中留下了内存泄露的隐患,这个问题很多人可能觉得没什么,就泄露点内存而已,只要程序逻辑没问题,但是如 ...

最新文章

  1. 【C/C++语言入门篇】-- 文件操作
  2. android导航条高度修改,Android中修改TabLayout底部导航条Indicator长短的方法
  3. mysql5.7环境搭建_mysql5.7.13环境搭建教程(解压缩版)
  4. VSCode 初次写vue项目并一键生成.vue模版
  5. 手机被锁在耳机模式了
  6. Spring Cloud Zuul重试机制探秘
  7. STM32工作笔记0026---平时说的电气烧了是什么意思
  8. 提高Entity Framework性能的一些建议
  9. php 工厂模式作用,PHP工厂模式的好处概述
  10. Java超详细的基础编程300题,附带答案,持续更新中~
  11. visio用例图箭头怎么画_visio2010绘制用例图-带图例
  12. Oracle中文乱码(中文变问号?)解决方法---简单粗暴高效
  13. 计算机Excel设置透视图,excel共享表格数据-EXCEL在共享模式中,如何让数据透视表能够自动刷新?...
  14. Excel怎么合并两个或者多个单元格里面的内容
  15. 路由器内部到底是啥结构?不懂就不算网工人
  16. 大前研一:聪明人必作的十件事
  17. 心理正常与异常的区分_判断心理正常异常三原则
  18. 自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别
  19. 动态路由器ensp二层三层交换_eNSP模拟实验-路由器和交换机在不同网段互通配置...
  20. Scratch-弹球游戏项目需求分析

热门文章

  1. 需求又变了,要不要怼回去?
  2. 怼天怼地怼空气的Linus 喜欢和什么样的人一起工作?
  3. 框架:SpringMVC常用注解总结
  4. Android --- Session ‘app’: Error Launching activity解决办法
  5. element ui 表格中的字太长,想要把多余的字变成...解决方法,一个属性即可
  6. Java 洛谷 P1008 三连击
  7. mysql 实时聚合分析_mysql滑动聚合/年初至今聚合原理与用法实例分析
  8. vscode怎么运行verilog语言_VScode中不同语言使用不同字体,如C/C++,VHDL
  9. 打造“新基建”核心支柱 数据中心产业期待提速提质
  10. 博物馆自动灭火系统应如何选择