关键函数
HHOOK SetWindowsHookEx(
int idHook, //hook type
HOOKPROC lpfn, //hook procedure KeyboardProc的函数名
HINSTANCE hMod, //hook procedure所属的DLL句柄
DWORD dwThreadId //将要挂钩的目标线程ID
);
HHOOK:返回值,钩子句柄,需要保留,等不使用钩子时通过UnhookWindowsHookEx函数卸载钩子。
idHook:钩子的拦截消息类型,选择钩子程序的拦截范围,具体值参考文章结尾的消息类型。
Lpfn:消息的回调函数地址,一般是填函数名。
hMod:钩子函数所在的实例的句柄。对于线程钩子,该参数为NULL;对于系统钩子,该参数为钩子函数所在的DLL句柄。在dll中可通过AfxInitExtensionModule(MousehookDLL, hInstance)获得DLL句柄。
dwThreadId:钩子所监视的线程的线程号,可通过GetCurrentThreadId()获得线程号。对于全局钩子,该参数为NULL(或0)。
回调函数
LRESULT CALLBACK HookProc ( int nCode, WPARAM wParam, LPARAM lParam )
nCode 指定是否需要处理该消息
wParam 和 lParam 包含该消息的附加消息
HookProc 可以看作是一个函数名的占位符。只要函数的原型一致,您可以给该函数取任何名字。至于以上的几个参数及返回值的具体含义各种类型的钩子都不相同。
两个重要的API,一个是SetWindowsHookEx,安装钩子;另一个是UnHookWindowsHookEx,卸载钩子。
这是一个应用程序从键盘或鼠标等获取信息再进而处理传递的示意图。
而HOOK就是在3步骤处建立类似检查站的东西对消息进行过滤、捕捉。
HOOK又分为全局钩子(又称系统钩子)和局部钩子(又称线程钩子)。就是对整个系统消息和对单线程勾取的不同。实现是SetWindowsHookEx的第四个参数。
当我们通过exe建立钩子后,一旦有程序发生键盘输入,OS就会自动把我们提前传入句柄的DLL注入相关程序,进而调用回调函数,实现对消息的处理。推测DLL的注入是为了回调函数在每个进程中的实现及可以访问该进程的内存。
C++ HOOK实现全局键盘钩子的详细过程_c++ 键盘钩子-CSDN博客有时间的话会打算实现一下这个用法。
HOOK代码的构件中比较关键的就是回调函数,它意味着我的HOOK要实现什么样的功能,是仅拦截,还是将获取的信息传递到其他文件中之类的,现在好像还没必要看勾链,所以代码中的CallNextHookEx因该只是实现非目的进程不拦截信息。
对Loadlibaray这个函数还有点小疑问。
Dll。main
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
//KeyHook.cpp
#include "stdio.h"
#include "windows.h"
//定义目标进程名为notepad.exe
#define DEF_PROCESS_NAME "notepad.exe"
//定义全局变量
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
//DllMain()函数在DLL被加载到进程后会自动执行
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved) {
switch (dwReason) {
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;//获取DLL句柄
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
char szPath[MAX_PATH] = { 0, };
char* p = NULL;
if (nCode >= 0) {
//释放键盘按键时,bit 31 : 0 => press, 1 => release
if (!(lParam & 0x80000000)) {
GetModuleFileNameA(NULL, szPath, MAX_PATH);
p = strrchr(szPath, '\\');
//比较当前进程名称,若为notepad.exe,则消息不会传递给应用程序或下一个钩子函数
//_stricmp()函数用于比较字符串,i表示不区分大小写,若两个值相等则返回0
if (!_stricmp(p + 1, DEF_PROCESS_NAME)) {
return 1;
}
}
}
//比较当前进程名称,若非notepad.exe,则消息传递给应用程序或下一个钩子函数
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
//在C++中调用C的库文件,用extern "C"告知编译器,因为C++支持函数重载而C不支持,两者的编译规则不同
#ifdef __cplusplus
extern "C" {
#endif
//__declspec,针对编译器的关键字,用于指出导出函数
//当调用导出函数HookStart()时,SetWindowsHookEx()函数就会将KeyboardProc()添加到键盘钩链
__declspec(dllexport) void HookStart() {
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);
}
__declspec(dllexport) void HookStop() {
if (g_hHook) {
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
#ifdef __cplusplus
}
#endif
药引exe的main
#include "conio.h"
//定义一些常量
#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"
//定义两个参数为空、返回值为void即没有的函数指针
typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)();
void main() {
//定义及初始化句柄变量和函数指针
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
//加载KeyHook.dll
hDll = LoadLibraryA(DEF_DLL_NAME);
//若加载不成功,则输出错误信息
if (hDll == NULL) {
printf("[-]无法加载%s [%d]\n", DEF_DLL_NAME, GetLastError());
return;
}
//获取导出函数地址
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
//开始钩取
HookStart();
//直至用户输入“q”退出钩取
printf("[*]等待输入 'q' 来停止钩取...\n");
while (_getch() != 'q');
//终止钩取
HookStop();
//卸载KeyHook.dll
FreeLibrary(hDll);
}