現在的鼠標功能越來越強大,不單單只有左右中三鍵,有更多的按鍵,比如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);
}
寫到了這里,就是那么多了,其實也沒多少技術含量的事情,接下來怎么玩看各位的想法了,比方說自動打怪,設置快捷鍵什么的。