基于钩子的改键

来源:互联网 发布:js只允许输入数字 编辑:程序博客网 时间:2024/05/28 05:16

玩dota时候,大多的人会使用改建精灵,在网上找了找资料,发现这个其实不难编,于是自己实现了一个。

首先,要了解什么是钩子(HOOK,彼得潘里的虎克船长就是这个)。我们都知道在windows程序是靠消息驱动的,比如说我们在某程序中点击了鼠标,那么,系统会向这个程序发送一个鼠标消息,在通过其绑定的回调函数来处理这个鼠标消息。钩子就是在系统传递消息时把这个消息截获,然后按照我们的意愿对其处理。

在改键中,我们希望截获某个键消息,然后用一个其他的键的消息来替代它,这个过程正好是钩子的能力范围之内。

我们来看看如何使用钩子,这里我要介绍一个钩子函数 “HHOOK SetWindowsHookEx(          intidHook,    HOOKPROC lpfn,    HINSTANCE hMod,    DWORD dwThreadId);”

这个在MSDN里可已找到。

我们先看一下他的参数:

idHook这个参数是设置我们要绑定什么样的钩子,windows系统的消息是多种的,那么对于消息操作的钩子也应该是多种的。比如说,鼠标钩子和键盘钩子就是不同类型的。

lpfn这个参数是设定回调函数的,对于截获的消息,我们要如何处理它,就在这个函数里面写相应的代码。(注意,这是一个函数指针也就是函数名)。

hMod这个参数有一点小复杂,因为它与后面的dwThreadId有关,如果这里是NULL那么dwThreadId就是当前调用这个函数的线程id。这个参数的意义在于如果我们是用的是全局的钩子(也就是对所有桌面的上的线程的消息均截获),那么就需要从dll(动态链接库)中加载钩子,而此参数就是指向那个动态链接库句柄。

dwThreadId如果不是全局钩子,那么这个就是调用函数的线程的id,一般使用GetCurrentThreadId()函数。如果是全局钩子,这里置为0。

我们在总结一下后两个参数,如果你的SetWindowsHookEX函数写在了你要监视的线程中,那么就不需要dll,那么hMod置为NULL,dwThreadId设置为当前线程id。如果你要监视其他线程的消息,那么就需要使用到dll,那么就需要指定dll的句柄,dwThreadId置为0。

在改键的程序中,我们使用的钩子类型是WH_KEYBOARD_LL,这是个底层的键盘钩子,他对应的回调函数我们叫LowLevelKeyboardProc(int code ,WPARAM wParam,LPARAM lParam);

我们来解释一个回调函数的参数:

code参数如果是非0值,那么不能在更进一步的处理,而且必须调用CallNextHookEx(HHOOk hMod)函数,并返回此函数的返回值。

wParam参数是指定我们键盘是什么状态,有WM_KEYDOW、WM_KEYUP、WM_SYSKEYDOWN、WM_SYSKEYUP这四种状态。

了Param是一个结构体指针,这里面有我们具体按下了哪个键的键值。一下是具体格式,我们只用第一个vkCode这里存的是键值。

typedef struct {    DWORD vkCode;    DWORD scanCode;    DWORD flags;    DWORD time;    ULONG_PTR dwExtraInfo;} KBDLLHOOKSTRUCT, *PKBDLLHOOKSTRUCT;

以下是代码

1.dll部分的代码

keyHookDLL.h

#ifndef _KEYHOOKDLL_H_#define _KEYHOOKDLL_H_#ifdef DLL_EXPORTS#define DLLAPI __declspec(dllexport)#else#define DLLAPI __declspec(dllimport)#endifextern HMODULE g_hInist;HHOOK hHook = NULL;BYTE* changeKeys = NULL;UINT count_keys = 0;void DLLAPI setHook(BYTE* data,UINT count);//设置键盘钩子的键值映射void DLLAPI freeHook();//释放钩子LRESULT CALLBACK LowLevelKeyboardProc(int code,WPARAM wParam,LPARAM lParam);#endif

keyHookDLL.cpp

#include "stdafx.h"#define DLL_EXPORTS#include "KeyHookDLL.h"void DLLAPI setHook(BYTE* data,UINT count){hHook = SetWindowsHookEx(WH_KEYBOARD_LL,LowLevelKeyboardProc,g_hInist,0);changeKeys = new BYTE[count];for (UINT i = 0;i<count;i++){changeKeys[i] = data[i];}count_keys = count;}void DLLAPI freeHook(){UnhookWindowsHookEx(hHook);delete[] changeKeys;}BOOL iskeydown = FALSE;LRESULT CALLBACK LowLevelKeyboardProc(int code,WPARAM wParam,LPARAM lParam){KBDLLHOOKSTRUCT* hookInfo = (KBDLLHOOKSTRUCT*)lParam;if (code == HC_ACTION){if (WM_KEYDOWN == wParam && !iskeydown){if (changeKeys[0] == hookInfo->vkCode){iskeydown = TRUE;keybd_event(VK_NUMPAD7,0,0,0);keybd_event(VK_NUMPAD7,0,KEYEVENTF_KEYUP,0);iskeydown = FALSE;return 1;}if (changeKeys[1] == hookInfo->vkCode){iskeydown = TRUE;keybd_event(VK_NUMPAD8,0,0,0);keybd_event(VK_NUMPAD8,0,KEYEVENTF_KEYUP,0);iskeydown = FALSE;return 1;}if (changeKeys[2] == hookInfo->vkCode){iskeydown = TRUE;keybd_event(VK_NUMPAD4,0,0,0);keybd_event(VK_NUMPAD4,0,KEYEVENTF_KEYUP,0);iskeydown = FALSE;return 1;}if (changeKeys[3] == hookInfo->vkCode){iskeydown = TRUE;keybd_event(VK_NUMPAD5,0,0,0);keybd_event(VK_NUMPAD5,0,KEYEVENTF_KEYUP,0);iskeydown = FALSE;return 1;}if (changeKeys[4] == hookInfo->vkCode){iskeydown = TRUE;keybd_event(VK_NUMPAD1,0,0,0);keybd_event(VK_NUMPAD1,0,KEYEVENTF_KEYUP,0);iskeydown = FALSE;return 1;}if (changeKeys[5] == hookInfo->vkCode){iskeydown = TRUE;keybd_event(VK_NUMPAD2,0,0,0);keybd_event(VK_NUMPAD2,0,KEYEVENTF_KEYUP,0);iskeydown = FALSE;return 1;}}}return CallNextHookEx(hHook,code,wParam,lParam);//return 1;}


MainDLL.cpp

// HookDLL.cpp : 定义 DLL 应用程序的入口点。//#include "stdafx.h"#ifdef _MANAGED#pragma managed(push, off)#endifHMODULE g_hInist = NULL;// 调用dll的句柄BOOL APIENTRY DllMain( HMODULE hModule,                       DWORD  ul_reason_for_call,                       LPVOID lpReserved ){switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:g_hInist = hModule;break;case DLL_THREAD_ATTACH:break;case DLL_PROCESS_DETACH:break;case DLL_THREAD_DETACH:break;}    return TRUE;}#ifdef _MANAGED#pragma managed(pop)#endif


建立一个机遇对话框的MFC工程。添加如下的对话框资源

keyHookDlg.h

// KeyHookDlg.h : 头文件//#pragma once// CKeyHookDlg 对话框class CKeyHookDlg : public CDialog{// 构造public:CKeyHookDlg(CWnd* pParent = NULL);// 标准构造函数// 对话框数据enum { IDD = IDD_KEYHOOK_DIALOG };protected:virtual void DoDataExchange(CDataExchange* pDX);// DDX/DDV 支持// 实现protected:HICON m_hIcon;// 生成的消息映射函数virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();DECLARE_MESSAGE_MAP()public:BOOL isHooked;BOOL isUnHooked;public:// 小键盘数字键映射到其他键上CString m_num7;CString m_num8;CString m_num4;CString m_num5;CString m_num1;CString m_num2;BYTE m_changeKey[6];afx_msg void OnBnClickedBtnHook();afx_msg void OnBnClickedBtnUnhook();};

keyHookDlg.cpp

// KeyHookDlg.cpp : 实现文件//#include "stdafx.h"#include "KeyHook.h"#include "KeyHookDlg.h"#include "KeyHookDLL.h"#ifdef _DEBUG#define new DEBUG_NEW#endif#pragma comment(lib,"HookDLL.lib")// 用于应用程序“关于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialog{public:CAboutDlg();// 对话框数据enum { IDD = IDD_ABOUTBOX };protected:virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现protected:DECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD){}void CAboutDlg::DoDataExchange(CDataExchange* pDX){CDialog::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)END_MESSAGE_MAP()// CKeyHookDlg 对话框CKeyHookDlg::CKeyHookDlg(CWnd* pParent /*=NULL*/): CDialog(CKeyHookDlg::IDD, pParent),isHooked(FALSE),isUnHooked(FALSE), m_num7(""), m_num8(""), m_num4(""), m_num5(""), m_num1(""), m_num2(""){m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);m_changeKey[0] = VK_NUMPAD7;m_changeKey[1] = VK_NUMPAD8;m_changeKey[2] = VK_NUMPAD4;m_changeKey[3] = VK_NUMPAD5;m_changeKey[4] = VK_NUMPAD1;m_changeKey[5] = VK_NUMPAD2;}void CKeyHookDlg::DoDataExchange(CDataExchange* pDX){CDialog::DoDataExchange(pDX);DDX_Text(pDX, IDC_EDIT_NUM7, m_num7);DDX_Text(pDX, IDC_EDIT_NUM8, m_num8);DDX_Text(pDX, IDC_EDIT_NUM4, m_num4);DDX_Text(pDX, IDC_EDIT_NUM5, m_num5);DDX_Text(pDX, IDC_EDIT_NUM1, m_num1);DDX_Text(pDX, IDC_EDIT_NUM2, m_num2);}BEGIN_MESSAGE_MAP(CKeyHookDlg, CDialog)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()//}}AFX_MSG_MAPON_BN_CLICKED(IDC_BTN_HOOK, &CKeyHookDlg::OnBnClickedBtnHook)ON_BN_CLICKED(IDC_BTN_UNHOOK, &CKeyHookDlg::OnBnClickedBtnUnhook)END_MESSAGE_MAP()// CKeyHookDlg 消息处理程序BOOL CKeyHookDlg::OnInitDialog(){CDialog::OnInitDialog();// 将“关于...”菜单项添加到系统菜单中。// IDM_ABOUTBOX 必须在系统命令范围内。ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != NULL){CString strAboutMenu;strAboutMenu.LoadString(IDS_ABOUTBOX);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动//  执行此操作SetIcon(m_hIcon, TRUE);// 设置大图标SetIcon(m_hIcon, FALSE);// 设置小图标// TODO: 在此添加额外的初始化代码return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE}void CKeyHookDlg::OnSysCommand(UINT nID, LPARAM lParam){if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialog::OnSysCommand(nID, lParam);}}// 如果向对话框添加最小化按钮,则需要下面的代码//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,//  这将由框架自动完成。void CKeyHookDlg::OnPaint(){if (IsIconic()){CPaintDC dc(this); // 用于绘制的设备上下文SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);// 使图标在工作矩形中居中int cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// 绘制图标dc.DrawIcon(x, y, m_hIcon);}else{CDialog::OnPaint();}}//当用户拖动最小化窗口时系统调用此函数取得光标显示。//HCURSOR CKeyHookDlg::OnQueryDragIcon(){return static_cast<HCURSOR>(m_hIcon);}void CKeyHookDlg::OnBnClickedBtnHook(){// TODO: 在此添加控件通知处理程序代码UpdateData();if (m_num7.GetLength()>1){MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);return;}if (m_num8.GetLength()>1){MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);return;}if (m_num4.GetLength()>1){MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);return;}if (m_num5.GetLength()>1){MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);return;}if (m_num1.GetLength()>1){MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);return;}if (m_num2.GetLength()>1){MessageBox(_T("can't be string !"),_T("ERROR"),IDOK);return;}if (!isHooked && !isUnHooked){BYTE* _keys = new BYTE[6];_keys[0] = *((BYTE*)(m_num7.GetBuffer()));_keys[1] = *((BYTE*)(m_num8.GetBuffer()));_keys[2] = *((BYTE*)(m_num4.GetBuffer()));_keys[3] = *((BYTE*)(m_num5.GetBuffer()));_keys[4] = *((BYTE*)(m_num1.GetBuffer()));_keys[5] = *((BYTE*)(m_num2.GetBuffer()));setHook(_keys,6);isHooked = TRUE;isUnHooked = TRUE;delete[] _keys;}else{MessageBox(_T("you have already Hooked"),_T("Notice"),IDOK);return;}}void CKeyHookDlg::OnBnClickedBtnUnhook(){// TODO: 在此添加控件通知处理程序代码if (isHooked && isUnHooked){freeHook();isUnHooked = FALSE;isHooked = FALSE;}else{MessageBox(_T("you have already Hooked"),_T("Notice"),IDOK);return;}}


实验结果,按下数字键“1”。

这里的MFC工程文件,我知列举了我有代码修改的部分,也就是对话框部分。

由于本人的水平有限,希望大家有更好的方法或者技术一起分享一下。

原创粉丝点击