Hook入门

2014-07-24

基本概念

Windows消息机制

Hook(钩子)

运行机制

核心函数

C# hook示例

基本概念[1]

Windows消息机制[5]

Windows操作系统是建立在事件驱动机制之上的,系统各部分之间的沟通也都是通过消息的相互传递而实现的。但在通常情况下,应用程序只能处理来自进程内部的消息或是从其他进程发过来的消息(借助进程间通信技术,如剪贴板,管道等)。

图1 Windows应用程序消息处理机制

(1)操作系统接收到应用程序的窗口消息,将消息投递到该应用程序的消息队列中。[5]

(2)应用程序在消息循环中调用GetMessage 函数从消息队列中取出一条一条的消息。取出消息后,应用程序可以对消息进行一些预处理,例如,放弃对某些消息的响应,或者调用TranslateMessage 产生新的消息。

(3)应用程序调用DispatchMessage,将消息回传给操作系统。消息是由MSG 结构体对象来表示的,其中就包含了接收消息的窗口的句柄。因此,DispatchMessage 函数总能进行正确的传递。DispatchMessage 函数分派一个消息到窗口过程,由窗口过程函数对消息进行处理。DispachMessage 实际上是将消息回传给操作系统,由操作系统调用窗口过程函数对消息进行处理(响应)。”

(4)系统利用WNDCLASS 结构体的lpfnWndProc 成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理(即“系统给应用程序发送了消息”)。”

Hook(钩子)

如果需要对在进程外传递的消息进行拦截处理就必须采取一种称为HOOK的技术。HOOK作为Windows操作系统中非常重要的一种系统接口,用它可以轻松截获并处理在其他应用程序之间传递的消息,并由此可以完成一些普通应用程序难以实现的特殊功能。

Hook就是在图1中的第(2)步和第(4)步之间进行的额外工作(个人猜想,有待考证)。

运行机制[1]

钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。

每一个HOOK实质上都由系统维护着一个指针列表,其指针指向HOOK的各个处理函数,我们称之为HOOK子程。当与指定的Hook类型关联的消息发生时,系统就把这个消息传递到Hook子程。一些Hook子程可以只监视消息,或者修改消息,或者停止消息的前进,避免这些消息传递到下一个Hook子程或者目的窗口。

Hook子程是一个应用程序定义的回调函数(CALLBACK Function),不能定义成某个类的成员函数,只能定义为普通的C函数。用以监视系统或某一特定类型的事件,这些事件可以是与某一特定线程关联的,也可以是系统中所有线程的事件。

最近安装的Hook放在链的开始,而最早安装的钩子放在最后,也就是后加入的先获得控制权。

Windows 并不要求Hook子程的卸载顺序一定得和安装顺序相反。每当有一个钩子被卸载,Windows 便释放其占用的内存,并更新整个Hook链表。如果程序安装了钩子,但是在尚未卸载钩子之前就结束了,那么系统会自动为它做卸载钩子的操作。

在使用钩子时根据其监视范围的不同可以将其分为全局钩子和线程钩子两大类,其中线程钩子指定某个线程ID(可以是当前线程),只能监视该线程;全局钩子可以对同一个窗口下的所有线程进行监视。这里的全局HOOK的本质还是由触发HOOK机制的线程调用自身进程空间中的代码进行处理,所以我们的HOOK子程代码必须映射进该线程所在的进程的地址空间,即通过DLL的方式实现。下面描述实现过程:

首先我们编写HOOK驱动器,将HOOK.dll映射进内存中,安装好HOOK后,进行HOOK监视:

核心函数[1]

使用Windows HOOK所需要的核心函数不多,只有四个:

  • SetWindowsHookEx():安装一个HOOK
  • HOOK 子程:HOOK的处理函数,如GetMsgProc, KeyboardProc等
  • CallNextHookEx():调用HOOK链的下一个HOOK子程
  • UnhookWindowsHookEx():卸载一个HOOK

函数SetWindowsHookEx:

 HHOOK SetWindowsHookEx(
//idHook用来标识HOOK类型,比如鼠标信息用WH_MOUSE,键盘消息用WH_KEYBOARD等
int idHook,
//lpfn指向一个具体的HOOK子程,用于实际处理拦截的消息
HOOKPROC lpfn,
//hMod用来标识HOOK子程所在的模块,\
//如果是一个全局HOOK,则是一个载入内存的DLL句柄(使用GetModuleHandle得到);如果是一个内部线程HOOK,则为NULL即可。
HINSTANCE hMod,
//dwThread指明HOOK的范围,
//如果是0则表示监视运行在同一个窗口下的所有线程,否则指定一个具体的线程ID即可。
DWORD dwThreadId
);

函数UnhookWindowsHookEx:

 BOOL UnhookWindowsHookEx(
//参数为SetWindowsHookEx()的返回值
HHOOK hhk
);

函数CallNextHookEx

 LRESULT CallNextHookEx(
//参数为SetWindowsHookEx()的返回值
HHOOK hhk,
//NCode为传给钩子过程的事件代码。
int nCode,
//wParam和lParam 分别是传给钩子子程的wParam值,其具体含义与钩子类型有关。
WPARAM wParam,
LPARAM lParam
);

HOOK 子程,函数KeyboardProc:

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

C# hook示例

示例代码

step 1:创建一个WinForm应用程序,把与hook相关的方法放在一个文件‘GlobalHook.cs’中:

 using System;

 using System.Runtime.InteropServices;
using System.Reflection;
using System.Windows.Forms; namespace Test
{
//Declare wrapper managed KeyboardHookStruct class.
[StructLayout(LayoutKind.Sequential)]
public class KeyboardHookStruct
{
public int vkCode; //Specifies a virtual-key code. The code must be a value in the range 1 to 254.
public int scanCode; // Specifies a hardware scan code for the key.
public int flags; // Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
public int time; // Specifies the time stamp for this message.
public int dwExtraInfo; // Specifies extra information associated with the message.
} public class GlobalHook
{
#region native methods
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, GlobalHookProc lpfn, IntPtr hInstance, int threadId); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook); [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
#endregion #region fileds
private const int WM_KEYDOWN = 0x100;
private const int WM_SYSKEYDOWN = 0x104; static int hKeyboardHook = ; //Declare keyboard hook handle as int.
//values from Winuser.h in Microsoft SDK.
public const int WH_KEYBOARD_LL = ; //keyboard hook constant
#endregion #region delegates and events
public event KeyEventHandler KeyDown; public delegate int GlobalHookProc(int nCode, Int32 wParam, IntPtr lParam);
GlobalHookProc KeyboardHookProcedure; //Declare KeyboardHookProcedure as HookProc type.
#endregion #region methods
public void Start()
{
// install Keyboard hook
if (hKeyboardHook == )
{
KeyboardHookProcedure = new GlobalHookProc(KeyboardHookProc); hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,
KeyboardHookProcedure,
//IntPtr.Zero,
Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[]),
); //If SetWindowsHookEx fails.
if (hKeyboardHook == )
{
throw new Exception("SetWindowsHookEx ist failed.");
}
}
} public void Stop()
{
bool retKeyboard = true; if (hKeyboardHook != )
{
retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
hKeyboardHook = ;
} //If UnhookWindowsHookEx fails.
if (!retKeyboard)
throw new Exception("UnhookWindowsHookEx failed.");
} private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
{
// it was ok and someone listens to events
if ((nCode >= ) && (KeyDown != null))
{
KeyboardHookStruct MyKeyboardHookStruct =
(KeyboardHookStruct)Marshal.PtrToStructure(lParam,
typeof(KeyboardHookStruct)); // raise KeyDown
if (KeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
{
Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
KeyEventArgs e = new KeyEventArgs(keyData);
KeyDown(this, e);
}
}
//消息不再传递给下一个HOOK子程,也不会再发送给目的窗口
return ;
//消息传递给下一个HOOK子程
//return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
}
#endregion
} }

Step 2:在现有个Form1.cs[Design]界面中拖入一个控件‘Lable’,它的代码文件‘Form1.cs’内容如下:

 using System;
using System.Text;
using System.Windows.Forms; namespace Test
{
public partial class Form1 : Form
{
private GlobalHook hook; public Form1()
{
InitializeComponent();
}
private void MyKeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
this.label1.Text = e.KeyCode.ToString();
} private void FormMain_Load(object sender, EventArgs e)
{
//全局钩子
hook = new GlobalHook();
hook.KeyDown += new KeyEventHandler(this.MyKeyDown);
hook.Start();
} private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
hook.Stop();
}
}
}

Step 3:为Form1界面把Step 2 加入处理方法注册到事件:FormClosing = FormMainClosing,Load=FormMain_Load。

Step 4:Build project,打开生成的Test.exe,当我们操作键盘时,无论Test.exe当前是active状态,或notepad当前是active状态,Test.exe的label控件都会显示出你键盘输入的字符。

注意:

  • 当debug时,会在step 1代码66行抛错。
  • 由于在HOOK子程KeyboardHookProc最后return 1,即消息不再传递给下一个HOOK子程,也不会再发送给目的窗口,因此,在若想notepad用键盘输入信息,它不会显示。

参考

[1] 【Windows核心编程学习笔记】HOOK入门

[2] HOOK专题[来自微软中国社区]

[3] HOOK技术的一些简单总结

[4] windows钩子程序入门(一)

[5] dows编程 第三回 windows程序的生与死(中)

Hook入门的更多相关文章

  1. 2015某编程网易语言vip课堂全套教程 包含post,hook入门到精通等

    2015某编程网易语言vip课堂全套教程 包含post,hook入门到精通等  官方论坛弄来的  如果在官方下载需要权限的  挺不错教程 想学习易语言入门到精通 post hook  js改写的可以看 ...

  2. 小白也能看懂的插件化DroidPlugin原理(二)-- 反射机制和Hook入门

    前言:在上一篇博文<小白也能看懂的插件化DroidPlugin原理(一)-- 动态代理>中详细介绍了 DroidPlugin 原理中涉及到的动态代理模式,看完上篇博文后你就会发现原来动态代 ...

  3. React Hook 入门使用

    React Hook 是什么 1.没有比官网说的更好的 HOOK 1. React Hook 官方 2. 用我们自己的话说,它是一个钩子函数,用来处理组件间的状态的一个方法,暂时理解为一个高阶函数吧. ...

  4. 简单全局HOOK拦截大部分键盘消息

    前言:学习HOOK中,万一老师讲解HOOK入门教程:http://www.cnblogs.com/del/category/124150.html http://www.cnblogs.com/del ...

  5. 小白也能看懂的插件化DroidPlugin原理(三)-- 如何拦截startActivity方法

    前言:在前两篇文章中分别介绍了动态代理.反射机制和Hook机制,如果对这些还不太了解的童鞋建议先去参考一下前两篇文章.经过了前面两篇文章的铺垫,终于可以玩点真刀实弹的了,本篇将会通过 Hook 掉 s ...

  6. Android Hook神器:XPosed入门与登陆劫持演示

    前段时间写了一篇关于Cydia Substrate广告注入的文章,大家都直呼过瘾.但是,真正了解这一方面的同学应该知道,其实还有一个比Cydia Substrate更出名的工具:XPosed. 不是因 ...

  7. HOOK API入门之Hook自己程序的MessageBoxW(简单入门)

    说到HOOK,我看了很多的资料和教程,无奈就是学不会HOOK,不懂是我的理解能力差,还是你们说的 不够明白,直到我看了以下这篇文章,终于学会了HOOK: http://blog.sina.com.cn ...

  8. 【安全性测试】利用反编译查看对应activity的方法采用hook技术绑定劫持_入门

    本次主要为了研究手机端的安全性而写的一篇文章,在基于自己对手机安全性的研究下,想到了这些工具之间的结合,当然这也算是第一次对手机安全研究勇敢地踏出一步,也不知道是否成功,还是准备撞南墙撞到底吧! 使用 ...

  9. HDU 1698 Just a Hook (线段树区间更新入门题)

    Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

随机推荐

  1. 【BZOJ】【1041】【HAOI2008】圆周上的点

    数学 orz hzwer 完全不会做…… 很纠结啊,如果将来再遇到这种题,还是很难下手啊…… 引用题解: [分析]: 样例图示: 首先,最暴力的算法显而易见:枚举x轴上的每个点,带入圆的方程,检查是否 ...

  2. OrzFAng系列–树 解题报告

    题目描述 方方方种下了三棵树,两年后,第二棵树长出了n个节点,其中1号节点是根节点. 给定一个n个点的树 支持两种操作 方方方进行m次操作,每个操作为: (1)给出两个数i,x,将第i个节点的子树中, ...

  3. Hello world,Hello 2015,Bye 2014

    序 在我写下“在”这个字的时候已经是2014-12-31 19:59,14年最后一天了,总觉得不写点东西祭奠一下那些被自己虐死的脑细胞,心里就不舒服. 那就从生活,工作,学习三个方面,总结一下吧. 生 ...

  4. Mozilla推荐的CSS属性书写顺序及命名规则

    传说中的Mozilla推荐 /* mozilla.org Base Styles * maintained by fantasai */ /* Suggested order: * display * ...

  5. jQuery中的Deferred-详解和使用

    首先,为什么要使用Deferred? 先来看一段AJAX的代码: var data; $.get('api/data', function(resp) { data = resp.data; }); ...

  6. HDU 1385 Minimum Transport Cost (Dijstra 最短路)

    Minimum Transport Cost http://acm.hdu.edu.cn/showproblem.php?pid=1385 Problem Description These are ...

  7. Google Protocol Buffers简介

    什么是 protocol buffers ? Protocol buffers 是一种灵活.高效的序列化结构数据的自动机制--想想XML,但是它更小,更快,更简单.你只需要把你需要怎样结构化你的数据定 ...

  8. spring_150803_service

    实体类: package com.spring.model; public class DogPet { private int id; private String name; private in ...

  9. android-non-ui-to-ui-thread-communications-part-4-of-5

    In parts 1-3 of this series, I have explored three different means for an Android non-UI thread to c ...

  10. 李洪强漫谈iOS开发[C语言-043]-判断较早日期

    李洪强漫谈iOS开发[C语言-043]-判断较早日期