1. 程式人生 > >初級遊戲外掛編程詳解 windows運行原理+遊戲輔助編程 遊戲外掛編程

初級遊戲外掛編程詳解 windows運行原理+遊戲輔助編程 遊戲外掛編程

穩定 程序員 操作系統 上下 open 服務 dll stdio.h 五個

詳解遊戲輔助編程

【目錄】

1-什麽是Windows API

2-Windows進程

3-Windows 的內存的運行原理

4-windows 中句柄的概念

5-Windows的變量類型

6-輔助實現的原理

7-編程實現遊戲輔助

8-怎樣查找內存地址

9-總結

準備軟件:VC,CheatEngineer5.5

學習這部分內容,你必須要掌握C語言的基礎知識,非常基礎的語法就行了。這篇文章的內容適合剛開始接觸編程的人,高手請飄過。

【1】什麽是windows API

Windows API 中文翻譯過來就是windows應用程序接口(Application Programming Interface)

我們知道,我們在使用C/C++的時候,會包含很多的頭文件,例如最常用的stdio.h,裏面有很多函數的聲明,例如使用scanf函數能輸入數據,使用printf函數能打印文本到屏幕上顯示出來。又例如math.h裏面有很多數學計算函數,如sqrt求平方根的函數。我們只要包含這些頭文件,就能調用這些函數。只要給函數傳遞相應的參數,就能實現我們想要的效果,這些函數就是C語言給我們提供的“接口”。

所謂windows應用程序接口,其實就是windows提供給程序員的一組函數。

Windows api 包含幾千個可調用的函數,這些函數能讓我們程序員操作系統的方方面面。

我們就可以調用者幾千個函數實現各種功能。如調用ExitWindowsEx來關閉計算機,CopyFile來復制文件,MessageBox來彈出系統提示框,用BitBlt來繪制圖像等等。而調用這幾千函數的條件,就是在代碼的開頭加上#include <windows.h>就行了,因為絕大部分的函數聲明都在windows.h裏面,我們只要包含了這個頭文件,就能夠使用這些函數。

所以可以這麽理解

Windows API =windows提供的幾千個函數

【windows進程】

關於進程,我想大家都應該熟悉這個名詞。在應用程序無響應無法關閉的時候,我們常常使用CTRL + ALT +DELETE來叫出任務管理器

我們可以看到一個進程列表,其中列出了所有正在運行的進程

技術分享圖片

進程的概念:進程就是一個正在執行的程序。

假如我們要運行一個程序,首先找到文件(如qq.exe)或者快捷方式,雙擊打開。操作系統就會把這個程序的文件從硬盤上加載到內存中,等加載完畢後,CPU開始執行包含在程序中的代碼,然後系統進程列表多了一個qq.exe,這就是進程——正在運行的應用程序。等到這個程序qq.exe運行結束了,系統就會釋放這個程序在內存中占用的空間,並且從進程列表裏移除這個程序。

進程的特點:每個進程都是相互獨立的,互不幹擾的。通常情況下,一個進程只能操作自己的代碼和數據,無法對其他進程造成影響。這也保證了windows系統運行的穩定性。

進程=正在內存中運行的程序

【windows內存的運行原理】

這個是外掛編程的最重要部分,我們分幾個方面討論:1 什麽是內存?2 進程的邊界--虛擬內存空間 3 打開進程的邊界

什麽是內存

內存是計算機中重要的部件之一,它是與CPU進行溝通的橋梁,其存取速度比硬盤快很多倍。內存對應是電腦的內存條,能存儲數據,但是與硬盤不同,在內存中的數據一斷電就會消失,並且存取數據的速度是硬盤的幾十倍,所以將程序加載到內存中運行將大大提高程序的運行速度。現在一般的電腦內存1GB,2GB,4GB或者更高的8GB。

計算機中所有程序的運行都是在內存中進行的,因此內存的性能對計算機的影響非常大,並且CPU只能執行和處理在內存中代碼和資源。

程序的存在形式實際上是這樣的:我們的程序文件,首先是以二進制存在於硬盤上的,有的遊戲特別大,幾個GB,當我們打開資源管理器時,查看的,其實就是硬盤上的文件。當我們運行程序時,程序會適當的從硬盤加載自己需要的數據,然後開始運行。

由於內存比較少,當程序不需要這些數據的時候,就會從內存中釋放一些不需要的資源,以保證內存的充足。

總的看來當執行一個程序的時候,系統會將exe文件中的代碼和資源從硬盤加載到內存中,加載完成後,CPU開始執行程序入口點的代碼,一個程序開始運行。

程序結束時,將釋放所有這個進程占用的資源,以避免內存的浪費。

概括來說:內存,其實存儲運行中的程序代碼和數據的地方(CPU只能處理內存中的代碼和數據),當進程結束時,該進程所有占用的內存空間將被系統釋放。

進程的邊界--虛擬內存空間、

我們設想一下,假如一個系統中有很多的程序在運行,我們只有一個內存空間,這樣的話,一個程序在讀寫數據的過程中,由於程序自己本身的缺陷,錯誤的讀寫了其他程序的數據,這樣就很容易影響其他程序,造成其他程序的崩潰。這當然是我們不願意見到的。

為了解決程序之間使用內存互相幹擾的問題,於是便有了虛擬內存。

Windows的虛擬內存機制為每個進程分配了4GB的虛擬內存空間。這裏我們不免產生疑問,我們的實際物理內存空間只有1GB或者2GB的,等等,怎麽能給每個進程分配4GB內存空間呢?

實際上,這4GB空間最下面的64KB是空指針賦值區域,系統保留。內存最上面的2G被系統占用。我們能夠訪問和使用的,也只有中間這一部分內存而已。這部分內存將近2GB,我想對所有程序來說,存儲代碼和數據都是夠用的。

但是每個進程都有這麽大內存可以用,這些內存空間從何而來呢。Windows在執行一個程序的時候,為了節約內存空間,將程序內存中暫時用不到的數據,以二進制存儲到硬盤上(這些二進制文件其實就是頁面文件)。等到需要的時候,再從硬盤上加載到內存中,這樣就以犧牲少量CPU運行時間為代價,將硬盤當做內存使用。使得多程序同時執行成為了可能。這4GB中的數據,有的是存在於硬盤上的,有的是存在於內存中,所以說,每個進程的4GB內存空間,是“虛擬”內存。

虛擬內存的地址一般用十六進制表示,以字節為單位,地址範圍是0x00000000~0xFFFFFFFF(0x表示十六進制)。

由於這樣的機制,我們不難發現,進程之間就不能相互影響,保證了各進程的穩定性。例如有兩個進程A和B,進程A可以在它自己內存空間0x12345678地址存儲數據,進程B也可以在0x12345678地址處存儲數據。當進程A訪問0x12345678處的內存時,訪問的是進程A的內存空間;當進程B訪問0x12345678處的內存時,訪問的是進程B的內存空間。進程A無法訪問位於進程B的內存空間中的數據,反之亦然。

這就是進程的邊界--每個進程分配了4GB的虛擬內存空間,它們在自己的內存空間內運行,不會相互幹擾,保護了系統和各進程的穩定性。

為什麽要以16進制表示地址

編程中,我們一般用十進制表示一個數,因為C/C++是高級語言。

例如x=78;
不過,由於數據在計算機中的表示,最終以二進制的形式存在,所以有時候使用二進制,可以更直觀地解決 問題。但二進制數太長了。比如int 類型占用4個字節,32位。比如100,用int類型的二進制數表達將是:  
0000 0000 0000 0000 0110 0100   
面對這麽長的數進行思考或操作,沒有人會喜歡。因此,C,C++ 沒有提供在代碼直接寫二進制數的方法。用16進制可以解決這個問題。因為,進制越大,數的表達長度也就越短。

例如同樣一個地址。用十進制和十六效果對比下:

十進制 十六進制
1258965451 4B0A49CB

5600 000015E0

85693 00014EBD

8965231 0088CC6F

這樣可以看到用十六進制表現更直觀一些。並且,在很多軟件中,數值的表示都是用十六進制的,例如WinHex,CE,OllyDBG,幾乎所有與內存有關的數據都是用十六進制表示。用十六進制最大的優點就是縮小了表達長度。

打開進程的邊界

當然所有的事情不是絕對的,我們有很多時候也需要訪問其他進程中的數據。例如,殺毒軟件要監視其他進程的行為,防止其他進程做有害系統的事,殺毒軟件往往就會在其他進程裏註入自己的代碼,以此來監視其他進程的行為。這就是訪問其他進程數據的最好例子。

當然,想要訪問其他進程的內存空間,需要強大的windows api。我們知道,windows api是系統提供給程序員的一組強大的函數,幾乎能實現任何關於系統的任何操作。

我們要打開進程的邊界,修改其他進程的數據,當然需要調用相應的函數。

我們的目的很明確,就是要修改其他進程(如遊戲進程)的數據。

下面的一些函數是訪問其他進程數據和外掛編程必備的函數,在後面介紹外掛編程的時候,我會詳細的介紹這幾個函數,現在先熟悉一下。

1. 查找窗口函數

FindWindow

2. 通過窗口獲取進程ID函數

GetWindowThreadProcessId

3. 打開進程函數

OpenProcess

4. 寫其他進程數據的函數

WriteProcessMemory

5. 讀其他進程數據的函數

ReadProcessMemory

6. 關閉句柄的函數

CloseHandle

這六個函數,就是寫一個簡單外掛的所有函數。在後面我會詳細說明打開進程邊界的步驟,以及如何使用這些函數。

【windows 中句柄的概念】

句柄,是整個windows編程的基礎。一個句柄是指使用的一個唯一的整數值,即一個四字節長的數值,來標誌系統中的不同對象。

打個比喻,一個學校有很多很多的學生,為了識別這些學生,我們會給每個學生分配一個學號。當我們要找一個學生的時候,我們只需要知道這個學生的學號,然後查閱相關信息就能找到這個學生。

同樣,在windows系統中有很多很多的對象,如一個窗體,一個進程,一個圖標,一張圖片,甚至一塊內存空間等。Windows 給它們都分配了不同的一個unsigned int型數(即無符號整型數)來標識和區分它們。這個標識號,就叫做句柄。

我們可以通過系統分配的這個標識號,也就是句柄,來訪問這些對象。

例如,我們可以通過一個窗口的句柄,找到這個窗口,然後可以修改這個窗口的大小,標題名字等等。

我們可以通過一個進程的句柄,來結束這個進程。

我們可以通過一張圖片的句柄,來將這張圖片畫在屏幕上。

等等等等。

句柄,就是系統中每個對象的標識號,我們可以通過這些標識號,來訪問相應的系統對象,如窗體,進程等等。

【windows變量類型】

在windows編程中,我們往往會看到很多變量類型,如HANDLE,HWND,BYTE,DWORD等等。

這些變量類型是什麽呢,和我們熟悉的char,short,int等等的變量類型有什麽區別呢。

而經常要用到的 句柄 HANDLE類型,實質上是無類型指針void,HANDLE定義為:

typedef PVOID HANDLE;

HANDLE實際上就是一個PVOID,那PVOID又是什麽呢?

Typeef void *PVOID;

PVOID就是指向void的指針(void *)。

所以HANDLE = void*

那為什麽要多此一舉呢,直接用void*代替就行了,為什麽要用HNADLE呢?其實,這是為了讓程序員更能讀懂程序。只要看到變量類型就能知道這個變量是用來幹什麽的。

例如:我要用a,b來表示長方形的寬和高,如果都用int型我們要花費一番功夫才能理解這些意思。

但是我們分別定義兩種變量類型Width,Height就能一目了然了。

typedef int Width;

typedef int Height;

Width a; //定義width型變量a,一眼就能看出a變量是來表示寬度的

Height b; /定義wheight型變量b,一眼就能看出b變量是來表示高度的

聲明特定的變量類型其好處就不言而喻了,就是為了讓程序員更好的理解任何變量的作用,使人一目了然。

下面我們需要知道的變量類型以及這些變量類型的作用;

HANDLE =void * HANDLE型變量是一個對象句柄

HWND =void *HANDLE WINDOW 窗口句柄

DWORD = unsigned int 無符號整型,windows通常用來表示一個對象的序號ID

BYTE =unsigned char 無符號字符型,0255

我們做外掛,只需要了解上面3種windows數據類型就行。

另外我需要聲明一點:void *型的變量,一般都是用來表明內存地址的,

如 void * Ptr=0x12345678

這個ptr指針,表示的是0x12345678這個內存地址

指針我現在不想多說了,這部分在後面的編程中會看到

【輔助的的實現原理】

我們現在做的只是遊戲外掛,有一句古話說的好:知己知彼,方能百戰不殆。我們要做遊戲的輔助,就要先知道遊戲是怎樣運行的。

我們知道,一個遊戲進程的有很多數據,例如,一個角色的HP,一個角色的經驗,他的金錢,等級,以及裝備都是通過變量來存儲的。我們只要找到這些變量在內存中的地址,然後通過某種方法去修改這些數的數值,就能達到修改遊戲的目的。

我們先來說說這些遊戲中德數據,怎麽判斷是什麽類型,怎麽得到在內存中占的空間大小。

例如,一個人的經驗一般用int型變量(4字節)來存儲,為什麽呢?

因為int型變量(4字節)的取值範圍是2147483648~2147483647,而short型變量(2字節)的範圍是,由於short的範圍太小,而很多遊戲的經驗值一般都超過這個範圍,例如在地下城與勇士的遊戲中,我的經驗值是108866674523,一千多萬,所以,在這個遊戲中,一個人物的經驗值是必須是int型的,用short型變量會超出範圍導致程序運行出錯。

我們為什麽要知道這些變量占多大內存空間呢?那是因為我們在修改其他進程的數據的時候,我們必須首先要有三個參數:1:在哪個地址 2要修改成多少 3有多大的內存數據要被修改。例如:我們要修改的地址是0x00EFFAE0,要修改成1000000,由於這個變量是int型的,所以,有4字節的內存數據要被修改。這很容易讓我們想到遊戲程序的源代碼裏面有這條代碼

int exp; //人物經驗

然後&exp就等於0x00EFFAE0

好吧,開始進入正題了。遊戲輔助一般分為兩大部分:

一就是找出我們想要的內存地址。

二就是寫程序去修改這個內存地址的數到一個相應的數值。

我們先不談第一個部分,因為第一個部分變化性大,因為很多遊戲做了不同程度的保護,使我們找內存地址大費周折,例如CS。不過也有遊戲沒做任何保護,例如,植物大戰僵屍。但是,我們寫程序修改其他進程內存中的數據,這個方法是不變的。所以我先來說說如何修改其他進程中的內存數據。

第一步:查找遊戲窗口句柄

第二步:通過窗口句柄,獲取目標進程的ID

第三步:通過目標進程的ID,打開目標進程,獲得句柄

第四步:通過目標進程的句柄,修改目標進程的內存數據

第五步:關閉目標進程

我想有必要說一下,第一步,第二步是為第三步服務的,我們要修改某一進程的內存數據,必須獲得這個進程在系統的身份證,即進程的句柄。獲取一個進程的句柄有很多方法,我在這裏說的第一步,第二步,第三步是最常用的獲取目標進程句柄的方法。

但是,為什麽如此曲折的才能獲得目標進程的句柄呢,我想,主要和我們要使用的windows api 有關。

接下來我們就要具體說說這些強大的windows api函數了。我們之前說的所有知識,很多windows運行原理,都是為了理解下一節這些函數的調用。

並且下一節用到的函數較多,我們只有經常使用,我們才能掌握它們。

[編程實現輔助]

上一節我們說了:要修改一個進程的內存數據必須先獲得這個進程的句柄,就像我們要找一個人一樣,我們可以通過這個人的身份證,知道這個人住在哪裏,才能找到這個人。在windows系統裏面也一樣,我們只有知道這個進程的身份證--句柄,系統才能找到這個進程,並相應的按照我們的需求修改數據。

在開始說輔助編程之前,我先要說說一個很有用的函數,這個函數就是MessageBox,先來看看這個函數有什麽效果。

技術分享圖片 技術分享圖片

是不是有種熟悉的感覺,沒錯這就是我經常看見的windows提示,現在我們程序員可以自由操作windows提示。現在來具體說說MessageBox函數

首先來看看函數原型

int WINAPI MessageBox(

HWND hWnd,//消息窗口父窗口句柄

LPCTSTR lpText,//顯示的消息內容

LPCTSTR lpCaption, //消息框的標題

UINT uType);//消息框風格

第一個參數:消息框的父窗口句柄,為了方便,我們可以設為NULL,不影響輔助的使用。

第二個參數:顯示的消息內容,上面圖片示例中遊戲已經運行,遊戲沒有運行都屬於消息內容

第三個參數:消息窗口標題,上面圖片示例中都是“提示”。

第四個參數:消息框的風格,現在我只介紹三個常量

MB_OK:表示有確定按鈕

MB_ICONINFORMATON:表示有信息圖標,上左圖

MB_ICONERROR:表示有錯誤圖標上右圖

用 ” | ”符號同時使用多種風格,例如使用MB_OK|MB_ICONINFORMATION,確定按鈕和信息圖標,上左圖所示。MB_OK|MB_ICONERROR,確定按鈕和錯誤圖標,上右圖所示。

下面,進入正題我們來說說修改其他進程內存數據的第一,二,三步——獲取目標進程的句柄,並且學習相應的函數。

第一步:獲取目標遊戲窗口的句柄

在windows中,一個窗口的句柄數據類型是HWND,就是handle window的簡寫,我們可以這樣,前面我們已經說過windows變量類型的概念,HWND其實就是void*類型的,這裏我就不多說了。重點我們知道怎麽用。

Window API裏面有這麽一個函數,函數原型如下

HWND FindWindow(

LPCSTR lpClassName,

LPCSTR lpWindowName);

我們可以知道,返回值HWND類型的變量是存儲一個窗口的句柄的。那麽LPCSTR是什麽變量類型呢?

LPCSTR=char *,這個變量類型其實就是char *類型,是一個字符串的指針,以後我們只要看到LPCSTR類型的變量,我們就可以知道這個變量是存儲一個字符床地址的指針的。這就是聲明很多變量類型的好處,看到這個變量是什麽類型的,就知道這個變量是用來幹什麽的。

它有兩個參數,兩個參數都是字符串的指針

第一個參數lpClassName,這是要查找窗口的類名,關於窗口的類名,這裏沒有說明,主要是因為我們可以將此參數設為NULL,也基本不影響我們查找遊戲窗口的句柄。有興趣的可以看看《windws核心編程第5版》,上面說的很詳細。

第二個參數lpWindowName,這個就是主要參數了,目標窗口的標題,例如,植物大戰僵屍的窗口標題就是“植物大戰僵屍中文版”,紅色警戒窗口的標題就是“Red Alert 2”.但是有很多遊戲是全屏幕顯示的,不是窗體形式的,沒有標題,那我們怎麽知道它的標題呢?其實有一種很簡單的方法,遊戲全屏運行後,就是按開始菜單鍵將遊戲最小化。然後將鼠標移動到任務欄的遊戲圖標上,系統就會提示該窗體的標題。

知道了這兩個參數,我們可以這麽寫代碼調用這個函數。

HWND gameWindow=FindWindow(NULL,”植物大戰僵屍中文版”);

當這行代碼執行完畢後我們gameWindow這個變量就保存遊戲窗口的句柄。但是我們需要註意,當目標進程沒有運行,也就是不存在窗體標題為”植物大戰僵屍中文版”的窗體時,遊戲系統找不到這個窗口,FindWindow調用失敗,返回值為0,即gameWindow為0。所以這個函數,可以判斷遊戲有沒有運行。加上下面代碼就有這個效果

If(gameWindow==NULL)

//提示遊戲沒有運行

Else

//提示遊戲已運行

第二步:通過窗口句柄,獲得進程ID

Windows api提供了這樣一個函數,函數定義如下

DWORD GetWindowThreadProcessId(

HWND hWnd,

LPDWORD lpdwProcessId

);

我們可以看到,這個函數只有兩個參數,第一個參數,就是目標窗口的句柄(上例中的gameWindow),我們只要填上我們獲取到的窗口句柄就OK

第二個參數LPDWORD,LP代表指針,DWORD代表unsigned int,所以這個參數就代表unsigned int *,是一個無符號整數型的指針。那麽這個參數是什麽呢?

每個進程不僅有自己的句柄,還有自己的序號,在系統中叫ID,這個ID是DWORD型整數,在windows中就叫ProcessID

技術分享圖片

上面這張圖就是我用tasklist命令列舉出系統中正在運行的進程,上面的PID(ProcessID)就是該進程的標識號,可以看到,wininit.exe進程的ID是516 ,csrss.exe進程的ID是528,等等。我們可以通過目標窗口的句柄,知道這個窗體是屬於哪個進程的,然後通過這個函數我們就可以知道,這個窗體所在進程的ID,我們可以這樣使用。

DWORD pid;

GetWindowThreadProcessID(gameWindow,&pid);

第二個參數傳入一個DWORD變量的地址就行了。

當這行代碼運行完畢後。pid就自動填充了目標窗體所在進程的ID。

第三步通過目標進程的ID,打開目標進程,獲得句柄

Windows api提供了這樣一個函數

HANDLE OpenProcess(

DWORD dwDesiredAccess, //渴望得到的訪問權限(標誌)

BOOL bInheritHandle, // 是否繼承句柄

DWORD dwProcessId// 進程標示符即,進程ID

);

首先它的返回值是HANDLE類型的,返回值就是目標進程的句柄。再來看看這三個參數。

第一個參數,我們想要得到的訪問權限。這裏我們使用常量PROCESS_ALL_ACCESS,這個是在windows.h裏面定義的常量,註意要全部大寫。用了這個常量,就等於我們對系統說:“我要獲取對該進程操作的所有權限”,例如讀寫內存空間等等。等這個函數調用成功後,我們就可以獲取對該進程進行任何操作了。

第二個參數,我們不考慮,設為NULL。

第三個參數,就是我們獲得的進程ID。

我們可以這麽調用:

HANDLE hProcess=OpenPrcess(PROCESS_ALL_ACCESS,NULL,pid);

這樣這行代碼運行後,hProcess被填充了目標進程的句柄。當然,如果這個函數因為某種原因調用失敗的話hProcess就為NULL,所以我們可以加上下面的錯誤處理代碼

If(hProcess==NULL)

//提示打開進程失敗

好現在總結一下這個步驟。

我們先通過FindWindow找出目標窗體的句柄

再通過GetWindowThreadProcessId獲得目標進程的ID

最後通過OpenPocess打開進程,獲得句柄

最後需要特別註意一點,在windows xp上這麽寫代碼會運行正常,但是在windows7或者windows8上,程序必須要管理員權限。FindWindow會因為你沒有管理員權限而調用失敗,最後導致你的程序無法正確的獲得目標進程句柄。

第四步修改進程的內存數據

這是最關鍵的一步了,修改內存數據。前面我們已經說過,外掛主要分為兩個重要步驟,第一個是找內存地址,哪些數據是我們要修改的,如一個人的金錢,經驗,屬性,等級。我們要找出這些數據在進程中的內存地址。第二步就是寫程序去修改這些數據。寫程序修改數據的方法是一成不變的,但是找內存地址卻有很大的技巧性。這裏限於篇幅就不多說了,後面我會簡單的介紹一下找代碼的原理,然後我會推薦一些專門的文章給大家看的。

Window提供了下面一個函數來修改進程的內存數據,函數定義如下

BOOL WriteProcessMemory(

HANDLE hProcess,//目標進程句柄

LPVOID lpBaseAddress,//目標進程寫入地址

LPVOID lpBuffer,//自己進程中緩沖區地址

DWORD nSize,//緩沖區大小

LPDWORD lpNumberOfBytesWritten//實際數據長度,設為NULL

);

返回值是BOOL型,返回TRUE調用成功,FALSE調用失敗

第一個參數:目標進程的句柄,我們可以通過前三步獲得目標進程的句柄

第二個參數:目標寫入的起始地址,即要將數據寫到目標進程哪個位置。這個就是我們找到的內存地址,例如一個人的血量內存地址是0x40000000

第三個參數:寫入的緩沖區地址

第四個參數;寫入的緩沖區大小

這第三個,第四個參數是什麽意思呢?其實WriteProcessMemory工作原理是這樣的

技術分享圖片

我們將自己進程中的數據,拷貝到其他進程中。自己這個緩沖區的地址就是第三個參數lpBuffer,緩沖區的大小事第四個參數nSize。這個函數將我們進程中lpBuffer地址起始處nSize大小的數據,原封不動的拷貝到目標進程的 lpBaseAddress(第二個參數)處。

假如,我們找到遊戲進程人物金錢的地址是0x40000000,,我們就可以這樣寫代碼

Int Money=10000000;//定義一個變量後自己進程已經分配一塊區域存這個變量

WriteProcessMemory(hProcess,(LPVOID)0x40000000,(LPVOID)&Money,,(DWORD)4,NULL);

第一個參數我們填入目標進程的句柄

第二個參數是目標進程的寫入地址,編譯器認識“0x”,知道0x40000000是一個16進制的數,我們將這個數強制轉換成LPVOID型,也就是void*型,無值型指針

第三個參數我們填入了我們進程Money變量的地址,並且將這個變量地址強制轉換成LPVOID型。

第四個參數由於在c++中int型變量默認占四個字節內存空間,所以我們填入4,並將這個數轉換成LPVOID型

第五個參數 填NULL

這就是WriteProcessMemory的用法,功能強大,能寫其他進程的數據。

這樣,我們就完成了第四步,寫遊戲內存數據,我們再次回到遊戲會發現,遊戲中相應的數據已經變成我們想要的數值了,看下圖,植物大戰僵屍修改後的結果

技術分享圖片

第五步關閉進程句柄

我們需要調用CloseHandle函數來關閉我們打開的進程,這個函數的使用方法很簡單。

CloseHandle(hProcess);

它只有一個參數------要關閉的句柄這裏我們填入我們打開的進程句柄即可。

至此我們已經看到了一個完整的程序代碼

我們來回顧一下

技術分享圖片

我想沒有什麽能比真實的遊戲輔助源代碼更好的了解外掛了,下面,我將給出Vc++6.0環境下,植物大戰僵屍輔助的真實源代碼。我把修改代碼放到了ChangeGame函數裏面,我們只要在main函數或者WinMain中調用這個函數就ok

void ChangeGame()

{

//通過標題獲取窗口

HWND gameWindow=FindWindow(NULL,"植物大戰僵屍中文版");

if(!gamewindow)

MessageBox(NULL,”遊戲未運行”,”錯誤”,MB_OK|MB_ICONERROR);

//獲取進程標示符pid

DWORD pid;

GetWindowThreadProcessId(gameWindow,&pid);

//打開進程

HANDLE hprocess=OpenProcess(PROCESS_ALL_ACCESS,0,pid);

//修改數據

int sun=56789;

WriteProcessMemory(hprocess,(void *)0x1429E2B0,&sun,4,0);

//關閉進程

CloseHandle(hprocess);

}

[怎樣查找內存地址]

CE是一個強大的工具,它的搜索速度非常快,一般的遊戲程序用它來搜索內存能在短短數秒內搜索幾百萬甚至幾千萬個內存地址。為我們編寫外掛提供了很大的幫助。

好,廢話不多說,邊上圖邊解說,先來學學簡單的內存修改,熟悉熟悉CE的操作。

第一步:選擇目標進程

技術分享圖片 技術分享圖片

先點擊左上角的第一個按鈕,會出現右圖進程列表。該列表中列舉出了系統中所有正在運行的程序。我們選擇植物大戰僵屍程序(PlantVsZombies.exe)。這樣就完成了選擇目標進程。

第二部:搜索陽光的內存地址

先運行植物大戰僵屍,開始一局遊戲,我們的目的是要修改陽光。於是先看看陽光現在是多少,150,很好(下左圖)。我們在CE值裏面的填入150,點擊首次掃描(下右圖)

技術分享圖片 技術分享圖片

看CE左邊的地址欄,我們可以看到,我們用CE搜索到了145個地址(下左圖)。這說明在這個遊戲中,有這麽多個地址的值是150.。但是我們所需要的陽光的變量,只是這麽多其中的一個,所以我們種一顆植物,現在陽光變成了50,然後我們輸入50,點擊再次掃描(下右圖)

技術分享圖片 技術分享圖片

再次掃描其實是在上次掃描的結果中,再次搜索這些結果的值。當我們通過遊戲操作修改我們所需要的變量的值的時候,通過再次搜索就很容易把我們想要的變量找出來。之後會出現下面的結果

技術分享圖片 技術分享圖片

我們可以看到,結果地址裏面只有一個了,沒錯,這個搜索到的地址0x1429E2B0就是我們要找到陽光的地址。我們雙擊這個地址,這個地址會出現在下面。的編輯列表裏,我們再雙擊編輯列表裏這個地址的值,輸入56789,點確定。這個時候我們回遊戲就會發現已經變成我們想要的值了。

好了,這是找內存的最簡單的方法。但是還有一些問題。

第一個問題:

事實上,有很多遊戲都做了一些保護措施。例如你的植物大戰僵屍裏面金幣顯示的是1120,但是實際上在內存中這個值是112。如果你一開始搜1120,並且按照這個方法搜下去的話會一個地址也搜不到。相同的例子還有很多。例如俠盜飛車人物的血量上面顯示的是87,在內存中這個數值是17023+87,流星蝴蝶劍中你看到你的血是230,但是在內存中你的血的值是2300等等。

第二個問題:

如果你重啟植物大戰僵屍後會發現,陽光變量的地址發生了改變,變成0x143780B4,這是為什麽呢。因為植物大戰僵屍中,存儲陽光的變量地址是動態分配的,每次分配的地址都是不一樣呢。

這就是編程中的局部變量,這個局部變量所屬的函數執行完了,這個局部變量就會從內存中釋放。等下次再次執行這個函數時,這個局部變量又會從內存中重新分配空間。

這樣每次運行植物大戰僵屍,由於動態分配,每次陽光變量的的地址都是不一樣的。

所以說,搜索也是有一定復雜性的。第一個問題可以通過模糊搜索,多次搜索解決。第二個問題,可以通過查找變量的偏移地址,計算出準確的陽光地址(這個需要ReadProcessMemory函數)或者通過反匯編。

雖然聽起來很復雜,但是這些搜索技巧其實都是很簡單的,這裏就不一一介紹了。因為要說搜索內存的方法的話,說清楚沒有幾千字是不行的。這篇文章主要是說外掛編程的原理,關於內存搜索的方法,我找到幾篇很好的文章,大家看一看就足夠了:

CE找內存偏移地址圖文教程:

http://bbs.52miji.com/thread-1224-1-1.html

CE搜索內存技巧:

http://www.v5pc.com/thread-572-1-1.html

關於找內存地址就說到這裏。

[總結]

遊戲輔助,其實就是用來修改遊戲,方便玩家玩遊戲的的一個軟件工具。

這篇文章所說的遊戲修改都是初級的遊戲修改,只涉及到修改內存變量。事實上,像高級的遊戲修改,例如消除冷卻時間,修改遊戲界面,改變遊戲運行邏輯等等,這些知識還遠遠不夠。這些都需要最低級而又最強大的匯編語言來解決。通過反匯編逆向分析遊戲代碼,修改遊戲代碼而達到令人咋舌效果。

例如LOL盒子,至少運用了DLL遠線程註入,函數擋截,反匯編代碼修改等等技術,所以能夠給遊戲添上一些輔助功能。

學習編程的道路很漫長,有很多的非常有用知識不是課堂上能學到的,等著你自己去發現,去挖掘。等你深入到某一領域的時候,你會發現,這裏還有非常廣闊的天空,你要用一生的時間去探索它,掌握它。

初級遊戲外掛編程詳解 windows運行原理+遊戲輔助編程 遊戲外掛編程