使用 Cheat Engine 修改 Kingdom Rush 中的金錢、生命、星
最近想學習一下 CE,剛好看見遊戲庫裡裝了 Kingdom Rush 就拿它來研究吧。這裡寫的東西,需要一些 Cheat Engine 的基礎,可以看看教程。
這裡主要是看寫的註釋,來理解指令碼。(如果什麼都不想看,可以直接複製貼上 CE 自動彙編(AA)指令碼)
我學習的連結:
Cheat Engine 基礎教程 CE Tutorial | Ganlv's Blog
參考連結:
技術理解
我對一些用到的技術的簡單理解:
- 程式碼注入 :在程式執行時,將自己寫的程式碼,替換掉原有的程式碼
- AOB :(Array Of Byte)在記憶體中搜索特定的一串資料,以決定注入程式碼的位置
- 人造指標 :單獨找個地方,記錄變數所在的地址
資料儲存結構
資料在記憶體中的儲存結構:
Cheat Engine 相關彙編知識
此指令碼用到的 CE 彙編指令:
命令例子 | 功能 |
---|---|
mov ebx,0000FFFF | Move,暫存器直接賦值 |
mov ebx,eax | Move,將右邊直接給左邊 |
mov ebx,[eax] | Move,將右邊所指的值給左邊。[ ]代表括號內的是指標,操作時,操作其指向的記憶體值 |
cmp ebx,eax | Compare,比較兩暫存器值,若相等則 ZF 位 置 1(左減右) |
je <label> |
Jump if Equal, ZF = 1 時跳轉,可跳轉至標記段程式碼 |
jne <label> |
Jump if Not Equal, ZF = 0 時跳轉,可跳轉至標記段程式碼 |
jmp <label> |
Jump |
此指令碼用到的 CE 自動彙編函式:(Auto Assembler)
函式 | 引數 | 作用 |
---|---|---|
alloc | alloc(SymbolName, Size, AllocateNearThisAddress OPTIONAL) | Allocates a memory block of Size bytes and defines the SymbolName in the script, pointing to the beginning of the allocated memory block. |
dealloc | dealloc(SymbolName) | Deallocates a block of memory allocated with alloc. |
label | label(LabelName) | Enables the word 'LabelName' to be used as a symbol. |
aobScanModule | aobScanModule(SymbolName, ModuleName, AOBString) | Scans the memory used by the module ModuleName for a specific byte pattern defined by AOBString and sets the resulting address to the symbol SymbolName. |
registerSymbol | registerSymbol(SymbolName) | Adds a symbol to the user-defined symbol list so cheat tables and the memory browser can use that name instead of an address. |
unregisterSymbol | unregisterSymbol(SymbolName) | Removes a symbol from the user-defined symbol list. No error will occur if the symbol doesn't exist. |
此指令碼用到的 CE 組合語言資料型別:
型別 | 佔用空間 |
---|---|
Bit(整型) | 1 位 |
Byte(整型) | 8 位(位元組) |
2 byte(整型) | WORD(字) |
4 Bytes(整型) | DWORD(雙字) |
8 Bytes(整型) | QWORD(四字) |
Float(單浮點) | DWORD(雙字) |
Double(雙浮點) | QWORD(四字) |
String(字串) | 任意長度 |
Array of bytes(AOB) | 任意長度 |
CE 常用暫存器:
The x86 architecture has 8 General-Purpose Registers (GPR)
x86 架構有 8 個通用暫存器(32 位系統)
"E" (for "extended"), 32 bits.
General-Purpose Registers:EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI
Cheat Engine 彙編程式碼
學過 CE 的,通過註釋,應該能看懂
{ Game : Kingdom Rush.exe Version: Date : 2022-06-19 Author : Tsanfer 更改金錢、生命、星 (使用 AOB 和人造指標) } [ENABLE] aobscanmodule(INJECT,lua51.dll,8B 29 8B 49 04 89 2C C2) // AOB 匹配入口 alloc(newmem,1024,INJECT) // 分配 1024 位元組個記憶體,用於程式碼注入 alloc(man_pointer,24) // 動態分配記憶體,用於存放3個人造指標 // 註冊全域性符號 registersymbol(INJECT) registersymbol(man_pointer) // 人造指標 // 宣告標號 label(original_code) label(return) label(restore_reg) label(check_is_gold) label(check_is_gold_END) label(check_is_live) label(check_is_live_END) label(check_is_star) label(check_is_star_END) // 資料儲存結構: // 偏移 資料 變數型別 // 0 具體值 Double // 8 指向值所屬的公共結構 指標 // └─> 0 // 10 此公共結構的名稱 字串 // 程式執行順序:(彙編程式碼如沒有跳轉,預設從上往下執行) // INJECT -> newmem -> check_is_gold -> check_is_live // -> check_is_star -> reset_reg -> original_code -> return // 注入程式碼段 newmem: pushfd // 儲存所有標誌位 push eax // eax 壓棧儲存,為後續操作騰出一個暫存器 mov eax,[ecx+08] // 將當前值所屬的公共型別所在的地址,給 eax // 判斷此值的型別是否為金錢(player_gold) check_is_gold: cmp dword ptr [eax+10],'play' // 記憶體雙字比較 jne check_is_gold_END // 如不匹配,則停止後續比較,跳到此比較的結尾 cmp dword ptr [eax+14],'er_g' jne check_is_gold_END cmp word ptr [eax+18],'ol' // 記憶體字比較 jne check_is_gold_END cmp byte ptr [eax+1A],'d' // 記憶體位元組比較 jne check_is_gold_END mov [man_pointer],ecx // 匹配成功,將指向此值的指標儲存在申請的記憶體中(製作人造指標) check_is_gold_END: // 判斷此值的型別是否為生命(lives) check_is_live: cmp dword ptr [eax+10],'live' jne check_is_live_END cmp byte ptr [eax+14],'s' jne check_is_gold_END mov [man_pointer+8],ecx // 將指標儲存在第二個記憶體位置 // (64位系統的指標大小為 64 bit,每個記憶體地址大小為 8bit,則需要平移8個記憶體地址,8x8=64) check_is_live_END: // 判斷此值的型別是否為升級用的星(total_stars) check_is_star: cmp dword ptr [eax+10],'tota' jne check_is_star_END cmp dword ptr [eax+14],'l_st' jne check_is_star_END cmp word ptr [eax+18],'ar' jne check_is_star_END cmp byte ptr [eax+1A],'s' jne check_is_star_END mov [man_pointer+10],ecx check_is_star_END: // 恢復臨時使用的暫存器的值 restore_reg: pop eax popfd // 還原所有標誌位 jmp original_code // 原始程式碼 original_code: mov ebp,[ecx] mov ecx,[ecx+04] jmp return // 跳到返回 // 程式入口 INJECT: jmp newmem return: // 返回 [DISABLE] // 還原始碼 INJECT: db 8B 29 8B 49 04 // 登出全域性符號 unregistersymbol(INJECT) unregistersymbol(man_pointer) // 釋放記憶體 dealloc(newmem) dealloc(man_pointer) { // ORIGINAL CODE - INJECTION POINT: lua51.dll+1BDA lua51.dll+1BBC: 23 48 08 - and ecx,[eax+08] lua51.dll+1BBF: 6B C9 18 - imul ecx,ecx,18 lua51.dll+1BC2: 03 4D 14 - add ecx,[ebp+14] lua51.dll+1BC5: 83 79 0C FB - cmp dword ptr [ecx+0C],-05 lua51.dll+1BC9: 75 3A - jne lua51.dll+1C05 lua51.dll+1BCB: 39 41 08 - cmp [ecx+08],eax lua51.dll+1BCE: 75 35 - jne lua51.dll+1C05 lua51.dll+1BD0: 83 79 04 FF - cmp dword ptr [ecx+04],-01 lua51.dll+1BD4: 74 36 - je lua51.dll+1C0C lua51.dll+1BD6: 0F B6 46 FD - movzx eax,byte ptr [esi-03] // ---------- INJECTING HERE ---------- lua51.dll+1BDA: 8B 29 - mov ebp,[ecx] lua51.dll+1BDC: 8B 49 04 - mov ecx,[ecx+04] // ---------- DONE INJECTING ---------- lua51.dll+1BDF: 89 2C C2 - mov [edx+eax*8],ebp lua51.dll+1BE2: 89 4C C2 04 - mov [edx+eax*8+04],ecx lua51.dll+1BE6: 8B 06 - mov eax,[esi] lua51.dll+1BE8: 0F B6 CC - movzx ecx,ah lua51.dll+1BEB: 0F B6 E8 - movzx ebp,al lua51.dll+1BEE: 83 C6 04 - add esi,04 lua51.dll+1BF1: C1 E8 10 - shr eax,10 lua51.dll+1BF4: FF 24 AB - jmp dword ptr [ebx+ebp*4] lua51.dll+1BF7: 0F B6 46 FD - movzx eax,byte ptr [esi-03] }
然後再手動新增 3 個人造指標的地址(man_pointer 和 man_pointer+8 和 man_pointer+10),就行了
Cheat Engine 使用介面:
本文由 Tsanfer's Blog 釋出!
「其他文章」
- Python 中生成器的原理
- 對開源框架躍躍欲試,卻在寫的時候犯了難?
- 一文讀懂數倉中的pg_stat
- Linux系列之查詢命令
- 聊聊支付流程的設計與實現邏輯
- (資料庫提權——Redis)Redis未授權訪問漏洞總結
- springboot的@ConditionalOnBean註解
- 使用 Cheat Engine 修改 Kingdom Rush 中的金錢、生命、星
- Java String類
- 一次 Keepalived 高可用的事故,讓我重學了一遍它!
- 面試突擊61:說一下MySQL事務隔離級別?
- 小樣本利器2.文字對抗 半監督 FGSM & VAT & FGM程式碼實現
- Spring框架系列(7) - Spring IOC實現原理詳解之IOC初始化流程
- crane:字典項與關聯資料處理的新思路
- 面試突擊60:什麼情況會導致 MySQL 索引失效?
- Java遞迴實現評論多級回覆
- Docker 與 K8S學習筆記(二十四)—— 工作負載的使用
- vue 的常用事件
- 158_模型_Power BI 使用 DAX SVG 打通製作商業圖表幾乎所有可能
- 資料庫系列:MySQL索引優化總結(綜合版)