一、 前言
所謂游戲外掛,其實(shí)是一種游戲外輔程序,它可以協(xié)助玩家自動(dòng)產(chǎn)生游戲動(dòng)作、修
改游戲網(wǎng)絡(luò )數據包以及修改游
戲內存數據等,以實(shí)現玩家用最少的時(shí)間和金錢(qián)去完成功力升級和過(guò)關(guān)斬將。雖然,現
在對游戲外掛程序的"合法"
身葜謁搗詛?,灾q飫鏤也幌攵源朔⒈砣魏胃鋈艘餳?,让时间去说明一切皞?/p>
不管游戲外掛程序是不是"合法"身份,但是它卻是具有一定的技術(shù)含量的,在這
些小小程序中使用了許多高端
技術(shù),如攔截Sock技術(shù)、攔截API技術(shù)、模擬鍵盤(pán)與鼠標技術(shù)、直接修改程序內存技術(shù)
等等。本文將對常見(jiàn)的游戲外掛
中使用的技術(shù)進(jìn)行全面剖析。
二、認識外掛
游戲外掛的歷史可以追溯到單機版游戲時(shí)代,只不過(guò)當時(shí)它使用了另一個(gè)更通俗易
懂的名字??游戲修改器。它可
以在游戲中追蹤鎖定游戲主人公的各項能力數值。這樣玩家在游戲中可以達到主角不掉
血、不耗費魔法、不消耗金錢(qián)
等目的。這樣降低了游戲的難度,使得玩家更容易通關(guān)。
隨著(zhù)網(wǎng)絡(luò )游戲的時(shí)代的來(lái)臨,游戲外掛在原有的功能之上進(jìn)行了新的發(fā)展,它變得
更加多種多樣,功能更加強大
,操作更加簡(jiǎn)單,以至有些游戲的外掛已經(jīng)成為一個(gè)體系,比如《石器時(shí)代》,外掛品
種達到了幾十種,自動(dòng)戰斗、
自動(dòng)行走、自動(dòng)練級、自動(dòng)補血、加速、不遇敵、原地遇敵、快速增加經(jīng)驗值、按鍵精
靈……幾乎無(wú)所不包。
游戲外掛的設計主要是針對于某個(gè)游戲開(kāi)發(fā)的,我們可以根據它針對的游戲的類(lèi)型
可大致可將外掛分為兩種大類(lèi)
。
一類(lèi)是將游戲中大量繁瑣和無(wú)聊的攻擊動(dòng)作使用外掛自動(dòng)完成,以幫助玩家輕松搞
定攻擊對象并可以快速的增加
玩家的經(jīng)驗值。比如在《龍族》中有一種工作的設定,玩家的工作等級越高,就可以駕
馭越好的裝備。但是增加工作
等級卻不是一件有趣的事情,毋寧說(shuō)是重復枯燥的機械勞動(dòng)。如果你想做法師用的杖,
首先需要做基本工作--?砍樹(shù)。
砍樹(shù)的方法很簡(jiǎn)單,在一棵大樹(shù)前不停的點(diǎn)鼠標就可以了,每10000的經(jīng)驗升一級。這
就意味著(zhù)玩家要在大樹(shù)前不停的
點(diǎn)擊鼠標,這種無(wú)聊的事情通過(guò)"按鍵精靈"就可以解決。外掛的"按鍵精靈"功能可以讓
玩家擺脫無(wú)趣的點(diǎn)擊鼠標的工
作。
另一類(lèi)是由外掛程序產(chǎn)生欺騙性的網(wǎng)絡(luò )游戲封包,并將這些封包發(fā)送到網(wǎng)絡(luò )游戲服
務(wù)器,利用這些虛假信息欺騙
服務(wù)器進(jìn)行游戲數值的修改,達到修改角色能力數值的目的。這類(lèi)外掛程序針對性很
強,一般在設計時(shí)都是針對某個(gè)
游戲某個(gè)版本來(lái)做的,因為每個(gè)網(wǎng)絡(luò )游戲服務(wù)器與客戶(hù)端交流的數據包各不相同,外掛
程序必須要對欺騙的網(wǎng)絡(luò )游戲
服務(wù)器的數據包進(jìn)行分析,才能產(chǎn)生服務(wù)器識別的數據包。這類(lèi)外掛程序也是當前最流
利的一類(lèi)游戲外掛程序。
另外,現在很多外掛程序功能強大,不僅實(shí)現了自動(dòng)動(dòng)作代理和封包功能,而且還
提供了對網(wǎng)絡(luò )游戲的客戶(hù)端程
序的數據進(jìn)行修改,以達到欺騙網(wǎng)絡(luò )游戲服務(wù)器的目的。我相信,隨著(zhù)網(wǎng)絡(luò )游戲商家的
反外掛技術(shù)的進(jìn)展,游戲外掛
將會(huì )產(chǎn)生更多更優(yōu)秀的技術(shù),讓我們期待著(zhù)看場(chǎng)技術(shù)大戰吧......
三、外掛技術(shù)綜述
可以將開(kāi)發(fā)游戲外掛程序的過(guò)程大體上劃分為兩個(gè)部分:
前期部分工作是對外掛的主體游戲進(jìn)行分析,不同類(lèi)型的外掛分析主體游戲的內容
也不相同。如外掛為上述談到
的外掛類(lèi)型中的第一類(lèi)時(shí),其分析過(guò)程常是針對游戲的場(chǎng)景中的攻擊對象的位置和分布
情況進(jìn)行分析,以實(shí)現外掛自
動(dòng)進(jìn)行攻擊以及位置移動(dòng)。如外掛為外掛類(lèi)型中的第二類(lèi)時(shí),其分析過(guò)程常是針對游戲
服務(wù)器與客戶(hù)端之間通訊包數
據的結構、內容以及加密算法的分析。因網(wǎng)絡(luò )游戲公司一般都不會(huì )公布其游戲產(chǎn)品的通
訊包數據的結構、內容和加密
算法的信息,所以對于開(kāi)發(fā)第二類(lèi)外掛成功的關(guān)鍵在于是否能正確分析游戲包數據的結
構、內容以及加密算法,雖然
可以使用一些工具輔助分析,但是這還是一種堅苦而復雜的工作。
后期部分工作主要是根據前期對游戲的分析結果,使用大量的程序開(kāi)發(fā)技術(shù)編寫(xiě)外
掛程序以實(shí)現對游戲的控制或
修改。如外掛程序為第一類(lèi)外掛時(shí),通常會(huì )使用到鼠標模擬技術(shù)來(lái)實(shí)現游戲角色的自動(dòng)
位置移動(dòng),使用鍵盤(pán)模擬技術(shù)
來(lái)實(shí)現游戲角色的自動(dòng)攻擊。如外掛程序為第二類(lèi)外掛時(shí),通常會(huì )使用到擋截Sock和擋
截API函數技術(shù),以擋截游戲服
務(wù)器傳來(lái)的網(wǎng)絡(luò )數據包并將數據包修改后封包后傳給游戲服務(wù)器。另外,還有許多外掛
使用對游戲客戶(hù)端程序內存數
據修改技術(shù)以及游戲加速技術(shù)。
本文主要是針對開(kāi)發(fā)游戲外掛程序后期使用的程序開(kāi)發(fā)技術(shù)進(jìn)行探討,重點(diǎn)介紹的
如下幾種在游戲外掛中常使用
的程序開(kāi)發(fā)技術(shù):
● 動(dòng)作模擬技術(shù):主要包括鍵盤(pán)模擬技術(shù)和鼠標模擬技術(shù)。
● 封包技術(shù):主要包括擋截Sock技術(shù)和擋截API技術(shù)。
四、動(dòng)作模擬技術(shù)
我們在前面介紹過(guò),幾乎所有的游戲都有大量繁瑣和無(wú)聊的攻擊動(dòng)作以增加玩家的
功力,還有那些數不完的迷宮
,這些好像已經(jīng)成為了角色游戲的代名詞?,F在,外掛可以幫助玩家從這些繁瑣而無(wú)聊
的工作中擺脫出來(lái),專(zhuān)注于游
戲情節的進(jìn)展。外掛程序為了實(shí)現自動(dòng)角色位置移動(dòng)和自動(dòng)攻擊等功能,需要使用到鍵
盤(pán)模擬技術(shù)和鼠標模擬技術(shù)。
下面我們將重點(diǎn)介紹這些技術(shù)并編寫(xiě)一個(gè)簡(jiǎn)單的實(shí)例幫助讀者理解動(dòng)作模擬技術(shù)的實(shí)現
過(guò)程。
1. 鼠標模擬技術(shù)
幾乎所有的游戲中都使用了鼠標來(lái)改變角色的位置和方向,玩家僅用一個(gè)小小的鼠
標,就可以使角色暢游天下。
那么,我們如何實(shí)現在沒(méi)有玩家的參與下角色也可以自動(dòng)行走呢。其實(shí)實(shí)現這個(gè)并不
難,僅僅幾個(gè)Windows API函數就
可以搞定,讓我們先來(lái)認識認識這些API函數。
(1) 模擬鼠標動(dòng)作API函數mouse_event,它可以實(shí)現模擬鼠標按下和放開(kāi)等動(dòng)作。
VOID mouse_event(
DWORD dwFlags, // 鼠標動(dòng)作標識。
DWORD dx, // 鼠標水平方向位置。
DWORD dy, // 鼠標垂直方向位置。
DWORD dwData, // 鼠標輪子轉動(dòng)的數量。
DWORD dwExtraInfo // 一個(gè)關(guān)聯(lián)鼠標動(dòng)作輔加信息。
);
其中,dwFlags表示了各種各樣的鼠標動(dòng)作和點(diǎn)擊活動(dòng),它的常用取值如下:
MOUSEEVENTF_MOVE 表示模擬鼠標移動(dòng)事件。
MOUSEEVENTF_LEFTDOWN 表示模擬按下鼠標左鍵。
MOUSEEVENTF_LEFTUP 表示模擬放開(kāi)鼠標左鍵。
MOUSEEVENTF_RIGHTDOWN 表示模擬按下鼠標右鍵。
MOUSEEVENTF_RIGHTUP 表示模擬放開(kāi)鼠標右鍵。
MOUSEEVENTF_MIDDLEDOWN 表示模擬按下鼠標中鍵。
MOUSEEVENTF_MIDDLEUP 表示模擬放開(kāi)鼠標中鍵。
(2)、設置和獲取當前鼠標位置的API函數。獲取當前鼠標位置使用GetCursorPos()
函數,設置當前鼠標位置使用
SetCursorPos()函數。
BOOL GetCursorPos(
LPPOINT lpPoint // 返回鼠標的當前位置。
);
BOOL SetCursorPos(
int X, // 鼠標的水平方向位置。
int Y //鼠標的垂直方向位置。
);
通常游戲角色的行走都是通過(guò)鼠標移動(dòng)至目的地,然后按一下鼠標的按鈕就搞定
了。下面我們使用上面介紹的API
函數來(lái)模擬角色行走過(guò)程。
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);//模擬放開(kāi)鼠標右鍵。
2. 鍵盤(pán)模擬技術(shù)
在很多游戲中,不僅提供了鼠標的操作,而且還提供了鍵盤(pán)的操作,在對攻擊對象
進(jìn)行攻擊時(shí)還可以使用快捷鍵
。為了使這些攻擊過(guò)程能夠自動(dòng)進(jìn)行,外掛程序需要使用鍵盤(pán)模擬技術(shù)。像鼠標模擬技
術(shù)一樣,Windows API也提供了
一系列API函數來(lái)完成對鍵盤(pán)動(dòng)作的模擬。
模擬鍵盤(pán)動(dòng)作API函數keydb_event,它可以模擬對鍵盤(pán)上的某個(gè)或某些鍵進(jìn)行按下
或放開(kāi)的動(dòng)作。
VOID keybd_event(
BYTE bVk, // 虛擬鍵值。
BYTE bScan, // 硬件掃描碼。
DWORD dwFlags, // 動(dòng)作標識。
DWORD dwExtraInfo // 與鍵盤(pán)動(dòng)作關(guān)聯(lián)的輔加信息。
);
其中,bVk表示虛擬鍵值,其實(shí)它是一個(gè)BYTE類(lèi)型值的宏,其取值范圍為1-254。有
關(guān)虛擬鍵值表請在MSDN上使用
關(guān)鍵字"Virtual-Key Codes"查找相關(guān)資料。bScan表示當鍵盤(pán)上某鍵被按下和放開(kāi)
時(shí),鍵盤(pán)系統硬件產(chǎn)生的掃描碼
,我們可以MapVirtualKey()函數在虛擬鍵值與掃描碼之間進(jìn)行轉換。dwFlags表示各種
各樣的鍵盤(pán)動(dòng)作,它有兩種取
值:KEYEVENTF_EXTENDEDKEY和KEYEVENTF_KEYUP。
下面我們使用一段代碼實(shí)現在游戲中按下Shift+R快捷鍵對攻擊對象進(jìn)行攻擊。
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);//放開(kāi)R鍵。
keybd_event(VK_CONTROL,MapVirtualKey(VK_CONTROL,0),
KEYEVENTF_KEYUP,0);//放開(kāi)CTRL鍵。
3. 激活外掛
上面介紹的鼠標和鍵盤(pán)模擬技術(shù)實(shí)現了對游戲角色的動(dòng)作部分的模擬,但要想外掛
能工作于游戲之上,還需要將
其與游戲的場(chǎng)景窗口聯(lián)系起來(lái)或者使用一個(gè)激活鍵,就象按鍵精靈的那個(gè)激活鍵一樣。
我們可以用GetWindow函數來(lái)枚
舉窗口,也可以用Findwindow函數來(lái)查找特定的窗口。另外還有一個(gè)FindWindowEx函數
可以找到窗口的子窗口,當游
戲切換場(chǎng)景的時(shí)候我們可以用FindWindowEx來(lái)確定一些當前窗口的特征,從而判斷是否
還在這個(gè)場(chǎng)景,方法很多了,
比如可以GetWindowInfo來(lái)確定一些東西,比如當查找不到某個(gè)按鈕的時(shí)候就說(shuō)明游戲
場(chǎng)景已經(jīng)切換了等等辦法。當使
用激活鍵進(jìn)行關(guān)聯(lián),需要使用Hook技術(shù)開(kāi)發(fā)一個(gè)全局鍵盤(pán)鉤子,在這里就不具體介紹全
局鉤子的開(kāi)發(fā)過(guò)程了,在后面
的實(shí)例中我們將會(huì )使用到全局鉤子,到時(shí)將學(xué)習到全局鉤子的相關(guān)知識。
4. 實(shí)例實(shí)現
通過(guò)上面的學(xué)習,我們已經(jīng)基本具備了編寫(xiě)動(dòng)作式游戲外掛的能力了。下面我們將
創(chuàng )建一個(gè)畫(huà)筆程序外掛,它實(shí)
現自動(dòng)移動(dòng)畫(huà)筆字光標的位置并寫(xiě)下一個(gè)紅色的"R"字。以這個(gè)實(shí)例為基礎,加入相
應的游戲動(dòng)作規則,就可以實(shí)現
一個(gè)完整的游戲外掛。這里作者不想使用某個(gè)游戲作為例子來(lái)開(kāi)發(fā)外掛(因沒(méi)有游戲商
家的授權?。。?,如讀者感興
趣的話(huà)可以找一個(gè)游戲試試,最好僅做測試技術(shù)用。
首先,我們需要編寫(xiě)一個(gè)全局鉤子,使用它來(lái)激活外掛,激活鍵為F10。創(chuàng )建全局
鉤子步驟如下:
(1).選擇MFC AppWizard(DLL)創(chuàng )建項目ActiveKey,并選擇MFC Extension DLL
(共享MFC拷貝)類(lèi)型。
(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).在A(yíng)ctiveKey.cpp文件中加入聲明"#include ActiveKey.h"。
(4).在A(yíng)ctiveKey.cpp文件中加入共享數據段,代碼如下:
//Shared data section
#pragma data_seg("sharedata")
HHOOK glhHook=NULL; //鉤子句柄。
HINSTANCE glhInstance=NULL; //DLL實(shí)例句柄。
#pragma data_seg()
(5).在A(yíng)ctiveKey.def文件中設置共享數據段屬性,代碼如下:
SETCTIONS
shareddata READ WRITE SHARED
(6).在A(yíng)ctiveKey.cpp文件中加入CkeyHook類(lèi)的實(shí)現代碼和鉤子函數代碼:
//鍵盤(pán)鉤子處理函數。
extern "C" LRESULT WINAPI KeyboardProc(int nCode,WPARAM wParam,LPARAM
lParam)
{
if( nCode >= 0 )
{
if( wParam == 0X79 )//當按下F10鍵時(shí),激活外掛。
{
//外掛實(shí)現代碼。
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);//模擬放開(kāi)鼠標左鍵。
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);//放開(kāi)R鍵。
keybd_event(VK_SHIFT,MapVirtualKey(VK_SHIFT,0),KEYEVENTF_KEYUP,0);//放開(kāi)
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);//設置鍵
盤(pán)鉤子。
return glhHook;
}
//卸載全局鉤子。
BOOL CKeyHook::Stop()
{
BOOL bResult = TRUE;
if( glhHook )
bResult = UnhookWindowsHookEx(glhHook);//卸載鍵盤(pán)鉤子。
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加入動(dòng)態(tài)MFC類(lèi)庫中
glhInstance = hInstance;
//插入保存DLL實(shí)例句柄
}
else if (dwReason == DLL_PROCESS_DETACH)
{
TRACE0("NotePadHOOK.DLL Terminating!\n");
//終止這個(gè)鏈接庫前調用它
AfxTermExtensionModule(ActiveKeyDLL);
}
return 1;
}
(8).編譯項目ActiveKey,生成ActiveKey.DLL和ActiveKey.lib。
接著(zhù),我們還需要創(chuàng )建一個(gè)外殼程序將全局鉤子安裝了Windows系統中,這個(gè)外殼
程序編寫(xiě)步驟如下:
(1).創(chuàng )建一個(gè)對話(huà)框模式的應用程序,項目名為Simulate。
(2).在主對話(huà)框中加入一個(gè)按鈕,使用ClassWizard為其創(chuàng )建CLICK事件。
(3).將ActiveKey項目Debug目錄下的ActiveKey.DLL和ActiveKey.lib拷貝到
Simulate項目目錄下。
(4).從"工程"菜單中選擇"設置",彈出Project Setting對話(huà)框,選擇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).啟動(dòng)畫(huà)筆程序,選擇文本工具并將筆的顏色設置為紅色,將鼠標放在任意位置
后,按F10鍵,畫(huà)筆程序自動(dòng)移
動(dòng)鼠標并寫(xiě)下一個(gè)紅色的大寫(xiě)R。圖一展示了按F10鍵前的畫(huà)筆程序的狀態(tài),圖二展示了
按F10鍵后的畫(huà)筆程序的狀態(tài)。
圖一:按F10前狀態(tài)(001.jpg)
圖二:按F10后狀態(tài)(002.jpg)
五、封包技術(shù)
通過(guò)對動(dòng)作模擬技術(shù)的介紹,我們對游戲外掛有了一定程度上的認識,也學(xué)會(huì )了使
用動(dòng)作模擬技術(shù)來(lái)實(shí)現簡(jiǎn)單的
動(dòng)作模擬型游戲外掛的制作。這種動(dòng)作模擬型游戲外掛有一定的局限性,它僅僅只能解
決使用計算機代替人力完成那
么有規律、繁瑣而無(wú)聊的游戲動(dòng)作。但是,隨著(zhù)網(wǎng)絡(luò )游戲的盛行和復雜度的增加,很多
游戲要求將客戶(hù)端動(dòng)作信息及
時(shí)反饋回服務(wù)器,通過(guò)服務(wù)器對這些動(dòng)作信息進(jìn)行有效認證后,再向客戶(hù)端發(fā)送下一步
游戲動(dòng)作信息,這樣動(dòng)作模擬
技術(shù)將失去原有的效應。為了更好地"外掛"這些游戲,游戲外掛程序也進(jìn)行了升級換
代,它們將以前針對游戲用戶(hù)
界面層的模擬推進(jìn)到數據通訊層,通過(guò)封包技術(shù)在客戶(hù)端擋截游戲服務(wù)器發(fā)送來(lái)的游戲
控制數據包,分析數據包并修
改數據包;同時(shí)還需按照游戲數據包結構創(chuàng )建數據包,再模擬客戶(hù)端發(fā)送給游戲服務(wù)
器,這個(gè)過(guò)程其實(shí)就是一個(gè)封包
的過(guò)程。
封包的技術(shù)是實(shí)現第二類(lèi)游戲外掛的最核心的技術(shù)。封包技術(shù)涉及的知識很廣泛,
實(shí)現方法也很多,如擋截
WinSock、擋截API函數、擋截消息、VxD驅動(dòng)程序等。在此我們也不可能在此文中將所
有的封包技術(shù)都進(jìn)行詳細介紹,
故選擇兩種在游戲外掛程序中最常用的兩種方法:擋截WinSock和擋截API函數。
1. 擋截WinSock
眾所周知,Winsock是Windows網(wǎng)絡(luò )編程接口,它工作于Windows應用層,它提供與
底層傳輸協(xié)議無(wú)關(guān)的高層數據傳
輸編程接口。在Windows系統中,使用WinSock接口為應用程序提供基于TCP/IP協(xié)議的網(wǎng)
絡(luò )訪(fǎng)問(wèn)服務(wù),這些服務(wù)是由
Wsock32.DLL動(dòng)態(tài)鏈接庫提供的函數庫來(lái)完成的。
由上說(shuō)明可知,任何Windows基于TCP/IP的應用程序都必須通過(guò)WinSock接口訪(fǎng)問(wèn)網(wǎng)
絡(luò ),當然網(wǎng)絡(luò )游戲程序也不例
外。由此我們可以想象一下,如果我們可以控制WinSock接口的話(huà),那么控制游戲客戶(hù)
端程序與服務(wù)器之間的數據包也
將易如反掌。按著(zhù)這個(gè)思路,下面的工作就是如何完成控制WinSock接口了。由上面的
介紹可知,WinSock接口其實(shí)是
由一個(gè)動(dòng)態(tài)鏈接庫提供的一系列函數,由這些函數實(shí)現對網(wǎng)絡(luò )的訪(fǎng)問(wèn)。有了這層的認
識,問(wèn)題就好辦多了,我們可以
制作一個(gè)類(lèi)似的動(dòng)態(tài)鏈接庫來(lái)代替原WinSock接口庫,在其中實(shí)現WinSock32.dll中實(shí)現
的所有函數,并保證所有函數
的參數個(gè)數和順序、返回值類(lèi)型都應與原庫相同。在這個(gè)自制作的動(dòng)態(tài)庫中,可以對我
們感興趣的函數(如發(fā)送、接
收等函數)進(jìn)行擋截,放入外掛控制代碼,最后還繼續調用原WinSock庫中提供的相應
功能函數,這樣就可以實(shí)現對網(wǎng)
絡(luò )數據包的擋截、修改和發(fā)送等封包功能。
下面重點(diǎn)介紹創(chuàng )建擋截WinSock外掛程序的基本步驟:
(1) 創(chuàng )建DLL項目,選擇Win32 Dynamic-Link Library,再選擇An empty DLL
project。
(2) 新建文件wsock32.h,按如下步驟輸入代碼:
① 加入相關(guān)變量聲明:
HMODULE hModule=NULL; //模塊句柄
char buffer[1000]; //緩沖區
FARPROC proc; //函數入口指針
② 定義指向原WinSock庫中的所有函數地址的指針變量,因WinSock庫共提供70多
個(gè)函數,限于篇幅,在此就只選
擇幾個(gè)常用的函數列出,有關(guān)這些庫函數的說(shuō)明可參考MSDN相關(guān)內容。
//定義指向原WinSock庫函數地址的指針變量。
SOCKET (__stdcall *socket1)(int ,int,int);//創(chuàng )建Sock函數。
int (__stdcall *WSAStartup1)(WORD,LPWSADATA);//初始化WinSock庫函數。
int (__stdcall *WSACleanup1)();//清除WinSock庫函數。
int (__stdcall *recv1)(SOCKET ,char FAR * ,int ,int );//接收數據函數。
int (__stdcall *send1)(SOCKET ,const char * ,int ,int);//發(fā)送數據函
數。
int (__stdcall *connect1)(SOCKET,const struct sockaddr *,int);//創(chuàng )建連
接函數。
int (__stdcall *bind1)(SOCKET ,const struct sockaddr *,int );//綁定函
數。
......其它函數地址指針的定義略。
(3) 新建wsock32.cpp文件,按如下步驟輸入代碼:
① 加入相關(guān)頭文件聲明:
#include
#include
#include "wsock32.h"
② 添加DllMain函數,在此函數中首先需要加載原WinSock庫,并獲取此庫中所有
函數的地址。代碼如下:
BOOL WINAPI DllMain (HANDLE hInst,ULONG ul_reason_for_call,LPVOID
lpReserved)
{
if(hModule==NULL){
//加載原WinSock庫,原WinSock庫已復制為wsock32.001。
hModule=LoadLibrary("wsock32.001");
}
else return 1;
//獲取原WinSock庫中的所有函數的地址并保存,下面僅列出部分代碼。
if(hModule!=NULL){
//獲取原WinSock庫初始化函數的地址,并保存到WSAStartup1中。
proc=GetProcAddress(hModule,"WSAStartup");
WSAStartup1=(int (_stdcall *)(WORD,LPWSADATA))proc;
//獲取原WinSock庫消除函數的地址,并保存到WSACleanup1中。
proc=GetProcAddress(hModule i,"WSACleanup");
WSACleanup1=(int (_stdcall *)())proc;
//獲取原創(chuàng )建Sock函數的地址,并保存到socket1中。
proc=GetProcAddress(hModule,"socket");
socket1=(SOCKET (_stdcall *)(int ,int,int))proc;
//獲取原創(chuàng )建連接函數的地址,并保存到connect1中。
proc=GetProcAddress(hModule,"connect");
connect1=(int (_stdcall *)(SOCKET ,const struct sockaddr
*,int ))proc;
//獲取原發(fā)送函數的地址,并保存到send1中。
proc=GetProcAddress(hModule,"send");
send1=(int (_stdcall *)(SOCKET ,const char * ,int ,int ))proc;
//獲取原接收函數的地址,并保存到recv1中。
proc=GetProcAddress(hModule,"recv");
recv1=(int (_stdcall *)(SOCKET ,char FAR * ,int ,int ))proc;
......其它獲取函數地址代碼略。
}
else return 0;
return 1;
}
③ 定義庫輸出函數,在此可以對我們感興趣的函數中添加外掛控制代碼,在所有
的輸出函數的最后一步都調用原
WinSock庫的同名函數。部分輸出函數定義代碼如下:
//庫輸出函數定義。
//WinSock初始化函數。
int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA
lpWSAData)
{
//調用原WinSock庫初始化函數
return WSAStartup1(wVersionRequired,lpWSAData);
}
//WinSock結束清除函數。
int PASCAL FAR WSACleanup(void)
{
return WSACleanup1(); //調用原WinSock庫結束清除函數。
}
//創(chuàng )建Socket函數。
SOCKET PASCAL FAR socket (int af, int type, int protocol)
{
//調用原WinSock庫創(chuàng )建Socket函數。
return socket1(af,type,protocol);
}
//發(fā)送數據包函數
int PASCAL FAR send(SOCKET s,const char * buf,int len,int flags)
{
//在此可以對發(fā)送的緩沖buf的內容進(jìn)行修改,以實(shí)現欺騙服務(wù)器。
外掛代碼......
//調用原WinSock庫發(fā)送數據包函數。
return send1(s,buf,len,flags);
}
//接收數據包函數。
int PASCAL FAR recv(SOCKET s, char FAR * buf, int len, int flags)
{
//在此可以擋截到服務(wù)器端發(fā)送到客戶(hù)端的數據包,先將其保存到buffer中。
strcpy(buffer,buf);
//對buffer數據包數據進(jìn)行分析后,對其按照玩家的指令進(jìn)行相關(guān)修改。
外掛代碼......
//最后調用原WinSock中的接收數據包函數。
return recv1(s, buffer, len, flags);
}
.......其它函數定義代碼略。
(4)、新建wsock32.def配置文件,在其中加入所有庫輸出函數的聲明,部分聲明代
碼如下:
LIBRARY "wsock32"
EXPORTS
WSAStartup @1
WSACleanup @2
recv @3
send @4
socket @5
bind @6
closesocket @7
connect @8
......其它輸出函數聲明代碼略。
)、從"工程"菜單中選擇"設置",彈出Project Setting對話(huà)框,選擇Link標
簽,在"對象/庫模塊"中輸入
Ws2_32.lib。
(6)、編譯項目,產(chǎn)生wsock32.dll庫文件。
(7)、將系統目錄下原wsock32.dll庫文件拷貝到被外掛程序的目錄下,并將其改名
為wsock.001;再將上面產(chǎn)生的
wsock32.dll文件同獎吹獎煌夤頁(yè)絳虻哪柯枷?。謪Q縷舳蝸煩絳潁聳庇蝸煩絳?br> 將先加載我們自己制作的
wsock32.dll文件,再通過(guò)該庫文件間接調用原WinSock接口函數來(lái)實(shí)現訪(fǎng)問(wèn)網(wǎng)絡(luò )。上面
我們僅僅介紹了擋載WinSock的
實(shí)現過(guò)程,至于如何加入外掛控制代碼,還需要外掛開(kāi)發(fā)人員對游戲數據包結構、內
容、加密算法等方面的仔細分析
(這個(gè)過(guò)程將是一個(gè)艱辛的過(guò)程),再生成外掛控制代碼。關(guān)于數據包分析方法和技
巧,不是本文講解的范圍,如您
感興趣可以到網(wǎng)上查查相關(guān)資料。
2.6) 在A(yíng)ctiveKey.cpp中加入頭文件聲明 "#include "wsock32.h"。 從"工程"菜
單中選擇"設置",彈出
Project Setting對話(huà)框,選擇Link標簽,在"對象/庫模塊"中輸入Ws2_32..lib。
(7) 重新編譯ActiveKey項目,產(chǎn)生ActiveKey.dll文件,將其拷貝到Simulate.exe
目錄下。運行Simulate.exe并
啟動(dòng)全局鉤子。激活任意應用程序,按F11鍵后,運行此程序中可能調用MessageBoxA函
數的操作,看看信息框是不是
有所變化。同樣,如此程序正在接收網(wǎng)絡(luò )數據包,就可以實(shí)現封包功能了。
六、結束語(yǔ)
除了以上介紹的幾種游戲外掛程序常用的技術(shù)以外,在一些外掛程序中還使用了游戲數據修改技術(shù)、游戲加速技術(shù)等。在這篇文章里,就不逐一介紹

