• <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>

            Xiao.Zhu C++

            Xiao.Zhu C++

              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              29 隨筆 :: 14 文章 :: 17 評論 :: 0 Trackbacks
            通過對動作模擬技術(shù)的介紹,我們對游戲外掛有了一定程度上的認(rèn)識,也學(xué)會了使用動作模擬技術(shù)來實(shí)現(xiàn)簡單的動作模擬型游戲外掛的制作。這種動作模擬型游戲外掛有一定的局限性,它僅僅只能解決使用計(jì)算機(jī)代替人力完成那么有規(guī)律、繁瑣而無聊的游戲動作。但是,隨著網(wǎng)絡(luò)游戲的盛行和復(fù)雜度的增加,很多游戲要求將客戶端動作信息及時(shí)反饋回服務(wù)器,通過服務(wù)器對這些動作信息進(jìn)行有效認(rèn)證后,再向客戶端發(fā)送下一步游戲動作信息,這樣動作模擬技術(shù)將失去原有的效應(yīng)。為了更好地 外掛 這些游戲,游戲外掛程序也進(jìn)行了升級換代,它們將以前針對游戲用戶界面層的模擬推進(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í)現(xiàn)方法也很多,如擋截 WinSock 、擋截 API 函數(shù)、擋截消息、 VxD 驅(qū)動程序等。在此我們也不可能在此文中將所有的封包技術(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 動態(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è)動態(tài)鏈接庫提供的一系列函數(shù),由這些函數(shù)實(shí)現(xiàn)對網(wǎng)絡(luò)的訪問。有了這層的認(rèn)識,問題就好辦多了,我們可以制作一個(gè)類似的動態(tài)鏈接庫來代替原 WinSock 接口庫,在其中實(shí)現(xiàn) WinSock32.dll 中實(shí)現(xiàn)的所有函數(shù),并保證所有函數(shù)的參數(shù)個(gè)數(shù)和順序、返回值類型都應(yīng)與原庫相同。在這個(gè)自制作的動態(tài)庫中,可以對我們感興趣的函數(shù)(如發(fā)送、接收等函數(shù))進(jìn)行擋截,放入外掛控制代碼,最后還繼續(xù)調(diào)用原 WinSock 庫中提供的相應(yīng)功能函數(shù),這樣就可以實(shí)現(xiàn)對網(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庫中的所有函數(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 <windows.h>
                #include <stdio.h>
                #include "wsock32.h" 
                添加DllMain函數(shù),在此函數(shù)中首先需要加載原WinSock庫,并獲取此庫中所有函數(shù)的地址。代碼如下:
                BOOL WINAPI DllMain (HANDLE hInst,ULONG ul_reason_for_call,LPVOID lpReserved)
                {
                 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ù),在此可以對我們感興趣的函數(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)
                 {
                // 在此可以對發(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);
                // buffer 數(shù)據(jù)包數(shù)據(jù)進(jìn)行分析后,對其按照玩家的指令進(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 對話框,選擇 Link 標(biāo)簽,在 對象 / 庫模塊 中輸入 Ws2_32.lib
               (6) 、編譯項(xiàng)目,產(chǎn)生 wsock32.dll 庫文件。
               (7) 、將系統(tǒng)目錄下原 wsock32.dll 庫文件拷貝到被外掛程序的目錄下,并將其改名為 wsock.001 ;再將上面產(chǎn)生的 wsock32.dll 文件同樣拷貝到被外掛程序的目錄下。重新啟動游戲程序,此時(shí)游戲程序?qū)⑾燃虞d我們自己制作的 wsock32.dll 文件,再通過該庫文件間接調(diào)用原 WinSock 接口函數(shù)來實(shí)現(xiàn)訪問網(wǎng)絡(luò)。上面我們僅僅介紹了擋載 WinSock 的實(shí)現(xiàn)過程,至于如何加入外掛控制代碼,還需要外掛開發(fā)人員對游戲數(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)對應(yīng)用程序調(diào)用的包括 WinSock API 函數(shù)在內(nèi)的所有 API 函數(shù)的擋截。如果您的外掛程序僅打算對 WinSock 的函數(shù)進(jìn)行擋截的話,您可以只選擇使用上小節(jié)介紹的擋截 WinSock 技術(shù)。隨著大量外掛程序在功能上的擴(kuò)展,它們不僅僅只提供對數(shù)據(jù)包的擋截,而且還對游戲程序中使用的 Windows API 或其它 DLL 庫函數(shù)的擋截,以使外掛的功能更加強(qiáng)大。例如,可以通過擋截相關(guān) API 函數(shù)以實(shí)現(xiàn)對非中文游戲的漢化功能,有了這個(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)起來會很繁瑣,也不穩(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í),它就會顯示 “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)プ⒁猓摱沃邪恍┍环Q為輸入地址表( IAT Import Address Table )的數(shù)據(jù)列表。每個(gè)用隱式方式加載的 API 所在的 DLL 都有一個(gè) IAT 與之對應(yīng),同時(shí)一個(gè) API 的地址也與 IAT 中一項(xiàng)相對應(yīng)。當(dāng)一個(gè)應(yīng)用程序加載到內(nèi)存中后,針對每一個(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ù)代碼
              從上面對 PE 文件格式的分析可知,修改調(diào)用 API 函數(shù)代碼其實(shí)是修改被調(diào)用 API 函數(shù)在輸入地址表中 IAT 項(xiàng)內(nèi)容。由于 Windows 系統(tǒng)對應(yīng)用程序指令代碼地址空間的嚴(yán)密保護(hù)機(jī)制,使得修改程序指令代碼非常困難,以至于許多高手為之編寫 VxD 進(jìn)入 Ring0 。在這里,我為大家介紹一種較為方便的方法修改進(jìn)程內(nèi)存,它僅需要調(diào)用幾個(gè) Windows 核心 API 函數(shù),下面我首先來學(xué)會一下這幾個(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)程根本不會訪問到替代函數(shù)代碼。注入方法有很多,如利用全局鉤子注入、利用注冊表注入擋截 User32 庫中的 API 函數(shù)、利用 CreateRemoteThread 注入(僅限于 NT/2000 )、利用 BHO 注入等。因?yàn)槲覀冊趧幼髂M技術(shù)一節(jié)已經(jīng)接觸過全局鉤子,我相信聰明的讀者已經(jīng)完全掌握了全局鉤子的制作過程,所以我們在后面的實(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)用來存儲被擋截 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].VirtualAddress == 0) return NULL; 
                 // 定位第一個(gè) IAT 位置。  
                 PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pDOSHeader + (DWORD)(pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)); 
                 // 根據(jù)輸入庫名稱循環(huán)檢查所有的 IAT ,如匹配則返回該 IAT 地址,否則檢測下一個(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) 

              // 檢測此 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; 
               // 檢測是否為擋截函數(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í),啟動擋截 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對話框,選擇Link標(biāo)簽,在對象/庫模塊中輸入Ws2_32..lib
               (7)  重新編譯 ActiveKey 項(xiàng)目,產(chǎn)生 ActiveKey.dll 文件,將其拷貝到 Simulate.exe 目錄下。運(yùn)行 Simulate.exe 并啟動全局鉤子。激活任意應(yīng)用程序,按 F11 鍵后,運(yùn)行此程序中可能調(diào)用 MessageBoxA 函數(shù)的操作,看看信息框是不是有所變化。同樣,如此程序正在接收網(wǎng)絡(luò)數(shù)據(jù)包,就可以實(shí)現(xiàn)封包功能了。
            posted on 2007-04-28 15:56 Xiao.Zhu 閱讀(758) 評論(0)  編輯 收藏 引用

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


            婷婷久久综合| 久久99国产精一区二区三区| 久久久久这里只有精品| 亚洲欧美国产日韩综合久久| 欧美伊人久久大香线蕉综合| 久久国产色AV免费看| 94久久国产乱子伦精品免费| 亚洲国产精品狼友中文久久久| 亚洲国产精品无码久久久不卡| 久久久久国产精品| 久久婷婷五月综合国产尤物app| 久久国产精品-国产精品| 色偷偷88欧美精品久久久| 97超级碰碰碰久久久久| 久久青青草原精品国产软件| 97久久天天综合色天天综合色hd| 久久久久香蕉视频| 亚洲国产精品久久久久婷婷老年 | 精品人妻久久久久久888| 91久久香蕉国产熟女线看| 久久不见久久见免费视频7| 久久亚洲精品无码观看不卡| 99久久婷婷免费国产综合精品| 久久99久国产麻精品66| 久久亚洲2019中文字幕| 精品久久久久久久久久久久久久久| 久久精品九九亚洲精品| 色欲久久久天天天综合网| 婷婷久久综合| 色诱久久av| 久久中文精品无码中文字幕| 99久久国产综合精品网成人影院| 久久精品亚洲中文字幕无码麻豆| 久久无码AV一区二区三区| 亚洲国产小视频精品久久久三级| 久久久久久A亚洲欧洲AV冫| 国内精品久久久久久久涩爱| 国产国产成人久久精品| 久久精品夜色噜噜亚洲A∨| 国产欧美久久一区二区| 久久99国产精品99久久|