現(xiàn)在很多游戲都是把一些信息存入內(nèi)存單元的,那么我們只需要修改具體內(nèi)存值就能修改游戲中的屬性,很多網(wǎng)絡(luò)游戲也不外于此。
曾幾何時(shí),一些網(wǎng)絡(luò)游戲也是可以用內(nèi)存外掛進(jìn)行修改的,后來被發(fā)現(xiàn)后,這些游戲就把單一內(nèi)存地址改成多內(nèi)存地址校驗(yàn),加大了修改難度,不過仍然可以通過內(nèi)存分析器可以破解的。諸如“FPE”這樣的軟件便提供了一定的內(nèi)存分析功能。
“FPE”是基于內(nèi)存外掛的佼佼者,是家喻戶曉的游戲修改軟件。很多同類的軟件都是模仿“FPE”而得到玩家的認(rèn)可。而“FPE”實(shí)現(xiàn)的技術(shù)到現(xiàn)在都沒有公開,很多人只能夠通過猜測(cè)“FPE”的實(shí)現(xiàn)方法,實(shí)現(xiàn)同類外掛。筆者也曾經(jīng)模仿過“FPE”實(shí)現(xiàn)相應(yīng)的功能,如“內(nèi)存修改”、“內(nèi)存查詢”等技術(shù)。稍后會(huì)對(duì)此技術(shù)進(jìn)行剖析。
既然要做內(nèi)存外掛,那么就必須對(duì)Windows的內(nèi)存機(jī)制有所了解。計(jì)算機(jī)的內(nèi)存(RAM)總是不夠用的,在操作系統(tǒng)中內(nèi)存就有物理內(nèi)存和虛擬內(nèi)存之分,因?yàn)槌绦騽?chuàng)建放入物理內(nèi)存的地址都是在變化的,所以在得到游戲?qū)傩詴r(shí)并不能夠直接訪問物理內(nèi)存地址。在v86模式下,段寄存器使用方法與實(shí)模式相同,那么可以通過段寄存器的值左移4位加上地址偏移量就可以得到線性地址,而程序創(chuàng)建時(shí)在線性地址的中保留4MB-2GB的一段地址,游戲中屬性便放于此。在windows中把虛擬內(nèi)存塊稱之為頁(yè),而每頁(yè)為4KB,在訪問內(nèi)存時(shí)讀取游戲?qū)傩詴r(shí),為了不破壞數(shù)據(jù)完整性的快速瀏覽內(nèi)存地址值,最好一次訪問一頁(yè)。
在操作進(jìn)程內(nèi)存時(shí),不需要再使用匯編語言,Windows中提供了一些訪問進(jìn)程內(nèi)存空間的API,便可以直接對(duì)進(jìn)程內(nèi)存進(jìn)行操作。但初學(xué)者一般掌握不了這一項(xiàng)技術(shù),為了使初學(xué)者也能夠?qū)?nèi)存進(jìn)行操作,做出基于內(nèi)存控制的外掛,筆者把一些內(nèi)存操作及一些內(nèi)存操作邏輯進(jìn)行了封裝,以控件形式提供給初學(xué)者。控件名為:MpMemCtl。
初學(xué)者在使用此控件時(shí),要先安裝外掛引擎控件包(在此后的每篇文章中外掛引擎控件包僅提供與該文章相應(yīng)的控制控件),具體控件安裝方式,請(qǐng)參閱《Delphi指南》,由于篇幅所限,恕不能詳細(xì)提供。
在引擎安裝完成后,便可以在Delphi中的組件欄內(nèi),找到[MP GameControls]控件組,其中可以找到[MpMemCtl]控件。初學(xué)者可以使用此控件可以對(duì)內(nèi)存進(jìn)行控制。
一、 得到進(jìn)程句柄
需要操作游戲內(nèi)存,那么首先必須確認(rèn)要操作的游戲,而游戲程序在運(yùn)行時(shí)所產(chǎn)生的每一個(gè)進(jìn)程都有一個(gè)唯一的句柄。
使用控件得到句柄有三種方法:
1、 通過控件打開程序得到句柄。
在控件中,提供了startProgram方法,通過該方法,可以打開程序得到進(jìn)程句柄,并且可以返回進(jìn)程信息。
PProcInfo: PROCESS_INFORMATION;
MpMemCtl.startProgram(
FilePath:String; //程序路徑
var aProc_Info:PROCESS_INFORMATION //進(jìn)程信息
):BOOLEAN
該方法提供了兩個(gè)參數(shù),第一個(gè)參數(shù)為要打開的程序路徑,第二個(gè)參數(shù)為打開程序后所創(chuàng)建進(jìn)程的進(jìn)程信息。使用這個(gè)方法在得到進(jìn)程信息的同時(shí),并給控件的ProcHandle(進(jìn)程句柄)屬性進(jìn)行了附值,這時(shí)可以使用控件直接對(duì)內(nèi)存進(jìn)程讀寫操作。其應(yīng)用實(shí)例如下:
Var
PProcInfo: PROCESS_INFORMATION;
begin
MpMemCtl1.startProgram(edit1.Text, PProcInfo)
2、通過控件根據(jù)程序名稱得到句柄。
在控件中,對(duì)系統(tǒng)運(yùn)行進(jìn)程也有了相應(yīng)的描述,控件提供了兩個(gè)方法,用于根據(jù)程序名稱得到相應(yīng)的進(jìn)程句柄。getProcIDs()可以得到系統(tǒng)現(xiàn)在所運(yùn)行的所有程序的名稱列表。getProcID()可以通過所運(yùn)行程序名稱,得到相應(yīng)進(jìn)程的句柄。
getProcIDs():TStrings //所返回為多行字符串型
getProcID(
aProcName:String //應(yīng)用程序名稱
):Thandle; //應(yīng)用程序進(jìn)程句柄
其應(yīng)用實(shí)例如下:
首先可以通過getProcIDs()并把參數(shù)列表返回ComboBox1.Items里:
ComboBox1.Items:=MpMemCtl1.getProcIDs();
接著可以通過getProcID()得到相應(yīng)的進(jìn)程句柄,并給控件的ProcHandle(進(jìn)程句柄)屬性進(jìn)行了附值,這時(shí)可以使用控件直接對(duì)內(nèi)存進(jìn)程讀寫操作。
MpMemCtl1.getProcID(ComboBox1.Text)
3、通過控件根據(jù)窗口名稱得到句柄。
在控件中,控件提供了兩個(gè)方法,用于根據(jù)窗口名稱得到相應(yīng)的進(jìn)程句柄。可以通過getALLWindow()得到所有在進(jìn)程中運(yùn)行的窗口。getWinProcHandle()可以通過相應(yīng)的窗口名稱,得到相應(yīng)的進(jìn)程的句柄。
getALLWindow(
aHandle:THandle //傳入當(dāng)前窗口的句柄
):TStrings; //返回當(dāng)前所有運(yùn)行窗口的名稱
getWinProcHandle(
aWindowName:String //傳入當(dāng)前窗口名稱
):Thandle; //返回窗口的句柄
其應(yīng)用實(shí)例如下:
首先可以通過getALLWindow ()并把參數(shù)列表返回ComboBox1.Items里:
ComboBox1.Items:=MpMemCtl1. getALLWindow(Handle);
接著可以通過getWinProcHandle ()得到相應(yīng)的進(jìn)程句柄,并給控件的ProcHandle(進(jìn)程句柄)屬性進(jìn)行了附值,這時(shí)可以使用控件直接對(duì)內(nèi)存進(jìn)程讀寫操作。
MpMemCtl1. getWinProcHandle (ComboBox1.Text);
二、使游戲暫停
在程序中,為了便于更好的得到游戲的當(dāng)前屬性。在控件中提供了游戲暫停方法。只需要調(diào)用該方法,游戲便可以自由的暫停或啟動(dòng)。該方法為:pauseProc()
pauseProc(
aType:integer //控制類型
)
控制類型只能夠傳入?yún)?shù)0或1,0代表使游戲暫停,1代表取消暫停。其應(yīng)用實(shí)例如下:
MpMemCtl1.pauseProc(0); //暫停游戲
MpMemCtl1.pauseProc(1); //恢復(fù)暫停
三、讀寫內(nèi)存值
游戲?qū)傩云鋵?shí)寄存在內(nèi)存地址值里,游戲中要了解或修改游戲?qū)傩裕梢酝ㄟ^對(duì)內(nèi)存地值的讀出或?qū)懭胪瓿伞?
通過控件,要讀寫內(nèi)存地址值很容易。可以通過調(diào)用控件提供的getAddressValue()及setAddressValue()兩個(gè)方法即可,在使用方法之前,要確認(rèn)的是要給ProcHandle屬性進(jìn)行附值,因?yàn)閷?duì)內(nèi)存的操作必須基于進(jìn)程。給ProcHandle屬性附值的方法,在上文中已經(jīng)介紹。無論是對(duì)內(nèi)存值進(jìn)行讀還是進(jìn)行寫,都要明確所要操作的內(nèi)存地址。
getAddressValue( //讀取內(nèi)存方法
aAddress:pointer; //操作的內(nèi)存地址
var aValue:integer //讀出的值
):Boolean;
setAddressValue( //寫入內(nèi)存方法
aAddress:pointer; //操作的內(nèi)存地址
aValue:integer //寫入的值
):Boolean;
要注意的是,傳入內(nèi)存地址時(shí),內(nèi)存地址必須為Pointer型。其應(yīng)用實(shí)例如下:
讀取地址值(如果“主角”等級(jí)所存放的地址為4549632):
var
aValue:Integer;
begin
MpMemCtl1.getAddressValue(Pointer(‘4549632’),aValue);
這時(shí)aValue變量里的值為內(nèi)存地址[4549632]的值。
寫入地址值:
MpMemCtl1.setAddressValue(Pointer(Strtoint(‘4549632’)),strtoint(87));
通過該方法可以把要修改的內(nèi)存地址值改為87,即把“主角”等級(jí)改為87。
四、內(nèi)存地址值分析
在游戲中要想要到游戲?qū)傩源娣诺膬?nèi)存地址,那么就對(duì)相應(yīng)內(nèi)存地址進(jìn)行內(nèi)存分析,經(jīng)過分析以后才可得到游戲?qū)傩源娣诺娜舜娴刂贰?
控件提供兩種基于內(nèi)存地址的分析方法。一種是按精確地址值進(jìn)行搜索分析,另一種是按內(nèi)存變化增減量進(jìn)行搜索分析。
1、 如果很明確的知道當(dāng)前想要修改的地址值,那么就用精確地址值進(jìn)行搜索分析
在游戲中,需要修改人物的經(jīng)驗(yàn)值,那么首先要從游戲畫面上獲得經(jīng)驗(yàn)值信息,如游戲人物當(dāng)前經(jīng)驗(yàn)值為9800,需要把經(jīng)驗(yàn)值調(diào)高,那么這時(shí)候就需要對(duì)人物經(jīng)驗(yàn)值在內(nèi)存中搜索得到相應(yīng)的內(nèi)存地址,當(dāng)然很可能在內(nèi)存中地址值為9800的很多,第一次很可能搜索出若干個(gè)地址值為9800的地址。等待經(jīng)驗(yàn)值再有所變化,如從9800變?yōu)榱?0000時(shí),再次進(jìn)行搜索,那么從剛剛所搜索到的地址中,便可以進(jìn)一步獲得范圍更少的內(nèi)存地址,以此類推,那么最后可得到經(jīng)驗(yàn)值具體存放的地址。
如要用控件來實(shí)現(xiàn)內(nèi)存值精確搜索,其實(shí)方法很簡(jiǎn)單,只需要調(diào)用該控件的Search()方法即可。但是在搜索之前要確認(rèn)搜索的范圍,正如前文中所說:“而程序創(chuàng)建時(shí)在線性地址的中保留4MB-2GB的一段地址”,所以要搜索的地址應(yīng)該是4MB-2GB之間,所以要把控件的MaxAddress屬性設(shè)為2GB,把控件的MinAddress屬性設(shè)為4MB。還有一個(gè)需要確認(rèn)的是需要搜索的值,那么應(yīng)該把SearchValue屬性設(shè)置為當(dāng)前搜索的值。如果需要顯示搜索進(jìn)度那么可以把ShowGauge屬性掛上一個(gè)相應(yīng)的TGauge控件(該控件為進(jìn)度條控件)。
search(
isFirst:Boolean //是否是第一次進(jìn)行搜索
):Boolean
在搜索分析時(shí)為了提高搜索效率、實(shí)現(xiàn)業(yè)務(wù)邏輯,那么需要傳入一個(gè)參數(shù),從而確認(rèn)是否是第一次進(jìn)行內(nèi)存。其應(yīng)用實(shí)例如下:
maxV:=1024*1024*1024;
maxV:=2*MaxV;
minV:=4*1024*1024;
V:=StrToInt(Edit1.Text);
with MpMemCtl1 do
begin
MaxAddress:=maxV;
MinAddress:=minV;
SearchValue:=SeaarchV;
ShowGauge:=Gauge1;
Search(first)
end;
if first then first:=false;
2、 如果不明確當(dāng)前想要修改的地址值,只知道想要修改的值變大或變小,那么就按內(nèi)存變化增減量進(jìn)行搜索分析。
如有些游戲的人物血值不顯示出來,但要對(duì)人物血值進(jìn)行修改,那么只有借助于內(nèi)存量增減變化而進(jìn)行搜索分析出該人物血值存放的地址。如果人物被怪物打了一下,那么人物血值就會(huì)減少,那么這時(shí)候就用減量進(jìn)行搜索分析,如果人物吃了“血”人物血值就會(huì)增加,那么這時(shí)候就用增量進(jìn)行搜索分析。經(jīng)過不斷搜索,最后會(huì)把范圍放血值的內(nèi)存地址給搜索出來。
如要用控件來實(shí)現(xiàn)內(nèi)存值精確搜索,其實(shí)方法很簡(jiǎn)單,只需要調(diào)用該控件的compare()方法即可。MaxAddress、MinAddress屬性設(shè)置上面章節(jié)中有詳細(xì)介紹,在此不再重提。在此分析中不需要再指定SearchValue屬性。如果需要顯示搜索進(jìn)度那么可以把ShowGauge屬性掛上一個(gè)相應(yīng)的TGauge控件。
compare (
isFirst:Boolean //是否是第一次進(jìn)行搜索
aType:Integer //搜索分析類型
):Boolean
在搜索分析時(shí)為了提高搜索效率、實(shí)現(xiàn)業(yè)務(wù)邏輯,那么需要傳入一個(gè)參數(shù),從而確認(rèn)是否是第一次進(jìn)行內(nèi)存。搜索分析類型有兩種:如果參數(shù)值為0,那么就代表增量搜索。如果參數(shù)值為1,那么就代表減量搜索。其應(yīng)用實(shí)例如下:
if RadioButton1.Checked then v:=0
else v:=1;
maxV:=1024*1024*1024;
maxV:=2*MaxV;
minV:=4*1024*1024;
with MpMemCtl1 do
begin
MaxAddress:=maxV;
MinAddress:=minV;
ShowGauge:=Gauge1;
compare(first,v);
end;
if first then first:=false;
五、得到內(nèi)存地址值
在控件中,提供獲得分析后內(nèi)存地址列表的方法,只需要調(diào)用getAddressList()方法,便可以獲得分析過程中或分析結(jié)果地址列表。但如果使用的是按內(nèi)存變化增減量進(jìn)行搜索分析的方法,那么第一次可能會(huì)搜索出來很多的地址,致使返回速度過長(zhǎng),那么建議使用getAddressCount()方法確定返回列表為一定長(zhǎng)度后才給予返回。
getAddressList():TStrings //返回地址字符串列表
getAddressCount():Integer //返回地址字符串列表長(zhǎng)度
其應(yīng)用實(shí)例如下:
if MpMemCtl1.getAddressCount() <100 then
listbox1.Items:=MpMemCtl1.getAddressList();
通過以上五個(gè)步驟,便可以整合成一個(gè)功能比較完備的,基于內(nèi)存控制方法的游戲外掛。有了“FPE”的關(guān)鍵部份功能。利用此工具,通過一些方法,不僅僅可以分析出來游戲?qū)傩詥蝺?nèi)存地址,而且可以分析出一部份多內(nèi)存游戲?qū)傩源娣诺刂贰?
posted on 2007-07-20 17:26
聶文龍 閱讀(1907)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
c++