本节内容
Windows消息钩取并利用小实例进行理解该过程
源码分析
关键下钩子的API的原型
1 | HHOOK SetWindowsHookExW( |
示例程序源码
1 | //HookMain.exe源码 |
HookMain的源码比较简单,就是载入KeyHook.dll并且导入HookStart和HookStop用于控制钩子。
1 | //KeyHook.dll源码 |
KeyHook.dll则是控制钩子和钩子的内部逻辑,回调函数KeyboardProc是自写的逻辑完成的功能是在接受全局钩子钩取到的消息时判断该消息所对应的应用程序是否是notepad.exe,若为notepad.exe则return 1 否则CallNextHookEx。下钩HookStart 断钩HookStop。
梳理一下过程:
Hookmain.exe调用LoadLibrary尝试载入KeyHook.dll文件,成功则LoadLibrary返回KeyHook.dll的对应句柄
成功载入KeyHook.dll由于Windows载入DLL的机制会使得KeyHook.DllMain因为dwReason = DLL_PROCESS_ATTACH触发,执行相应的行为(g_hInstance = hinstDLL:实际上hinstDLL是指向KeyHook.dll自身的实例句柄)
HookMain.exe接着获取了KeyHook.dll下的两个API :HookStart & HookStop,并启动了HookStart
程序回到KeyHook.dll内部,执行SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0); 含义是:钩子处理的消息类型是WH_KEYBOARD,一下是SetWindowsHookEx.idHook的可取值int类型(代号标识),比较多这里只列出源码中的WH_KEYBOARD,更多信息请自行查阅文档
Value Meanning WH_KEYBOARD2 Installs a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook procedure. 表明钩子SetWindowsHookEx处理的是键盘事件,回调函数是KeyHook.KeyboardProc这是由于(g_hInstance指向KeyHook.dll的实例句柄),最后一个参数0表明钩子是个全局钩子。
当钩子的触发事件(键盘事件)发生,会调用KeyHook.KeyboardProc,该函数的原型:
1
2
3
4
5LRESULT CALLBACK KeyboardProc(
_In_ int code, //how to process the message
_In_ WPARAM wParam, //contain information about a keystroke message
_In_ LPARAM lParam //contain information about a keystroke message
);第一个参数code:
A code the hook procedure uses to determine how to process the message. If code is less than zero, the hook procedure must pass the message to the CallNextHookEx function without further processing and should return the value returned by CallNextHookEx. This parameter can be one of the following values.
表明钩子相应处理过程如何处理钩取到的消息,当该值小于0,则不对钩取到的内容做更多处理,必须让KeyboardProc()函数返回CallNextHookEx()
Value Meaning HC_ACTION 0 The wParam and lParam parameters contain information about a keystroke message. HC_NOREMOVE 3 The wParam and lParam parameters contain information about a keystroke message, and the keystroke message has not been removed from the message queue. (An application called the PeekMessage function, specifying the PM_NOREMOVE flag.) 钩子回调函数KeyboardProc判断消息来源是否是关心的进(线)程,不是则放行,是则做截取(截取后可以进行相应的修改操作影响最终输入的内容,比如往记事本中写入内容,截取后将其修改成其他内容)由于不是本节的主题内容,这里截取后并未做更多的处理。
调试程序
程序运行在终端会提示关键字符串press 'q' to quit!
给了快速定位main函数的方式:
在0x00401006处调用LoadLibarayA() 载入KeyHook.dll 文件并且在0x0040103D处获取KeyHook.dll.HookStart()的地址在之后也载入了KeyHook.dll.HookStop()函数:
并且在不远处的代码0x0040104B的call ebx
对KeyHook.dll.HookStart() 进行调用,这里跟随:
由地址空间的变化可以确定此时所在的空间是KeyHook.dll的空间
前面的几个push操作是对SetWindowsHookExW参数的入栈,这是下钩的函数。
之后retn回程序用户地址空间,并且完成终端输出:press 'q' to quit!
并且后续出现了一个条件判断循环,不难判断出这是对键盘输入 ‘ q ’的循环判断,0x71对应的ascii码也是 ‘ q ’
接下来保持==HookMain.exe(管理员权限)==的运行(这是为了保持钩子处于开启的状态),然后OD附加notepad.exe进行调试观察。
运行notepad.exe,随后使用OD进行进程附加,附加成功后打开debugging Options打开载入‘终端与新模块DLL’选项,这样当有新模块载入时会中断程序运行
预期的效果是在notepad.exe中使用键盘,然后触发钩子,注入KeyHook.dll然后OD会中断notepad.exe的运行。
双击KeyHook.dll条目,跳转到KeyHook.dll的入口:
(ps:成功载入KeyHook.dll后需要取消OD中 DebuggingOptions 中的新模块加入中断的选项,已经载入了就不需要了,以保证不出错)