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