现在的鼠标功能越来越强大,不单单只有左右中三键,有更多的按键,比如5键,7键,20+键、、、我也顺便淘了一个5键的鼠标。但是无奈国产货(Understand?),鼠标侧面的两个键完全白费了,只能用来当前进后退键。看到罗技、Razor的鼠标按键自定义爽爽的,心里就不舒服,然后萌生了这个想法——自己来重新DIY下这俩个鸡肋按键。

网上资料倒是一大堆,先分析下这个工程怎么实现。

由于是从打开软件开始这两个按键一直要处于DIY的条件下,所以远程注入不能实现,否则要一一注入那还不麻烦死?杀毒软件也要来一直阻止你的。丢个例子,试试就知道有多蛋疼了:

#include <windows.h>
#include <iostream.h>
 
bool EnableDebugPriv()
{
    HANDLE hToken;
    LUID sedebugnameValue;
    TOKEN_PRIVILEGES tkp;
 
    if (!OpenProcessToken(GetCurrentProcess(),
        TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
        return false;
    }
 
    if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue)) {
        CloseHandle(hToken);
        return false;
    }
 
    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = sedebugnameValue;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
 
    if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) {
        CloseHandle(hToken);
        return false;
    }
 
    return true;
}
 
BOOL InitDll(const char *DllFullPath, const DWORD dwRemoteProcessId)
{
EnableDebugPriv();
 
HANDLE hRemoteProcess;

 

打开远程线程

hRemoteProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwRemoteProcessId );
 
char *pszLibFileRemote;

 

使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间

   

pszLibFileRemote = (char *) VirtualAllocEx( hRemoteProcess, NULL, lstrlen(DllFullPath)+1,
MEM_COMMIT, PAGE_READWRITE);

 

使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间

 

WriteProcessMemory(hRemoteProcess, pszLibFileRemote, (void *) DllFullPath,
lstrlen(DllFullPath)+1, NULL);
 
DWORD dwID;
LPVOID pFunc = LoadLibraryA;
HANDLE hRemoteThread = CreateRemoteThread(hRemoteProcess, NULL, 0,
(LPTHREAD_START_ROUTINE)pFunc, pszLibFileRemote, 0, &dwID );
 
 
if(hRemoteThread == NULL)
{
cout<<"注入线程失败!"<<endl;
return 0;
}
    CloseHandle(hRemoteProcess);
    CloseHandle(hRemoteThread);
 
    return TRUE;
}
 
int main()
{
工作区间\\源程序\\远程注入\\Debug\\iGazeU.dll", 576) ;//这个dll你所要注入的dll文件,这个”数字”是你想注入的进程的PID号
    return 0;
}

 

所以另寻方法了。

熟悉Windows的童鞋也许能通过修改注册表实现小部分功能,比方说把按键映射,连编写都省了,但是绝对做不出自定义想要的功能,比方说复制粘贴什么的。

最后,只有靠一种方法了,就是用全局钩子。

全局钩子?没怎么看过的童鞋一定觉得很神秘,360有时候也会提示带有Hook字样的内容,其实钩子只是一个纸老虎。

Windows和DOS最大的区别就是消息。现在学编程的童鞋肯定知道一个面向过程的程序是怎么编的,但是可能并不清楚面向对象的程序是怎么写的,比方说一个状态栏带有动态时间显示的记事本是怎么显示的?如果用面向过程写,记事本要输入文字,又要定时刷新时间,两个事情怎么可能同时做?所以说,消息就是Windows提醒程序什么时候做什么事情,程序只要循环不断接收消息就行了。就像这样:

while (GetMessage(&msg, NULL, 0, 0))  //程序永远在接收消息
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) 
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

我们现在研究的核心,就是怎么让Windows在向程序发送鼠标侧键消息的时候把这个消息拦截掉,改掉并换成自定义的消息,这就是钩子的本来面目。

最重要的一关被我们跨过去了,现在是实战阶段了。

打开你的VS2010,或者VC6也一样,由于MFC比较方便,很多代码都是自动添加好的,所以我们以MFC为例:

1、新建一个 MFC AppWizard(dll) 工程,选择 MFC扩展DLL(使用共享MFC DLL),建立工程名为TestHook。

为什么要建立DLL(动态连接库)?Windows坑爹的规定,没办法,人家是老大,只能听他的,Linux没试过,应该会好很多。

DLL是什么?普及下,就是独立出来的库,比方说你有n+1个程序都要使用

{
a ^= b ^= a ^= b;
}

那么你只需写一个这样的功能,做成DLL,你就可以让n+1个程序都可以用它了,是不是很方便?

2、打开stdafx.h,在#include <afxwin.h>上面加入:

#define _WIN32_WINNT 0x500

在stdafx.h最后面加上:

#include <windows.h>
#include <winuser.h>

这是什么意思呢?你可以打开winuser.h看看,原来_WIN32_WINNT是用来区分系统版本的,我们的程序只能在Windows 2000和以上版本的电脑才能运行。

3、打开TestHook.def,在最下面加入:

SECTIONS
mydata READ WRITE SHARED

4、现在来做几个函数了,用类来管理比较方便,应该说是一个好习惯。先来定义一个类:新建头文件MouseHook.h,写上:

class AFX_EXT_CLASS CMouseHook:public CObject
{
public:
CMouseHook();
~CMouseHook();
 
BOOL startHook();
BOOL stopHook();
static LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam);
};

再新建一个MouseHook.cpp,添加以下代码:

#include "stdafx.h"
#include "MouseHook.h"
 
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
 
#pragma data_seg("mydata")
HHOOK glhHook = NULL;
HINSTANCE glhInstance = NULL;
#pragma data_seg()
 
CMouseHook::CMouseHook()
{
}
 
CMouseHook::~CMouseHook()
{
stopHook();
}
LRESULT CALLBACK CMouseHook::MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
return CallNextHookEx(glhHook, nCode, wParam, lParam);
}
BOOL CMouseHook::startHook()
{
BOOL bResult = FALSE; 
glhHook = SetWindowsHookEx(WH_MOUSE_LL, CMouseHook::MouseProc, glhInstance, 0); 
 
if(glhHook != NULL) 
bResult = TRUE; 
 
return bResult; 
}
 
BOOL CMouseHook::stopHook()
{
if (glhHook == NULL)
{
MessageBox(NULL, _T("NULL HOOK"), _T("glhHook"), MB_OK);
}
 
BOOL bResult = FALSE; 
 
bResult = UnhookWindowsHookEx(glhHook); 
if(bResult) 
{ 
MessageBox(NULL, _T("UnhookWindowsHookEx Success"), _T("glhHook"), MB_OK);
glhHook = NULL; 
}
 
return bResult;
}

5、现在是关键代码了,我们的代码都在

LRESULT CALLBACK CMouseHook::MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
return CallNextHookEx(glhHook, nCode, wParam, lParam);
}

中完成,具体代码如下:

LRESULT CALLBACK CMouseHook::MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
LPMSLLHOOKSTRUCT pMouseHook = (MSLLHOOKSTRUCT*)lParam;
if (nCode >= 0)
{
If (wParam==WM_XBUTTONDOWN && HIWORD(pMouseHook->mouseData)==XBUTTON1)
{
//写上自己代码
return TRUE;
}
 
if (wParam == WM_XBUTTONUP && HIWORD(pMouseHook->mouseData)==XBUTTON1)
{
//写上自己代码
return TRUE;
}
 
if (wParam == WM_XBUTTONDOWN && HIWORD(pMouseHook->mouseData)==XBUTTON2)
{
//写上自己代码
return TRUE;
}
 
if (wParam == WM_XBUTTONUP && HIWORD(pMouseHook->mouseData)==XBUTTON2)
{
//写上自己代码
return TRUE;
}
}
return CallNextHookEx(glhHook, nCode, wParam, lParam);
}

用VC6的童鞋会说了,为什么我编译下后程序出现N多错误?其实是VC6太老了,有些库比较新,没有得到更新。只要在cpp的#include "stdafx.h"下加上这些:

#define WM_XBUTTONDOWN 0x020B
#define WM_XBUTTONUP 0x020C
#define XBUTTON1 0x1
#define XBUTTON2 0x2

是不是行了呢?

 

注意:VC6要把_T()去掉,比方说_T("glhHook")变成"glhHook"。

由于5键鼠标不是常规鼠标,而我们使用的是低级钩子(Low-Level Hook)因此要用MSLLHOOKSTRUCT结构体。

 

6、做完DLL当然要有程序去调用啊,接下来做一个调用程序(以VC6为例):

(1)新建一个MFC EXE工程Mouse,把做完的Dll工程下的Debug目录下的TestHook.lib和Dll工程目录中的MouseHook.h拷贝到新建工程的目录下,按快捷键Alt+F7打开工程设置,点击 连接 选项卡,在对象/库模块中填写TestHook.lib,打开MouseDlg.h,在#include "stdafx.h"下方加入

#include "MouseHook.h",

然后在class CMouseDlg : public CDialog下的public:下加入:

CMouseHook m_hook;

切换到MouseDlg.cpp,在BOOL CMouseDlg::OnInitDialog()下的SetIcon(m_hIcon, FALSE); // Set small icon

下加入

m_hook.startHook();

然后编译链接吧。

 

现在,把做完的TestHook.dll和Mouse.exe放在同一个目录下,打开是不是发现鼠标的侧键被屏蔽了?因为我们到现在都没有加入自己想要的功能,又是运行了直接return TRUE; 所以截取的消息直接被吃掉了!

加个小功能吧,有些童鞋的鼠标左右键不好用了,可以临时代替应急:

LRESULT CALLBACK CMouseHook::MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
int cx=GetSystemMetrics(SM_CXSCREEN);//得到屏幕宽度
int cy=GetSystemMetrics(SM_CYSCREEN);//得到屏幕高度
LONG ldx = pMouseHook->pt.x * 65535 / cx;
LONG ldy = pMouseHook->pt.y * 65535 / cy;
 
LPMSLLHOOKSTRUCT pMouseHook = (MSLLHOOKSTRUCT*)lParam;
 
if (nCode >= 0)
{
If (wParam==WM_XBUTTONDOWN && HIWORD(pMouseHook->mouseData)==XBUTTON1)
{
INPUT input[1];
ZeroMemory(&input, sizeof(INPUT));
 
//鼠标左键弹起
input[0].type = INPUT_MOUSE;
input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
input[0].mi.dx = ldx;
input[0].mi.dy = ldy;
 
SendInput(
1, // count of input events
input, // array of input events
sizeof(INPUT) // size of structure
);
 
return TRUE;
}
 
if (wParam == WM_XBUTTONUP && HIWORD(pMouseHook->mouseData)==XBUTTON1)
{
INPUT input[1];
ZeroMemory(&input, sizeof(INPUT));
 
//鼠标左键弹起
input[0].type = INPUT_MOUSE;
input[0].mi.dwFlags = MOUSEEVENTF_LEFTUP;
input[0].mi.dx = ldx;
input[0].mi.dy = ldy;
 
SendInput(
1, // count of input events
input, // array of input events
sizeof(INPUT) // size of structure
);
 
return TRUE;
}
 
if (wParam == WM_XBUTTONDOWN && HIWORD(pMouseHook->mouseData)==XBUTTON2)
{
INPUT input[1];
ZeroMemory(&input, sizeof(INPUT));
 
//鼠标左键弹起
input[0].type = INPUT_MOUSE;
input[0].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
input[0].mi.dx = ldx;
input[0].mi.dy = ldy;
 
SendInput(
1, // count of input events
input, // array of input events
sizeof(INPUT) // size of structure
);
 
return TRUE;
}
 
if (wParam == WM_XBUTTONUP && HIWORD(pMouseHook->mouseData)==XBUTTON2)
{
INPUT input[1];
ZeroMemory(&input, sizeof(INPUT));
 
//鼠标左键弹起
input[0].type = INPUT_MOUSE;
input[0].mi.dwFlags = MOUSEEVENTF_RIGHTUP;
input[0].mi.dx = ldx;
input[0].mi.dy = ldy;
 
SendInput(
1, // count of input events
input, // array of input events
sizeof(INPUT) // size of structure
);
 
return TRUE;
}
}
return CallNextHookEx(glhHook, nCode, wParam, lParam);
}

 

写到了这里,就是那么多了,其实也没多少技术含量的事情,接下来怎么玩看各位的想法了,比方说自动打怪,设置快捷键什么的。