我对一些用到的技术的简单理解:代码注入:在程序运行时,将自己写的代码,替换掉原有的代码AOB:(Array Of Byte)在内存中搜索特定的一串数据,以决定注入代码的位置人造指针:单独找个地方,记录变量所在的地址 最新博客链接
最近想学习一下 CE,刚好看见游戏库里装了 Kingdom Rush 就拿它来研究吧。这里写的东西,需要一些 Cheat Engine 的基础,可以看看教程。
这里主要是看写的注释,来理解脚本。(如果什么都不想看,可以直接复制粘贴 CE 自动汇编(AA)脚本)
我学习的链接:
你能学会的 Cheat Engine 零基础入门教程
Cheat Engine 基础教程 CE Tutorial | Ganlv's Blog
CE 教学实例应用-由浅入深学习游戏修改 [全十课]
参考链接:
Cheat Engine 官方 WIKI
x86 汇编指令列表
我对一些用到的技术的简单理解:
数据在内存中的存储结构:

此脚本用到的 CE 汇编指令:
x86 汇编指令列表
| 命令例子 | 功能 |
|---|---|
| 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)
Cheat Engine 官方 WIKI
| 函数 | 参数 | 作用 |
|---|---|---|
| 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
学过 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],ecxcheck_is_star_END:// 恢复临时使用的寄存器的值restore_reg: pop eax popfd // 还原所有标志位 jmp original_code// 原始代码original_code: mov ebp,[ecx] mov ecx,[ecx+04] jmp return // 跳到返回// 程序入口INJECT: jmp newmemreturn: // 返回[DISABLE]// 还原代码INJECT: db 8B 29 8B 49 04// 注销全局符号unregistersymbol(INJECT)unregistersymbol(man_pointer)// 释放内存dealloc(newmem)dealloc(man_pointer){// ORIGINAL CODE - INJECTION POINT: lua51.dll+1BDAlua51.dll+1BBC: 23 48 08 - and ecx,[eax+08]lua51.dll+1BBF: 6B C9 18 - imul ecx,ecx,18lua51.dll+1BC2: 03 4D 14 - add ecx,[ebp+14]lua51.dll+1BC5: 83 79 0C FB - cmp dword ptr [ecx+0C],-05lua51.dll+1BC9: 75 3A - jne lua51.dll+1C05lua51.dll+1BCB: 39 41 08 - cmp [ecx+08],eaxlua51.dll+1BCE: 75 35 - jne lua51.dll+1C05lua51.dll+1BD0: 83 79 04 FF - cmp dword ptr [ecx+04],-01lua51.dll+1BD4: 74 36 - je lua51.dll+1C0Clua51.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],ebplua51.dll+1BE2: 89 4C C2 04 - mov [edx+eax*8+04],ecxlua51.dll+1BE6: 8B 06 - mov eax,[esi]lua51.dll+1BE8: 0F B6 CC - movzx ecx,ahlua51.dll+1BEB: 0F B6 E8 - movzx ebp,allua51.dll+1BEE: 83 C6 04 - add esi,04lua51.dll+1BF1: C1 E8 10 - shr eax,10lua51.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 发布!