现在的鼠标功能越来越强大,不单单只有左右中三键,有更多的按键,比如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()
{
InitDll("E:\\工作区间\\源程序\\远程注入\\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个程序都要使用 void max(int *a, int *b)
{
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);
}
写到了这里,就是那么多了,其实也没多少技术含量的事情,接下来怎么玩看各位的想法了,比方说自动打怪,设置快捷键什么的。