冰楓論壇

 找回密碼
 立即註冊
搜索
查看: 7960|回覆: 0
打印 上一主題 下一主題

[破解教程] CE 教程:進階篇CE Tutorial Games

  [複製鏈接]

366

主題

35

好友

1984

積分

金牌會員

飛翔至夢想的彼端~

Rank: 6Rank: 6

UID
86990
帖子
7161
主題
366
精華
0
積分
1984
楓幣
2440
威望
1881
存款
0
贊助金額
150
推廣
0
GP
70
閱讀權限
70
性別
保密
在線時間
1310 小時
註冊時間
2014-12-27
最後登入
2023-2-14

積分勳章 解說達人 在線終極王 論壇支持王 論壇粉絲 發帖達人 懶人勳章 太陽勳章 長老勳章 神手勳章 2016年紀念勳章 私服達人 音樂勳章 2017年紀念勳章 2017端午節紀念勳章 Android勳章 2018年紀念勳章 VIP會員 熱心助人 2018萬聖節紀念勳章 聖誕節紀念勳章 聖誕節紀念勳章2 2020年紀念勳章

跳轉到指定樓層
1
發表於 2019-3-30 22:03:57 |只看該作者 |倒序瀏覽
本帖最後由 xxx9638527410 於 2019-3-30 19:53 編輯

作者:Ganlv
不確定能否附上網址,暫且不提供。
------------------------------------------
最近看見一篇關於CE最新版遊戲的資訊。
個人覺得滿有趣,所以將文章轉載過來。
除此之外,應該有不少人對此感興趣。
所以,至少來說,讓這篇文章沉澱於此。
供未來的那些人,路過時可以閱讀。

--------------------------------------------------

CE 教程:進階篇CE Tutorial Games
本文大約1 萬字,閱讀可能需要很長時間。本文包含大量圖片,全文加載可能需要10MB 以上的流量。
注意事項
本文使用署名-非商業性使用-相同方式共享4.0國際(CC BY-NC-SA 4.0)許可證發布(可以參考此協議的中文翻譯)。您可以隨意分享本文章的鏈接。全文轉載的具體注意事項請閱讀協議內容,轉載的話最好在下面回帖告訴我一聲。
請注意:本文仍有可能更新,全文轉載的話可能需要考慮更新的問題。
我用的是本文起草時(2019年3月26日)的最新版Cheat Engine 6.8.3(在2019年2月9日發布的)
請盡量在看懂CE教程:基礎篇CE Tutorial之後再來看本篇文章。
緒言
2018 年6 月8 日,Cheat Engine 6.8 發布,軟件中新增了一個Cheat Engine Tutorial Games。這個新的小遊戲有3 關,並不是特別難修改,但是卻很有意思。因為它不再是之前的教學程序那種技能教學,而是一種有目的實戰教學。
每一關目標只有一個,但辦法是多種多樣的。這篇文章盡可能利用不同方法來解決問題。重點不是修改這個小遊戲,重點是理解其中的思路。
教程打開CE Tutorial Games
菜單欄→ Help → Cheat Engine Tutorial Games
231709hw5chhzodybrrc8t

第1 關
231758tyvxwayp916d1d39

第1 關:每5 次射擊你必須重新裝填,在這個過程中目標會回復血量,嘗試找到一種方法消滅目標。
[Tips]
遊戲中使用空格鍵射擊。
第1 關嘗試 1
有數字的時候肯定先嘗試搜索數字,畢竟這個是最方便快捷、最直觀準確的方式。
右下角有個數字5,新掃描,搜索4字節的精確數值5。
射一發之後再搜索4。
不知道你們搜索到沒有,反正我是沒有。
第1 關嘗試 2
我懷疑上一種方法有缺陷,可能子彈沒撞到目標和撞到目標時,遊戲的數據是兩種狀態。
我勾選了Pause the game while scanning。
231808y3bnxbhbw1zv4nbm

在子彈射出過程中搜索4,結果發現還是不行。
第1 關嘗試 3
有時候遊戲中顯示的數字並不是內存中實際存儲的數據,你看到的只是計算結果。
[Info]
如果你學習過簡單的編程知識,你應該了解堆內存和棧內存的區別
堆內存(這裡的堆Heap與數據結構的堆Heap完全無關,這只是一種名稱)通常是使用malloc函數分配的,一旦分配完僅用來存儲某個確定結構、數組、對象,通常直到使用free釋放之前都代表同一遊戲數據。堆內存在傳遞的時候只會傳遞指針。
棧內存,棧內存是不斷復用的,棧內存通常用作函數的局部變量、參數、返回值,函數調用時一層一層嵌套的,調用一個函數,棧就會增長一塊,一個函數調用完返回了,棧就會縮短,下一個函數再調用,這塊內存就會被重新使用。所以棧內存是會快速變化的,搜索到棧內存通常都沒有什麼意義。你應該聽過ESP 和EBP,SP 就是Stack Pointer,棧指針,描述棧頂在什麼內存位置的寄存器。
這裡有兩篇擴展閱讀(說實話我自己都沒看):基於棧的內存分配-維基百科內存管理動態內存分配-維基百科
我猜測他顯示5的時候其實內存中是已發射0顆子彈。5只是一個局部變量的計算結果,他在棧中只存在很短的一段時間,搜索是搜索不到的。
所以顯示5的時候搜索0,顯示4的時候搜索1。
231835c6xqqeqllhy9plph

[Comment]
這個地址是在運行之後分配的,你的搜索結果可能和我不一樣
把搜索結果添加到下方地址列表中,點擊左側的小方塊鎖定,這樣就可以了。
[Tips]
這個遊戲中顯示的是5而存儲的是0。類似的,某些遊戲的貨幣可能都是10的倍數,比如500金幣,內存中存儲的可能就是50,而不是顯示的500。例如:植物大戰殭屍。
231843c3eeinnxdwfex3wd

第1 關嘗試 4
你以為這樣就結束了嗎?
這個遊戲真的很有意思。你的最終目的是要打敗敵人,那如果我直接把敵人就設置為1 滴血,會怎麼樣呢?
血條沒有具體數值,我們使用未知初始值(Unknown initial value)來搜索。
類型怎麼選呢?血量這種東西一般我會先試試Float(單精度浮點型),然後再試試4 字節整數,如果不行的話再試試8 字節和雙精度浮點型,再不行的話就方案吧。
231850f788xyt0xa2cw2x1

這裡搜索了好幾次還剩幾個結果,憑感覺應該是第一個,因為敵人回血回到滿的時候,第一個數值恰好是100。
直接把敵人血量改為1,然後發射子彈。
一發入魂。
第1 關嘗試 5
找到剛才那兩個內存地址之後,我們還可以嘗試代碼注入,但是由於第1 關是在太簡單了,沒有必要這樣大費周章請來代碼注入這種複雜的東西,內存修改搞定就行。代碼注入的應用在下一關會提到。
第2 關
231859ia803pl74gg2e8pl

第2 關:這兩個敵人和你相比擁有更多的血量、造成更多的傷害。消滅他們。提示/警告:敵人和玩家是相關聯的。
[Tips]
遊戲中使用左右方向鍵控制旋轉,使用上方向鍵控制前進,使用空格鍵射擊。
第2 關嘗試 1
敵人兩個人一起打我們,每次要掉4 滴血,我們總共才100 滴血,而我們打敵人,每次大概就掉1/100,而且對面還有兩個人。
這誰頂得住啊!
我們直接搜自己的血量,把血量改成上千,然後激情對射。
來呀,互相傷害啊!
231918xl95kxrtzd3urnn9

然後...
第2 關Plus
231930erpgxpdgcntrdhny

第2 關加強:你將會為之付出代價!啟動究極炸彈。3、2、1。
231945jlglxog42og7dvo5

啊!我死了。
231958iudozo30da06q03c

9199血的我被炸到-1滴血。
第2 關嘗試 2
怎麼辦呢?
我們發現,我們的子彈飛行速度比較快,我可以先把兩個人都打到只剩1 滴血,然後殺掉其中一個,另一個會啟動究極炸彈,這時我只需要一發小子彈就能把對面打死。
232103z1b12ynjibykk2hh

然而。
232115yyzloz9tf4qz5qwp

我太天真了。
對面另外一個雖然也殘血,但是啟動究極炸彈的時候能回血。
第2 關嘗試 3
肯定有人覺得麻煩了,你直接搜索敵人血量改成1 不就得了,對面就算回血,就再改成1。
232127gm1r3trztw5lg7og

232146tthbqtltgqlbmrbl

果然,他又回到了21滴血,我再改成1滴血,然後開火。
232156nxbvu39a3ui8u0il

誰讓他的炸彈飛的慢呢~
第2 關嘗試 4
這麼贏得好像比較不保險,萬一遊戲作者把敵人導彈的速度調的比我們子彈快,那不就完蛋了。
我們來從根本上解決問題。
找出修改我們自己血量的指令,Find out what writes to this address。
232209yz8dsez3pt3vpvzm

然後把這個語句替換成NOP (No operation),原來修改血量的代碼就會變成什麼也不做。
232232haw7gay7zzczoclg

再與敵人打幾個回合,發現,我們不掉血了,但敵人也不掉血了。
Tip/Warning: E​​nemy and player are related
提示/警告:敵人和玩家是相關聯的。
這就是“共用代碼”(Shared Code),敵人和我們減血的代碼是共用的,不能簡單地修改為NOP,我們需要做一些判斷。
第2 關嘗試 5
我們先把指令還原。
232248fzoj9ioio9jbol6o

你可以分別對這三個地址使用Find out what writes to this address,看看什麼指令寫入了這個地址,你應該會有所發現。
232255ylt5zc3ugj2tfz4c

你可以看到,分別向這三個地址寫的指令是同一條指令(指令地址相同,就是圖中的10003F6A3)。
既然這幾個血量的修改是通過相同的代碼,那麼就表示玩家的數據存儲方式和敵人的數據存儲方式是相同的,至少血量都是在+60的位置存儲。
我們的想法是:從儲存玩家和敵人信息的結構體中找出一些差別,然後靠代碼注入構造一個判斷,如果是玩家自己的話則跳過,不扣血。
這裡使用Dissect data/structures。
232318htccqctycttu0vps

裡面默認已經有一個地址了,我們再額外添加兩個地址。
232331kebczie0s42bnjcu

然後填入三個血量地址 - 60,要注意這裡需要減掉60,因為+60之後的是血量地址,把這個60減掉才是結構體的開頭。
232343sir992f32s92ibxu

232400ottoxfueogzqk6oo

因為兩個同類的結構肯定不能重疊,所以這裡我可以算一下兩個結構體的距離,一個結構體最大隻有160 字節,再大就會重疊了。
[Tips]
通常情況下,兩個結構體會相距比較遠,你可以適當設置這個數值,比如設置一個1024甚至4096字節之類的,反正你覺得應該足夠就行。
232410y0sorc0etwjl5w0t

我們的邏輯就是
  1. if (*(p + 70) == 0) { // 0 表示是玩家自己
  2.     // 什么也不干
  3. } else {
  4.     // 正常扣血
  5. }
複製代碼
Find out what writes to this address→ Show disassembler→ Tools→ Auto Assemble→ Template→Code injection
232431h51r9hf9fm5nggnr

這是自動生成的代碼
  1. alloc(newmem,2048,"gtutorial-x86_64.exe"+3F6A3)
  2. label(returnhere)
  3. label(originalcode)
  4. label(exit)

  5. newmem: //this is allocated memory, you have read,write,execute access
  6. //place your code here

  7. originalcode:
  8. sub [rax+60],edx
  9. ret
  10. add [rax],al

  11. exit:
  12. jmp returnhere

  13. "gtutorial-x86_64.exe"+3F6A03:
  14. jmp newmem
  15. nop
  16. returnhere:
複製代碼
代碼注入的原理就是把原來那個位置的指令換成jmp,跳轉到我們新申請的一塊內存中,程序正常運行到這裡就會跳轉到我們新申請的那塊內存中,然後執行我們的指令,我們自己寫的指令的最後一條指令是跳轉回原來的位置,這樣程序中間就會多執行一段我們的指令了。
不過這裡有一點問題,
originalcode:
sub [rax+60],edx
ret
add [rax],al
ret 語句之後是另外一個函數了,我們這樣修改的話,如果有人調用那個函數就會出錯,我們把注入點往前挪一下。
  1. gtutorial-x86_64.exe+3F6A0 - 48 89 C8              - mov rax,rcx
  2. gtutorial-x86_64.exe+3F6A3 - 29 50 60              - sub [rax+60],edx
  3. gtutorial-x86_64.exe+3F6A6 - C3                    - ret
  4. gtutorial-x86_64.exe+3F6A7 - 00 00                 - add [rax],al
複製代碼
重新生成一個Code injection,注入點設置為上一條語句gtutorial-x86_64.exe+3F6A0。
  1. alloc(newmem,2048,gtutorial-x86_64.exe+3F6A0)
  2. label(returnhere)
  3. label(originalcode)
  4. label(exit)

  5. newmem: //this is allocated memory, you have read,write,execute access
  6. //place your code here

  7. originalcode:
  8. mov rax,rcx
  9. sub [rax+60],edx

  10. exit:
  11. jmp returnhere

  12. gtutorial-x86_64.exe+3F6A0:
  13. jmp newmem
  14. nop
  15. returnhere:
複製代碼
其他部分不用動,我們直接在originalcode上修改
originalcode:
mov rax,rcx
cmp [rax+70],0
je exit // 如果等于 0,则表示玩家,跳到 exit,不执行下一条 sub 语句
sub [rax+60],edx
exit:

[Tips]
雙斜線後面是註釋,刪除掉也可以。
然後點擊 Execute
我們可以簡單修改一下,然後File→Assign to current cheat table
  1. [ENABLE]
  2. alloc(newmem,2048,gtutorial-x86_64.exe+3F6A0)
  3. label(returnhere)
  4. label(originalcode)
  5. label(exit)

  6. newmem:

  7. originalcode:
  8. mov rax,rcx
  9. cmp [rax+70],0
  10. je exit
  11. sub [rax+60],edx

  12. exit:
  13. jmp returnhere

  14. gtutorial-x86_64.exe+3F6A0:
  15. jmp newmem
  16. nop
  17. returnhere:

  18. [DISABLE]
  19. gtutorial-x86_64.exe+3F6A0:
  20. mov rax,rcx
  21. sub [rax+60],edx
複製代碼
233754lugiwz29g5x4nng5
第2 關嘗試 6
剛才的代碼改的還不夠好,我們可以像敵人的究極炸彈打我們一樣,將敵人一擊致命。
originalcode 部分修改成
  1. originalcode:
  2. mov rax,rcx
  3. cmp [rax+70],0
  4. je exit
  5. mov edx,[rax+60]
  6. sub [rax+60],edx
複製代碼
直接令edx等於敵人血量,然後敵人血量會被扣掉edx,這樣敵人直接就被秒了。
第2 關嘗試 7
不,第二關還沒有結束,我們的還可以繼續深入研究下去。
“扣血函數”是同一個的函數,但是調用“扣血函數”的地方肯定是不一樣的。
我們可以找到調用他的位置。
我們在sub [rax+60],edx處下斷點
  1. gtutorial-x86_64.exe+3F6A0 - 48 89 C8              - mov rax,rcx
  2. gtutorial-x86_64.exe+3F6A3 - 29 50 60              - sub [rax+60],edx
  3. gtutorial-x86_64.exe+3F6A6 - C3                    - ret
複製代碼
發射一發子彈,等待他命中敵人或命中自己,斷點會觸發。
這個斷點應該會觸發3次,每次你需要觀察一下右側寄存器窗口中的rax的數值來判斷這個代表扣誰的血。
232519x8way7y8qx5tzwym

然後跟著ret返回到他調用的位置,上一條語句一定是call。
玩家扣血前後的代碼
  1. gtutorial-x86_64.exe+3DFB8 - E8 E3160000           - call gtutorial-x86_64.exe+3F6A0
  2. gtutorial-x86_64.exe+3DFBD - 48 8B 4B 28           - mov rcx,[rbx+28]
複製代碼
單步執行返回之後指針停留在gtutorial-x86_64.exe+3DFBD,前一條一定是一個call指令,就是call gtutorial-x86_64.exe+3F6A0。
gtutorial-x86_64.exe+3F6A0 這個地址就是之前那個扣血函數。
gtutorial-x86_64.exe+3F6A0 - 48 89 C8              - mov rax,rcx
gtutorial-x86_64.exe+3F6A3 - 29 50 60              - sub [rax+60],edx
gtutorial-x86_64.exe+3F6A6 - C3                    - ret
同理可以知道敵人被打中時扣血的代碼
左側敵人扣血代碼
gtutorial-x86_64.exe+3E0ED - E8 AE150000           - call gtutorial-x86_64.exe+3F6A0
右側敵人扣血代碼
gtutorial-x86_64.exe+3E1D6 - E8 C5140000           - call gtutorial-x86_64.exe+3F6A0
這個遊戲很有意思,命中敵人和命中玩家使用的是不同的代碼,僅僅扣血使用的是相同的代碼。
[Comment]
這就是傳說中的面向複製粘貼型編程。
既然他們使用的是不同的代碼,這個fastcall由沒有影響棧平衡,那麼我可以直接把gtutorial-x86_64.exe+3DFB8這一行用NOP替換掉。
232537gknpnzk6ufmz65vr

[Tips]
ret語句的作用是返回調用處,call的時候會往棧頂壓一個返回之後應該執行的地址。
簡單一個ret語句就是跳回棧頂那個地址的位置,然後再把棧頂那個地址彈出
也有ret 8這樣的語句,就是先從棧中彈出8個字節(相當於add esp,8),然後再執行返回。之所以這樣所是因為調用這個函數之前,往棧中壓入了8個字節的參數(比如兩個4字節整數),函數返回之前必須恢復棧平衡。
gtutorial-x86_64.exe+3F6A6這個ret語句,後面沒有參數,應該不會影響棧平衡。
現在也可以讓敵人打我們不掉血,我們打敵人正常掉血了。
第2 關嘗試 8
現在來分析一下gtutorial-x86_64.exe+3F6A0這個扣血函數,這個函數總共就3條指令,函數有2個參數,分別是rcx和edx,rcx為結構體的指針,edx為扣血的數量,沒有返回值。
[Info]
這種用寄存器傳遞參數來調用函數方法是典型的fastcall。
我們需要分析一下call之前是什麼確定了edx的值。
以玩家扣血為例,我們需要看call之前的幾行代碼。
  1. gtutorial-x86_64.exe+3DF98 - FF 90 28010000        - call qword ptr [rax+00000128]
  2. gtutorial-x86_64.exe+3DF9E - 84 C0                 - test al,al
  3. gtutorial-x86_64.exe+3DFA0 - 0F84 D0000000         - je gtutorial-x86_64.exe+3E076
  4. gtutorial-x86_64.exe+3DFA6 - 48 8B 53 40           - mov rdx,[rbx+40]
  5. gtutorial-x86_64.exe+3DFAA - 49 63 C4              - movsxd  rax,r12d
  6. gtutorial-x86_64.exe+3DFAD - 48 8B 04 C2           - mov rax,[rdx+rax*8]
  7. gtutorial-x86_64.exe+3DFB1 - 8B 50 70              - mov edx,[rax+70]
  8. gtutorial-x86_64.exe+3DFB4 - 48 8B 4B 28           - mov rcx,[rbx+28]
  9. gtutorial-x86_64.exe+3DFB8 - E8 E3160000           - call gtutorial-x86_64.exe+3F6A0
複製代碼
在call qword ptr [rax+00000128]處下斷點,然後回到遊戲中發射子彈。會發現,剛一發射子彈立刻就斷下來了。我猜測這裡應該是碰撞檢測,然後下面的test和je來做判斷,如果碰撞上了,則執行扣血函數,沒撞上則直接跳過這部分代碼,不扣血。
怎麼驗證一下呢?把je改成jne,看看是不是子彈沒撞上的時候就直接扣血了。
修改以後的確是這樣的,而且之前是一次掉4滴血,現在連自己的子彈都會把自己打掉血,一次會掉5滴血。
把這裡je改成jmp即可。CE會提示原來指令是6字節,新指令是5字節,是否用NOP填充多餘的,選是就行了。
現在子彈會從我們上方飛過,而不與我們產生碰撞,而敵人卻依然會中彈。
第3 關
232553pruluj0lewplp6wu

第3 關:把每個平台標記為綠色可以解鎖那扇門。注意:敵人會將你一擊致命(然後就失敗了)玩的愉快!提示:有很多解決方案。比如:找到與敵人的碰撞檢測,或者Teleport(傳送),或者飛行,或者...
第3 關嘗試 1
看樣子,不開掛也能過啊。
第3 關Plus
看來我還是too young, too naïve.
232610s06mjqfooufgtp0p

第3 關加強:門雖然解鎖了,但是敵人把門堵住了。
第3 關嘗試 2
最簡單的就是搜索人物坐標了。把人物直接改到門那裡,不用“通過”敵人,而是直接瞬移過去。
計算機中,2D 遊戲一般是左負、右正,上下的正負不一定。3D 遊戲一般高度方向上正、下負,東西南北的正負不一定。
[Info]
2D 遊戲,如果使用計算機繪圖的坐標系則是下正、上負,如果使用數學中的坐標則是上正、下負。
232636c1zofae1eq9qdtae

232643ilqa2hlzsz1lllrf

3D 遊戲也有兩種坐標系。一種是向上為y 軸(這是沿襲2D 坐標的慣例),然後一般是向右為x,向屏幕外為z(也有向屏幕內為z 的)。另一種則是向上為z,水平面中向北為y,向東為x。
搜索Float(單精度浮點型)未知初始值,然後向右移動人物,搜索增大了的數值,然後向左移動人物,搜索減小了的數值,反复幾次,你應該能看到剩下一個唯一的數值。過程中還可以不移動人物,搜索未改變的數值。
[Tips]
你可以在設置中給常用搜索功能添加快捷鍵。這樣不用切出遊戲就可以進行下一次掃描了。
232701iuyygnagm79zyfhc

添加到地址列表中,然後改名為“X坐標”。然後復制粘貼,修改地址,把地址+4即為Y坐標。
[Info]
這裡+4還是-4主要看內存中的排列方式。一般X排在Y前面,所以要+4。對於3D遊戲,你搜索高度可能搜到的是Y也可能是Z,你可以使用右鍵→ Browse this memory region,然後右鍵→ Display Type→ Float來看看前後的內存數據,然後在遊戲中移動一下,憑感覺決定X、Y、Z 。
232714tutwknckzr92z57l

232730iazmwjmnztwqjxi2

移動一下人物,大概估計一下坐標的範圍,整個遊戲區域對應的X和Y是-1到1直接的值。估計一下門的X坐標,把X坐標改成0.97。
Well Done
232808b4tdz4gxjoef7es0

你戰勝了全部三個遊戲,幹得漂亮!
第3 關嘗試 3
上面的方法很簡單也很實用,不過我們還可以繼續“玩”這個遊戲。
我們可不可以直接把所有的平台都改成綠色呢?
因為每個平台只有兩種狀態,而且只能從紅變成綠,這樣很不利於搜索,而且我也不知道他是怎麼存儲的,不知道紅和綠兩個狀態的值都是多少。
這個我嘗試了很多種辦法,例如:
  • 紅的時候搜0,綠的時候搜1,然後撞敵人撞死,再搜0。
  • 紅的時候搜未知初始值,綠的時候搜改變了的數值,然後撞敵人撞死,再搜改變了的數值。
  • 把類型改為Byte(單字節類型),因為bool類型都是佔用1字節的。
  • 其實我還懷疑是不是每次撞死都會重新申請內存,這樣就更麻煩了。
最後,我使用“紅的時候搜Byte 類型未知初始值,綠的時候搜改變了的數值,然後撞敵人撞死,再搜改變了的數值”的方法找到了一點線索。雖然沒有找到具體的數據存儲地址,但是我找到了絕對相關的一組數據。這組數據每次顏色轉換都會相應的來回改變。
232827ixsseqc6xqshhe5q

雖然沒有找到具體與台階有關的數值,但是注意圖中015F1AD8這個值,他的含義似乎是已經點亮的平台的數量
我直接把這個數字改成12的話,雖然沒有讓所有的平台都變綠,但是依然觸發“門解鎖、敵人堵門”這一事件了。
我突然有個想法,就是我直接站在門上,然後把數值修改為12,我已經在門上了,敵人就堵不到我了。
結果真的可以。
232839ylke0dqy4k2270lz

第3 關嘗試 4
4BFEEB60那些255和204看樣子像是RGB值。如果我手動添加4BFEEB60類型設為4字節,顯示十六進制值。結果就是FF00FF00,4個字節分別是ARGB,就是不透明的綠色。紅色的平台則是FFCC0000,不透明的暗紅色。
232903np5ltj56x7f9lt95

但是這些數值改了也沒什麼用,應該就是每一像素的顏色。
上面那個015ABE78,手動添加這個地址,並設置成Float類型的話,就會發現,紅色的時候是0.8,綠色的時候是0。同理015ABE7C,紅色的時候是0,綠色的時候是1。
把這兩個數值改成其他的,你會發現平台的顏色也變了。
232917gi1iteih2pxhqgug

我又發現一個有趣的現象,如果我把平台的顏色鎖定為紅色,然後讓人物站上去,這時“已變綠平台數”那個計數器會快速增長。所以你有什麼想法?
這就是為什麼我找不到一個bool 型變量來描述平台是否變綠,因為他的代碼根本沒有這樣一個變量,他的邏輯應該大致是這樣的。
  1. if (collision) {
  2.     R = 0;
  3.     if (G != 1) {
  4.         count++;
  5.         G = 1;
  6.     }
  7. }
複製代碼
如果站到平台上了,則令紅色為0,如果綠色不為1,則計數器+1,並令綠色為1。
這裡面沒有出現flag 這種東西來表示平台是否變綠色,他直接用顏色來判斷的。
第3 關嘗試 5
找到與敵人的碰撞檢測,或者Teleport(傳送),或者飛行,或者...
關卡說明中告訴裡一部分思路,TP 已經試過了,現在我們來試試飛行。
所謂的飛行其實就是把重力改小,或者是像玩Flappy Bird 那樣一跳一跳的,可以一直在天上飛著。
首先來找到重力大小。
重力會影響速度,速度影響坐標,我們現在只知道坐標的地址,我們可以通過查找寫入,然後分析附近代碼來找到速度,然後進而找到重力加速度。
y = y0 + vy * t
計算位置需要先讀取Y坐標y0,然後加上速度差,在賦值給y。
這裡有個小技巧,就是對同一個地址同時使用查找寫入和查找訪問,這樣我們很容易地找到了寫入的地址,然後在查找訪問窗口中,寫入地址以前的幾個讀取都很可疑。
232944fa8s89s9os898vac

第二條寫入指令,在跳起來懸空的時候,計數器不會增加,應該是當人物接觸到地面的時候,防止人物穿過地面用的。我們只看第一條。
Show disassembler,我把gtutorial-x86_64.exe+40491到gtutorial-x86_64.exe+40506截取出來。
  1. gtutorial-x86_64.exe+4048D - 48 8B 43 28           - mov rax,[rbx+28]
  2. gtutorial-x86_64.exe+40491 - F3 44 0F10 40 28      - movss xmm8,[rax+28] { 读取 Y 坐标 }
  3. gtutorial-x86_64.exe+40497 - 48 8B 43 28           - mov rax,[rbx+28]
  4. gtutorial-x86_64.exe+4049B - F3 0F5A 48 28         - cvtss2sd xmm1,[rax+28] { 再次读取 Y 坐标 }
  5. gtutorial-x86_64.exe+404A0 - F3 0F5A 53 78         - cvtss2sd xmm2,[rbx+78] { 读取 Y 速度 }
  6. gtutorial-x86_64.exe+404A5 - F2 0F2A C6            - cvtsi2sd xmm0,esi { esi 是毫秒数 }
  7. gtutorial-x86_64.exe+404A9 - F2 0F5E 05 AF382400   - divsd xmm0,[gtutorial-x86_64.exe+283D60] { 除以 1000 }
  8. gtutorial-x86_64.exe+404B1 - F2 0F59 C2            - mulsd xmm0,xmm2 { 速度乘时间 }
  9. gtutorial-x86_64.exe+404B5 - F2 0F5C C8            - subsd xmm1,xmm0 { Y 坐标减去位移 }
  10. gtutorial-x86_64.exe+404B9 - F2 44 0F5A C9         - cvtsd2ss xmm9,xmm1 { double 转 float }......
  11. gtutorial-x86_64.exe+40506 - F3 44 0F11 48 28      - movss [rax+28],xmm9 { 赋值给 [rax+28] }
複製代碼
[Comment]
註釋是我自己加的。這個是根據邏輯和感覺猜出來的,也有可能猜錯。不過這個簡單的速度位移公式,一般來說分析應該是正確的。
注意這幾條
gtutorial-x86_64.exe+40497 - 48 8B 43 28           - mov rax,[rbx+28]
gtutorial-x86_64.exe+4049B - F3 0F5A 48 28         - cvtss2sd xmm1,[rax+28] { 再次读取 Y 坐标 }
gtutorial-x86_64.exe+404A0 - F3 0F5A 53 78         - cvtss2sd xmm2,[rbx+78] { 读取 Y 速度 }
Y坐標的內存地址是[[["gtutorial-x86_64.exe"+37DC50]+760]+28]+24,rbx應該是一級指針的值[["gtutorial-x86_64.exe"+37DC50]+760],那麼Y速度的地址應該就是[["gtutorial-x86_64.exe"+37DC50]+760]+78。
手動添加Y速度的地址,然後把速度改成3,你會發現人物跳了起來。
232958w462d5b4dnvczvez

剛才設成3跳的有點高,添加一個上箭頭的熱鍵,設置值為1。如果長按的話就會勻速向上飛。
233012dl88198j8xd8f1tt

第3 關嘗試 6
剛才改速度已經成功了,現在我們來改重力加速度。
還是查找寫入。
233026bair0xjeiejnnf4j

  • 第1 個在脫離地面之後不計數,應該是地面支撐
  • 第2 個隨時都會觸發,應該是重力加速度導致的
  • 第3 個是起跳時觸發
  • 第4 個則是長按跳躍連跳時觸發
我突然有個想法,起跳時觸發的那條語句,一定有什麼限制他,讓他只能在地面上起跳,而不能在空中起跳。
Show disassembler.
  1. gtutorial-x86_64.exe+3FE9A - C6 43 74 01           - mov byte ptr [rbx+74],01 { 1 }
  2. gtutorial-x86_64.exe+3FE9E - 80 7B 7C 00           - cmp byte ptr [rbx+7C],00 { 0 }
  3. gtutorial-x86_64.exe+3FEA2 - 0F85 93000000         - jne gtutorial-x86_64.exe+3FF3B
  4. gtutorial-x86_64.exe+3FEA8 - 8B 05 823E2400        - mov eax,[gtutorial-x86_64.exe+283D30] { (1.45) }
  5. gtutorial-x86_64.exe+3FEAE - 89 43 78              - mov [rbx+78],eax
複製代碼
經過分析和猜測,[rbx+74]表示是否按下跳躍鍵,[rbx+7C]表示是否懸空。
jne 表示如果懸空則不允許跳。
直接把jne那條語句NOP掉,就可以實現無限連跳了。剛才設置的熱鍵都用不著了。
你也嘗試可以修改gtutorial-x86_64.exe+283D30這個地址的數值,它表示跳躍初速度。
上面的方法修改之後,長按不會一直向上飛,必須像Flappy Bird一樣一下一下的。如果你想長按就一直向上飛,那就把第4條指令前面的jne也NOP掉。
  1. gtutorial-x86_64.exe+406F8 - 80 7B 7C 00           - cmp byte ptr [rbx+7C],00 { 0 }
  2. gtutorial-x86_64.exe+406FC - 75 0B                 - jne gtutorial-x86_64.exe+40709
  3. gtutorial-x86_64.exe+406FE - 8B 05 2C362400        - mov eax,[gtutorial-x86_64.exe+283D30] { (1.45) }
  4. gtutorial-x86_64.exe+40704 - 89 43 78              - mov [rbx+78],eax
複製代碼
第3 關嘗試 7
剛才跑題了,我們繼續來找重力加速度
v = v0 + g * t
分析一下第2 個指令附近
  1. gtutorial-x86_64.exe+40709 - F3 0F5A 43 78         - cvtss2sd xmm0,[rbx+78] { 读取速度 }
  2. gtutorial-x86_64.exe+4070E - F2 0F5C 05 52362400   - subsd xmm0,[gtutorial-x86_64.exe+283D68] { 减去 0.1 }
  3. gtutorial-x86_64.exe+40716 - F2 0F5A C0            - cvtsd2ss xmm0,xmm0 { double 转 float }
  4. gtutorial-x86_64.exe+4071A - F3 0F11 43 78         - movss [rbx+78],xmm0 { 写入速度 }
複製代碼
這個邏輯好簡單啊,與時間都無關,就是每次計算把Y 速度減0.1。
手動添加地址gtutorial-x86_64.exe+283D68,類型為double,然後把重力加速度調小就行了。

第3 關嘗試 8
我們還有什麼辦法?我可不可以把敵人固定住,讓他不要移動,或者移到屏幕外,總之讓他別妨礙我們就行了。
用同樣搜索自己坐標的方法搜索敵人的坐標。只不過自己的坐標可以自己控制,敵人的坐標只能隨他們移動了。
找到3個X坐標之後+4就是Y坐標。
把這些坐標鎖定,可行。把已變綠平台數改成12,這些敵人又不聽話了,又開始堵門了,鎖定似乎對他們不好使。
查找寫入他們的指令
233145semneicag4xg26gc

既然他堵住門時會一直觸發第5 條,那麼我就簡單粗暴一點,直接把第5 條指令NOP 掉,這樣我就可以從外部修改這個數值了。
233157keczoiq5mltddmke

第3 關嘗試 9
我們還可以想辦法直接開門。
查找訪問“已變綠平台數”的指令。
只有這一條
gtutorial-x86_64.exe+4098B - 48 63 93 88000000     - movsxd  rdx,dword ptr [rbx+00000088]
我們分析一下附近
  1. gtutorial-x86_64.exe+4098B - 48 63 93 88000000     - movsxd  rdx,dword ptr [rbx+00000088] { 读取已变绿平台数 }
  2. gtutorial-x86_64.exe+40992 - 48 8B 43 30           - mov rax,[rbx+30]
  3. gtutorial-x86_64.exe+40996 - 48 85 C0              - test rax,rax
  4. gtutorial-x86_64.exe+40999 - 74 08                 - je gtutorial-x86_64.exe+409A3
  5. gtutorial-x86_64.exe+4099B - 48 8B 40 F8           - mov rax,[rax-08] { [rax-08] 为平台数组最大下标 }
  6. gtutorial-x86_64.exe+4099F - 48 83 C0 01           - add rax,01 { 最大下标 + 1 即为总平台数 }
  7. gtutorial-x86_64.exe+409A3 - 48 39 C2              - cmp rdx,rax { 比较已变绿平台数和总平台数 }
  8. gtutorial-x86_64.exe+409A6 - 7C 17                 - jl gtutorial-x86_64.exe+409BF
  9. gtutorial-x86_64.exe+409A8 - 48 8B 43 60           - mov rax,[rbx+60] { 二级指针 }
  10. gtutorial-x86_64.exe+409AC - C6 40 18 00           - mov byte ptr [rax+18],00 { 开门 }
  11. gtutorial-x86_64.exe+409B0 - C6 43 7D 01           - mov byte ptr [rbx+7D],01 { 堵门 }
  12. gtutorial-x86_64.exe+409B4 - 48 8B 43 68           - mov rax,[rbx+68]
  13. gtutorial-x86_64.exe+409B8 - 48 89 83 80000000     - mov [rbx+00000080],rax
  14. gtutorial-x86_64.exe+409BF - 48 83 7B 28 00        - cmp qword ptr [rbx+28],00 { 0 }
複製代碼
[rbx+00000088]為已變綠平台數,而已變綠平台數的地址為[["gtutorial-x86_64.exe"+37DC50]+760]+88,所以rbx = [["gtutorial-x86_64.exe"+37DC50]+760],所以可以求得開門地址為[[["gtutorial-x86_64.exe"+37DC50]+760]+60]+18,堵門的地址為[["gtutorial-x86_64.exe"+37DC50]+760]+7D。

我們直接執行mov byte ptr [rax+18],00這條開門語句的內容就行了。手動添加開門地址,Byte類型,然後修改為0。這樣我們躲過敵人就可以進門了,不用讓平台變綠,也不會被堵住。
233210ei6byi6mtoydkkdy

請注意上面動畫中,修改完數值之後右下角門的變化。
第3 關嘗試10
終於要到碰撞檢測了。第2 關中,我們讓子彈直接忽略玩家,繼續向前飛。第3 關我們也可以讓敵人忽略玩家,即使碰到了也不會死亡。
碰撞檢測肯定會讀取二者的X、Y 坐標。查找訪問敵人Y 坐標的指令。
最開始只看到1 條指令。
gtutorial-x86_64.exe+39DDE - F3 0F10 4B 28         - movss xmm1,[rbx+28]
但是查看附近代碼的時候我看到 call qword ptr [gtutorial-x86_64.exe+3825E0] { ->opengl32.glTranslatef }
  1. gtutorial-x86_64.exe+39DDE - F3 0F10 4B 28         - movss xmm1,[rbx+28]
  2. gtutorial-x86_64.exe+39DE3 - F3 0F10 05 7D7F2400   - movss xmm0,[gtutorial-x86_64.exe+281D68] { (0.00) }
  3. gtutorial-x86_64.exe+39DEB - 0F57 C8               - xorps xmm1,xmm0
  4. gtutorial-x86_64.exe+39DEE - F3 0F10 43 24         - movss xmm0,[rbx+24]
  5. gtutorial-x86_64.exe+39DF3 - F3 0F10 15 757F2400   - movss xmm2,[gtutorial-x86_64.exe+281D70] { (0.00) }
  6. gtutorial-x86_64.exe+39DFB - FF 15 DF873400        - call qword ptr [gtutorial-x86_64.exe+3825E0] { ->opengl32.glTranslatef }
複製代碼
所以這個應該是在繪圖指令前讀取Y 坐標,這個應該不是碰撞檢測的代碼。
我懷疑是不是碰撞檢測和其他代碼混在一起,所以只有一次讀取。
我沿著這個附近單步調試了很長時間。
終於,一次不經意間我發現問題了。請觀察下面的動圖。
233232i005gta4pp01ann5
原始的代碼很可能是這樣的。
  1. float player_w_2 = player_w / 2.0f;
  2. float enemy_w_2 = enemy_w / 2.0f;
  3. if (enemy_x - enemy_w_2 < player_x + player_w_2 && player_x - player_w_2 < enemy_x + enemy_w_2) {
  4.     float player_h_2 = player_h / 2.0f;
  5.     float enemy_h_2 = enemy_h / 2.0f;
  6.     if (enemy_y - enemy_h_2 < player_y + player_h_2 && player_y - player_h_2 < enemy_y + enemy_h_2) {
  7.         // 碰撞
  8.     }
  9. }
複製代碼
邏輯短路。如果X 坐標不在敵人寬度範圍內,那麼直接就不用判斷Y 坐標了,就不會對Y 坐標造成訪問。
解決這個問題之後,我們又找到這條語句。
gtutorial-x86_64.exe+39B45 - F3 0F10 43 28         - movss xmm0,[rbx+28]
在周圍分析一下。
  1. gtutorial-x86_64.exe+39B26 - FF 90 E0000000        - call qword ptr [rax+000000E0] { movss xmm0,[100284490]
  2.                                                                                      xmm0 = 0.1 }
  3. gtutorial-x86_64.exe+39B2C - F3 0F10 4E 30         - movss xmm1,[rsi+30]
  4. gtutorial-x86_64.exe+39B31 - F3 0F58 0D 1F822400   - addss xmm1,dword ptr [gtutorial-x86_64.exe+281D58] { (1.00) }
  5. gtutorial-x86_64.exe+39B39 - F3 0F59 0D 1F822400   - mulss xmm1,[gtutorial-x86_64.exe+281D60] { (0.50) }
  6. gtutorial-x86_64.exe+39B41 - F3 0F59 C8            - mulss xmm1,xmm0 { xmm1 = 0.5 * 0.1 }
  7. gtutorial-x86_64.exe+39B45 - F3 0F10 43 28         - movss xmm0,[rbx+28] { 读取敌人 Y 坐标 }
  8. gtutorial-x86_64.exe+39B4A - F3 0F5C C1            - subss xmm0,xmm1 { 减掉敌人高度的一半 }

  9. ......

  10. gtutorial-x86_64.exe+39B56 - C3                    - ret
複製代碼
跟踪這個函數的返回,你會發現一片新天地。
  1. gtutorial-x86_64.exe+3A72E - 48 89 CB              - mov rbx,rcx { rbx 为敌人指针 }
  2. gtutorial-x86_64.exe+3A731 - 48 89 D6              - mov rsi,rdx
  3. gtutorial-x86_64.exe+3A734 - 40 B7 00              - mov dil,00 { 0 }
  4. gtutorial-x86_64.exe+3A737 - 83 7B 58 00           - cmp dword ptr [rbx+58],00 { 0 }
  5. gtutorial-x86_64.exe+3A73B - 75 58                 - jne gtutorial-x86_64.exe+3A795 { 如果 [rbx+58] != 0,则使用普通碰撞算法,否则使用简化碰撞算法 }
  6. gtutorial-x86_64.exe+3A73D - 48 89 F1              - mov rcx,rsi { rsi 为玩家指针 }
  7. gtutorial-x86_64.exe+3A740 - E8 6BFFFFFF           - call gtutorial-x86_64.exe+3A6B0 { xmm0 = [rcx+44] 玩家碰撞半径 }
  8. gtutorial-x86_64.exe+3A745 - 0F28 F0               - movaps xmm6,xmm0 { xmm6 = [rcx+44] 玩家碰撞半径 }
  9. gtutorial-x86_64.exe+3A748 - 48 89 D9              - mov rcx,rbx { rbx 为敌人指针 }
  10. gtutorial-x86_64.exe+3A74B - E8 60FFFFFF           - call gtutorial-x86_64.exe+3A6B0 { xmm0 = [rcx+44] 敌人碰撞半径 }
  11. gtutorial-x86_64.exe+3A750 - F3 0F10 4E 24         - movss xmm1,[rsi+24] { xmm1 = 玩家 X }
  12. gtutorial-x86_64.exe+3A755 - F3 0F5C 4B 24         - subss xmm1,[rbx+24] { xmm1 = 玩家 X - 敌人 X }
  13. gtutorial-x86_64.exe+3A75A - 0F54 0D 0F641E00      - andps xmm1,[gtutorial-x86_64.exe+220B70] { 取绝对值 }
  14. gtutorial-x86_64.exe+3A761 - F3 0F59 C9            - mulss xmm1,xmm1 { 平方 }
  15. gtutorial-x86_64.exe+3A765 - F3 0F10 56 28         - movss xmm2,[rsi+28] { xmm2 = 玩家 Y }
  16. gtutorial-x86_64.exe+3A76A - F3 0F5C 53 28         - subss xmm2,[rbx+28] { xmm2 = 玩家 Y - 敌人 Y }
  17. gtutorial-x86_64.exe+3A76F - 0F54 15 FA631E00      - andps xmm2,[gtutorial-x86_64.exe+220B70] { 取绝对值 }
  18. gtutorial-x86_64.exe+3A776 - F3 0F59 D2            - mulss xmm2,xmm2 { 平方 }
  19. gtutorial-x86_64.exe+3A77A - F3 0F58 D1            - addss xmm2,xmm1 { 相加 }
  20. gtutorial-x86_64.exe+3A77E - F3 0F51 D2            - sqrtss xmm2,xmm2 { xmm2 = 敌人、玩家中心距离 }
  21. gtutorial-x86_64.exe+3A782 - 0F28 CE               - movaps xmm1,xmm6
  22. gtutorial-x86_64.exe+3A785 - F3 0F58 C8            - addss xmm1,xmm0 { xmm1 = 敌人碰撞半径+玩家碰撞半径 }
  23. gtutorial-x86_64.exe+3A789 - 0F2F CA               - comiss xmm1,xmm2
  24. gtutorial-x86_64.exe+3A78C - 40 0F97 C7            - seta dil
  25. gtutorial-x86_64.exe+3A790 - E9 B4000000           - jmp gtutorial-x86_64.exe+3A849
  26. gtutorial-x86_64.exe+3A795 - 83 7B 58 01           - cmp dword ptr [rbx+58],01 { 1 }
  27. gtutorial-x86_64.exe+3A799 - 0F85 AA000000         - jne gtutorial-x86_64.exe+3A849 { 如果 [rbx+58] != 1 则不判断碰撞,前面已将 dil 设为 0 }
  28. gtutorial-x86_64.exe+3A79F - 48 89 D9              - mov rcx,rbx
  29. gtutorial-x86_64.exe+3A7A2 - 48 89 D8              - mov rax,rbx
  30. gtutorial-x86_64.exe+3A7A5 - 48 8B 00              - mov rax,[rax]
  31. gtutorial-x86_64.exe+3A7A8 - FF 90 F8000000        - call qword ptr [rax+000000F8] { xmm0 = 敌人 left }
  32. gtutorial-x86_64.exe+3A7AE - 0F28 F0               - movaps xmm6,xmm0
  33. gtutorial-x86_64.exe+3A7B1 - 48 89 F1              - mov rcx,rsi
  34. gtutorial-x86_64.exe+3A7B4 - 48 89 F0              - mov rax,rsi
  35. gtutorial-x86_64.exe+3A7B7 - 48 8B 00              - mov rax,[rax]
  36. gtutorial-x86_64.exe+3A7BA - FF 90 00010000        - call qword ptr [rax+00000100] { xmm0 = 玩家 right }
  37. gtutorial-x86_64.exe+3A7C0 - 0F2F C6               - comiss xmm0,xmm6
  38. gtutorial-x86_64.exe+3A7C3 - 0F8A 7D000000         - jp gtutorial-x86_64.exe+3A846
  39. gtutorial-x86_64.exe+3A7C9 - 0F86 77000000         - jbe gtutorial-x86_64.exe+3A846
  40. gtutorial-x86_64.exe+3A7CF - 48 89 F1              - mov rcx,rsi
  41. gtutorial-x86_64.exe+3A7D2 - 48 89 F0              - mov rax,rsi
  42. gtutorial-x86_64.exe+3A7D5 - 48 8B 00              - mov rax,[rax]
  43. gtutorial-x86_64.exe+3A7D8 - FF 90 F8000000        - call qword ptr [rax+000000F8] { xmm0 = 玩家 left }
  44. gtutorial-x86_64.exe+3A7DE - 0F28 F0               - movaps xmm6,xmm0
  45. gtutorial-x86_64.exe+3A7E1 - 48 89 D9              - mov rcx,rbx
  46. gtutorial-x86_64.exe+3A7E4 - 48 89 D8              - mov rax,rbx
  47. gtutorial-x86_64.exe+3A7E7 - 48 8B 00              - mov rax,[rax]
  48. gtutorial-x86_64.exe+3A7EA - FF 90 00010000        - call qword ptr [rax+00000100] { xmm0 = 敌人 right }gtutorial-x86_64.exe+3A7F0 - 0F2F C6               - comiss xmm0,xmm6
  49. gtutorial-x86_64.exe+3A7F3 - 7A 51                 - jp gtutorial-x86_64.exe+3A846
  50. gtutorial-x86_64.exe+3A7F5 - 76 4F                 - jna gtutorial-x86_64.exe+3A846
  51. gtutorial-x86_64.exe+3A7F7 - 48 89 D9              - mov rcx,rbx
  52. gtutorial-x86_64.exe+3A7FA - 48 89 D8              - mov rax,rbx
  53. gtutorial-x86_64.exe+3A7FD - 48 8B 00              - mov rax,[rax]
  54. gtutorial-x86_64.exe+3A800 - FF 90 08010000        - call qword ptr [rax+00000108] { xmm0 = 敌人 top }
  55. gtutorial-x86_64.exe+3A806 - 0F28 F0               - movaps xmm6,xmm0
  56. gtutorial-x86_64.exe+3A809 - 48 89 F1              - mov rcx,rsi
  57. gtutorial-x86_64.exe+3A80C - 48 89 F0              - mov rax,rsi
  58. gtutorial-x86_64.exe+3A80F - 48 8B 00              - mov rax,[rax]
  59. gtutorial-x86_64.exe+3A812 - FF 90 10010000        - call qword ptr [rax+00000110] { xmm0 = 玩家 bottom }
  60. gtutorial-x86_64.exe+3A818 - 0F2F C6               - comiss xmm0,xmm6
  61. gtutorial-x86_64.exe+3A81B - 7A 29                 - jp gtutorial-x86_64.exe+3A846
  62. gtutorial-x86_64.exe+3A81D - 76 27                 - jna gtutorial-x86_64.exe+3A846
  63. gtutorial-x86_64.exe+3A81F - 48 89 F1              - mov rcx,rsi
  64. gtutorial-x86_64.exe+3A822 - 48 8B 06              - mov rax,[rsi]
  65. gtutorial-x86_64.exe+3A825 - FF 90 08010000        - call qword ptr [rax+00000108] { xmm0 = 玩家 top }
  66. gtutorial-x86_64.exe+3A82B - 0F28 F0               - movaps xmm6,xmm0
  67. gtutorial-x86_64.exe+3A82E - 48 89 D9              - mov rcx,rbx
  68. gtutorial-x86_64.exe+3A831 - 48 8B 03              - mov rax,[rbx]
  69. gtutorial-x86_64.exe+3A834 - FF 90 10010000        - call qword ptr [rax+00000110] { xmm0 = 敌人 bottom }
  70. gtutorial-x86_64.exe+3A83A - 0F2F C6               - comiss xmm0,xmm6
  71. gtutorial-x86_64.exe+3A83D - 7A 07                 - jp gtutorial-x86_64.exe+3A846
  72. gtutorial-x86_64.exe+3A83F - 76 05                 - jna gtutorial-x86_64.exe+3A846
  73. gtutorial-x86_64.exe+3A841 - 40 B7 01              - mov dil,01 { 碰撞设置 dil 为 1 }
  74. gtutorial-x86_64.exe+3A844 - EB 03                 - jmp gtutorial-x86_64.exe+3A849
  75. gtutorial-x86_64.exe+3A846 - 40 B7 00              - mov dil,00 { 0 }
  76. gtutorial-x86_64.exe+3A849 - 40 0FB6 C7            - movzx eax,dil { 碰撞函数返回值为 eax }
  77. gtutorial-x86_64.exe+3A84D - 90                    - nop
  78. gtutorial-x86_64.exe+3A84E - 66 0F6F 74 24 20      - movdqa xmm6,[rsp+20]
  79. gtutorial-x86_64.exe+3A854 - 48 8D 64 24 30        - lea rsp,[rsp+30]
  80. gtutorial-x86_64.exe+3A859 - 5E                    - pop rsi
  81. gtutorial-x86_64.exe+3A85A - 5F                    - pop rdi
  82. gtutorial-x86_64.exe+3A85B - 5B                    - pop rbx
  83. gtutorial-x86_64.exe+3A85C - C3                    - ret
複製代碼
上面是完整的碰撞算法分析。
其實並沒有這麼麻煩,我們只需要知道返回值是eax,如果eax == 1則表示碰撞,eax == 0則表示未碰撞。
我們跟踪ret 返回。
  1. gtutorial-x86_64.exe+4093A - FF 90 28010000        - call qword ptr [rax+00000128] { eax = 是否碰撞 }
  2. gtutorial-x86_64.exe+40940 - 84 C0                 - test al,al
  3. gtutorial-x86_64.exe+40942 - 74 11                 - je gtutorial-x86_64.exe+40955 { eax == 0 则跳转 }
  4. gtutorial-x86_64.exe+40944 - 48 8B 4B 28           - mov rcx,[rbx+28]
  5. gtutorial-x86_64.exe+40948 - 48 8B 43 28           - mov rax,[rbx+28]
  6. gtutorial-x86_64.exe+4094C - 48 8B 00              - mov rax,[rax]
  7. gtutorial-x86_64.exe+4094F - FF 90 20010000        - call qword ptr [rax+00000120] { 碰撞之后执行的事件 }
  8. gtutorial-x86_64.exe+40955 - 48 8B 53 38           - mov rdx,[rbx+38]
複製代碼
je修改成jmp即可。
233248dcdowdv2ozvyuvvv

第3 關嘗試11
如果[rbx+58] != 0,則使用普通碰撞算法,否則使用簡化碰撞算法,如果[rbx+58] != 1則不判斷碰撞。所以我們可以令[rbx+58] = 2這樣兩個碰撞就都沒了。
手動添加[[[["gtutorial-x86_64.exe"+37DC50]+760]+38]+0]+58,4字節,設置為2。
隱藏問題
你有沒有註意到你執行代碼注入以後標題欄會由Step 2變成Step 2 (Integrity check error)
完整性檢查錯誤
遊戲中內置的檢測工具發現你修改了他們的程序指令。怎麼辦呢?
他們是怎麼檢測的呢?
原理很簡單,就是比較代碼區域的內存。
避免被發現的方法並不是如何偽造內存讓他們別發現。通常檢測程序運行在另一個線程,直接關掉那個線程就行了。
首先在地址列表中手動添加我們剛才修改的地址gtutorial-x86_64.exe+3F6A3,然後查找誰在訪問這個地址。
[Tips]
一般正常的程序不會訪問程序代碼部分的內存的,他們運行所要的數據和都在常量區、全局變量區、堆、棧中,代碼區是額外的一個區域,他們之間都是隔離開的。要訪問程序自己的代碼區的程序都不是正常的程序。
我這裡找到了3 個,然後選擇其中一個(我這裡就選第一個了)
233305d6q6tb0lk0dc0eqy

Show disassembler,然後下斷點。
233343ivayld6axarbdd77

然後我們需要做的就是記住標題上的線程編號。然後在Memory Viewer中View→ Threadlist→右鍵點擊剛才的線程編號→ Freeze thread。
233353za4orvzv4y29afac

233325i7y2cc29mcwc2cyy

你打敗了3 個“遊戲”,並且你打敗了完整性檢查!
幹的真的漂亮!
總結
本文通過3個小遊戲的二十多種思路,向你展示了很多破解思路。
您應該學習並理解這種思路,主要是如何通過內存地址找代碼,如果通過代碼找內存地址。
本文還講述了一些小技巧,如何搜索坐標這種未知數值的內存數據。
最後簡單講解了內存校驗的原理與簡易破解方法。
希望您不僅能從本文中學到Cheat Engine 工具的使用方法,還能學到更廣闊的破解思想。
最後如果你想繼續研究,你可以對照GTutorial源代碼進行研究,看看原作者的註釋,你能看到他給你預留了很多變量用於破解。



232456es65prp5smrezrss (735.3 KB, 下載次數: 5)

232456es65prp5smrezrss

已有 3 人評分楓幣 威望 GP 收起 理由
verywant + 50 + 3 + 3 精品文章!
TzYiGa + 50 很棒的文章 (假裝自己看的懂 XD.
fps273860 + 1 + 1 內容很多 等有時間再看 感覺很專業 推個!.

總評分: 楓幣 + 101  威望 + 3  GP + 4   查看全部評分

收藏收藏4 推1 噓0


把本文推薦給朋友或其他網站上,每次被點擊增加您在本站積分: 1彩票
https://www.youtube.com/watch?v=vajUsmisNFQ  運命の人
https://www.youtube.com/watch?v=W1N1DYewwsk -螢
https://www.youtube.com/watch?v=abIRuMGldi8 一番の宝物
https://www.youtube.com/watch?v=HyyXq3GDJq0 Your Beats
https://www.youtube.com/watch?v=Turf7WDB3iY Angel Beats
https://www.youtube.com/watch?v=iyCFdilBCPw   -花篝
https://www.youtube.com/watch?v=Qae1QVgXrP8  -崩壞小丑
https://www.youtube.com/watch?v=mAEPs2PvyrQ  -Doll
https://www.youtube.com/watch?v=zyJJlPSeEpo  -世界的盡頭
https://www.youtube.com/watch?v=qhDMohNzxvo  -After Dark
https://www.youtube.com/watch?v=VpdLEk4MwmU  -雪の華
https://www.youtube.com/watch?v=q30xr2IH4pw  -Even Heaven
https://www.youtube.com/watch?v=mw9abYUxEwg  -夜行列車
https://www.youtube.com/watch?v=t6wBKcYIFGE  -緣結空蟬
https://www.youtube.com/watch?v=dfbBh-0LTHo   - April Showers
https://www.youtube.com/watch?v=ojG9C6L61oA  -君に最後の口づけを
https://www.youtube.com/watch?v=0Y4KXMjDJjU  -君がくれたもの
https://www.youtube.com/watch?v=Re6JJ_njYR4  -歌に形はないけれど
https://www.youtube.com/watch?v=TLWkm-SNDS8  -太陽と向日葵
https://www.youtube.com/watch?v=xV5B4fzBy9c  -他の誰かより悲しい恋をしただけ
https://www.youtube.com/watch?v=2k9Rk0-O2xE  -煙
https://www.youtube.com/watch?v=e4oVMSEPpww  -Letter Song
https://www.youtube.com/watch?v=em7yXGreZ_M  -最初の淚
https://www.youtube.com/watch?v=zW8_NGpjcnw  -温馨的家
https://www.youtube.com/watch?v=csGCZvNEOKc  -モトカレ
https://www.youtube.com/watch?v=XxTIrpWEh1o  -ねぇ/吶
https://www.youtube.com/watch?v=Ua9Ek1kE8dA -ゴシック
https://www.youtube.com/watch?v=HWgxbptCcuA  Violet Cry
https://www.youtube.com/watch?v=qIoDWTF0qSo 深い森
https://www.youtube.com/watch?v=JwCF0ueGmVM 緋弾のアリア「Scarlet Ballest」
https://www.youtube.com/watch?v=vYV-XJdzupY Hitohira No Hanabira
複製連結並發給好友,以賺取推廣點數
簡單兩步驟,註冊、分享網址,即可獲得獎勵! 一起推廣文章換商品、賺$$
高級模式
B Color Image Link Quote Code Smilies |上傳

廣告刊登意見回饋關於我們管群招募本站規範DMCA隱私權政策

Copyright © 2011-2024 冰楓論壇, All rights reserved

免責聲明:本網站是以即時上載留言的方式運作,本站對所有留言的真實性、完整性及立場等,不負任何法律責任。

而一切留言之言論只代表留言者個人意見,並非本網站之立場,用戶不應信賴內容,並應自行判斷內容之真實性。

小黑屋|手機版|冰楓論壇

GMT+8, 2024-11-18 08:47

回頂部