想必大家或多或少都有聽過Cheat Engine吧,它裡面附有一個教學用的程式(Cheat Engine Tutorial v3.4),最近攻略完畢,順便分享並記錄一下自己的作法,我會從Step 2 ~ Step 9 慢慢補齊內容
Step 2
首先,我們要先找到100這個數值在記憶體裡的位置,在Cheat Engine的視窗這裡
輸入要找的值,這邊輸入100,掃描類型選擇"精確數值",數值類型選擇"4 Bytes",
一般整數的數值大部分是存成4 bytes的形式,點選"新的掃描"
紅色的列表為可能存在的數值,回到"Step 2"視窗點"Hit me"改變Health,假設點擊之後Health變為96
在紅色框框中可以看到有一個值變成96,點選那一個位址,點擊一個指向右下角方向的紅色箭號
前面有個"啟動"選擇框(checkbox)點下去來鎖定數值,把數值改成1000後
完成
Step 3這裡沒有告訴你他的數值,這裡我們只能用"窮舉法"應付,回到Cheat Engine視窗
掃描類型選擇"未知的初始數值"
回"Step 3"視窗點一下"Hit me",如果你的眼力好一點,你可以看到有顯示出一個被減去的數值,範例這裡是"-4"
回到Cheat Engine視窗,數值欄輸入4,掃描類型選擇"數值減少了... ",按"再次掃描"
左方的列表有列出找到減去4的數值位址,這裡可以依照你的經驗以及直覺判斷有些位址不會是我們要找的數值可能位址(例如:不斷變動的數值,我們要找的值只會對"Hit me"按鈕有反應;有些表示為0的也可以忽略掉),然後重複上一步驟再點一次"Hit me",以範例為例減少數值為8
有沒有注意到0126BB88的位址由418變成410,沒錯八九不離十就是它了,把它加入監看列表,數值改成5000,結束
Step 4
這一部分比較簡單,流程與Step 2大致相同,唯一不同的地方是我們之前找的數值類型是整數,這裡是是帶有小數點的數字。
在程式中會使用浮點數來表示帶有小數的數字,大部分區分成單精確度浮點數(float)與雙倍精確度浮點數(double),如果你有修過程式語言或是計算機概論的課程應該會知道它是甚麼。
在這裡我們只要把"數值類型"換成"浮點數"和"雙浮點數",其他部分跟Step 2一樣各做一遍,這裡就不再贅述。
Step 5
這裡依照Step 2的步驟找到數值的位址
對它點選右鍵=>"找出是甚麼改寫了這個位址"
會出現這一個視窗
回到"Step 5"視窗,點擊"Change Value"
點選出現的那一個指令,右邊點"顯示反彙編程序",出現記憶體檢視器
右鍵點選"使用空指令取代",讓改變數值的行為失效(以nop指令取代之)
回到Step 5視窗,你會發現Change Value的行為失效,完成
Step 6
傳說中的指標,想了解指標是甚麼的同學請參閱其他文章,這裡可以幫助你更了解指標在底層運作的行為方式。
依照Step 5的步驟,做到"下列代碼寫入XXX"的地方,記得先別點"Change Pointer"
點右方的"詳細資訊"
注意這一段指令,"mov [rdx], eax" => "mov [rdx+0], eax"
base address = rdx = 0x01221500
offset = 0
先把它記下來,然後回到Cheat Engine,搜尋0x1221500,這裡將Hex的點選欄打勾
點選"手動加入位址"
把0x1221500的位址0x1002CAA40輸入進去,offset為0
鎖住改5000,按"Change Pointer",完成
Step 7
依照Step 5的步驟,做到出現"記憶體檢視器"的部分
菜單選項"工具"->"自動彙編"
菜單選項"模板"->"代碼注入"
在newmem label下面部分加入指令,把"sub dword ptr ......"的部分前面sub取代為add,後面的01取代為02即可,最後在加入"jmp exit"指令即可,按"執行"
回"Step 7"點"Hit me",完成
Step 8
首先找3012這個值的位址
右鍵選單->找出甚麼前往這個位址,然後點Change value
之後看詳細資料找位址指標數值
把offset(18)記下來待會會用到
然後再找03380890這個值在哪裡,提醒一下它是hexadecimal不是decimal
重複上述步驟......
通常會卡在這個地方
這一段instruction是 rsi, [rsi],沒有offset
回到cheat engine注意剛剛的搜尋
3380890的位址是128B920,128B920就是我們要找的值
然後繼續上述的步驟......
到最後
接下來加入指標
然後按Change pointer檢查結果
鎖住改5000後按Change pointer,Congratulation !!
Step 9
目標:讓Player 1和Player 2贏得遊戲
實際測試後左邊兩個attack按鈕扣血比右邊還要多,而且右邊每次只減1,
在血的方面右邊是左邊的五倍,
按下Restart game and autoplay後好像attack左邊兩個players的頻率也比較高,
不管如何是不可能靠運氣打敗右邊的player的,
先來看看他們的Health是在記憶體的哪個位置以及扣血是怎麼運作的
首先先找到Health的位置,數值類型選浮點數(有提示)
然後看是甚麼改寫Player 1 Dave,反組譯後打開記憶體檢視器與詳細資訊
要找的位址可能是在010D5F00,其他Player也是用這個方式來尋找,不過我們不能保證
資料真的永遠是在010D5F00,
如果按下Restart game按鈕,剛剛找的四個數值沒有跟著重置,所以資料的位址是會改變的
再重複上述的步驟,重新找到四個浮點數的值,並重找資料的位址
以Dave為例,資料位址為0577A0D0,尋找存放0577A0D0的記憶體在哪裡
然後其他players也是
把不變的刪掉,然後在加入指標(offset=08)
這樣子就可以分辨每一個player
然後打開剛剛的記憶體檢視器
每個Player都共用這一份程式碼而
在Tutorial-x86_64.exe+2E561 - subss xmm1,xmm0地方大概是執行扣血的地方
不過這不是重點,我們關注的地方是寫回資料的部分
Tutorial-x86_64.exe+2E577 - movss [rbx+08],xmm0
在這邊做code injection,加入判斷是哪個Player的assembly code
組語碼我已經寫好了
push rax
push rcx
mov rax, <Player1 address>
mov rax, [rax]
add rax, 08
mov rcx, rbx
add rcx, 08
cmp rax, rcx
pop rcx
pop rax
je "Tutorial-x86_64.exe"+2E57C
push rax
push rcx
mov rax, <Player2 address>
mov rax, [rax]
add rax, 08
mov rcx, rbx
add rcx, 08
cmp rax, rcx
pop rcx
pop rax
je "Tutorial-x86_64.exe"+2E57C
執行邏輯比較看目前的目標位址到底是不是屬於Player 1 和Player 2
是Player 1 或Player 2就跳過不執行movss [rbx+08],xmm0
這樣就可以避免改變Player 1 & 2的Health
以目前的位址的測資為例,
組語碼為
push rax
push rcx
mov rax, 010CC248
mov rax, [rax]
add rax, 08
mov rcx, rbx
add rcx, 08
cmp rax, rcx
pop rcx
pop rax
je "Tutorial-x86_64.exe"+2E57C
push rax
push rcx
mov rax, 010CC250
mov rax, [rax]
add rax, 08
mov rcx, rbx
add rcx, 08
cmp rax, rcx
pop rcx
pop rax
je "Tutorial-x86_64.exe"+2E57C
使用工具->自動匯編->模板->代碼注入,
把上述組語的腳本輸入執行code injection
記得放在newmem:下面,originalcode:上面
結果:
2019.8.7 更新
這裡我更新使用Cheat Engine 6.8.3
來更新一下Step 9的部分,因為基本上並沒有分析Player的資料/位址分析的很完整,所以每次重來一遍都要重做資料/位址的分析。這裡是我重新分析Dave的結果(方法請參考前面的部分)
這裡可以看到有位址是綠色的(紅框處),代表說它可能是位在資料區段(data segment)等每次程式讀進記憶體中位址不會改變的地方。善用這個特性,我們可以從這個不變的位址出發找到我們要的資料,也不必重新再找一遍(大前提是要找的到)。
接下來把011B55D8和1002CBA80兩個部分整合,各個offset部分請參考description對應的指令
其他Player的部分一樣使用同樣方法找出來。
code injection的腳本會做一些調整
newmem:
push rax
// Dave
mov rax, ["Tutorial-x86_64.exe"+2CBA80]
mov rax, [rax+828]
cmp rax, rbx
je no_attack
// Eric
mov rax, ["Tutorial-x86_64.exe"+2CBA80]
mov rax, [rax+830]
cmp rax, rbx
je no_attack
pop rax
attack:
movss [rbx+08],xmm0
jmp return
no_attack:
pop rax
jmp return
address:
jmp newmem
return:
一樣是針對"movss [rbx+08],xmm0"指令打patch,但是這裡改從新找到的綠色位址出發,然後找到下圖紅色框框處來分辨Player Dave和Eric,比對結果是的話就略過寫入資料的指令
附上CE table