• 使用 Cheat Engine 修改 Kingdom Rush 中的金钱、生命、星


    最新博客链接

    最近想学习一下 CE,刚好看见游戏库里装了 Kingdom Rush 就拿它来研究吧。这里写的东西,需要一些 Cheat Engine 的基础,可以看看教程。

    这里主要是看写的注释,来理解脚本。(如果什么都不想看,可以直接复制粘贴 CE 自动汇编(AA)脚本)

    我学习的链接:

    你能学会的 Cheat Engine 零基础入门教程

    Cheat Engine 基础教程 CE Tutorial | Ganlv's Blog

    CE 教学实例应用-由浅入深学习游戏修改 [全十课]

    参考链接:

    Cheat Engine 官方 WIKI

    x86 汇编指令列表

    技术理解

    我对一些用到的技术的简单理解:

    • 代码注入:在程序运行时,将自己写的代码,替换掉原有的代码
    • AOB:(Array Of Byte)在内存中搜索特定的一串数据,以决定注入代码的位置
    • 人造指针:单独找个地方,记录变量所在的地址

    数据存储结构

    数据在内存中的存储结构:

    https://cdn.tsanfer.com/image/2022-7-2_15-05-43.png

    Cheat Engine 相关汇编知识

    此脚本用到的 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

    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 使用界面

    https://cdn.tsanfer.com/image/2022-7-2_15-07-38.png

    本文由Tsanfer's Blog 发布!

  • 相关阅读:
    Quartz cron表达式
    Apache NiFi 核心概念和关键特性
    Hive llap服务安装说明及测试(一)
    nifi生产环境使用
    DataX 中Transformer的使用
    vue2.0之过渡动画,分别用钩子函数,animated,原生css实现(前端网备份)
    js对对象数组的某一字段排序(前端网备份)
    浏览器之禁扒(前端网备份)
    iframe 从父像子穿参数(前端网备份)
    关于小程序仿微博导航效果(前端网备份 )
  • 原文地址:https://www.cnblogs.com/Tsanfer/p/16437812.html
Copyright © 2020-2023  润新知