2015年2月23日 星期一

高級遊戲修改,打造自己的修改器(VC++VB)具搜索功能的修改器

作者:sheep
來源:http://ibf.tw/jSI5Q
備註:如果作者不希望被轉帖到此處,請告知,小弟會馬上刪帖。
-------------------------------------------------------------------------------------  


大陸寫或翻譯的文章,我把它轉繁再貼過來,因為巴哈排版的關係,會有點亂,你可
以去看原本的連結,不過是簡體
一.高級遊戲修改
二.打造自己的遊戲修改器(VC++)
三.打造自己的遊戲修改器(VB)
四.如何編寫通用遊戲修改器 第二版(注:有搜尋比對記憶體功能的完整修改器,這
篇是在寫平常我們用的修改器,如GM8,它們如何搜索,如何比對,如何鎖定和抓圖的
原理,最簡單可以先看)

一.
http://blog.gameres.com/show.asp?BlogID=1596&column=2299
高級遊戲修改
Advanced Game Training by [sheep] a.k.a RECLAIM!
************************************************



(best viewed in 1024x768)


課程 : 2 (1部分)

主題: 代碼注射 - 動態記憶體位址(DMA) 轉為 靜態位址.

需要工具: Softice, 記憶體搜索工具 (呃,選擇) gamehack.



你現在有了一個講解高級遊戲修改的教程... 在後面幾個月中我希望寫一些關於
高級遊戲修改各方面的系列教程......這一課,我將教你一些你需要的技能,
你將可以從 遊戲修改新手 變為 遊戲黑客(Game Hacker)..

在我們開始實際操作之前,我要告訴你,如果你沒有一些基礎知識就來嘗試這篇教
程,你不會有什麼大收穫的...

我建議你最起碼先先看看我以前的教程,包括動態記憶體位址的所有細節..
(nMing- 就是這篇教程的第一部分)
同樣你也需要至少懂得一點 組合語言 的基礎知識...

全部搞掂我們就繼續......


SOFTICE SETUP
*************

當你按 CTRL-D 彈出softice時你應該看到下面的視窗..

REGISTER WINDOW
寄存器視窗 - 這個視窗總是在最上面,顯示所有寄存器的內容..
(WR [return]) (用 WR 打開或關閉)

DUMP WINDOW
資料視窗 - 一般位於最上面那個視窗的附近,分兩邊顯示,一邊是ascii碼
,一邊是十六進位
(WD [return]) (用 WD 打開或關閉)

CODE WINDOW
代碼視窗 - 這是主視窗.. 在資料視窗的下面.. 包含所有將要運行的程
式碼當你彈出softice..
(WC [return]) 代碼是組合語言..
(用 WC 打開或關閉)

LANGUAGE instrutions..

括弧裏的內容是你要輸入的命令以打開不同的視窗..
同樣你需要輸入 CODE ON 將在彙編指令左邊顯示機器碼,右邊是記憶體位址...


LESSON START
************

ok..要完成用代碼注射法去擊敗DMA的最終目標,你需要先學習一些新的概念,
下面我列出了我們將要學習的幾個概念...


i) 代碼注射原理
ii) 偏移量理論
iii) 動態記憶體位址轉靜態位址概要
iv) 代碼注射法的實際操作

一旦你弄懂了前面3個概念你就可以去解決第4個了....祝你好運:))


代碼注射原理
*********************

代碼注射是高級遊戲黑客用來完成一些特定任務時所用的一種方法,是在不能用
一般的修改方法修改時所用的..

代碼注射的主要目的就是在遊戲中創建一條路徑讓它去執行我們自己的代碼...
在遊戲執行完我們的代碼後,一定要讓它跳回去繼續執行遊戲自己的代碼...
代碼的類型是有限制的,是什麼? 當然,就是 彙編 :))

代碼注射是很靈活的,你可以做你想做的任何事...
breaking free of the restrictions of the normal NOP hacks....
(nMing - 這句話我實在不知如何翻譯,:-| )

例如...
下面是一個代碼片斷...這只是一個例子,所以不要緊....


0120:00008096 01585A ADD [EAX+5A],EBX
0120:00008099 E9xxxxxxxx JMP 500000<--- 這裏創建一條通道,通往我們的代碼
0120:0000809B B486 MOV AH,86
0120:0000809F 55 PUSH EBP
0120:000080A0 1E PUSH DS
0120:000080A1 50 PUSH EAX
0120:000080A2 E86E078ED8 CALL D88E8815


0120:00500000 我們的代碼!
0120:00500002 我們的代碼!
0120:00500004 我們的代碼!
0120:00500006 JMP 809B <--- 一旦我們的代碼執行完後就跳回去...

如果你在創建<通道>的時候破壞了任何一條有用的彙編指令,...你就必須在<我們
的代碼>中重新建一條被你破壞掉的指令...不然那很可能這個遊戲就被你毀掉了
...不要忘了這只是它的原理...一個完全詳盡的"怎樣做(HOW TO)"將在[代碼注射
法的實際操作]中講到...

偏移量理論
*************

我們都知道,無論我們開始一個新遊戲或者是重新啟動後, 動態記憶體位址(DMA)
都會引起遊戲中的變數改變位置... 即使這是真的..
DMA 的變數, 實際上它們之間總是呆在相對的同一個位置.. 這是什麼意思?? 我
聽見你在問了? :))

好了.. 我來給你們舉一個例子...


00456777 : LIFE TOTAL(生命總數)
00456778 :
.........:
.........:
etc......:
.........:
00456999 : BULLET TOTAL(子彈總數)


這上面的表是 兩個DMA變數(動態變數) 的假想情形..
我們的生命總數在 456777 這個地址,子彈總數在位址 456999 , 如果你把那兩個
地址相減,
你就會知道什麼叫做 偏移量 了,... 456999h-456777h = 222h (h = hex).
你將會明白為什麼我們需要偏移量了,馬上..

OK!! 我重新啟動我的遊戲!!!

00659123 : LIFE TOTAL(生命總數)
00659124 :
.........:
.........:
etc......:
.........:
00659345 : BULLET TOTAL(子彈總數)

現在我們重新啟動了,變數改變了位置.. 但是!! 注意這個實際的位址 659345h-659123h = 222h.
即使變數被分配到記憶體的不同位置,但是它們之間總是保持著相同的距離..
這就是整個過程的鑰匙..

它們分別放在相同距離的地方的原因,就是每個變數實際上是分配在記憶體中的
某一塊區域裏的...
當遊戲運行的時候,記憶體中將會有一大塊地方包含遊戲所有的變數...

用偏移量我們可以把我們需要的所有變數製作成一幅詳細的地圖.. 我們需要做的
就是找一個變數當作主變數,然後找到這個主變數的位址..
從這個位址你立即就可以知道其他的變數位於記憶體的什麼地方...

另一個例子....

這裏是一幅虛構的偏移量地圖...

HEALTH COUNTER 位置 = MASTER VARIABLE(主變數) <--- 查找這個變數的位址.
BULLETS 位置 = MASTER VARIABLE + 3445h
LIVES 位置 = MASTER VARIABLE + 2345h
SKILL POINTS 位置 = MASTER VARIABLE + 334h

現在.. 我們開始我們的遊戲... 然後查找 HEALTH COUNTER..

000657444 : HEALTH COUNTER

我們現在可以把那幅地圖換成真實的位址...像這樣..

HEALTH COUNTER 位置 = 657444h + 0h = 657444h
BULLETS 位置 = 657444h + 3445h = 65A889h
LIVES 位置 = 657444h + 2345h = 659789h
SKILL POINTS 位置 = 657444h + 334h = 657778h

那麼,現在你可以看見一幅"變數"地圖,只用一個主變數我們就可以用偏移量找到
所有其他的變數 :)) 是個又酷又可愛的東西 :)))

好了.. 要繼續..明白偏移量是非常必要的..如果你不明白那你就上去再重新讀
....


動態記憶體位址(DMA)轉靜態位址概要
*********************

一旦你明白了至少是"代碼注射"的基礎,在我解釋動態記憶體位址轉靜態位址的
原理的時候你就不會有太多的麻煩...

概要講起來應該像這樣 :-

我們在遊戲代碼中找到一個地方,它包含一個指標(POINTER)指向一個我們用到的
變數,這個變數也就是 生命, 健康, 子彈 等等...

一旦我們找到了這個指標,我們就需要確定它是不是一個"特殊的指標"(SPECIFIC POINTER)意思就是它是不是唯一的!! 指向1個位置.. 如果它是一個普通的,它將
迴圈穿過很多地方,會應付很多其他記憶體位址..
它對我們來說沒有用...

一個指標將看起來像這樣.. MOV EAX,[EBX+00000208]
'(nMing - 他所說的指針是EBX+00000208 )

EBX 包含一個DMA的基本位址.. 而 208 是偏移量,再加上來自指標的EBX,指向我
們的變數(生命LIVES)..我們知道 EBX+208 = 生命(LIVES).. 我們現在需要去查
明這是不是一個"特殊的指標"(僅僅指向1個位址),而不是一個普通的指標
(指向和應付很多地址). 我們要做這個就必須設一個中斷點在
EAX,[EBX+00000208] 這條指令上...然後按F5退出softice環境..
每當softice再次彈出時,看看EBX的內容..如果EBX裏的數值改變了,如果是,那麼
這個指標對我們就沒有什麼好處..
如果EBX裏的位址是"特殊的指標",你就應該檢查這個數值至少10 或 20 次,以確定
它沒有改變...

好了,一旦我們找到了我們要的"特殊的指標".. 我們需要把放在EBX裏的 DMA基值
儲藏起來 (注意!! 不會總是EBX包含DMA基值..它可以是其他任何寄存器)
你要看看指針用的是什麼寄存器..

例如..

MOV EAX,[EBX+00000208] <-- EBX 包含DMA基值
MOV EAX,[ECX+00004564] <-- ECX 包含DMA基值
MOV EAX,[EDX+00001122] <-- EDX 包含DMA基值
MOV EAX,[ESI+00000234] <-- ESI 包含DMA基值
MOV EAX,[EDI+00004408] <-- EDI 包含DMA基值

留意上面的所有東西.. 指標能具有很多種,除了上面我想的,還有很多類型,
你會在所有遊戲中遇到...

好了.. 那麼我們需要儲藏 DMA基值 到靜態記憶體中.. 我們要做的是, 創建一
條路徑通向我們的代碼(代碼注射)..
我們的代碼會把放在EBX裏的DMA基值儲藏到靜態位址中.. 那麼我們的修改器就
可以在任何時候讀出這個位址了... 讀時還得加上偏移量..
然後再寫入數值到那個指針..

這裏是一幅圖表用來更好的解釋一下它...

下面,我們來尋找我們的指標...


我們還沒有製作通道之前...
-----------------------------

0041A736 MOV EAX,[EBX+00000208] <----- 指標在這裏...
0041A73C FLD REAL4 PTR [EBX+000000CC]
0041A742 FDIV REAL4 PTR [EBX+000000C8]
0041A748 MOV [00786CA4],EAX

我們做了通道之後...
----------------------------

0041A736 JMP 9000000 <----- 通道做好了..
0041A73C FLD REAL4 PTR [EBX+000000CC]
0041A742 FDIV REAL4 PTR [EBX+000000C8]
0041A748 MOV [00786CA4],EAX



我們自己的代碼...
-------------------

09000005 MOV EAX,[EBX+00000208] ; 重新建立剛才我們毀掉的指令
09000010 MOV [9000100],EBX ; 把存在EBX裏的DMA基值傳送到一個靜態位址..
09000015 JMP 41A73C ; 跳回遊戲的迴圈..
09000100 0000000000 ; 儲藏EBX的位置

當你完成了上面的代碼以後,現在..你就可以從 9000100 這個位址讀出EBX的值..
即使當遊戲中的記憶體位置改變了,
這個代碼也會把正確的值儲藏到 9000100 這個位址裏... 所以,你要做的就是
讀出這個值..再加上偏移量208,就得
到了生命(LIVES)的準確位置... 記住?? lives = EBX+208...

這些幾乎是全部的交易.. 我們還可以從程式碼中讀出 9000100 的數值,我將在
下面的實際操作部分中向你們展示這些...
不要擔心這都不是很難理解... 不管是否討厭你還是得再讀這部分... 為了在
實際操作部分中能在我充分的解釋下把他們全部理解....


代碼注射法的實際操作
***********************************

在這個教程中我選擇了一個叫SPACE TRIPPER的遊戲作為示例...(謝謝keyboardjunkie)
你可以在這裏下載.. http://www.pompom.org.uk/STpage1.htm

讓我們開始吧...

首先我們要做的就是找到一個主變數.. 我選擇 生命(LIVES) ..
不大幸運的就是這個遊戲在找 生命(LIVES) 的值的時候,很煩人...但是,只要
你有一點彙編知識就會好了...(現在你得用到所有東西 :))

好.. 我們用通常的搜索.. 死 .. 搜索死..

如果你做得正確你將找到僅僅一個位址.. 它應該是 786ca4
你可以試試去改這個位址,你會注意到螢幕上的值變了... 但是.你的
生命(LIVES) 仍然和以前一樣,那是因為你沒有找到生命(LIVES)的真正的地址..
別擔心!! 我並不期待什麼.. 我將告訴你為什麼.. 這個遊戲不是在每次開
始新遊戲時分配記憶體,而是在每次你死後分配記憶體....所以,你不可能用記
憶體搜索工具找到真正的值.. 還好,在螢幕上顯示生命(LIVES)的變數的數值是
靜態的... 所以,你就可以靠這個數值很快找到真正的位址...

下面是教你怎麼做...


用剛才找到的那個地址在 softice 裏設一個中斷點,, 像這樣..
輸入 BPM 786ca4 W (回車)

死掉..

softice 彈出來!!!!

你應該看到這個...


0041A736 8B8308020000 MOV EAX,[EBX+00000208]; 指標把生命(LIVES)傳到EAX裏(EAX = 2)
0041A73C D983CC000000 FLD REAL4 PTR [EBX+000000CC]
0041A742 D8B3C8000000 FDIV REAL4 PTR [EBX+000000C8]
0041A748 A3A46C7800 MOV [00786CA4],EAX ;
把生命(LIVES)真正的值傳到用來顯示生命(LIVES的變數中
; (在螢幕上顯示LIVES的變數= 2 )

就像你看到的,[EBX+00000208]是一個指標,用來把生命(LIVES)的真正的位址保
存在記憶體中..
EBX包含著DMA基址,以後我們需要把它儲存到其他地方...
還有,把208跟DMA基址相加就得到生命(LIVES)真正的地址.
( EBX+208=真的生命地址)從真的生命位址中(EBX+00000208)
傳到EAX的值就是在螢幕上顯示生命(LIVES)的值... 要證明這些你就要做以下..

在 softice 裏..

輸入 D EBX+208 (回車)

現在改一下資料視窗裏最上面最靠左的那個數值, 生命(LIVES)的值就會變了..
好,這個就是LIVES的真正的記憶體位址...
真幸運,我們只用一個中斷點就得到了兩個重要的東西... 我們找到了生命
(LIVES)的真正地址... 同樣我們也可以用這種方法看看指標是特殊的指標還是一般的...再次來到 SOFTICE..

首先,先清除所有其他的中斷點 .. 那麼

輸入 BC* (回車)

然後在 41a736 位址設一個中斷點...

輸入 BPX 41A736 (回車)

然後按 F5 退出SOFTICE

softice 應該會彈出!! 馬上...

看看EBX的內容.. 看看它的值有沒有變??

再按 F5..

再看看EBX的內容 :))

EBX裏的值變了嗎?? 如果沒有!! we are IN!!! YAY!!!


那麼,我們現在找到了可以把它保存為靜態位址的一個有用的指標..
現在要做的就是找一塊好的地方放入我們的代碼!! 有幾種方法可以用...

1. 在你想要注射的地方的附近到處看看有沒有一大串的 NOP ..
(這是空的代碼).. 同樣一大串 0 也標誌著這是我們寫入代碼的好地方...

2. 如果沒有找到,那就在SOFTICE裏搜索 909090909090.. 看看手冊如果
你不懂怎樣去搜索.. 如今你也應該會了..

3. 這是我用的方法.. 有人告訴我他們用這種方法遇到了問題,但是我用這種
方法做了大概20到30個修改器,沒有一個不工作...
所以直到現在我繼續用這種方法...

i) 在softice裏輸入 TASK (回車) ..
ii) 找到遊戲表單的名字..
iii) 輸入 MAP32 <遊戲表單的名字> (回車)
iv) 然後將顯示遊戲各部分的資訊.. 我們想做的就是在 .data 部分的
最後增加代碼..
下面是我從 Space Tripper 裏看到的資訊...


:map32 spacetripper
Owner Obj Name Obj# Address Size Type
SPACETRIPP.text 0001 017F:00401000 0004BE10 CODE RO
SPACETRIPP.data 0002 0187:0044D000 00006718 IDATA RW
SPACETRIPP.bss 0003 0187:00454000 0035CDB4 UDATA RW
SPACETRIPP.idata 0004 0187:007B1000 000014F0 IDATA RW
SPACETRIPP.rsrc 0005 0187:007B3000 0000095C IDATA RO SHARED

.data 部分起始於44d000 ,但是我們要用的是這部分的最後面的地方.
(這裏通常有一些緩衝空間,所以我們可以高高興興的用它,
而不用去碰那些淩亂的遊戲本身的資料). 再看看下一個部分,
.bss這部分的起始位置就是.data的最底部..

那麼, 讓我們來搜尋一個漂亮的地方好插入<我們的代碼>..


再次來到softice裏面...

輸入 D 454000 (回車)

我把資料視窗向上滑動一點就到 data部分 的最後了...
你會注意到當我們向上滑動一點點後,在資料視窗最上面的文字就會改變了...


SPACETRIPPER! .bss

SPACETRIPPER! .data+<offset>

這個你只需要把資料視窗向上滑動一點就可以了..

輸入 EB (回車)

然後用PAGE UP / PAGE DOWN按鍵可以把視窗向上或下移動 :))

我堅決認為 453f00 是放入我們的代碼的好地方.. 所以第一件事就是把這個位址
記下來.. 以免你忘記...

現在我們有了<我們的代碼>的地址.. 我們需要返回在(41a736)這裏的指標,
還有做一個通道.. 它仍然在代碼窗裏,但是你現在看不見...
所以...

輸入 U 41a736 (回車)

41a736 就顯示在代碼窗的最上面了..

現在我們創建通道...

***注意!!***
下面的步驟一定要做完 .. 你不能破壞他們中的任何一個,或者嘗試退出softice,
這樣你會毀掉這個遊戲....

輸入 A 41a736 (回車)
輸入 jmp 453f00 (回車)
輸入 nop (回車) ( 我們需要用nop去掉多餘的機器碼來避免出錯 )
輸入 (回車) ( 回車將退出上面調用的編輯(A)模式 )


在這一點,你應該在代碼窗看到的變化..是從...

原始代碼...
0041A736 8B8308020000 MOV EAX,[EBX+00000208]
0041A73C D983CC000000 FLD REAL4 PTR [EBX+000000CC]
0041A742 D8B3C8000000 FDIV REAL4 PTR [EBX+000000C8]
0041A748 A3A46C7800 MOV [00786CA4],EAX



通道創建以後...
0041A736 E9C5970300 JMP 00453F00
0041A73B 90 NOP
0041A73C D983CC000000 FLD REAL4 PTR [EBX+000000CC] <-- 我們的代碼執行完後要跳回這裏..
0041A742 D8B3C8000000 FDIV REAL4 PTR [EBX+000000C8]
0041A748 A3A46C7800 MOV [00786CA4],EAX

ok.. 下一步...

輸入 U 453f00 (回車)
輸入 A 453f00 (回車)

我們現在準備創建我們的代碼...

首先,我們必須重新建立在做通道時被破壞掉的那一條指令...

輸入 mov eax,[ebx+208] (回車)

現在我們要把指標儲藏到靜態位址中.. 在你周圍的所有位址都是靜態的..
所以,你可以選你喜歡的任何一個.. 不要太靠近代碼部分.. 我選擇 453f30....

輸入 mov dword ptr [453f30],ebx (回車)

現在,最後我們需要跳回遊戲代碼區...

輸入 jmp 41a73c (回車) <-- 41a73c 就是在建通道時用了NOP指令的下一行...

輸入 (回車) <-- 讓你退出編輯(A)模式...

好.. 這些做完以後... 代碼區應該看起來像這樣....



通道部分...
0041A736 E9C5970300 JMP 00453F00
0041A73B 90 NOP
0041A73C D983CC000000 FLD REAL4 PTR [EBX+000000CC]
0041A742 D8B3C8000000 FDIV REAL4 PTR [EBX+000000C8]
0041A748 A3A46C7800 MOV [00786CA4],EAX

我們的代碼...
00453F00 8B8308020000 MOV EAX,[EBX+00000208]
00453F06 891D303F4500 MOV [00453F30],EBX
00453F0C E92B68FCFF JMP 0041A73C
00453F11 0000 ADD [EAX],AL
00453F13 0000 ADD [EAX],AL

如果你的代碼不像我的這個樣子,那你可能做錯了某些東西..
你得回去重新做....

現在!! 如果你的代碼跟我一樣.. 你現在就可以按 F5 退出softice了...
還有遊戲應該運行正常...

我們現在可以做一些漂亮的動作來看看它的實際工作情況....

按 CTRL-D 返回到softice ..

輸入 D *453f30+280 (回車)

(如果它說了類似無效位址(INVALID ADDRESS)的話)) 你就退出softice然後返回softice再輸入,然後回車...

如果一切如計畫中的,那麼在資料視窗的左上角你就會看到生命(LIVES)真正的地址了.. 你可以改這個位址的值,生命也會變了....


現在到了非常cool的部分!!!

失去所有生命.. 就是說 GAME OVER..

開始一個新遊戲.. (不要載入遊戲) 就開始一個新遊戲...

如果你看著資料視窗.. 你會注意到生命(LIVES)位置的位址的值發生改變了..
明顯的!!
它是 DMA !! :)))

但是.. 我們的代碼仍然在工作... 它仍然可以把DMA的值儲藏到靜態位址中.
你要做的就是再找到生命的值.. 就是輸入那個命令.. :))

輸入 D *453f30+280 (回車)

現在,在資料視窗的左上角你又可以看到生命真正的位址了..

pretty damn cool eh?? :))

我們已經擊敗了DMA .. DMA的基址將會始終被我們知道!!!! 儲藏在453f30,
還有還要加上208.. 你就得到了生命的真正的地址...


FINAL WORDS
***********

做完這些以後,我們可以用來做些什麼呢?
-------------------------

我敢肯定一些聰明的傢伙已經把這個用在他們的修改器上了...
除了那些慢一點的人,我會為他們解釋的...

一旦代碼注射到遊戲中以後.. 你可以使用 READPROCESSMEMORY 函數來讀取 453f30 裏的DMA基址.. 然後你再往 DMA基址+208
裏寫入數值.. 這做起來一樣的簡單...


注射代碼注射劑! :)
-------------------------------

當你完成所有步驟以後,你總不會想每次都經歷那些步驟去注射代碼吧!..
所以你需要製作一個修改器為你去注射這些代碼..
這很簡單.. 實際上所有的修改器用的都是同一種方法,用一個過程...
除非你想比平常花更多的位元組.....


拿這篇教程做例子...
----------------------------

讓注射劑時刻工作.. 這是在你的修改器上要實現的..


在 41a736 這個位址..

你應該要注入 E9,C5,97,03,00

在 5f3f00 這個位址..

你應該要注入 8B,83,08,02,00,00,89,1D,30,3F,45,00,E9,2B,68,FC,FF

還有.. 你要能從 5f3f30 這個位址讀出 DMA基址 ..

基本上你要做的就是把所有那些指令的機器碼寫入正確的記憶體位址中...

警告!! 應該總是先寫入<我們的代碼>的機器碼.... 然後再寫入<通道>的機器碼..


FEW!!! 另一篇教程完成了.. 兄弟們!! 這可是不少的工作啊..
(man!! its a lot of work)



********************************
如果你什麼問題或者注解可以email給我...
RECLAIM22@HOTMAIL.COM

訪問我的網站以獲得更多的教程指南.. http://WWW.SHEEPREC.CJB.NET

我要向那些支持我和鼓勵我的人致意....

Odin, MiraMax, Keyboard Junkie, Calligula, Orr, DarkLighter, Kilby,
LordFinal, EverLast.
MiNiSTER, [NTSC], [Drone], Rizzah, Bengi...

沒有順序.. 就是所有人..

沒有留言:

張貼留言