基于钩子的改键

news/2024/11/17 23:39:35/

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

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

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

我们来看看如何使用钩子,这里我要介绍一个钩子函数 “HHOOK SetWindowsHookEx(          int idHook,    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工程文件,我知列举了我有代码修改的部分,也就是对话框部分。

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


http://www.ppmy.cn/news/526903.html

相关文章

dota是java中的_用java开发dota英雄最华丽的技能(实例讲解)

爱java 爱dota&#xff0c;突发奇想想用java开发dota操作最华丽的英雄之一的卡尔的技能&#xff0c;因为本人系小白&#xff0c;代码不足的地方还请包涵&#xff0c;有同样爱好的同学欢迎一起研究学习。 先把我的代码呈上 import java.util.*; public class TestDotakaer{ publ…

dota是java中的_用java开发dota英雄最华丽的技能

爱java 爱dota&#xff0c;突发奇想想用dota操作最华丽的英雄之一的卡尔的技能&#xff0c;因为本人系小白&#xff0c;代码不足的地方还请包涵&#xff0c;有同样爱好的同学欢迎一起研究学习。 先把我的代码呈上 import java.util.*; public class TestDotakaer{ public stati…

dota2 自定义官方服务器,DOTA2官网更新:服务器扩容 首部官方漫画发布

DOTA2全新宣传画 随着GamsCom的临近&#xff0c;更多DOTA2的截图也相继流出&#xff0c;身为主策划之一的IceFrog也曝光了DOTA2的全新原画。 http://img3.cache.netease.com/photo/0031/2011-08-15/7BG5AVI44NQF0031.jpg http://img4.cache.netease.com/photo/0031/2011-08-15/…

一个简易的dota改键助手

那年,刚开始玩dota的时候, 发现物品栏的快捷键太不好按鸟(集中在小键盘的数字键)。于是各种改键工具应运而生,像最经典的warkey, 不知不觉就用了好多年。如今,各种改键工具越来越强大了, 支持各种,甚至是喊话什么的~不过最经典的warkey,实际上只是简单的键盘映射, …

【Linux】VNC xfc4安装指导

1.xfce4软件介绍 Xfce是一个自由软件&#xff0c;运行在类Unix操作系统 (如Linux、FreeBSD 和 Solaris)上&#xff0c;提供轻量级桌面环境。 Xfce快速、轻量&#xff0c;界面美观和对用户友好。 Xfce由独立的软件组件构成&#xff0c;可根据需要单独使用或者组合在一起提供计算…

嵌入式VNC远程桌面配置终极指南

伙计们&#xff0c;终极指南在手&#xff0c;小赞点起来哦&#xff01; 1.开发板VNC服务器安装 #1.终端输入如下指令安装tightvncserver sudo apt-get install tightvncserver#2.终端输入下方指令初次运行vncserver vncserver#3.之后会提示输入密码&#xff08;两次&#xff…

局域网中Openstack的VNC安全配置

局域网中Openstack的VNC安全配置 1.前言 Openstack虚拟机的VNC是通过开启计算节点5900-5999端口来实现网页显示的&#xff0c;当外人知道计算节点的IP地址后可以通过VNC端口访问到所有的虚拟机&#xff0c;非常的不安全&#xff0c;其中为了防止出现虚拟机被攻击的情况&#…

【vnc远程桌面】未联网状态下使用离线包配置vnc

系统为ubuntu18.04 amd64版本。 本教程使用vino配置远程服务器。 首先&#xff0c;离线安装vino包后远程传输给待控制服务器&#xff1a; sudo apt update sudo apt-get download $(apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks …