什么是键盘钩子

大家好!今天让小编来大家介绍下关于什么是键盘钩子的问题,以下是小编对此问题的归纳整理,让我们一起来看看吧。

文章目录列表:

什么是键盘钩子

什么是鼠标钩子

钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。Hook API是指Windows开放给程序员的编程接口,使得在用户级别下可以对操作系统进行控制,也就是一般的应用程序都需要调用API来完成某些功能,Hook API的意思就是在这些应用程序调用真正的系统API前可以先被截获,从而进行一些处理再调用真正的API来完成功能。1) 键盘钩子和低级键盘钩子可以监视各种键盘消息。 (2) 鼠标钩子和低级鼠标钩子可以监视各种鼠标消息。 (3) 外壳钩子可以监视各种Shell事件消息。比如启动和关闭应用程序。 (4) 日志钩子可以记录从系统消息队列中取出的各种事件消息。 (5) 窗口过程钩子监视所有从系统消息队列发往目标窗口的消息

钩子程序钩‘a’字母

钩子有很多种,和键盘相关的钩子一般是普通键盘钩子和低级键盘钩子。
低级钩子和普通键盘钩子都能够屏蔽消息。
但它们的运作方式不一样。低级钩子安装后,低级钩子的处理函数总是在安装低级钩子的那个进程中进行,也就是说如果键盘驱动产生一个按键消息或调用 keybd_event 函数模拟产生一个按键消息,系统在将该消息投递到相应线程的消息队列前,会产生进程切换,即,如果正在用记事本输入文本时,CPU原本执行的都是记事本程序的指令,当按下键的时候,由于产生了消息,WINDOWS 系统会查看低级钩子链,如果有注册过的低级钩子,系统就会将CPU从记事本程序中剥夺(当前记事本时间片运行完后),记事本程序会被暂时挂起。系统将进行进程切换,调用安装过低级钩子的那个程序来运行,因此,按键之后,CPU不再执行记事本程序,而是钩子程序。当钩子处理完后,系统必须完成整个钩子链的调用,最后才重新唤醒记事本程序来运行。
因此,像低级钩子这种类型是对系统性能有损伤的,不能过分依赖低级钩子。且由于低级钩子处理程序它总是在自己的进程中运行,因此,低级钩子不会知道是那个进程中产生了按键消息。譬如前面举例的记事本。GetModuleFileName(NULL, szMod, _countof(szMod)) 获取到的进程名永远是低级钩子程序,而不是记事本程序。
由于低级钩子程序是在自己的进程中运行的,因此低级钩子不需要写成DLL形式。由于DLL不会被注入到其他进程,DLL和EXE其实是一样的。
普通键盘钩子必须以DLL形式存在,因为普通键盘钩子的处理函数总是需要被注入到其他进程中,如运行中的记事本,当调用 SetWindowsHookEx 时,由于会传递钩子处理函数的地址,因此系统总是能够知道DLL模块是什么,然后系统会将该DLL 通过LoadLibrary*形式插入到其他的进程中,这样低级钩子处理函数连同DLL中其他所有的代码都被注入到了其他进程中。此时,譬方记事本程序运行过程中,我们按键所产生的按键消息,WINDOWS同样要检查该记事本程序是否有登记在册的普通键盘钩子,如果有,系统变会让钩子处理函数执行,执行完后,记事本程序才能处理在消息队列中的按键消息,当然,如果钩子屏蔽了该消息,则记事本不会在消息队列中发现该消息了。所有的动作,都不会引起进程间的切换,因此普通钩子的性能比低级钩子的性能来得乐观!通过 GetModuleFileName(NULL, szMod, _countof(szMod)) 还能知道是哪个进程产生了按键消息。
我贴下低级钩子处理函数和普通钩子处理函数是如何工作的,楼主看了满意,我将这两种钩子的工程发给楼主。(代码有点凌乱,见谅,何时百度也能提供代码缩进和语法高亮插件就好了!)
普通钩子:
struct _KeyInfo {
DWORD key;// 键值
DWORD time;// 产生按键的时间
_KeyInfo *next;// 下一个按键信息
_KeyInfo *tail;// 最后一个按键信息
};
typedef _KeyInfo KeyInfo, *PKeyInfo;
HMODULE g_hModule;
HHOOK g_hHook;
PKeyInfo g_keys;
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
LRESULT lRet = CallNextHookEx(g_hHook, nCode, wParam, lParam);
// nCode 小于 0 时,必须返回 CallNextHookEx 的返回值。
if (nCode < 0) {
return lRet;
}

if (nCode == HC_ACTION) {
BOOL bKeyDown = (lParam >> 31 == 0);
if (bKeyDown != FALSE) {
if (IsKeyAllowed(wParam, GetTickCount()) == FALSE) {
// 屏蔽该按键。
lRet = 1;
// 构造需要发送给监视器的屏蔽消息文本。
TCHAR sz[256] = { 0 }, szTime[32], szMod[MAX_PATH];
SYSTEMTIME st;
LPCTSTR format = _T(" %s 按键 { %s } 被屏蔽。(\"%s\" 进程名)");
GetLocalTime(&st);// 获取时间。
GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, _T("HH:mm:ss"),
szTime, _countof(szTime));
GetModuleFileName(NULL, szMod, MAX_PATH);// 获取进程名。

// 构造屏蔽消息。
StringCchPrintf(sz, _countof(sz), format, szTime,
szKeyStringTable[(BYTE)wParam], szMod);
DWORD cb = 0;
StringCbLength(sz, sizeof(sz), (size_t*)&cb);

// 发送按键被屏蔽消息。
NotifyMonitor(g_hMonitor, sz, cb + sizeof(TCHAR));
}
}
}
return lRet;
}
BOOL IsKeyAllowed(DWORD key, DWORD time) {
BOOL bAllowed = TRUE;
PKeyInfo pki;
// 移除已超过两秒的按键信息。
while (g_keys != NULL) {
if ((time – g_keys->time) < 2000) {
break;
}
pki = g_keys;
if (g_keys->next != NULL) {
g_keys->next->tail = g_keys->tail;
}
g_keys = g_keys->next;
delete pki;
}
// 检查是否有重复的按键。
for (pki = g_keys; pki != NULL; pki = pki->next) {
if (pki->key == key) {
break;
}
}
if (pki != NULL) {
bAllowed = FALSE;
}
else {
pki = new KeyInfo;
pki->key = key;
pki->time = time;
pki->next = NULL;
if (g_keys == NULL) {
g_keys = pki;
g_keys->tail = pki;
}
else {
// 将新产生的按键信息加入到队尾,采用先进先出算法。
g_keys->tail->next = pki;
g_keys->tail = pki;
}
}

return bAllowed;
}
BOOL NotifyMonitor(HWND hMonitor, LPTSTR pszMsg, DWORD cb) {
BOOL bRet = FALSE;
COPYDATASTRUCT cds = { 0 };
cds.cbData = cb;
cds.lpData = pszMsg;

bRet = (SendMessage(hMonitor, WM_COPYDATA, 0, (LPARAM)(LPVOID)&cds) == 0);
return bRet;
}
// 按键字符串表。
LPCTSTR szKeyStringTable[] =
{
/* 0x00 – 0x0F */
_T("未定义(0x00)"),
。。。
/* 0x40 – 0x4F */
_T("未定义(0x40)"),
_T("A"),
_T("B"),
_T("C"),
_T("D"),
_T("E"),
_T("F"),
_T("G"),
。。。
_T("保留(0xFF)")
};
============================================================
低级钩子:
LRESULT CALLBACK LLKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
LRESULT lRet = CallNextHookEx(g_hHook, nCode, wParam, lParam);
// nCode 小于 0 时,必须返回 CallNextHookEx 的返回值。
if (nCode < 0) {
return lRet;
}
if (nCode == HC_ACTION) {
if (wParam == WM_KEYDOWN) {
PKBDLLHOOKSTRUCT pkhs = (PKBDLLHOOKSTRUCT)lParam;
if (IsKeyAllowed(pkhs) == FALSE) {
// 屏蔽该按键。
lRet = 1;
// 构造需要发送给监视器的屏蔽消息文本。
TCHAR sz[256], szTime[32];
SYSTEMTIME st;
LPTSTR format = _T("%s 按键 { %s } 被屏蔽。");
GetLocalTime(&st);
GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, _T("HH:mm:ss"),
szTime, _countof(szTime));
StringCchPrintf(sz, _countof(sz), format, szTime,
szKeyStringTable[(BYTE)pkhs->vkCode]);
// 显示按键被屏蔽的消息。
AddStringToListBox(GetDlgItem(g_hDlg, IDL_MONITOR), sz);
}
}
}
return lRet;
}
BOOL IsKeyAllowed(PKBDLLHOOKSTRUCT pkhs) {
BOOL bAllowed = TRUE;
PKeyInfo pki;
// 移除已超过两秒的按键信息。
while (g_keys != NULL) {
if ((pkhs->time – g_keys->time) < 2000) {
break;
}
pki = g_keys;
if (g_keys->next != NULL) {
g_keys->next->tail = g_keys->tail;
}
g_keys = g_keys->next;
delete pki;
}
// 检查是否有重复的按键。
for (pki = g_keys; pki != NULL; pki = pki->next) {
if (pki->key == pkhs->vkCode) {
break;
}
}
if (pki != NULL) {
bAllowed = FALSE;
}
else {
pki = new KeyInfo;
pki->key = pkhs->vkCode;
pki->time = pkhs->time;
pki->next = NULL;
if (g_keys == NULL) {
g_keys = pki;
g_keys->tail = pki;
}
else {
// 将新产生的按键信息加入到队尾,采用先进先出算法。
g_keys->tail->next = pki;
g_keys->tail = pki;
}
}
return bAllowed;
}

什么是键盘钩子

简单的讲,键盘钩子就是通过编程实现屏蔽系统热键。
————————————
fingerwin解释得不错。不好一两句话让你明白透彻。
http://bbs.xml.org.cn/blog/more.asp?name=%BE%ED%BB%FD%C4%DA%BA%CB&id=11193
如果你有兴趣,建议你把上面的这篇文章读懂。

什么是键盘钩子?

分几种,有进程钩子,就是只是捕获某一个特定进程的键盘输入。有程序钩子,只捕获某一个程序的键盘输入。还有全局钩子,可以捕获所有进程或者程序的键盘输入。捕获就是说钩子程序能获取你输入的信息,比方说原来有一些QQ木马就是安装键盘钩子,用于记录你输入的QQ号和密码,又或者一些游戏的盗号程序就是这样的。

以上就是小编对于什么是键盘钩子问题和相关问题的解答了,什么是键盘钩子的问题希望对你有用!

文章来自互联网,只做分享使用。发布者:,转转请注明出处:https://www.kuzhihao.com/article/234962.html

(0)

相关推荐