VOID mouse_event(
DWORD dwFlags, // 鼠標動作標識。
DWORD dx, // 鼠標水平方向位置。
DWORD dy, // 鼠標垂直方向位置。
DWORD dwData, // 鼠標輪子轉動的數量。
DWORD dwExtraInfo // 壹個關聯鼠標動作輔加信息。
);
其中,dwFlags表示了各種各樣的鼠標動作和點擊活動,它的常用取值如下:
MOUSEEVENTF_MOVE 表示模擬鼠標移動事件。
MOUSEEVENTF_LEFTDOWN 表示模擬按下鼠標左鍵。
MOUSEEVENTF_LEFTUP 表示模擬放開鼠標左鍵。
MOUSEEVENTF_RIGHTDOWN 表示模擬按下鼠標右鍵。
MOUSEEVENTF_RIGHTUP 表示模擬放開鼠標右鍵。
MOUSEEVENTF_MIDDLEDOWN 表示模擬按下鼠標中鍵。
MOUSEEVENTF_MIDDLEUP 表示模擬放開鼠標中鍵。
(2)、設置和獲取當前鼠標位置的API函數。獲取當前鼠標位置使用GetCursorPos()函數,設置當前鼠標位置使用SetCursorPos()函數。
BOOL GetCursorPos(
LPPOINT lpPoint // 返回鼠標的當前位置。
);
BOOL SetCursorPos(
int X, // 鼠標的水平方向位置。
int Y //鼠標的垂直方向位置。
);
通常遊戲角色的行走都是通過鼠標移動至目的地,然後按壹下鼠標的按鈕就搞定了。下面我們使用上面介紹的API函數來模擬角色行走過程。
CPoint oldPoint,newPoint;
GetCursorPos(&oldPoint); //保存當前鼠標位置。
newPoint.x = oldPoint.x+40;
newPoint.y = oldPoint.y+10;
SetCursorPos(newPoint.x,newPoint.y); //設置目的地位置。
mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0);//模擬按下鼠標右鍵。
mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0);//模擬放開鼠標右鍵。
2. 鍵盤模擬技術
在很多遊戲中,不僅提供了鼠標的操作,而且還提供了鍵盤的操作,在對攻擊對象進行攻擊時還可以使用快捷鍵。為了使這些攻擊過程能夠自動進行,外掛程序需要使用鍵盤模擬技術。像鼠標模擬技術壹樣,Windows API也提供了壹系列API函數來完成對鍵盤動作的模擬。
模擬鍵盤動作API函數keydb_event,它可以模擬對鍵盤上的某個或某些鍵進行按下或放開的動作。
VOID keybd_event(
BYTE bVk, // 虛擬鍵值。
BYTE bScan, // 硬件掃描碼。
DWORD dwFlags, // 動作標識。
DWORD dwExtraInfo // 與鍵盤動作關聯的輔加信息。
);
其中,bVk表示虛擬鍵值,其實它是壹個BYTE類型值的宏,其取值範圍為1-254。有關虛擬鍵值表請在MSDN上使用關鍵字“Virtual-Key Codes”查找相關資料。bScan表示當鍵盤上某鍵被按下和放開時,鍵盤系統硬件產生的掃描碼,我們可以MapVirtualKey()函數在虛擬鍵值與掃描碼之間進行轉換。dwFlags表示各種各樣的鍵盤動作,它有兩種取值:
KEYEVENTF_EXTENDEDKEY和KEYEVENTF_KEYUP。
下面我們使用壹段代碼實現在遊戲中按下Shift+R快捷鍵對攻擊對象進行攻擊。
keybd_event(VK_CONTROL,MapVirtualKey(VK_CONTROL,0),0,0); //按下CTRL鍵。
keybd_event(0x52,MapVirtualKey(0x52,0),0,0);//鍵下R鍵。
keybd_event(0x52,MapVirtualKey(0x52,0), KEYEVENTF_KEYUP,0);//放開R鍵。
keybd_event(VK_CONTROL,MapVirtualKey(VK_CONTROL,0),
KEYEVENTF_KEYUP,0);//放開CTRL鍵。
3. 激活外掛
上面介紹的鼠標和鍵盤模擬技術實現了對遊戲角色的動作部分的模擬,但要想外掛能工作於遊戲之上,還需要將其與遊戲的場景窗口聯系起來或者使用壹個激活鍵,就象按鍵精靈的那個激活鍵壹樣。我們可以用GetWindow函數來枚舉窗口,也可以用Findwindow函數來查找特定的窗口。另外還有壹個FindWindowEx函數可以找到窗口的子窗口,當遊戲切換場景的時候我們可以用FindWindowEx來確定壹些當前窗口的特征,從而判斷是否還在這個場景,方法很多了,比如可以GetWindowInfo來確定壹些東西,比如當查找不到某個按鈕的時候就說明遊戲場景已經切換了等等辦法。當使用激活鍵進行關聯,需要使用Hook技術開發壹個全局鍵盤鉤子,在這裏就不具體介紹全局鉤子的開發過程了,在後面的實例中我們將會使用到全局鉤子,到時將學習到全局鉤子的相關知識。
4. 實例實現
通過上面的學習,我們已經基本具備了編寫動作式遊戲外掛的能力了。下面我們將創建壹個畫筆程序外掛,它實現自動移動畫筆字光標的位置並寫下壹個紅色的“R”字。以這個實例為基礎,加入相應的遊戲動作規則,就可以實現壹個完整的遊戲外掛。這裏作者不想使用某個遊戲作為例子來開發外掛(因沒有遊戲商家的授權啊!),如讀者感興趣的話可以找壹個遊戲試試,最好僅做測試技術用。
首先,我們需要編寫壹個全局鉤子,使用它來激活外掛,激活鍵為F10。創建全局鉤子步驟如下:
(1).選擇MFC AppWizard(DLL)創建項目ActiveKey,並選擇MFC Extension DLL(***享MFC拷貝)類型。
(2).插入新文件ActiveKey.h,在其中輸入如下代碼:
#ifndef _KEYDLL_H
#define _KEYDLL_H
class AFX_EXT_CLASS CKeyHook:public CObject
{
public:
CKeyHook();
~CKeyHook();
HHOOK Start(); //安裝鉤子
BOOL Stop(); //卸載鉤子
};
#endif
(3).在ActiveKey.cpp文件中加入聲明”#include ActiveKey.h”。
(4).在ActiveKey.cpp文件中加入***享數據段,代碼如下:
//Shared data section
#pragma data_seg("sharedata")
HHOOK glhHook=NULL; //鉤子句柄。
HINSTANCE glhInstance=NULL; //DLL實例句柄。
#pragma data_seg()
(5).在ActiveKey.def文件中設置***享數據段屬性,代碼如下:
SETCTIONS
shareddata READ WRITE SHARED
(6).在ActiveKey.cpp文件中加入CkeyHook類的實現代碼和鉤子函數代碼:
//鍵盤鉤子處理函數。
extern "C" LRESULT WINAPI KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
if( nCode >= 0 )
{
if( wParam == 0X79 )//當按下F10鍵時,激活外掛。
{
//外掛實現代碼。
CPoint newPoint,oldPoint;
GetCursorPos(&oldPoint);
newPoint.x = oldPoint.x+40;
newPoint.y = oldPoint.y+10;
SetCursorPos(newPoint.x,newPoint.y);
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//模擬按下鼠標左鍵。
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);//模擬放開鼠標左鍵。
keybd_event(VK_SHIFT,MapVirtualKey(VK_SHIFT,0),0,0); //按下SHIFT鍵。
keybd_event(0x52,MapVirtualKey(0x52,0),0,0);//按下R鍵。
keybd_event(0x52,MapVirtualKey(0x52,0),KEYEVENTF_KEYUP,0);//放開R鍵。
keybd_event(VK_SHIFT,MapVirtualKey(VK_SHIFT,0),KEYEVENTF_KEYUP,0);//放開SHIFT鍵。
SetCursorPos(oldPoint.x,oldPoint.y);
}
}
return CallNextHookEx(glhHook,nCode,wParam,lParam);
}
CKeyHook::CKeyHook(){}
CKeyHook::~CKeyHook()
{
if( glhHook )
Stop();
}
//安裝全局鉤子。
HHOOK CKeyHook::Start()
{
glhHook = SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,glhInstance,0);//設置鍵盤鉤子。
return glhHook;
}
//卸載全局鉤子。
BOOL CKeyHook::Stop()
{
BOOL bResult = TRUE;
if( glhHook )
bResult = UnhookWindowsHookEx(glhHook);//卸載鍵盤鉤子。
return bResult;
}
(7).修改DllMain函數,代碼如下:
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
//如果使用lpReserved參數則刪除下面這行
UNREFERENCED_PARAMETER(lpReserved);
if (dwReason == DLL_PROCESS_ATTACH)
{
TRACE0("NOtePadHOOK.DLL Initializing!\n");
//擴展DLL僅初始化壹次
if (!AfxInitExtensionModule(ActiveKeyDLL, hInstance))
return 0;
new CDynLinkLibrary(ActiveKeyDLL);
//把DLL加入動態MFC類庫中
glhInstance = hInstance;
//插入保存DLL實例句柄
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("NotePadHOOK.DLL Terminating!\n");
//終止這個鏈接庫前調用它
AfxTermExtensionModule(ActiveKeyDLL);
}
return 1;
}
(8).編譯項目ActiveKey,生成ActiveKey.DLL和ActiveKey.lib。
接著,我們還需要創建壹個外殼程序將全局鉤子安裝了Windows系統中,這個外殼程序編寫步驟如下:
(1).創建壹個對話框模式的應用程序,項目名為Simulate。
(2).在主對話框中加入壹個按鈕,使用ClassWizard為其創建CLICK事件。
(3).將ActiveKey項目Debug目錄下的ActiveKey.DLL和ActiveKey.lib拷貝到Simulate項目目錄下。
(4).從“工程”菜單中選擇“設置”,彈出Project Setting對話框,選擇Link標簽,在“對象/庫模塊”中輸入ActiveKey.lib。
(5).將ActiveKey項目中的ActiveKey.h頭文件加入到Simulate項目中,並在Stdafx.h中加入#include ActiveKey.h。
(6).在按鈕單擊事件函數輸入如下代碼:
void CSimulateDlg::OnButton1()
{
// TODO: Add your control notification handler code here
if( !bSetup )
{
m_hook.Start();//激活全局鉤子。
}
else
{
m_hook.Stop();//撤消全局鉤子。
}
bSetup = !bSetup;
}
(7).編譯項目,並運行程序,單擊按鈕激活外掛。
(8).啟動畫筆程序,選擇文本工具並將筆的顏色設置為紅色,將鼠標放在任意位置後,按F10鍵,畫筆程序自動移動鼠標並寫下壹個紅色的大寫R。圖壹展示了按F10鍵前的畫筆程序的狀態,圖二展示了按F10鍵後的畫筆程序的狀態。
圖壹:按F10前狀態(001.jpg)
圖二:按F10後狀態(002.jpg)