From:https://www.jianshu.com/p/1cbde2276752

Windows Hook(钩子)函数详解:https://wenku.baidu.com/view/fd9088aaf46527d3250ce059.html

环境:vs 2019,添加Windows.h头文件。

核心函数:SetWindowsHookEx(),UnhookWindowsHookEx(),CallNextHookEx()
你可能在其他api文档看到SetWindowsHookExA、SetWindowsHookExW这两个函数,他们是编码上的区别,A指ASCII,W指wide-char也就是unicode编码,但系统已经帮我们用宏处理好,只需调用 SetWindowsHookEx 就行了,自动选择对应编码那个函数。

Hook:建议先百度自行稍微了解

HINSTANCE、HMODULE、HANDLE、HWND是一样的东西,都是各种 typedef 绕来绕去,区别只在于人类使用时附加的语义,实际上都是一串数字。下面就混着用了。
下面仅以最基本的控制台应用示例。

非常简单的一个例子,运行后鼠标移动即可看到效果:

LRESULT CALLBACK mymouse(int nCode, WPARAM wParam, LPARAM lParam)
{cout<<"yes"<<endl;return CallNextHookEx(NULL, nCode, wParam, lParam);
}int main()
{HHOOK mouseHook = SetWindowsHookEx(WH_MOUSE_LL, mymouse, 0, 0);MSG msg;while (GetMessage(&msg, NULL, NULL, NULL)){}UnhookWindowsHookEx(mouseHook);return 0;
}

1.mymouse函数:
LRESULT:long result,实际就是个宏,当long(长整数)就行。
CALLBACK:也是宏,实际上是__stdcall,函数修饰关键字,详细自行可百度。
WPARAM、LPARAM:还是宏,当整数就行。不同的值有不同含义,具体看msdn。

为什么这样定义?
因为SetWindowsHookEx()的第二个参数就是接受这样的一个函数指针。简单了解使用函数指针
该函数返回0时,消息继续往下传递;返回1时消息不再往下传递。

为什么是return CallNextHookEx()而不是直接return 0或1?
SetWindowsHookEx()把这个钩子放在Hook链头,不调用这个方法其他链节点的Hook不执行。另外该函数的第一个参数没用,直接null就行。

2.HHOOK类型:
代表加进去的钩子,后面解除钩子的函数UnhookWindowsHookEx(),参数就是这个的对象。

3.MSG、while:
为了不让程序结束,用一个while卡住它。

为什么不用while(true)?
看第5点

4.SetWindowsHookEx函数:
原型:

HHOOK WINAPI SetWindowsHookEx(_In_ int       idHook,_In_ HOOKPROC  lpfn,_In_ HINSTANCE hMod,_In_ DWORD     dwThreadId
);

看到陌生的不用头大,都是关键字和宏罢了,直接讲4个参数。
idHook:一个int值,不同的值对应不同的钩子(鼠标or键盘之类的)。vs中打出WH_后,看自动补全显示的名字,你就能了解了,不用死记硬背。WH就是window hook的意思。
lpfn:函数指针,类型就是mymouse函数那个类型。每当钩子获取到消息,就会调用该函数指针。
hMod:用于帮助找到真正的地址,下面会讲。
dwThreadId:DWORD也是一个宏,当整数就行。这里指你要把钩子挂到哪个线程中,所有进程的所有线程都可以选择,只不过其他进程的线程不一定挂的上,需要其他手段,不作细说。填0就是尝试把所有线程都给挂上,成了所谓的全局钩子(只是尝试,拒绝挂钩子的线程还是挂不上)。

当挂钩子失败时,该函数返回null。另外msdn讲解参数这段一定要看:
lpfn [in]
Type: HOOKPROC

A pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a thread created by a different process, the lpfn parameter must point to a hook procedure in a DLL. Otherwise, lpfn can point to a hook procedure in the code associated with the current process.
如果dwThreadId指定的线程并不是由当前进程创建的,那么子程(就是那个函数,如示例的mymouse)一定要写在dll里(动态链接库)。
原因:由于lpfn是指针和逻辑地址的机制,当前进程的函数地址不适用于其他进程。系统一般需要借助dll模块以确定函数入口地址(low-level除外,只有此时可以不写dll)。

hMod [in]
Type: HINSTANCE

A handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process.
当 dwThreadId指定的线程是当前进程创建的线程 子程的代码是在当前进程(非dll)写的,那么这个hMod必须填0(也就是null)。
值得一提,在dll中时,这个参数要填为该dll的句柄,可以有两种方法获取来填:
(1)把入口DllMain的参数hModule(HMODULE和HINSTANCE一样的)保存下来,填进去(推荐)
(2)通过函数GetModuleHandle(L"dll name")来获取(加L代表为unicode编码,不用加dll后缀)(不推荐)

绝大多数情况下会写成动态链接库(建议)。上面示例能算上特例,不写在dll中就要:首先是钩子类型不是WH_MOUSE而是WH_MOUSE_LL(LL代表low-level,另一种回调机制,写在dll中则两种都可以),不然没反应,其次第三个参数hMod写成0。

5.GetMessage函数:从线程消息队列获取消息,没有消息将进入等待态直至有消息被插入队列
(1)系统获取消息-----系统分发消息---(Hook链)-----消息抵达线程消息队列。(这下搞懂第一点说的消息是否继续传递和CallNextHookEx了吧)
(2)我们示例的控制台程序创建时默认是没有消息队列的,但调用相关函数时就自动创建消息队列,如GetMessage函数
(3)子程是由系统通过回调机制,把执行SetWindowsHookEx函数的线程叫回来去执行的,也就是示例中的mymouse函数是由主线程去执行的。因此,如果写成while(true),那么线程将无限运行,系统没有机制能够中断它的执行来把它叫过来执行子程。但是,调用GetMessage函数后,线程有了消息队列,而且我们没有往里面插入消息,线程就进入等待态,此时系统便能够控制该线程,让它去执行子程。也就是说,我们需要把这个线程弄成等待态去让系统获取控制权,所以你还可以使用while(true){ Sleep(100); }达到相同目的。
(4)由于上述原因,对于挂Hook的线程来说,这样一个循环查询消息队列是有必要的。
(5)如果系统发现hook的子程长期得不到执行(你的线程正在繁忙),那么系统会自动删除掉这个hook。这里就要注意了,不要安排耗时任务在该线程上。

C++ windows 平台的 Hook相关推荐

  1. Windows平台基于API Hook技术的WinInet网络库HttpDNS实现方案

    一.项目背景 学而思网校直播课堂在线安装程序,是一个独立的应用程序,提供学生端的安装功能,为了减少安装包体积,避免引入第三方网络库,使用的是操作系统的WinInet网络库.为了更好的优化网络,提高网络 ...

  2. Windows平台内核级文件访问

    1.背景     在windows平台下,应用程序通常使用API函数来进行文件访问,创建,打开,读写文件.从kernel32的CreateFile/ReadFile/WriteFile函数,到本地系统 ...

  3. 深入探究Windows平台客户端安全问题-进程地址空间入侵和白加黑高阶利用

    标 题: 深入探究Windows平台客户端安全问题-进程地址空间入侵和白加黑高阶利用 时 间: 2014-09-08,00:03:51 前言 为了避免被读者骂"标题党",笔者在文章 ...

  4. Windows平台搭建-----C语言

    上期我们已经进行Linux的平台搭建,今期我们就来搭建下我们最常用的.最适合初学者的一种方式,那就是搭建Windows平台开发环境,只需要两种工具即可,一个就是编辑器(编辑代码的工具),另一个就是编译 ...

  5. Windows平台下程序打包流程

    Windows平台下程序打包流程 1.所有测试完成之后.程序release编译完成 2.依赖库打包 执行deploy.bat 脚本打包最新的程序以及依赖库 3.可执行程序打包 打开打包工程文件.evb ...

  6. x264代码剖析(一):图文详解x264在Windows平台上的搭建

    x264代码剖析(一):图文详解x264在Windows平台上的搭建 X264源码下载地址:http://ftp.videolan.org/pub/videolan/x264/ 平台:win7 PC. ...

  7. windows 平台使用 VS2017 编译 libevent 源码

    一 依赖库编译 先要将其依赖的库编译好,其中openssl需要编译到libevent中,编译成libevent_openssl.lib库,zlib在新版本中只有示例用到. 1)windows 平台使用 ...

  8. logicaldoc 6.5 结合postgresql 9.x安装部署—基于windows平台

    2019独角兽企业重金招聘Python工程师标准>>> 湘中朱生   2012年9月于深圳 说明:原创内容,请勿转载! <1> 从官网下载部署包 官方网站提供源码包和集成 ...

  9. 细数 Windows 平台上的 NoSQL 数据库

    从可查询的分布式解决方案,如MongoDB,到简单的分布式Key/Value存储解决方案,如Cassandra.此外,还有Riak,Tokyo Cabinet,Voldemort,CouchDB和Re ...

最新文章

  1. 详解正则表达式匹配方法 match()
  2. c#输出一个平行四边形_如果Java 和 C# 同时出现,生态也差不多,你选择谁?
  3. Python:Selenium错误小结
  4. Ubuntu 开启telnet、ftp服务
  5. 可视化工具第一篇(百度Echarts)
  6. linux shell 编程
  7. 只有它才能让云计算、大数据、人工智能大放异彩?它究竟有什么魔力?
  8. 弄懂 JRE、JDK、JVM 之间的区别与联系
  9. Qt初学者的一些学习方法、参考资料
  10. 欠拟合与过拟合概念和局部加权回归
  11. zynq以太网官网例子调试
  12. 【20211228】【信号处理】一文读懂信号处理中频谱混叠、栅栏效应、频谱泄露的产生原因和解决方法
  13. 利用matlab函数创建数组
  14. (二三)计算机组成原理笔记整理之系统总线(总线判优方式,标准传输率,数据总线,地址总线与MDR,MAR的关系等)
  15. Word中批量更新域的两个小方法;更新角标;更新引用
  16. AtCoder Beginner Contest 275 A-D题解
  17. 【HD Tune变红/警告】硬盘 SMART 检测参数详解
  18. word研究报告排版要领
  19. Visual Studio安装SVN过程及作用
  20. wps指定路径不存在怎么办_wps指定路径不存在怎么办_十万人都不知道键盘上 F1~F12 的作用,你肯定想不到......

热门文章

  1. 分布式数据层中间件详解:如何实现分库分表+动态数据源+读写分离
  2. 会议交流 | 如何提升推荐系统的可解释性?——DataFunSummit2022知识图谱在线峰会...
  3. 领域应用 | 机器知道哪吒是部电影吗?解读阿里巴巴概念图谱AliCG
  4. 基于在线百科知识库的多义词词义消歧项目
  5. 【TensorFlow】实现、训练并评估简单的回归模型和分类模型
  6. 对知识图谱的告白:斯坦福大学CS520课程介绍
  7. NPOI “发现 中的部分内容有问题,是否要恢复此工作薄的内容?如果信任此工作薄的来源。。。”的问题的解决方法...
  8. python - 内置函数
  9. CodeForces 615C
  10. Junit 内部解密之一: Test + TestCase + TestSuite