<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 495,comments - 227,trackbacks - 0

    通過對動作模擬技術的介紹,我們對游戲外掛有了一定程度上的認識,也學會了使用動作模擬技術來實現簡單的動作模擬型游戲外掛的制作。這種動作模擬型游戲外掛有一定的局限性,它僅僅只能解決使用計算機代替人力完成那么有規律、繁瑣而無聊的游戲動作。但是,隨著網絡游戲的盛行和復雜度的增加,很多游戲要求將客戶端動作信息及時反饋回服務器,通過服務器對這些動作信息進行有效認證后,再向客戶端發送下一步游戲動作信息,這樣動作模擬技術將失去原有的效應。為了更好地 外掛 這些游戲,游戲外掛程序也進行了升級換代,它們將以前針對游戲用戶界面層的模擬推進到數據通訊層,通過封包技術在客戶端擋截游戲服務器發送來的游戲控制數據包,分析數據包并修改數據包;同時還需按照游戲數據包結構創建數據包,再模擬客戶端發送給游戲服務器,這個過程其實就是一個封包的過程。
      封包的技術是實現第二類游戲外掛的最核心的技術。封包技術涉及的知識很廣泛,實現方法也很多,如擋截 WinSock 、擋截 API 函數、擋截消息、 VxD 驅動程序等。在此我們也不可能在此文中將所有的封包技術都進行詳細介紹,故選擇兩種在游戲外掛程序中最常用的兩種方法:擋截 WinSock 和擋截 API 函數。
       1 ?擋截WinSock
      眾所周知, Winsock Windows 網絡編程接口,它工作于 Windows 應用層,它提供與底層傳輸協議無關的高層數據傳輸編程接口。在 Windows 系統中,使用 WinSock 接口為應用程序提供基于 TCP/IP 協議的網絡訪問服務,這些服務是由 Wsock32.DLL 動態鏈接庫提供的函數庫來完成的。
      由上說明可知,任何 Windows 基于 TCP/IP 的應用程序都必須通過 WinSock 接口訪問網絡,當然網絡游戲程序也不例外。由此我們可以想象一下,如果我們可以控制 WinSock 接口的話,那么控制游戲客戶端程序與服務器之間的數據包也將易如反掌。按著這個思路,下面的工作就是如何完成控制 WinSock 接口了。由上面的介紹可知, WinSock 接口其實是由一個動態鏈接庫提供的一系列函數,由這些函數實現對網絡的訪問。有了這層的認識,問題就好辦多了,我們可以制作一個類似的動態鏈接庫來代替原 WinSock 接口庫,在其中實現 WinSock32.dll 中實現的所有函數,并保證所有函數的參數個數和順序、返回值類型都應與原庫相同。在這個自制作的動態庫中,可以對我們感興趣的函數(如發送、接收等函數)進行擋截,放入外掛控制代碼,最后還繼續調用原 WinSock 庫中提供的相應功能函數,這樣就可以實現對網絡數據包的擋截、修改和發送等封包功能。
      下面重點介紹創建擋截 WinSock 外掛程序的基本步驟:
       (1)? 創建 DLL 項目,選擇 Win32?Dynamic-Link?Library ,再選擇 An?empty?DLL?project 。
       (2)? 新建文件 wsock32.h ,按如下步驟輸入代碼:
       ?加入相關變量聲明:
        HMODULE?hModule=NULL;?// 模塊句柄
        char?buffer[1000];?// 緩沖區
        FARPROC?proc;?// 函數入口指針?
       ?定義指向原WinSock庫中的所有函數地址的指針變量,因WinSock庫共提供70多個函數,限于篇幅,在此就只選擇幾個常用的函數列出,有關這些庫函數的說明可參考MSDN相關內容。
        // 定義指向原 WinSock 庫函數地址的指針變量。
        SOCKET?(__stdcall?*socket1)(int?,int,int);// 創建 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);// 發送數據函數。
        int?(__stdcall?*connect1)(SOCKET,const?struct?sockaddr?*,int);// 創建連接函數。
        int?(__stdcall?*bind1)(SOCKET?,const?struct?sockaddr?*,int?);// 綁定函數。
        ...... 其它函數地址指針的定義略。?
       (3)? 新建 wsock32.cpp 文件,按如下步驟輸入代碼:
       ?加入相關頭文件聲明:
        #include?<windows.h>
        #include?<stdio.h>
        #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;
          // 獲取原創建 Sock 函數的地址,并保存到 socket1 中。
         proc=GetProcAddress(hModule,"socket");
          socket1=(SOCKET?(_stdcall?*)(int?,int,int))proc;
          // 獲取原創建連接函數的地址,并保存到 connect1 中。
          proc=GetProcAddress(hModule,"connect");
          connect1=(int?(_stdcall?*)(SOCKET?,const?struct?sockaddr?*,int?))proc;
          // 獲取原發送函數的地址,并保存到 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 庫結束清除函數。
         }
         // 創建 Socket 函數。
         SOCKET?PASCAL?FAR?socket?(int?af,?int?type,?int?protocol)
         {
          // 調用原 WinSock 庫創建 Socket 函數。
          return?socket1(af,type,protocol);
         }
         // 發送數據包函數
         int?PASCAL?FAR?send(SOCKET?s,const?char?*?buf,int?len,int?flags)
         {
        // 在此可以對發送的緩沖 buf 的內容進行修改,以實現欺騙服務器。
       外掛代碼 ......
        // 調用原 WinSock 庫發送數據包函數。
          return?send1(s,buf,len,flags);
         }
    //
    接收數據包函數。
         int?PASCAL?FAR?recv(SOCKET?s,?char?FAR?*?buf,?int?len,?int?flags)
         {
        // 在此可以擋截到服務器端發送到客戶端的數據包,先將其保存到 buffer 中。
        strcpy(buffer,buf);
        // buffer 數據包數據進行分析后,對其按照玩家的指令進行相關修改。
       外掛代碼 ......
        // 最后調用原 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?
        ...... 其它輸出函數聲明代碼略。
       (5) 、從 工程 菜單中選擇 設置 ,彈出 Project?Setting 對話框,選擇 Link 標簽,在 對象 / 庫模塊 中輸入 Ws2_32.lib
       (6) 、編譯項目,產生 wsock32.dll 庫文件。
       (7) 、將系統目錄下原 wsock32.dll 庫文件拷貝到被外掛程序的目錄下,并將其改名為 wsock.001 ;再將上面產生的 wsock32.dll 文件同樣拷貝到被外掛程序的目錄下。重新啟動游戲程序,此時游戲程序將先加載我們自己制作的 wsock32.dll 文件,再通過該庫文件間接調用原 WinSock 接口函數來實現訪問網絡。上面我們僅僅介紹了擋載 WinSock 的實現過程,至于如何加入外掛控制代碼,還需要外掛開發人員對游戲數據包結構、內容、加密算法等方面的仔細分析(這個過程將是一個艱辛的過程),再生成外掛控制代碼。關于數據包分析方法和技巧,不是本文講解的范圍,如您感興趣可以到網上查查相關資料。
       2. 擋截 API
      擋截 API 技術與擋截 WinSock 技術在原理上很相似,但是前者比后者提供了更強大的功能。擋截 WinSock 僅只能擋截 WinSock 接口函數,而擋截 API 可以實現對應用程序調用的包括 WinSock?API 函數在內的所有 API 函數的擋截。如果您的外掛程序僅打算對 WinSock 的函數進行擋截的話,您可以只選擇使用上小節介紹的擋截 WinSock 技術。隨著大量外掛程序在功能上的擴展,它們不僅僅只提供對數據包的擋截,而且還對游戲程序中使用的 Windows?API 或其它 DLL 庫函數的擋截,以使外掛的功能更加強大。例如,可以通過擋截相關 API 函數以實現對非中文游戲的漢化功能,有了這個利器,可以使您的外掛程序無所不能了。
      擋截 API 技術的原理核心也是使用我們自己的函數來替換掉 Windows 或其它 DLL 庫提供的函數,有點同擋截 WinSock 原理相似吧。但是,其實現過程卻比擋截 WinSock 要復雜的多,如像實現擋截 Winsock 過程一樣,將應用程序調用的所有的庫文件都寫一個模擬庫有點不大可能,就只說 Windows?API 就有上千個,還有很多庫提供的函數結構并未公開,所以寫一個模擬庫代替的方式不大現實,故我們必須另謀良方。
      擋截 API 的最終目標是使用自定義的函數代替原函數。那么,我們首先應該知道應用程序何時、何地、用何種方式調用原函數。接下來,需要將應用程序中調用該原函數的指令代碼進行修改,使它將調用函數的指針指向我們自己定義的函數地址。這樣,外掛程序才能完全控制應用程序調用的 API 函數,至于在其中如何加入外掛代碼,就應需求而異了。最后還有一個重要的問題要解決,如何將我們自定義的用來代替原 API 函數的函數代碼注入被外掛游戲程序進行地址空間中,因在 Windows 系統中應用程序僅只能訪問到本進程地址空間內的代碼和數據。
      綜上所述,要實現擋截 API 函數,至少需要解決如下三個問題:
       ●? 如何定位游戲程序中調用 API 函數指令代碼?
       ●? 如何修改游戲程序中調用 API 函數指令代碼?
       ●? 如何將外掛代碼(自定義的替換函數代碼)注入到游戲程序進程地址空間?
      下面我們逐一介紹這幾個問題的解決方法:
       (1)? 、定位調用 API 函數指令代碼
      我們知道,在匯編語言中使用 CALL 指令來調用函數或過程的,它是通過指令參數中的函數地址而定位到相應的函數代碼的。那么,我們如果能尋找到程序代碼中所有調用被擋截的 API 函數的 CALL 指令的話,就可以將該指令中的函數地址參數修改為替代函數的地址。雖然這是一個可行的方案,但是實現起來會很繁瑣,也不穩健。慶幸的是, Windows 系統中所使用的可執行文件( PE 格式)采用了輸入地址表機制,將所有在程序調用的 API 函數的地址信息存放在輸入地址表中,而在程序代碼 CALL 指令中使用的地址不是 API 函數的地址,而是輸入地址表中該 API 函數的地址項,如想使程序代碼中調用的 API 函數被代替掉,只用將輸入地址表中該 API 函數的地址項內容修改即可。具體理解輸入地址表運行機制,還需要了解一下 PE 格式文件結構,其中圖三列出了 PE 格式文件的大致結構。

      圖三: PE 格式大致結構圖 (003.jpg)
       PE 格式文件一開始是一段 DOS 程序,當你的程序在不支持 Windows 的環境中運行時,它就會顯示 “This?Program?cannot?be?run?in?DOS?mode” 這樣的警告語句,接著這個 DOS 文件頭,就開始真正的 PE 文件內容了。首先是一段稱為 “IMAGE_NT_HEADER” 的數據,其中是許多關于整個 PE 文件的消息,在這段數據的尾端是一個稱為 Data?Directory 的數據表,通過它能快速定位一些 PE 文件中段( section )的地址。在這段數據之后,則是一個 “IMAGE_SECTION_HEADER” 的列表,其中的每一項都詳細描述了后面一個段的相關信息。接著它就是 PE 文件中最主要的段數據了,執行代碼、數據和資源等等信息就分別存放在這些段中。
      在所有的這些段里,有一個被稱為 “.idata” 的段(輸入數據段)值得我們去注意,該段中包含著一些被稱為輸入地址表( IAT Import?Address?Table )的數據列表。每個用隱式方式加載的 API 所在的 DLL 都有一個 IAT 與之對應,同時一個 API 的地址也與 IAT 中一項相對應。當一個應用程序加載到內存中后,針對每一個 API 函數調用,相應的產生如下的匯編指令:?
       JMP?DWORD?PTR?[XXXXXXXX]?
      或
       CALL?DWORD?PTR?[XXXXXXXX]
      其中, [XXXXXXXX] 表示指向了輸入地址表中一個項,其內容是一個 DWORD ,而正是這個 DWORD 才是 API 函數在內存中的真正地址。因此我們要想攔截一個 API 的調用,只要簡單的把那個 DWORD 改為我們自己的函數的地址。
       (2)? 、修改調用 API 函數代碼
      從上面對 PE 文件格式的分析可知,修改調用 API 函數代碼其實是修改被調用 API 函數在輸入地址表中 IAT 項內容。由于 Windows 系統對應用程序指令代碼地址空間的嚴密保護機制,使得修改程序指令代碼非常困難,以至于許多高手為之編寫 VxD 進入 Ring0 。在這里,我為大家介紹一種較為方便的方法修改進程內存,它僅需要調用幾個 Windows 核心 API 函數,下面我首先來學會一下這幾個 API 函數:
        DWORD?VirtualQuery(
        LPCVOID?lpAddress,?//?address?of?region
        PMEMORY_BASIC_INFORMATION?lpBuffer,?//?information?buffer
        DWORD?dwLength?//?size?of?buffer
        );?
      該函數用于查詢關于本進程內虛擬地址頁的信息。其中, lpAddress 表示被查詢頁的區域地址; lpBuffer 表示用于保存查詢頁信息的緩沖; dwLength 表示緩沖區大小。返回值為實際緩沖大小。
        BOOL?VirtualProtect(
        LPVOID?lpAddress,?//?region?of?committed?pages
        SIZE_T?dwSize,?//?size?of?the?region
        DWORD?flNewProtect,?//?desired?access?protection
        PDWORD?lpflOldProtect?//?old?protection
        );?
      該函數用于改變本進程內虛擬地址頁的保護屬性。其中, lpAddress 表示被改變保護屬性頁區域地址; dwSize 表示頁區域大小; flNewProtect 表示新的保護屬性,可取值為 PAGE_READONLY 、 PAGE_READWRITE 、 PAGE_EXECUTE 等; lpflOldProtect 表示用于保存改變前的保護屬性。如果函數調用成功返回 “T” ,否則返回 “F”
      有了這兩個 API 函數,我們就可以隨心所欲的修改進程內存了。首先,調用 VirtualQuery() 函數查詢被修改內存的頁信息,再根據此信息調用 VirtualProtect() 函數改變這些頁的保護屬性為 PAGE_READWRITE ,有了這個權限您就可以任意修改進程內存數據了。下面一段代碼演示了如何將進程虛擬地址為 0x0040106c 處的字節清零。
        BYTE*?pData?=?0x0040106c;
        MEMORY_BASIC_INFORMATION?mbi_thunk;?
        // 查詢頁信息。
        VirtualQuery(pData,?&mbi_thunk,?sizeof(MEMORY_BASIC_INFORMATION));?
        // 改變頁保護屬性為讀寫。
        VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize,?
        PAGE_READWRITE,?&mbi_thunk.Protect);?
        // 清零。
        *pData?=?0x00;
        // 恢復頁的原保護屬性。
        DWORD?dwOldProtect;?
        VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize,?
        mbi_thunk.Protect,?&dwOldProtect);?
       (3) 、注入外掛代碼進入被掛游戲進程中
      完成了定位和修改程序中調用 API 函數代碼后,我們就可以隨意設計自定義的 API 函數的替代函數了。做完這一切后,還需要將這些代碼注入到被外掛游戲程序進程內存空間中,不然游戲進程根本不會訪問到替代函數代碼。注入方法有很多,如利用全局鉤子注入、利用注冊表注入擋截 User32 庫中的 API 函數、利用 CreateRemoteThread 注入(僅限于 NT/2000 )、利用 BHO 注入等。因為我們在動作模擬技術一節已經接觸過全局鉤子,我相信聰明的讀者已經完全掌握了全局鉤子的制作過程,所以我們在后面的實例中,將繼續利用這個全局鉤子。至于其它幾種注入方法,如果感興趣可參閱 MSDN 有關內容。
      有了以上理論基礎,我們下面就開始制作一個擋截 MessageBoxA recv 函數的實例,在開發游戲外掛程序?時,可以此實例為框架,加入相應的替代函數和處理代碼即可。此實例的開發過程如下:
       (1)? 打開前面創建的 ActiveKey 項目。
       (2)? ActiveKey.h 文件中加入 HOOKAPI 結構,此結構用來存儲被擋截 API 函數名稱、原 API 函數地址和替代函數地址。
        typedef?struct?tag_HOOKAPI?
        {?
        LPCSTR?szFunc;// HOOK API 函數名稱。
        PROC?pNewProc;// 替代函數地址。
        PROC?pOldProc;// API 函數地址。
        }HOOKAPI,?*LPHOOKAPI;?
       (3)? 打開 ActiveKey.cpp 文件,首先加入一個函數,用于定位輸入庫在輸入數據段中的 IAT 地址。代碼如下:
        extern?"C"?__declspec(dllexport)PIMAGE_IMPORT_DESCRIPTOR?
        LocationIAT(HMODULE?hModule,?LPCSTR?szImportMod)?
        // 其中, hModule 為進程模塊句柄; szImportMod 為輸入庫名稱。
        {?
        // 檢查是否為 DOS 程序,如是返回 NULL ,因 DOS 程序沒有 IAT 。
        PIMAGE_DOS_HEADER?pDOSHeader?=?(PIMAGE_DOS_HEADER)?hModule;?
        if(pDOSHeader->e_magic?!=?IMAGE_DOS_SIGNATURE)?return?NULL;?
         // 檢查是否為 NT 標志,否則返回 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].VirtualAddress?==?0)?return?NULL;?
         // 定位第一個 IAT 位置。 ?
         PIMAGE_IMPORT_DESCRIPTOR?pImportDesc?=?(PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDOSHeader?+?(DWORD)(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));?
         // 根據輸入庫名稱循環檢查所有的 IAT ,如匹配則返回該 IAT 地址,否則檢測下一個 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;?
        }?
      再加入一個函數,用來定位被擋截 API 函數的 IAT 項并修改其內容為替代函數地址。代碼如下:
        extern?"C"?__declspec(dllexport)?
        HookAPIByName(?HMODULE?hModule,?LPCSTR?szImportMod,?LPHOOKAPI?pHookApi)?
        // 其中, hModule 為進程模塊句柄; szImportMod 為輸入庫名稱; pHookAPI HOOKAPI 結構指針。
        {?
         // 定位 szImportMod 輸入庫在輸入數據段中的 IAT 地址。
         PIMAGE_IMPORT_DESCRIPTOR?pImportDesc?=?LocationIAT(hModule,?szImportMod);?
       if?(pImportDesc?==?NULL)?return?FALSE;?
         // 第一個 Thunk 地址。
         PIMAGE_THUNK_DATA?pOrigThunk?=?(PIMAGE_THUNK_DATA)((DWORD)hModule?+?(DWORD)(pImportDesc->OriginalFirstThunk));?
       ?// 第一個 IAT 項的 Thunk 地址。
         PIMAGE_THUNK_DATA?pRealThunk?=?(PIMAGE_THUNK_DATA)((DWORD)hModule?+?(DWORD)(pImportDesc->FirstThunk));?
         // 循環查找被截 API 函數的 IAT 項,并使用替代函數地址修改其值。
        while(pOrigThunk->u1.Function)?
    {?
      // 檢測此 Thunk 是否為 IAT 項。
    if((pOrigThunk->u1.Ordinal?&?IMAGE_ORDINAL_FLAG)?!=?IMAGE_ORDINAL_FLAG)?
    {
      ?// 獲取此 IAT 項所描述的函數名稱。
      PIMAGE_IMPORT_BY_NAME?pByName?=(PIMAGE_IMPORT_BY_NAME)((DWORD)hModule+(DWORD)(pOrigThunk->u1.AddressOfData));?
      if(pByName->Name[0]?==?‘\\0‘)?return?FALSE;?
       // 檢測是否為擋截函數。
    if(strcmpi(pHookApi->szFunc,?(char*)pByName->Name)?==?0)?
      ?{?
            MEMORY_BASIC_INFORMATION?mbi_thunk;
            // 查詢修改頁的信息。
            VirtualQuery(pRealThunk,?&mbi_thunk,?sizeof(MEMORY_BASIC_INFORMATION));?
    //
    改變修改頁保護屬性為 PAGE_READWRITE 。
            VirtualProtect(mbi_thunk.BaseAddress,mbi_thunk.RegionSize,?PAGE_READWRITE,?&mbi_thunk.Protect);?
    //
    保存原來的 API 函數地址。
       ?  if(pHookApi->pOldProc?==?NULL)?
    pHookApi->pOldProc?=?(PROC)pRealThunk->u1.Function;?
     ?//修改API函數IAT項內容為替代函數地址。
    pRealThunk->u1.Function?=?(PDWORD)pHookApi->pNewProc;?
    //
    恢復修改頁保護屬性。
    DWORD?dwOldProtect;?
           VirtualProtect(mbi_thunk.BaseAddress,?mbi_thunk.RegionSize,?mbi_thunk.Protect,?&dwOldProtect);?
         ?}?
    }?
     ?pOrigThunk++;?
     ?pRealThunk++;?
    }?
      SetLastError(ERROR_SUCCESS);?//設置錯誤為ERROR_SUCCESS,表示成功。
      return?TRUE;?
       }?
       (4)? 定義替代函數,此實例中只給 MessageBoxA recv 兩個 API 進行擋截。代碼如下:
        static?int?WINAPI?MessageBoxA1?(HWND?hWnd?,?LPCTSTR?lpText,?LPCTSTR?lpCaption,?UINT?uType)
        {
         // 過濾掉原 MessageBoxA 的正文和標題內容,只顯示如下內容。
    return?MessageBox(hWnd,?"Hook?API?OK!",?"Hook?API",?uType);?
        }?
        static?int?WINAPI?recv1(SOCKET?s,?char?FAR?*buf,?int?len,?int?flags?)
        {
        // 此處可以擋截游戲服務器發送來的網絡數據包,可以加入分析和處理數據代碼。
        return?recv(s,buf,len,flags);
        }?
       (5)? KeyboardProc 函數中加入激活擋截 API 代碼,在 if(?wParam?==?0X79?) 語句中后面加入如下 else?if 語句:
        ......
        // 當激活 F11 鍵時,啟動擋截 API 函數功能。
        else?if(?wParam?==?0x7A?)
        {?
         HOOKAPI?api[2];
    api[0].szFunc?="MessageBoxA";//
    設置被擋截函數的名稱。
    api[0].pNewProc?=?(PROC)MessageBoxA1;//
    設置替代函數的地址。
    api[1].szFunc?="recv";//
    設置被擋截函數的名稱。
    api[1].pNewProc?=?(PROC)recv1;?//
    設置替代函數的地址。
    //
    設置擋截 User32.dll 庫中的 MessageBoxA 函數。
    HookAPIByName(GetModuleHandle(NULL),"User32.dll",&api[0]);
    //
    設置擋截 Wsock32.dll 庫中的 recv 函數。
    HookAPIByName(GetModuleHandle(NULL),"Wsock32.dll",&api[1]);
        }
        ......?
       (6)? ActiveKey.cpp 中加入頭文件聲明 ?"#include?"wsock32.h" ?工程菜單中選擇設置,彈出Project?Setting對話框,選擇Link標簽,在對象/庫模塊中輸入Ws2_32..lib。
       (7)? 重新編譯 ActiveKey 項目,產生 ActiveKey.dll 文件,將其拷貝到 Simulate.exe 目錄下。運行 Simulate.exe 并啟動全局鉤子。激活任意應用程序,按 F11 鍵后,運行此程序中可能調用 MessageBoxA 函數的操作,看看信息框是不是有所變化。同樣,如此程序正在接收網絡數據包,就可以實現封包功能了。
    ?

    posted on 2007-03-08 17:23 SIMONE 閱讀(4523) 評論(2)  編輯  收藏 所屬分類: C++

    FeedBack:
    # 招聘游戲平臺插件外掛設計人員
    2008-06-23 13:54 | wangwang
    招聘游戲平臺插件外掛設計人員
    計算機相關專業
    有相關游戲平臺插件外掛設計經驗,需要有實戰經驗
    我公司已經獲得投資資金支持,實力雄厚,服務器已經多達上百臺,可以來我公司實地查看和交流,專職兼職均可
    能力強可以提供較大的發展機會,提供收入提成
    需要良好的團隊合作和溝通技能,強烈的責任感及團隊合作意識
    我公司在上海,詳細請聯系我1350-19568-14
      回復  更多評論
      
    # re: 外掛開發中的封包技術
    2011-04-19 11:47 | zzz
    adsaa  回復  更多評論
      
    主站蜘蛛池模板: 亚洲乱理伦片在线观看中字| 免费鲁丝片一级在线观看| 亚洲AV第一成肉网| 亚洲an天堂an在线观看| 亚洲AV伊人久久青青草原| 最新仑乱免费视频| 最近免费mv在线电影| 中文字幕视频免费在线观看| 国产成人综合亚洲| 亚洲精品午夜国产va久久| 77777_亚洲午夜久久多人| 亚洲成AV人片在线观看ww| 亚洲一级特黄大片在线观看| 免费jjzz在线播放国产| 好大好深好猛好爽视频免费| 青青青国产在线观看免费网站 | 无码专区AAAAAA免费视频| fc2成年免费共享视频18| 国产亚洲高清在线精品不卡| 亚洲国产欧洲综合997久久| 亚洲一区动漫卡通在线播放| 亚洲毛片一级带毛片基地| 久久久久久亚洲精品成人| 亚洲VA中文字幕无码一二三区 | 亚洲不卡影院午夜在线观看| 亚洲国产精品久久网午夜| 亚洲精品成人网站在线播放| 亚洲国产女人aaa毛片在线| 久久精品国产精品亚洲下载| 亚洲综合另类小说色区色噜噜| 国产一区二区三区免费在线观看| 精品久久免费视频| 性做久久久久免费观看| 四虎在线播放免费永久视频 | 亚洲av永久无码精品秋霞电影影院| 中文字幕人成人乱码亚洲电影| 中文字幕亚洲一区二区三区| 国产av无码专区亚洲av果冻传媒 | 一本岛v免费不卡一二三区| 精品国产免费一区二区三区| 中国极品美軳免费观看|