青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

elva

網(wǎng)絡(luò)游戲外掛編寫原理

一、 前言 

  所謂游戲外掛,其實(shí)是一種游戲外輔程序,它可以協(xié)助玩家自動(dòng)產(chǎn)生游戲動(dòng)作、修改游戲網(wǎng)絡(luò)數(shù)據(jù)包以及修改游戲內(nèi)存數(shù)據(jù)等, 以實(shí)現(xiàn)玩家用最少的時(shí)間和金錢去完成功力升級(jí)和過關(guān)斬將。雖然,現(xiàn)在對(duì)游戲外掛程序的“合法”身份眾說紛紜,在這里我不想對(duì)此發(fā)表任何個(gè)人意見,讓時(shí)間去 說明一切吧。

  不管游戲外掛程序是不是“合法”身份,但是它卻是具有一定的技術(shù)含量的,在這
些小小程序中使用了許多高端技術(shù),如攔截Sock技術(shù)、攔截API技術(shù)、模擬鍵盤與鼠標(biāo)技術(shù)、直接修改程序內(nèi)存技術(shù)等等。本文將對(duì)常見的游戲外掛中使用的技術(shù)進(jìn)行全面剖析。

  二、認(rèn)識(shí)外掛

  游戲外掛的歷史可以追溯到單機(jī)版游戲時(shí)代,只不過當(dāng)時(shí)它使用了另一個(gè)更通俗易
懂的名字??游戲修改器。它可以在游戲中追蹤鎖定游戲主人公的各項(xiàng)能力數(shù)值。這樣玩家在游戲中可以達(dá)到主角不掉血、不耗費(fèi)魔法、不消耗金錢等目的。這樣降低了游戲的難度,使得玩家更容易通關(guān)。

  隨著網(wǎng)絡(luò)游戲的時(shí)代的來臨,游戲外掛在原有的功能之上進(jìn)行了新的發(fā)展,它變得
更加多種多樣,功能更加強(qiáng)大,操作更加簡單,以至有些游戲的外掛已經(jīng)成為一個(gè)體系,比如《石器時(shí)代》,外掛品種達(dá)到了幾十種,自動(dòng)戰(zhàn)斗、自動(dòng)行走、自動(dòng)練級(jí)、自動(dòng)補(bǔ)血、加速、不遇敵、原地遇敵、快速增加經(jīng)驗(yàn)值、按鍵精靈……幾乎無所不包。

  游戲外掛的設(shè)計(jì)主要是針對(duì)于某個(gè)游戲開發(fā)的,我們可以根據(jù)它針對(duì)的游戲的類型
可大致可將外掛分為兩種大類。

  一類是將游戲中大量繁瑣和無聊的攻擊動(dòng)作使用外掛自動(dòng)完成,以幫助玩家輕松搞
定 攻擊對(duì)象并可以快速的增加玩家的經(jīng)驗(yàn)值。比如在《龍族》中有一種工作的設(shè)定,玩家的工作等級(jí)越高,就可以駕馭越好的裝備。但是增加工作等級(jí)卻不是一件有趣 的事情,毋寧說是重復(fù)枯燥的機(jī)械勞動(dòng)。如果你想做法師用的杖,首先需要做基本工作--?砍樹。砍樹的方法很簡單,在一棵大樹前不停的點(diǎn)鼠標(biāo)就可以了,每 10000的經(jīng)驗(yàn)升一級(jí)。這就意味著玩家要在大樹前不停的點(diǎn)擊鼠標(biāo),這種無聊的事情通過"按鍵精靈"就可以解決。外掛的"按鍵精靈"功能可以讓玩家擺脫無 趣的點(diǎn)擊鼠標(biāo)的工作。

  另一類是由外掛程序產(chǎn)生欺騙性的網(wǎng)絡(luò)游戲封包,并將這些封包發(fā)送到網(wǎng)絡(luò)游戲服務(wù)器,利用這些虛假信息欺騙服務(wù)器進(jìn)行游戲數(shù)值的修改,達(dá)到修改角色能力數(shù)值的目的。這類外掛程序針對(duì)性很
強(qiáng),一般在設(shè)計(jì)時(shí)都是針對(duì)某個(gè)游戲某個(gè)版本來做的,因?yàn)槊總€(gè)網(wǎng)絡(luò)游戲服務(wù)器與客戶端交流的數(shù)據(jù)包各不相同,外掛程序必須要對(duì)欺騙的網(wǎng)絡(luò)游戲服務(wù)器的數(shù)據(jù)包進(jìn)行分析,才能產(chǎn)生服務(wù)器識(shí)別的數(shù)據(jù)包。這類外掛程序也是當(dāng)前最流利的一類游戲外掛程序。

  另外,現(xiàn)在很多外掛程序功能強(qiáng)大,不僅實(shí)現(xiàn)了自動(dòng)動(dòng)作代理和封包功能,而且還
提供了對(duì)網(wǎng)絡(luò)游戲的客戶端程序的數(shù)據(jù)進(jìn)行修改,以達(dá)到欺騙網(wǎng)絡(luò)游戲服務(wù)器的目的。我相信,隨著網(wǎng)絡(luò)游戲商家的反外掛技術(shù)的進(jìn)展,游戲外掛將會(huì)產(chǎn)生更多更優(yōu)秀的技術(shù),讓我們期待著看場技術(shù)大戰(zhàn)吧......

  三、外掛技術(shù)綜述

  可以將開發(fā)游戲外掛程序的過程大體上劃分為兩個(gè)部分:

  前期部分工作是對(duì)外掛的主體游戲進(jìn)行分析,不同類型的外掛分析主體游戲的內(nèi)容也不相同。如外掛為上述談到的外掛類型中的第一類時(shí),其分析過程常是針對(duì)游戲的場景中的攻擊對(duì)象的位置和分布
情 況進(jìn)行分析,以實(shí)現(xiàn)外掛自動(dòng)進(jìn)行攻擊以及位置移動(dòng)。如外掛為外掛類型中的第二類時(shí),其分析過程常是針對(duì)游戲服務(wù)器與客戶端之間通訊包數(shù)據(jù)的結(jié)構(gòu)、內(nèi)容以及 加密算法的分析。因網(wǎng)絡(luò)游戲公司一般都不會(huì)公布其游戲產(chǎn)品的通訊包數(shù)據(jù)的結(jié)構(gòu)、內(nèi)容和加密算法的信息,所以對(duì)于開發(fā)第二類外掛成功的關(guān)鍵在于是否能正確分 析游戲包數(shù)據(jù)的結(jié)構(gòu)、內(nèi)容以及加密算法,雖然可以使用一些工具輔助分析,但是這還是一種堅(jiān)苦而復(fù)雜的工作。

  后期部分工作主要是根據(jù)前期對(duì)游戲的分析結(jié)果,使用大量的程序開發(fā)技術(shù)編寫外
掛 程序以實(shí)現(xiàn)對(duì)游戲的控制或修改。如外掛程序?yàn)榈谝活愅鈷鞎r(shí),通常會(huì)使用到鼠標(biāo)模擬技術(shù)來實(shí)現(xiàn)游戲角色的自動(dòng)位置移動(dòng),使用鍵盤模擬技術(shù)來實(shí)現(xiàn)游戲角色的自 動(dòng)攻擊。如外掛程序?yàn)榈诙愅鈷鞎r(shí),通常會(huì)使用到擋截Sock和擋截API函數(shù)技術(shù),以擋截游戲服務(wù)器傳來的網(wǎng)絡(luò)數(shù)據(jù)包并將數(shù)據(jù)包修改后封包后傳給游戲服 務(wù)器。另外,還有許多外掛使用對(duì)游戲客戶端程序內(nèi)存數(shù)據(jù)修改技術(shù)以及游戲加速技術(shù)。

  本文主要是針對(duì)開發(fā)游戲外掛程序后期使用的程序開發(fā)技術(shù)進(jìn)行探討,重點(diǎn)介紹的
如下幾種在游戲外掛中常使用的程序開發(fā)技術(shù):

  ● 動(dòng)作模擬技術(shù):主要包括鍵盤模擬技術(shù)和鼠標(biāo)模擬技術(shù)。

  ● 封包技術(shù):主要包括擋截Sock技術(shù)和擋截API技術(shù)。 
 
四、動(dòng)作模擬技術(shù)   

我們?cè)谇懊娼榻B過,幾乎所有的游戲都有大量繁瑣和無聊的攻擊動(dòng)作以增加玩家的功力,還有那些數(shù)不完的迷宮,這些好 像已經(jīng)成為了角色游戲的代名詞。現(xiàn)在,外掛可以幫助玩家從這些繁瑣而無聊的工作中擺脫出來,專注于游戲情節(jié)的進(jìn)展。外掛程序?yàn)榱藢?shí)現(xiàn)自動(dòng)角色位置移動(dòng)和自 動(dòng)攻擊等功能,需要使用到鍵盤模擬技術(shù)和鼠標(biāo)模擬技術(shù)。

下面我們將重點(diǎn)介紹這些技術(shù)并編寫一個(gè)簡單的實(shí)例幫助讀者理解動(dòng)作模擬技術(shù)的實(shí)現(xiàn)過程。

  1. 鼠標(biāo)模擬技術(shù)
  
  幾乎所有的游戲中都使用了鼠標(biāo)來改變角色的位置和方向,玩家僅用一個(gè)小小的鼠標(biāo),就可以使角色暢游天下。

那么,我們?nèi)绾螌?shí)現(xiàn)在沒有玩家的參與下角色也可以自動(dòng)行走呢。其實(shí)實(shí)現(xiàn)這個(gè)并不難,僅僅幾個(gè)Windows API函數(shù)就可以搞定,讓我們先來認(rèn)識(shí)認(rèn)識(shí)這些API函數(shù)。

  (1) 模擬鼠標(biāo)動(dòng)作API函數(shù)mouse_event,它可以實(shí)現(xiàn)模擬鼠標(biāo)按下和放開等動(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 表示模擬放開鼠標(biāo)左鍵。

   MOUSEEVENTF_RIGHTDOWN 表示模擬按下鼠標(biāo)右鍵。

   MOUSEEVENTF_RIGHTUP 表示模擬放開鼠標(biāo)右鍵。

   MOUSEEVENTF_MIDDLEDOWN 表示模擬按下鼠標(biāo)中鍵。

   MOUSEEVENTF_MIDDLEUP 表示模擬放開鼠標(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)的垂直方向位置。
    );

  通常游戲角色的行走都是通過鼠標(biāo)移動(dòng)至目的地,然后按一下鼠標(biāo)的按鈕就搞定了。下面我們使用上面介紹的API

函數(shù)來模擬角色行走過程。

   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);//模擬放開鼠標(biāo)右鍵。

  2. 鍵盤模擬技術(shù)

  在很多游戲中,不僅提供了鼠標(biāo)的操作,而且還提供了鍵盤的操作,在對(duì)攻擊對(duì)象進(jìn)行攻擊時(shí)還可以使用快捷鍵。為了使這些攻擊過程能夠自動(dòng)進(jìn)行,外掛程序需要使用鍵盤模擬技術(shù)。像鼠標(biāo)模擬技術(shù)一樣,Windows API也提供了一系列API函數(shù)來完成對(duì)鍵盤動(dòng)作的模擬。

  模擬鍵盤動(dòng)作API函數(shù)keydb_event,它可以模擬對(duì)鍵盤上的某個(gè)或某些鍵進(jìn)行按下或放開的動(dòng)作。

   VOID keybd_event(
     BYTE bVk, // 虛擬鍵值。
     BYTE bScan, // 硬件掃描碼。
     DWORD dwFlags, // 動(dòng)作標(biāo)識(shí)。
     DWORD dwExtraInfo // 與鍵盤動(dòng)作關(guān)聯(lián)的輔加信息。
   );

   其中,bVk表示虛擬鍵值,其實(shí)它是一個(gè)BYTE類型值的宏,其取值范圍為1-254。有關(guān)虛擬鍵值表請(qǐng)?jiān)贛SDN上使用關(guān)鍵字“Virtual- Key Codes”查找相關(guān)資料。bScan表示當(dāng)鍵盤上某鍵被按下和放開時(shí),鍵盤系統(tǒng)硬件產(chǎn)生的掃描碼,我們可以MapVirtualKey()函數(shù) 在虛擬鍵值與掃描碼之間進(jìn)行轉(zhuǎn)換。dwFlags表示各種各樣的鍵盤動(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);//放開R鍵。
   keybd_event(VK_CONTROL,MapVirtualKey(VK_CONTROL,0),
   KEYEVENTF_KEYUP,0);//放開CTRL鍵。

  3. 激活外掛

  上面介紹的鼠標(biāo)和鍵盤模擬技術(shù)實(shí)現(xiàn)了對(duì)游戲角色的動(dòng)作部分的模擬,但要想外掛能工作于游戲之上,還需要將其與游戲的場景窗口聯(lián)系起來或者使用一個(gè)激活鍵,就象按鍵精靈的那個(gè)激活鍵一樣。
我們可以用GetWindow函數(shù)來枚舉窗口,也可以用Findwindow函數(shù)來查找特定的窗口。另外還有一個(gè)FindWindowEx函數(shù)
可以找到窗口的子窗口,當(dāng)游戲切換場景的時(shí)候我們可以用FindWindowEx來確定一些當(dāng)前窗口的特征,從而判斷是否
還在這個(gè)場景,方法很多了,比如可以GetWindowInfo來確定一些東西,比如當(dāng)查找不到某個(gè)按鈕的時(shí)候就說明游戲
場景已經(jīng)切換了等等辦法。當(dāng)使用激活鍵進(jìn)行關(guān)聯(lián),需要使用Hook技術(shù)開發(fā)一個(gè)全局鍵盤鉤子,在這里就不具體介紹全
局鉤子的開發(fā)過程了,在后面的實(shí)例中我們將會(huì)使用到全局鉤子,到時(shí)將學(xué)習(xí)到全局鉤子的相關(guān)知識(shí)。

4. 實(shí)例實(shí)現(xiàn)

  通過上面的學(xué)習(xí),我們已經(jīng)基本具備了編寫動(dòng)作式游戲外掛的能力了。下面我們將創(chuàng)建一個(gè)畫筆程序外掛,它實(shí)

現(xiàn)自動(dòng)移動(dòng)畫筆字光標(biāo)的位置并寫下一個(gè)紅色的“R”字。以這個(gè)實(shí)例為基礎(chǔ),加入相應(yīng)的游戲動(dòng)作規(guī)則,就可以實(shí)現(xiàn)

一個(gè)完整的游戲外掛。這里作者不想使用某個(gè)游戲作為例子來開發(fā)外掛(因沒有游戲商家的授權(quán)啊!),如讀者感興

趣的話可以找一個(gè)游戲試試,最好僅做測(cè)試技術(shù)用。

  首先,我們需要編寫一個(gè)全局鉤子,使用它來激活外掛,激活鍵為F10。創(chuàng)建全局鉤子步驟如下:

  (1).選擇MFC AppWizard(DLL)創(chuàng)建項(xiàng)目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文件中加入共享數(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類的實(shí)現(xiàn)代碼和鉤子函數(shù)代碼:

   //鍵盤鉤子處理函數(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);//模擬放開鼠標(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);//放開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);//設(shè)置鍵盤鉤子。
return glhHook;
}
   //卸載全局鉤子。
   BOOL CKeyHook::Stop()
   {
   BOOL bResult = TRUE;
 if( glhHook )
   bResult = UnhookWindowsHookEx(glhHook);//卸載鍵盤鉤子。
   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類庫中
  glhInstance = hInstance;
  //插入保存DLL實(shí)例句柄
}
else if (dwReason == DLL_PROCESS_DETACH)
{
  TRACE0("NotePadHOOK.DLL Terminating!\n");
  //終止這個(gè)鏈接庫前調(diào)用它
  AfxTermExtensionModule(ActiveKeyDLL);
}
return 1;
   }

  (8).編譯項(xiàng)目ActiveKey,生成ActiveKey.DLL和ActiveKey.lib。

  接著,我們還需要?jiǎng)?chuàng)建一個(gè)外殼程序?qū)⑷帚^子安裝了Windows系統(tǒng)中,這個(gè)外殼序編寫步驟如下:

  (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ì)象/庫模塊”中輸入

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)畫筆程序,選擇文本工具并將筆的顏色設(shè)置為紅色,將鼠標(biāo)放在任意位置后,按F10鍵,畫筆程序自動(dòng)移

動(dòng)鼠標(biāo)并寫下一個(gè)紅色的大寫R。圖一展示了按F10鍵前的畫筆程序的狀態(tài),圖二展示了按F10鍵后的畫筆程序的狀態(tài)。


圖一:按F10前狀態(tài)(001.jpg)


圖二:按F10后狀態(tài)(002.jpg)


五、封包技術(shù)

  通過對(duì)動(dòng)作模擬技術(shù)的介紹,我們對(duì)游戲外掛有了一定程度上的認(rèn)識(shí),也學(xué)會(huì)了使用動(dòng)作模擬技術(shù)來實(shí)現(xiàn)簡單的

動(dòng)作模擬型游戲外掛的制作。這種動(dòng)作模擬型游戲外掛有一定的局限性,它僅僅只能解決使用計(jì)算機(jī)代替人力完成那

么有規(guī)律、繁瑣而無聊的游戲動(dòng)作。但是,隨著網(wǎng)絡(luò)游戲的盛行和復(fù)雜度的增加,很多游戲要求將客戶端動(dòng)作信息及

時(shí)反饋回服務(wù)器,通過服務(wù)器對(duì)這些動(dòng)作信息進(jìn)行有效認(rèn)證后,再向客戶端發(fā)送下一步游戲動(dòng)作信息,這樣動(dòng)作模擬

技術(shù)將失去原有的效應(yīng)。為了更好地“外掛”這些游戲,游戲外掛程序也進(jìn)行了升級(jí)換代,它們將以前針對(duì)游戲用戶

界面層的模擬推進(jìn)到數(shù)據(jù)通訊層,通過封包技術(shù)在客戶端擋截游戲服務(wù)器發(fā)送來的游戲控制數(shù)據(jù)包,分析數(shù)據(jù)包并修

改數(shù)據(jù)包;同時(shí)還需按照游戲數(shù)據(jù)包結(jié)構(gòu)創(chuàng)建數(shù)據(jù)包,再模擬客戶端發(fā)送給游戲服務(wù),這個(gè)過程其實(shí)就是一個(gè)封包

的過程。

  封包的技術(shù)是實(shí)現(xiàn)第二類游戲外掛的最核心的技術(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é)議無關(guān)的高層數(shù)據(jù)傳

輸編程接口。在Windows系統(tǒng)中,使用WinSock接口為應(yīng)用程序提供基于TCP/IP協(xié)議的網(wǎng)絡(luò)訪問服務(wù),這些服務(wù)是由

Wsock32.DLL動(dòng)態(tài)鏈接庫提供的函數(shù)庫來完成的。

  由上說明可知,任何Windows基于TCP/IP的應(yīng)用程序都必須通過WinSock接口訪問網(wǎng)絡(luò),當(dāng)然網(wǎng)絡(luò)游戲程序也不例

外。由此我們可以想象一下,如果我們可以控制WinSock接口的話,那么控制游戲客戶端程序與服務(wù)器之間的數(shù)據(jù)包也

將易如反掌。按著這個(gè)思路,下面的工作就是如何完成控制WinSock接口了。由上面的紹可知,WinSock接口其實(shí)是

由一個(gè)動(dòng)態(tài)鏈接庫提供的一系列函數(shù),由這些函數(shù)實(shí)現(xiàn)對(duì)網(wǎng)絡(luò)的訪問。有了這層的認(rèn)識(shí),問題就好辦多了,我們可以

制作一個(gè)類似的動(dòng)態(tài)鏈接庫來代替原WinSock接口庫,在其中實(shí)現(xiàn)WinSock32.dll中實(shí)現(xiàn)的所有函數(shù),并保證所有函數(shù)

的參數(shù)個(gè)數(shù)和順序、返回值類型都應(yīng)與原庫相同。在這個(gè)自制作的動(dòng)態(tài)庫中,可以對(duì)我們感興趣的函數(shù)(如發(fā)送、接

收等函數(shù))進(jìn)行擋截,放入外掛控制代碼,最后還繼續(xù)調(diào)用原WinSock庫中提供的相應(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 DLLproject。

  (2) 新建文件wsock32.h,按如下步驟輸入代碼:

  ① 加入相關(guān)變量聲明:

   HMODULE hModule=NULL; //模塊句柄
   char buffer[1000]; //緩沖區(qū)
   FARPROC proc; //函數(shù)入口指針

  ② 定義指向原WinSock庫中的所有函數(shù)地址的指針變量,因WinSock庫共提供70多個(gè)函數(shù),限于篇幅,在此就只選

擇幾個(gè)常用的函數(shù)列出,有關(guān)這些庫函數(shù)的說明可參考MSDN相關(guān)內(nèi)容。

   //定義指向原WinSock庫函數(shù)地址的指針變量。
   SOCKET (__stdcall *socket1)(int ,int,int);//創(chuàng)建Sock函數(shù)。
   int (__stdcall *WSAStartup1)(WORD,LPWSADATA);//初始化WinSock庫函數(shù)。
   int (__stdcall *WSACleanup1)();//清除WinSock庫函數(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庫,并獲取此庫中所有函數(shù)的地址。代碼如下:

   BOOL WINAPI DllMain (HANDLE hInst,ULONG ul_reason_for_call,LPVOIDlpReserved)
   {
    if(hModule==NULL){
     //加載原WinSock庫,原WinSock庫已復(fù)制為wsock32.001。
   hModule=LoadLibrary("wsock32.001");
  }
    else return 1;
//獲取原WinSock庫中的所有函數(shù)的地址并保存,下面僅列出部分代碼。
if(hModule!=NULL){
     //獲取原WinSock庫初始化函數(shù)的地址,并保存到WSAStartup1中。
proc=GetProcAddress(hModule,"WSAStartup");
   WSAStartup1=(int (_stdcall *)(WORD,LPWSADATA))proc;
     //獲取原WinSock庫消除函數(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;
}

  ③ 定義庫輸出函數(shù),在此可以對(duì)我們感興趣的函數(shù)中添加外掛控制代碼,在所有的輸出函數(shù)的最后一步都調(diào)用原

WinSock庫的同名函數(shù)。部分輸出函數(shù)定義代碼如下:

//庫輸出函數(shù)定義。
//WinSock初始化函數(shù)。
    int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA
lpWSAData)
    {
     //調(diào)用原WinSock庫初始化函數(shù)
     return WSAStartup1(wVersionRequired,lpWSAData);
    }
    //WinSock結(jié)束清除函數(shù)。
    int PASCAL FAR WSACleanup(void)
    {
     return WSACleanup1(); //調(diào)用原WinSock庫結(jié)束清除函數(shù)。
    }
    //創(chuàng)建Socket函數(shù)。
    SOCKET PASCAL FAR socket (int af, int type, int protocol)
    {
     //調(diào)用原WinSock庫創(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庫發(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ā)送到客戶端的數(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配置文件,在其中加入所有庫輸出函數(shù)的聲明,部分聲明代碼如下:

   LIBRARY "wsock32"
   EXPORTS
    WSAStartup @1
   WSACleanup @2
    recv @3
    send @4
    socket @5
   bind @6
   closesocket @7
   connect @8
        ......其它輸出函數(shù)聲明代碼略

       (5)、從“工程”菜單中選擇“設(shè)置”,彈出Project Setting對(duì)話框,選擇Link標(biāo)簽,在“對(duì)象/庫模塊”中輸入 

Ws2_32.lib。

  (6)、編譯項(xiàng)目,產(chǎn)生wsock32.dll庫文件。

  (7)、將系統(tǒng)目錄下原wsock32.dll庫文件拷貝到被外掛程序的目錄下,并將其改名為wsock.001;再將上面產(chǎn)生的

wsock32.dll文件同樣拷貝到被外掛程序的目錄下。重新啟動(dòng)游戲程序,此時(shí)游戲程序?qū)⑾燃虞d我們自己制作的

wsock32.dll文件,再通過該庫文件間接調(diào)用原WinSock接口函數(shù)來實(shí)現(xiàn)訪問網(wǎng)絡(luò)。上面我們僅僅介紹了擋載WinSock的

實(shí)現(xiàn)過程,至于如何加入外掛控制代碼,還需要外掛開發(fā)人員對(duì)游戲數(shù)據(jù)包結(jié)構(gòu)、內(nèi)容、加密算法等方面的仔細(xì)分析

(這個(gè)過程將是一個(gè)艱辛的過程),再生成外掛控制代碼。關(guān)于數(shù)據(jù)包分析方法和技巧,不是本文講解的范圍,如您

感興趣可以到網(wǎng)上查查相關(guān)資料。

2.擋截API

  擋截API技術(shù)與擋截WinSock技術(shù)在原理上很相似,但是前者比后者提供了更強(qiáng)大的
功能。擋截WinSock僅只能擋截

WinSock接口函數(shù),而擋截API可以實(shí)現(xiàn)對(duì)應(yīng)用程序調(diào)用的包括WinSock API函數(shù)在內(nèi)的
所有API函數(shù)的擋截。如果您的

外掛程序僅打算對(duì)WinSock的函數(shù)進(jìn)行擋截的話,您可以只選擇使用上小節(jié)介紹的擋截
WinSock技術(shù)。隨著大量外掛程

序在功能上的擴(kuò)展,它們不僅僅只提供對(duì)數(shù)據(jù)包的擋截,而且還對(duì)游戲程序中使用的
Windows API或其它DLL庫函數(shù)的

擋截,以使外掛的功能更加強(qiáng)大。例如,可以通過擋截相關(guān)API函數(shù)以實(shí)現(xiàn)對(duì)非中文游
戲的漢化功能,有了這個(gè)利器,

可以使您的外掛程序無所不能了。

  擋截API技術(shù)的原理核心也是使用我們自己的函數(shù)來替換掉Windows或其它DLL庫提
供的函數(shù),有點(diǎn)同擋截WinSock

原理相似吧。但是,其實(shí)現(xiàn)過程卻比擋截WinSock要復(fù)雜的多,如像實(shí)現(xiàn)擋截Winsock過
程一樣,將應(yīng)用程序調(diào)用的所

有的庫文件都寫一個(gè)模擬庫有點(diǎn)不大可能,就只說Windows API就有上千個(gè),還有很多
庫提供的函數(shù)結(jié)構(gòu)并未公開,所

以寫一個(gè)模擬庫代替的方式不大現(xiàn)實(shí),故我們必須另謀良方。

  擋截API的最終目標(biāo)是使用自定義的函數(shù)代替原函數(shù)。那么,我們首先應(yīng)該知道應(yīng)
用程序何時(shí)、何地、用何種方式

調(diào)用原函數(shù)。接下來,需要將應(yīng)用程序中調(diào)用該原函數(shù)的指令代碼進(jìn)行修改,使它將調(diào)
用函數(shù)的指針指向我們自己定

義的函數(shù)地址。這樣,外掛程序才能完全控制應(yīng)用程序調(diào)用的API函數(shù),至于在其中如
何加入外掛代碼,就應(yīng)需求而異

了。最后還有一個(gè)重要的問題要解決,如何將我們自定義的用來代替原API函數(shù)的函數(shù)
代碼注入被外掛游戲程序進(jìn)行地

址空間中,因在Windows系統(tǒng)中應(yīng)用程序僅只能訪問到本進(jìn)程地址空間內(nèi)的代碼和數(shù)
據(jù)。

  綜上所述,要實(shí)現(xiàn)擋截API函數(shù),至少需要解決如下三個(gè)問題:

  ● 如何定位游戲程序中調(diào)用API函數(shù)指令代碼?

  ● 如何修改游戲程序中調(diào)用API函數(shù)指令代碼?

  ● 如何將外掛代碼(自定義的替換函數(shù)代碼)注入到游戲程序進(jìn)程地址空間?

  下面我們逐一介紹這幾個(gè)問題的解決方法:

  (1) 、定位調(diào)用API函數(shù)指令代碼

  我們知道,在匯編語言中使用CALL指令來調(diào)用函數(shù)或過程的,它是通過指令參數(shù)中
的函數(shù)地址而定位到相應(yīng)的函

數(shù)代碼的。那么,我們?nèi)绻軐ふ业匠绦虼a中所有調(diào)用被擋截的API函數(shù)的CALL指令
的話,就可以將該指令中的函數(shù)

地址參數(shù)修改為替代函數(shù)的地址。雖然這是一個(gè)可行的方案,但是實(shí)現(xiàn)起來會(huì)很繁瑣,
也不穩(wěn)健。慶幸的是,Windows

系統(tǒng)中所使用的可執(zhí)行文件(PE格式)采用了輸入地址表機(jī)制,將所有在程序調(diào)用的
API函數(shù)的地址信息存放在輸入地

址表中,而在程序代碼CALL指令中使用的地址不是API函數(shù)的地址,而是輸入地址表中
該API函數(shù)的地址項(xiàng),如想使程

序代碼中調(diào)用的API函數(shù)被代替掉,只用將輸入地址表中該API函數(shù)的地址項(xiàng)內(nèi)容修改即
可。具體理解輸入地址表運(yùn)行

機(jī)制,還需要了解一下PE格式文件結(jié)構(gòu),其中圖三列出了PE格式文件的大致結(jié)構(gòu)。


  圖三:PE格式大致結(jié)構(gòu)圖(003.jpg)

  PE格式文件一開始是一段DOS程序,當(dāng)你的程序在不支持Windows的環(huán)境中運(yùn)行時(shí),
它就會(huì)顯示“This Program

cannot be run in DOS mode”這樣的警告語句,接著這個(gè)DOS文件頭,就開始真正的PE
文件內(nèi)容了。首先是一段稱為

“IMAGE_NT_HEADER”的數(shù)據(jù),其中是許多關(guān)于整個(gè)PE文件的消息,在這段數(shù)據(jù)的尾端
是一個(gè)稱為Data Directory的數(shù)

據(jù)表,通過它能快速定位一些PE文件中段(section)的地址。在這段數(shù)據(jù)之后,則是
一個(gè)“IMAGE_SECTION_HEADER”

的列表,其中的每一項(xiàng)都詳細(xì)描述了后面一個(gè)段的相關(guān)信息。接著它就是PE文件中最主
要的段數(shù)據(jù)了,執(zhí)行代碼、數(shù)

據(jù)和資源等等信息就分別存放在這些段中。

  在所有的這些段里,有一個(gè)被稱為“.idata”的段(輸入數(shù)據(jù)段)值得我們?nèi)プ?br>意,該段中包含著一些被稱為輸

入地址表(IAT,Import Address Table)的數(shù)據(jù)列表。每個(gè)用隱式方式加載的API所在
的DLL都有一個(gè)IAT與之對(duì)應(yīng),

同時(shí)一個(gè)API的地址也與IAT中一項(xiàng)相對(duì)應(yīng)。當(dāng)一個(gè)應(yīng)用程序加載到內(nèi)存中后,針對(duì)每一
個(gè)API函數(shù)調(diào)用,相應(yīng)的產(chǎn)生如

下的匯編指令:

  JMP DWORD PTR [XXXXXXXX]

  或

  CALL DWORD PTR [XXXXXXXX]

  其中,[XXXXXXXX]表示指向了輸入地址表中一個(gè)項(xiàng),其內(nèi)容是一個(gè)DWORD,而正是
這個(gè)DWORD才是API函數(shù)在內(nèi)存中

的真正地址。因此我們要想攔截一個(gè)API的調(diào)用,只要簡單的把那個(gè)DWORD改為我們自己
的函數(shù)的地址。

  (2) 、修改調(diào)用API函數(shù)代碼

  從上面對(duì)PE文件格式的分析可知,修改調(diào)用API函數(shù)代碼其實(shí)是修改被調(diào)用API函數(shù)
在輸入地址表中IAT項(xiàng)內(nèi)容。由

于Windows系統(tǒng)對(duì)應(yīng)用程序指令代碼地址空間的嚴(yán)密保護(hù)機(jī)制,使得修改程序指令代碼
非常困難,以至于許多高手為之

編寫VxD進(jìn)入Ring0。在這里,我為大家介紹一種較為方便的方法修改進(jìn)程內(nèi)存,它僅需
要調(diào)用幾個(gè)Windows核心API函

數(shù),下面我首先來學(xué)會(huì)一下這幾個(gè)API函數(shù):

   DWORD VirtualQuery(
   LPCVOID lpAddress, // address of region
   PMEMORY_BASIC_INFORMATION lpBuffer, // information buffer
   DWORD dwLength // size of buffer
   );

  該函數(shù)用于查詢關(guān)于本進(jìn)程內(nèi)虛擬地址頁的信息。其中,lpAddress表示被查詢頁
的區(qū)域地址;lpBuffer表示用于

保存查詢頁信息的緩沖;dwLength表示緩沖區(qū)大小。返回值為實(shí)際緩沖大小。

   BOOL VirtualProtect(
   LPVOID lpAddress, // region of committed pages
   SIZE_T dwSize, // size of the region
   DWORD flNewProtect, // desired access protection
   PDWORD lpflOldProtect // old protection
   );

  該函數(shù)用于改變本進(jìn)程內(nèi)虛擬地址頁的保護(hù)屬性。其中,lpAddress表示被改變保
護(hù)屬性頁區(qū)域地址;dwSize表示

頁區(qū)域大小;flNewProtect表示新的保護(hù)屬性,可取值為PAGE_READONLY、
PAGE_READWRITE、PAGE_EXECUTE等;

lpflOldProtect表示用于保存改變前的保護(hù)屬性。如果函數(shù)調(diào)用成功返回“T”,否則
返回“F”。

  有了這兩個(gè)API函數(shù),我們就可以隨心所欲的修改進(jìn)程內(nèi)存了。首先,調(diào)用
VirtualQuery()函數(shù)查詢被修改內(nèi)存的

頁信息,再根據(jù)此信息調(diào)用VirtualProtect()函數(shù)改變這些頁的保護(hù)屬性為
PAGE_READWRITE,有了這個(gè)權(quán)限您就可以

任意修改進(jìn)程內(nèi)存數(shù)據(jù)了。下面一段代碼演示了如何將進(jìn)程虛擬地址為0x0040106c處的
字節(jié)清零。

   BYTE* pData = 0x0040106c;
   MEMORY_BASIC_INFORMATION mbi_thunk;
   //查詢頁信息。
   VirtualQuery(pData, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION));
   //改變頁保護(hù)屬性為讀寫。
   VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize,
   PAGE_READWRITE, &mbi_thunk.Protect);
   //清零。
   *pData = 0x00;
   //恢復(fù)頁的原保護(hù)屬性。
   DWORD dwOldProtect;
   VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize,
   mbi_thunk.Protect, &dwOldProtect);
(3)、注入外掛代碼進(jìn)入被掛游戲進(jìn)程中

  完成了定位和修改程序中調(diào)用API函數(shù)代碼后,我們就可以隨意設(shè)計(jì)自定義的API函
數(shù)的替代函數(shù)了。做完這一切

后,還需要將這些代碼注入到被外掛游戲程序進(jìn)程內(nèi)存空間中,不然游戲進(jìn)程根本不會(huì)
訪問到替代函數(shù)代碼。注入方

法有很多,如利用全局鉤子注入、利用注冊(cè)表注入擋截User32庫中的API函數(shù)、利用
CreateRemoteThread注入(僅限于

NT/2000)、利用BHO注入等。因?yàn)槲覀冊(cè)趧?dòng)作模擬技術(shù)一節(jié)已經(jīng)接觸過全局鉤子,我相
信聰明的讀者已經(jīng)完全掌握了

全局鉤子的制作過程,所以我們?cè)诤竺娴膶?shí)例中,將繼續(xù)利用這個(gè)全局鉤子。至于其它
幾種注入方法,如果感興趣可

參閱MSDN有關(guān)內(nèi)容。

  有了以上理論基礎(chǔ),我們下面就開始制作一個(gè)擋截MessageBoxA和recv函數(shù)的實(shí)
例,在開發(fā)游戲外掛程序 時(shí),可

以此實(shí)例為框架,加入相應(yīng)的替代函數(shù)和處理代碼即可。此實(shí)例的開發(fā)過程如下:

  (1) 打開前面創(chuàng)建的ActiveKey項(xiàng)目。

  (2) 在ActiveKey.h文件中加入HOOKAPI結(jié)構(gòu),此結(jié)構(gòu)用來存儲(chǔ)被擋截API函數(shù)名
稱、原API函數(shù)地址和替代函數(shù)地

址。

   typedef struct tag_HOOKAPI
   {
   LPCSTR szFunc;//被HOOK的API函數(shù)名稱。
   PROC pNewProc;//替代函數(shù)地址。
   PROC pOldProc;//原API函數(shù)地址。
   }HOOKAPI, *LPHOOKAPI;

  (3) 打開ActiveKey.cpp文件,首先加入一個(gè)函數(shù),用于定位輸入庫在輸入數(shù)據(jù)段
中的IAT地址。代碼如下:

   extern "C" __declspec(dllexport)PIMAGE_IMPORT_DESCRIPTOR
   LocationIAT(HMODULE hModule, LPCSTR szImportMod)
   //其中,hModule為進(jìn)程模塊句柄;szImportMod為輸入庫名稱。
   {
   //檢查是否為DOS程序,如是返回NULL,因DOS程序沒有IAT。
   PIMAGE_DOS_HEADER pDOSHeader = (PIMAGE_DOS_HEADER) hModule;
   if(pDOSHeader->e_magic != IMAGE_DOS_SIGNATURE) return NULL;
    //檢查是否為NT標(biāo)志,否則返回NULL。
    PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDOSHeader+
(DWORD)(pDOSHeader-

>e_lfanew));
    if(pNTHeader->Signature != IMAGE_NT_SIGNATURE) return NULL;
    //沒有IAT表則返回NULL。
    
if(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Vir
tualAddress == 0)

return NULL;
    //定位第一個(gè)IAT位置。
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc =
(PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDOSHeader + (DWORD)

(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Virtu
alAddress));
    //根據(jù)輸入庫名稱循環(huán)檢查所有的IAT,如匹配則返回該IAT地址,否則檢測(cè)下
一個(gè)IAT。
    while (pImportDesc->Name)
    {
     //獲取該IAT描述的輸入庫名稱。
   PSTR szCurrMod = (PSTR)((DWORD)pDOSHeader +
(DWORD)(pImportDesc->Name));
   if (stricmp(szCurrMod, szImportMod) == 0) break;
   pImportDesc++;
    }
    if(pImportDesc->Name == NULL) return NULL;
   return pImportDesc;
   }

  再加入一個(gè)函數(shù),用來定位被擋截API函數(shù)的IAT項(xiàng)并修改其內(nèi)容為替代函數(shù)地址。
代碼如下:

   extern "C" __declspec(dllexport)
   HookAPIByName( HMODULE hModule, LPCSTR szImportMod, LPHOOKAPI
pHookApi)
   //其中,hModule為進(jìn)程模塊句柄;szImportMod為輸入庫名稱;pHookAPI為
HOOKAPI結(jié)構(gòu)指針。
   {
    //定位szImportMod輸入庫在輸入數(shù)據(jù)段中的IAT地址。
    PIMAGE_IMPORT_DESCRIPTOR pImportDesc = LocationIAT(hModule,
szImportMod);
  if (pImportDesc == NULL) return FALSE;
    //第一個(gè)Thunk地址。
    PIMAGE_THUNK_DATA pOrigThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule +
(DWORD)(pImportDesc-

>OriginalFirstThunk));
   //第一個(gè)IAT項(xiàng)的Thunk地址。
    PIMAGE_THUNK_DATA pRealThunk = (PIMAGE_THUNK_DATA)((DWORD)hModule +
(DWORD)(pImportDesc-

>FirstThunk));
    //循環(huán)查找被截API函數(shù)的IAT項(xiàng),并使用替代函數(shù)地址修改其值。
   while(pOrigThunk->u1.Function)
{
 //檢測(cè)此Thunk是否為IAT項(xiàng)。
if((pOrigThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG)
{
  //獲取此IAT項(xiàng)所描述的函數(shù)名稱。
 PIMAGE_IMPORT_BY_NAME pByName
=(PIMAGE_IMPORT_BY_NAME)((DWORD)hModule+(DWORD)(pOrigThunk-

>u1.AddressOfData));
 if(pByName->Name[0] == \0) return FALSE;
  //檢測(cè)是否為擋截函數(shù)。
if(strcmpi(pHookApi->szFunc, (char*)pByName->Name) == 0)
  {
       MEMORY_BASIC_INFORMATION mbi_thunk;
       //查詢修改頁的信息。
       VirtualQuery(pRealThunk, &mbi_thunk,
sizeof(MEMORY_BASIC_INFORMATION));
//改變修改頁保護(hù)屬性為PAGE_READWRITE。
       VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize,
PAGE_READWRITE,

&mbi_thunk.Protect);
//保存原來的API函數(shù)地址。
      if(pHookApi->pOldProc == NULL)
pHookApi->pOldProc = (PROC)pRealThunk->u1.Function;
  //修改API函數(shù)IAT項(xiàng)內(nèi)容為替代函數(shù)地址。
pRealThunk->u1.Function = (PDWORD)pHookApi->pNewProc;
//恢復(fù)修改頁保護(hù)屬性。
DWORD dwOldProtect;
       VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize,
mbi_thunk.Protect,

&dwOldProtect);
      }
}
  pOrigThunk++;
  pRealThunk++;
}
  SetLastError(ERROR_SUCCESS); //設(shè)置錯(cuò)誤為ERROR_SUCCESS,表示成功。
  return TRUE;
   }

  (4) 定義替代函數(shù),此實(shí)例中只給MessageBoxA和recv兩個(gè)API進(jìn)行擋截。代碼如下

   static int WINAPI MessageBoxA1 (HWND hWnd , LPCTSTR lpText, LPCTSTR
lpCaption, UINT uType)
   {
    //過濾掉原MessageBoxA的正文和標(biāo)題內(nèi)容,只顯示如下內(nèi)容。
return MessageBox(hWnd, "Hook API OK!", "Hook API", uType);
   }
   static int WINAPI recv1(SOCKET s, char FAR *buf, int len, int flags )
   {
   //此處可以擋截游戲服務(wù)器發(fā)送來的網(wǎng)絡(luò)數(shù)據(jù)包,可以加入分析和處理數(shù)據(jù)代
碼。
   return recv(s,buf,len,flags);
   }

  (5) 在KeyboardProc函數(shù)中加入激活擋截API代碼,在if( wParam == 0X79 )語句
中后面加入如下else if語句:

   ......
   //當(dāng)激活F11鍵時(shí),啟動(dòng)擋截API函數(shù)功能。
   else if( wParam == 0x7A )
   {
    HOOKAPI api[2];
api[0].szFunc ="MessageBoxA";//設(shè)置被擋截函數(shù)的名稱。
api[0].pNewProc = (PROC)MessageBoxA1;//設(shè)置替代函數(shù)的地址。
api[1].szFunc ="recv";//設(shè)置被擋截函數(shù)的名稱。
api[1].pNewProc = (PROC)recv1; //設(shè)置替代函數(shù)的地址。
//設(shè)置擋截User32.dll庫中的MessageBoxA函數(shù)。
HookAPIByName(GetModuleHandle(NULL),"User32.dll",&api[0]);
//設(shè)置擋截Wsock32.dll庫中的recv函數(shù)。
HookAPIByName(GetModuleHandle(NULL),"Wsock32.dll",&api[1]);
   }
   ......



    (6) 在ActiveKey.cpp中加入頭文件聲明 "#include "wsock32.h"。 從“工程”菜單中選擇“設(shè)置”,彈出 

Project Setting對(duì)話框,選擇Link標(biāo)簽,在“對(duì)象/庫模塊”中輸入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é)束語

  除了以上介紹的幾種游戲外掛程序常用的技術(shù)以外,在一些外掛程序中還使用了游
戲數(shù)據(jù)修改技術(shù)、游戲加速技術(shù)等。在這篇文章里,就不逐一介紹了。 
 

posted on 2008-02-19 09:01 葉子 閱讀(7516) 評(píng)論(4)  編輯 收藏 引用

Feedback

# re: 網(wǎng)絡(luò)游戲外掛編寫原理 2008-02-20 01:19 luguo

頂  回復(fù)  更多評(píng)論   

# re: 網(wǎng)絡(luò)游戲外掛編寫原理 2008-03-21 10:21 裁縫

收藏了 o(∩_∩)o...  回復(fù)  更多評(píng)論   

# re: 網(wǎng)絡(luò)游戲外掛編寫原理 2008-05-18 14:41 mardin

我按你說的第一個(gè)實(shí)例操作了一次,但是在Simulate編譯的時(shí)候.提示加入的ActiveKey.h中CKeyHook use undefined class 'AFX_EXT_CLASS'
我查了下.這個(gè)要在dll中才能用的吧..那這問題怎么解決阿....
  回復(fù)  更多評(píng)論   

# re: 網(wǎng)絡(luò)游戲外掛編寫原理 2008-05-19 17:38 葉子

Simulate是什啥?  回復(fù)  更多評(píng)論   


只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产精品久久久久久av福利软件 | 国产精品劲爆视频| 99国产精品自拍| 羞羞答答国产精品www一本| 国产啪精品视频| 久久精品99国产精品酒店日本| 狠狠色丁香婷婷综合| 欧美.日韩.国产.一区.二区| 亚洲永久字幕| 亚洲精品视频在线播放| 久久久久久色| 久久久噜噜噜| 亚洲日本中文| 亚洲人成久久| 日韩一区二区福利| 亚洲精品一级| 国产专区欧美精品| 欧美freesex8一10精品| 午夜精品福利在线| 亚洲制服av| 先锋影音网一区二区| 欧美一区二区三区日韩| 欧美在线二区| 亚洲欧美在线看| 亚洲欧美日韩综合| 欧美亚洲成人免费| 欧美视频在线观看免费网址| 欧美全黄视频| 国产乱人伦精品一区二区| 国产精品手机视频| 国产亚洲欧美日韩一区二区| 激情一区二区三区| 日韩亚洲欧美一区| 国产欧美日韩激情| 在线电影一区| 一区二区三区国产盗摄| 久久精品国产999大香线蕉| 国产精品区一区| 国产主播一区二区三区| 日韩视频在线免费观看| 午夜免费电影一区在线观看| 久久久激情视频| 亚洲精品国产系列| 性欧美video另类hd性玩具| 免费成人在线观看视频| 欧美天堂亚洲电影院在线播放| 国产一区二区三区精品久久久| 亚洲国产精品成人va在线观看| 亚洲欧美日韩综合一区| 亚洲精品自在在线观看| 欧美一区二区三区视频在线| 欧美日韩精品久久久| 亚洲激情成人在线| 亚洲——在线| 日韩一级免费| 美日韩精品视频| 男女av一区三区二区色多| 在线看国产一区| 99精品久久久| 国产精品夜夜夜一区二区三区尤| 欧美在线观看天堂一区二区三区| 亚洲一区二区四区| 国产一区二区三区四区五区美女 | 亚洲欧美日韩精品在线| 久久久久久电影| 午夜精品久久| 欧美精彩视频一区二区三区| 亚洲一区二区四区| 久久蜜臀精品av| 免费在线观看成人av| 欧美一区=区| 国产精品成人一区二区艾草| 欧美成人免费全部| 国产真实精品久久二三区| 亚洲人成久久| 亚洲国产欧美日韩| 久久精品一级爱片| 久久躁日日躁aaaaxxxx| 国产精品尤物福利片在线观看| 亚洲国产福利在线| 亚洲国产精品一区| 欧美第十八页| 亚洲国产精品va在看黑人| 在线免费高清一区二区三区| 亚洲欧美国产一区二区三区| 亚洲视频欧美视频| 国产日韩精品视频一区| 亚洲伊人网站| 久久久999国产| 亚洲国产成人精品女人久久久 | 美日韩免费视频| 午夜精品影院在线观看| 亚洲欧美日韩天堂| 久久激情视频久久| 亚洲第一级黄色片| 欧美日韩一区二区三区免费| 99视频一区| 久久日韩粉嫩一区二区三区| 在线观看中文字幕不卡| 欧美成在线视频| 亚洲欧美一区二区在线观看| 久久精品亚洲乱码伦伦中文 | 亚洲美女在线观看| 国产婷婷色一区二区三区在线 | 欧美日韩一区二区三区| 性欧美暴力猛交另类hd| 亚洲精品国产精品乱码不99按摩| 午夜老司机精品| 亚洲性线免费观看视频成熟| 国产一区高清视频| 国产精品中文字幕欧美| 欧美视频免费| 欧美四级伦理在线| 欧美精品1区2区| 欧美成人免费全部观看天天性色| 亚洲午夜精品网| 亚洲视频一区二区在线观看| 亚洲人在线视频| 日韩天天综合| 亚洲一区二区三区高清| 亚洲图片欧洲图片av| 亚洲午夜久久久| 亚洲欧美日韩中文视频| 性久久久久久久久久久久| 亚洲专区欧美专区| 亚洲欧美在线网| 久久午夜影视| 欧美视频网站| 国产午夜精品一区理论片飘花| 国产精品免费福利| 国产亚洲一级高清| 99在线精品观看| 欧美一区二区三区四区在线观看 | 午夜激情久久久| 欧美一区二区黄色| 欧美成人情趣视频| 亚洲国产精品久久久久婷婷884| 亚洲国产天堂久久国产91| 99精品国产热久久91蜜凸| 欧美一区二区黄| 亚洲精品欧美精品| 久久亚洲精品伦理| 国产亚洲在线观看| 一本久道综合久久精品| 老巨人导航500精品| 日韩一区二区精品葵司在线| 久久精品国产精品亚洲综合| 欧美日韩视频在线| 亚洲日韩欧美视频| 老司机免费视频久久| 性色av一区二区三区在线观看| 欧美黄色一区二区| 亚洲精品乱码久久久久久蜜桃91| 久久精品人人做人人综合| 一本色道88久久加勒比精品| 欧美成人情趣视频| 亚洲视频第一页| 99热精品在线| 欧美午夜电影完整版| 亚洲视频在线播放| 亚洲欧美日韩一区二区三区在线观看| 欧美日本精品| 香港成人在线视频| 久久精品国产99| 在线精品视频一区二区| 欧美成人影音| 欧美视频久久| 久久久青草婷婷精品综合日韩 | 精久久久久久| 欧美二区在线| 欧美午夜精品理论片a级大开眼界| 中文一区在线| 久久国产精品电影| 99精品视频免费观看| 正在播放亚洲一区| 极品日韩久久| 亚洲理伦在线| 亚洲欧美制服另类日韩| 国产综合香蕉五月婷在线| 伊人精品成人久久综合软件| 欧美jizz19hd性欧美| 欧美日韩精品免费看 | 国产亚洲福利| 亚洲麻豆av| 亚洲美女精品一区| 久久精品国产第一区二区三区最新章节 | 美日韩精品视频| 欧美在线短视频| 国产精品久久久久久久久免费桃花| 久久久久高清| 国产欧美一区二区在线观看| 亚洲国产天堂久久国产91| 国产日韩欧美在线看| 中国成人亚色综合网站| 亚洲免费观看高清完整版在线观看熊 | 亚洲性夜色噜噜噜7777| 欧美三级乱人伦电影| 亚洲久久一区| 亚洲午夜在线视频| 欧美午夜精品久久久久久人妖|