(8).編譯項目ActiveKey,生成ActiveKey.DLL和ActiveKey.lib。
接著,我們還需要創建一個外殼程序將全局鉤子安裝了Windows系統中,這個外殼程序編寫步驟如下:
(1).創建一個對話框模式的應用程序,項目名為Simulate。
(2).在主對話框中加入一個按鈕,使用ClassWizard為其創建CLICK事件。
(3).將ActiveKey項目Debug目錄下的ActiveKey.DLL和ActiveKey.lib拷貝到Simulate項目目錄下。
(4).從“工程”菜單中選擇“設置”,彈出Project Setting對話框,選擇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).啟動畫筆程序,選擇文本工具并將筆的顏色設置為紅色,將鼠標放在任意位置后,按F10鍵,畫筆程序自動移動
鼠標并寫下一個紅色的大寫R。圖一展示了按F10鍵前的畫筆程序的狀態,圖二展示了按F10鍵后的畫筆程序的狀態。
圖一:按F10前狀態(001.jpg)
圖二:按F10后狀態(002.jpg)
?
五、封包技術
通過對動作模擬技術的介紹,我們對游戲外掛有了一定程度上的認識,也學會了使用動作模擬技術來實現簡單的動作
模擬型游戲外掛的制作。這種動作模擬型游戲外掛有一定的局限性,它僅僅只能解決使用計算機代替人力完成那么有規律
、繁瑣而無聊的游戲動作。但是,隨著網絡游戲的盛行和復雜度的增加,很多游戲要求將客戶端動作信息及時反饋回服務
器,通過服務器對這些動作信息進行有效認證后,再向客戶端發送下一步游戲動作信息,這樣動作模擬技術將失去原有的
效應。為了更好地“外掛”這些游戲,游戲外掛程序也進行了升級換代,它們將以前針對游戲用戶界面層的模擬推進到數
據通訊層,通過封包技術在客戶端擋截游戲服務器發送來的游戲控制數據包,分析數據包并修改數據包;同時還需按照游
戲數據包結構創建數據包,再模擬客戶端發送給游戲服務器,這個過程其實就是一個封包的過程。
封包的技術是實現第二類游戲外掛的最核心的技術。封包技術涉及的知識很廣泛,實現方法也很多,如擋截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
#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;
//獲取原創建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);
}
.......其它函數定義代碼略。