給自己喜歡的遊戲升級

看雪資料發表於2004-10-27

偶有一個非常喜歡的遊戲,英雄無敵III,99年出的遊戲,現在連她的發行公司都破產了,這樣老掉牙的東西當然沒人更新樂(雖然現在有一批俄羅斯發燒友在做MOD)。但是能不能自己給她加入新的樂趣呢?
   怎麼不行泥~WE ARE CRACKERS !下面是偶給這個遊戲做的一個補丁的部分功能的過程。想想當初學CRACK的時候就是看著各位大大的文章長大的,現在有空也寫點什麼吧,但是水平很屁,純屬拋磚引玉:)
   英雄無敵這個遊戲有5個難度,但是作為一個骨灰玩家,這第5個難度已經不能滿足偶的被虐待欲了,於是想到為這個做遊戲增加難度的補丁。
   加大難度最直接的方法當然就是給電腦更多的錢和資源,和電腦英雄更多的經驗。於是製作這個補丁就分為3個步驟:
1,找出程式中加資源和戰鬥後加經驗的地方
2,寫自己的增加資源和經驗的函式,並做成DLL
3,修改遊戲EXE檔案,在遊戲中呼叫自己寫的在DLL中的函式
   LET’S GO!第一步很簡單,執行遊戲,在記憶體中找出存放錢和經驗的的地址,然後叫出SOFTICE,在找到的記憶體地址上設下斷點。切換到遊戲,度日(遊戲中每過一日就會根據你佔據的領地加錢和資源),SOFTICE跳出:
:0054ECD7 cmp dword ptr [ebp-20], 00000007
:0054ECDB jge 0054ECFA
:0054ECDD mov edx, dword ptr [ebp-20]
:0054ECE0 mov eax, dword ptr [ebp-1C]
:0054ECE3 mov ecx, dword ptr [eax+4*edx]
:0054ECE6 mov edx, dword ptr [ebp-20]
:0054ECE9 mov eax, dword ptr [ebp-18]
:0054ECEC add ecx, dword ptr [eax+4*edx]
:0054ECEF mov edx, dword ptr [ebp-20]
:0054ECF2 mov eax, dword ptr [ebp-1C]
:0054ECF5 mov dword ptr [eax+4*edx], ecx
:0054ECF8 jmp 0054ECCE
:0054ECFA jmp 0054EC79
這裡就是加資源和錢的迴圈,由於有6種資源和錢,所以這個迴圈要迴圈7次,每次增加一種資源。可以透過修改這裡的程式碼來讓電腦獲得更多的資源,但是必須要知道當前寫的記憶體單位是不是代表電腦的資源,如果不管就會導致連自己的資源也增加了,這樣起不到增強電腦的效果。
於是必須要找到遊戲中資料的絕對定址(虛擬地址)。資源這種東西在程式中肯定會為它分配一個永久的空間,而不會是在區域性棧中分配,也不會在堆中分配。
因此這是一個全域性變數。(即使C++中類把它的成員封裝了,但是編譯時還是會透過NAME-MANGLING生成一個全域性唯一的識別符號)
:00458098 imul edx, 00000168
:0045809E mov eax, dword ptr [00826D40]
:004580A3 lea ecx, dword ptr [eax+edx+00020AD0]
:004580AA mov edx, dword ptr [ebp-30]
:004580AD mov eax, dword ptr [ebp-34]
:004580B0 mov esi, dword ptr [ebp-30]
:004580B3 mov eax, dword ptr [eax+4*edx+0000009C]
從這裡知道資源的定址:
[82B0B4] * 168 + [826D40] + 20AD0 + 9C + 資源號 * 4
可以看出,這個定址是在編譯後就確定下來的。
然後找出判別是否為電腦的標誌:
[826D40] + 20AD0 + [82B0B4] * 168 + E2
   這裡可以看出一點物件導向的影子,這個遊戲聽說是用VC做的。[826D40] + 20AD0 是一個類的所有物件基地址,應該是每一個勢力就是這個類一個物件,並且編譯器為每個物件分配的資料空間為168H個位元組。[82B0B4]就是勢力的編號。知道了這些之後就可以進入第二步了。
   寫一個函式,作用是先判斷是不是電腦,如果是,就為它加上額外的資源。
入口引數為當前的勢力編號好了:
DLLIMPORT void AddResource(int iPlayerID);
函式中把透過上面兩個定址完成判斷和增加資源的任務。同樣加經驗的函式也是如此。是不是很簡單:)
寫完所有函式之後,就編譯成一個DLL
標頭檔案如下
#ifndef _PACKDll_H_
#define _PACKDll_H_

#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */


DLLIMPORT void Initialization();
DLLIMPORT int MakeMonster(int );
DLLIMPORT int ChangMntNmbToID(int );
DLLIMPORT void AddResource(int );
DLLIMPORT int AddExp(int );
DLLIMPORT int Remark(int );

#endif /* _DLL_H_ */
可以看出有6個出口函式。
   完成第二步進入第三步,在遊戲的EXE檔案中加入引用自己寫的DLL中函式的程式碼。首先是修改EXE的引入表,增入自己的DLL,這樣程式在執行的開始才會連線偶寫的DLL。
   用PE編輯工具LordPE開啟HEROES3.EXE,選擇引入表,回發現程式所有DLL和引入函式。增加一個DLL,輸入DLL中的所有引出函式名,LordPE就幫你把引入表做好了,是不是太簡單鳥?糊里糊塗就搞定了?THUNKRVA 是什麼?THUNKVAULE又是什麼東東?那看看真正的引入表是怎樣的吧:
43 66 50 61 63 6B 2E 64 6C 6C 00 00 00 49 6E 69
74 69 61 6C 69 7A 61 74 69 6F 6E 00 00 01 4D 61
6B 65 4D 6F 6E 73 74 65 72 00 00 02 43 68 61 6E
67 4D 6E 74 4E 6D 62 54 6F 49 44 00 03 00 41 64
64 52 65 73 6F 75 72 63 65 00 04 00 41 64 64 45
78 70 00 05 00 52 65 6D 61 72 6B 00 0B 10 47 00 
1C 10 47 00 2A 10 47 00 3C 10 47 00 4A 10 47 00 
53 10 47 00 
第一行43 66 50 61 63 6B 2E 64 6C 6C是這個DLL的名字,也就是CfPack.dll
然後有一個00,是名字的結束標誌。接下來的0000就是標誌第一個函式的開始
也是這個函式在DLL中的序號。49 6E 69 74 69 61 6C 69 7A 61 74 69 6F 6E 就是第一個引入函式的名字:Initialization接下來是結束附00,然後又是下一個函式。一共6個函式,到第6行的6B(紅色)結束。
下來是什麼呢?就是每個函式的THUNKVALUE也就是每個出口函式的符號在引入表中的地址。如6B接下來的0B 10 47,就是第一個函式的符號的地址。也就是第一行中紅色的00的地址0047100B,下來00是分隔符,然後是第二個引入函式符號的地址0047101C,指向第二行的紅字部分。這樣一共6個地址,分別指向6個引入函式的名字。
搞定引入表後,就可以該原始碼了。

   首先,在HEROES3.EXE中找到空白的部分,如
:007AB425 int 03
:007AB426 int 03
:007AB427 int 03
:007AB428 int 03
:007AB429 int 03
:007AB42A int 03
:007AB42B int 03
:007AB42C int 03
:007AB42D int 03
:007AB42E int 03
:007AB42F int 03
...
這裡有一大段空白的部分
然後跳到加資源的地方:
:0054ECD7 cmp dword ptr [ebp-20], 00000007
:0054ECDB jge 0054ECFA
:0054ECDD mov edx, dword ptr [ebp-20]
:0054ECE0 mov eax, dword ptr [ebp-1C]
:0054ECE3 mov ecx, dword ptr [eax+4*edx]
:0054ECE6 mov edx, dword ptr [ebp-20]
:0054ECE9 mov eax, dword ptr [ebp-18]
:0054ECEC add ecx, dword ptr [eax+4*edx]
:0054ECEF mov edx, dword ptr [ebp-20]
:0054ECF2 mov eax, dword ptr [ebp-1C]
:0054ECF5 mov dword ptr [eax+4*edx], ecx
:0054ECF8 jmp 0054ECCE
:0054ECFA jmp 007AB46D 
蘭色的部分被改了,跳到了空白的地方,然後,呵呵,怎麼搞都可以鳥!
把原來的int 03改成以下程式碼
:007AB46D 8B4DF8                  mov ecx, dword ptr [ebp-08]
:007AB470 51                      push ecx

* Reference To: CfPack.AddResource, Ord:0003h

:007AB471 FF1568108700            Call dword ptr [00871068]
:007AB477 83C404                  add esp, 00000004
:007AB47A E9FA37DAFF              jmp 0054EC79
:007AB47F CC                      int 03
呼叫CfPack.AddResource後跳會去,神不知,鬼不覺就執行給電腦加資源的過程,呵呵:)
這裡的Call dword ptr [00871068]呼叫的CfPack.AddResource地址是怎麼知道啊?[00871068]就是這個函式的地址了,這個地址是由引入表中的THUNKRVA:471068(系統在裝入DLL之後填寫的該函式介面的具體地址)加上程式裝如地址400000H得到的。
同理,其他功能也可以由類似方法完成。
當然了,還要寫一個圖形介面用來設定新的難度等級,把設定寫在一個檔案中,然後在DLL中加入讀取設定的函式Initialization()放在遊戲執行開始的地方執行就OK了。
呵呵,新的難度下又終於可以被電腦虐待了~~-_-!!
PS:這個補丁在英雄世界上可以下載。
http://www.heroworld.net/heroes4sit...Files/H3-AI.rar

相關文章