【CheatEngine基础教程】三、Cheat Engine Tutorial实战(补充:扫雷修改)
raindiffer
2021年08月11日 02:09
收录于文集
共12篇

本来没计划写这一章的,因为CE自带的小游戏就是很好的例子。但就像之前说的,我现在显卡坏了,玩不了那个小游戏,只能暂时坑掉这一节的内容。

要发文章,素材的选择很重要。首先我不能选收费的游戏,一来可能有真正意义上的版权问题,二来读者还得花钱买。其次,我应该选一个尽量简单的游戏,避免复杂的内容干扰正文。正想着找个替代素材的时候,今天我偶然看到同事妹子在玩扫雷,各种荡漾,那叫一个白啊,呸,我是说那叫一个快啊。接着我忽然意识到扫雷就是作为示例的绝佳选择。

所以有了本章的内容。

至于CE自带的小游戏,现在显卡还是个奢侈品,用不起,用不起。。。等我买的起显卡后会补上。

1 扫雷的版本

我使用的是Windows 7自带的扫雷,扫雷达人倾情推荐。我感受了下确实比Win10自带的华而不实的玩意儿好。

你可以在这里下载它:

代码块
JavaScript
自动换行
复制代码
http://www.downxia.com/downinfo/189133.html
复制成功

2 准备工作

扫雷属于比较好修改的游戏,基本上,凡是支持自定义的游戏,都比较好修改。因为我们可以设置一些“独特”的内容,更好的让我们分析游戏的内存和运行方式。

比如扫雷,我们可以自定义一个区域,有利于我们判断内存元素的意义:

这里我选择了高11宽12雷13的地图,便于后面分析。当然你也可以不做准备工作,因为我这边会把结果写出来的~

3 游戏时间的修改与分析

扫雷最容易改的就是时间了,因为有明确的文字。开始新游戏并点击第一个方块之后,开始计时。我们通过CE让游戏暂停,然后搜索这个值。

一般来说如果游戏涉及到时间计数,数据类型基本上就是单浮点(float)或者4字节。在这里不用常识,我直接告诉你这是个浮点数。

搜索完成后取消暂停,变动后再次暂停,然后搜索……最后排查到唯一的一个地址,然后查看是什么改写了这个地址,只有一条,我们看看这条地址的反汇编区域,这是个短而漂亮的函数,能告诉我们不少有用的信息:

记得先用Dissect Code功能(Ctrl+J)分析下代码,增加可读性。

蓝色高亮区域就是改写了地址的代码

注意看这一段是怎么工作的,先用movss xmm0,[rax+20]从时间地址中读取这个值,然后通过addss指令增加1,然后将结果写回时间地址。

我们直接修改Minesweeper.exe+2B753处的addss xmm0,[Minesweeper.exe+14454],将它nop掉,或者nop掉Minesweeper.exe+2B75B的movss [rax+20],xmm0,就可以实现时间冻结的效果:

代码块
clike
自动换行
复制代码
[ENABLE]
"Minesweeper.exe"+2B753:
  nop 8
 
[DISABLE]
"Minesweeper.exe"+2B753:
  //addss xmm0,[Minesweeper.exe+14454]
  db F3 0F 58 05 F9 8C FE FF
复制成功

等等,还没结束,来都来了,顺便找个指针呗。这里我们看到了,时间的地址是[rax+20],那往上找rax是怎么来的?

代码块
clike
自动换行
复制代码
Minesweeper.exe+2B74A - 48 8B 41 18   - mov rax,[rcx+18]
复制成功

那rcx呢?

代码块
clike
自动换行
复制代码
Minesweeper.exe+2B738 - 48 8B 0D F9F20700     - mov rcx,[Minesweeper.exe+AAA38]
复制成功

所以,时间的指针就是:

时间的指针

当然这里我不建议这么填,因为时间既然在偏移0x20的位置,它周围可能还有一些有用的数据。所以这里我建议使用分组功能,因为CE支持地址偏移量的写法,可以避免频繁输入。

在CE的地址列表栏中单击右键,选择Create Header,接着会弹出一个dui画框让我们输入这个组的名称,确认之后,又会弹出一个对话框,询问是为此Group header开启地址支持功能,选择Yes,即可创建一个具有地址功能的组,它可以像是一个普通的地址项目一样被编辑:

Group header

啊,不小心扯远了,我们继续看上面的函数,注意这条语句:

代码块
clike
自动换行
复制代码
Minesweeper.exe+2B744 - 83 79 38 01    - cmp dword ptr [rcx+38],01
复制成功

哦吼,又一个从我们找到的基址中搞出来的指针。而且这个与1对比就很值得深思,这种值的比较往往表示判断程序的某种运行状态。所以这个发现也要记录下来。

有了这些成果,我们可以正式往地址栏里添加地址了。

我们先创建一个支持地址功能的Header,然后修改它的地址:

我给它取名为Base

然后我们再添加一个新的地址项目(1字节),用来记录刚发现的那个未知的数据,我给它取名为unknown:

CE支持偏移量的操作,避免频繁输入

像上图,我直接使用了偏移量填在地址里,这是CE的重要特性,可以避免重复填写同样的内容。子项目会根据父项目的地址自动进行偏移量计算。所以unknow项目要处于base项目之中才有效。

然后我们在base下面添加一个header,我给它取名为foo,用来记录游戏时间和时间附近的其他数据:

最后的偏移量是0而非0x20是因为要从头算起

最后,我们在foo的下面建一个偏移量是+0x20的地址项目Time(float类型),用来记录时间,所有的工作就完成了:

CE地址列表结构

最后添加地址的内容我说的累赘了一些,主要是照顾不熟悉的读者。不过经过这次练习应该没问题了,以后我会能省则省……

4 数据结构分析

目前虽然我们只做出了时间冻结功能,但我们已经发现了两个数据结构。

第一个数据结构是基址Minesweeper.exe+AAA38指向的地址,假设这个地址是X,则X+0x38是那个跟1比较的未知的数值,而X+0x18则指向了存储着时间信息的第二个数据结构。

我们先用CE的结构查看功能看看X的情况,在我的机器上它的值是022686A0:

Game 结构

当我们启用定义结构功能(Ctrl+N)时,我们发现CE自动为我们添加了结构名Game,而其偏移量0x10的指针指向了UIBoardCanvas结构,这个一看就是显示UI的,和修改游戏关系不大;而在0x18处的指针,指向了一个名为Board的结构,这个就很值得重视了,除了时间之外,还可能存储着其他重要信息。

现在我们可以把之前的Header改名了,就按游戏的说法,base改成Game,而foo改成Board。

我们点开Board结构,从上往下看看它里面有什么:

偏移量0、1、2、4的数据看不懂,然后往下看,0x8、0xC,以及0x10的数据分别是13,11,12……你想到了什么?没错,结合之前的游戏自定义设置看,它们很可能分别是雷数、列数、以及行数。

在0x20~0x23的是4个byte,这个一看就是错的,因为它就是我们找到的时间,我们最好修改一下,换成正确的类型。

目前我们可知道的信息就这么多了,其他数据的作用都不清楚,为了了解那些数据的用途,并确认之前的猜测,我们切换到默认的简单模式重开一局,然后留意数据的变动:

初级模式新开后Board结构的状态

注意:重开游戏后,Board的地址会发生变动。

抱歉,之前犯糊涂,这里的行数列数标反了,我懒得重新截图凑合看吧。

果然,就像我们猜测的那样,0x08~0x10就是雷数行数和列数。另外我们可以看到0x24的值从之前的4变成了现在的1,所以我们可以猜测这个值表示游戏的难度:1初级,2中级,3高级,4自定义,并且通过切换游戏类型的方式验证,不过读者嫌麻烦就不必了,我已经验证过了,这个值也不重要。

随便点个格子开始游戏,进一步看看有什么变动吧。

经对比发现,开始之后,除了时间变化外,还有两个值也发生了变化,一个是0x18的值,从0变成了31;另一个是0x1C的值,从0变成了1。前者是什么还不好猜,后者我们也可以把它猜测成是某种状态。为了试验,我们把这个值从1改回0试试。

很遗憾,我们还是不太清楚这个值是干什么的,因为看上去对运行效果没影响,且点击任意格子后,这个值会变回1。

那就先放下,对于会变动的地址,直接上调试性价比极低。接着看其他的,我们在没点开的格子上插个旗试试。

插旗之后,偏移0x14的数值变成了1,有意思了,多插几个试试……最后结果很明显,偏移0x14的数值表示消耗的旗子(标记)的数量。

那再趟几个雷看看,改游戏就要多尝试。结果Boom,炸了,顺手看一眼0x1C,变成了5,很不规律,到底有啥用不知道,不管它,再重开一局吧:

再开

抱歉,之前犯糊涂,这里的行数列数标反了,我懒得重新截图凑合看吧。

开局点一格,0x18的位置变成了4。然后看一眼扫雷界面:

开启了4个格子

咦……?莫非0x18的这个值表示我们开启的格子数?多试试呗,再趟一个雷,果然……这个地址记录了点开的格子的数量。

那就由不得人不去想了,这里一共是81个格子,其中有10个有雷,我们把这个值改成71,看看会发生什么事情。

好像没有变化?趟个雷试试——最后的结果让人意外,我好像确实点到了雷上,但游戏却胜利了……

点雷而胜

很好,我们又有了新的收获,这么美丽的效果不写个脚本吗?新开游戏,看看是什么访问了这个地址,找到不少,我选择下图蓝色高亮的这条地址,因为它太赞了:

很好分析。

下面是我给这段代码加的注释:

代码块
clike
自动换行
复制代码
Minesweeper.exe+320AA - 41 8B 53 10    - mov edx,[r11+10]
// edx=列数
Minesweeper.exe+320AE - 41 8B 43 18    - mov eax,[r11+18]
// eax=点开的方块数
Minesweeper.exe+320B2 - 41 03 43 08    - add eax,[r11+08]
// eax=点开的方块数+雷数
Minesweeper.exe+320B6 - 41 0FAF 53 0C  - imul edx,[r11+0C]
// edx=行数*列数
Minesweeper.exe+320BB - 3B D0          - cmp edx,eax
//比较edx和eax,不等于则跳转
Minesweeper.exe+320BD - 75 0C          - jne Minesweeper.exe+320CB
复制成功

这段代码太棒了,本来我还想着手动赋个值什么的,结果不用,直接改跳转就完事了:

代码块
clike
自动换行
复制代码
[ENABLE]
"Minesweeper.exe"+320BD:
  nop 2

[DISABLE]
"Minesweeper.exe"+320BD:
  //jne Minesweeper.exe+320CB
  db 75 0C
复制成功

经测试,这段代码是有效的,不过一旦开启这个代码,胜利并开启游戏后,会直接弹出胜利窗口……这个就不是很好了,装还是要装一下的,不能连开局都跳过吧,所以我们换个写法,判断一下游戏是否开局了:

代码块
clike
自动换行
复制代码
[ENABLE]
alloc(newmem,2048,"Minesweeper.exe"+320B6)
label(originalcode)

newmem:
  //eax目前是开启的方块数+雷数
  //这里比较eax和雷数,如果相等则说明开启的方框数是0
  cmp eax,dword ptr [r11+08]
  je originalcode
  // 如果不相等,直接跳到目标地址:
  jmp Minesweeper.exe+320BF
originalcode:
  imul edx,[r11+0C]
  cmp edx,eax
  jne Minesweeper.exe+320CB

"Minesweeper.exe"+320B6:
  jmp newmem

[DISABLE]
dealloc(newmem)
"Minesweeper.exe"+320B6:
  //imul edx,[r11+0C]
  db 41 0F AF 53 0C
复制成功

这个代码就按我们预期的方式工作了,开局后,要点一下格子,才会胜利……

到目前为止,我们已经成为了绝对的扫雷之“王”……不过别急着得意或者无趣,我们看看还有什么值得关注的地方。

比如Board结构中偏移量0x50和0x58处的两个指针,看上去就很诱人啊。这种结构化的内容通常都会有意外的收获,我们可以稍微尝试一下。

新开一局游戏,点开一个方块(如果不点开,这些结构是无效的,所以我们可以推测出,游戏是在玩家打开第一个方格时才开始布雷,所以第一次点击肯定不会爆炸),然后展开0x50的指针并观察它的结构:

偏移为0x50的指针指向的结构

偏移量0处的数值是9,可能是行列的数量但只有一个,存疑不管。下面偏移量4、8和0x0C的地址的值都不知道其含义,也不管。接着往下看,偏移量0x10处是一个指针,点开后发现这个指针支线的数据结构是一个指针表,数一数,指针表的数量正好是9个,莫非天意呼,我们做个试验。用自定义的11*12格和13个雷的设置开局,看看在这种情况下这个结构是什么样的:

这个结构大概率是行数

注意看偏移量0处的12,以及下面高亮的部分,这个一看就是8字节的指针格式错误,修改一下之后:

修改后的

看,这次是12个指针了。由此我们可以做出如下总结:若Board的地址为X,则X+0x50的指针指向的结构是游戏中“列”的信息Y,其中Y+0x00的值是列的数量Z,而从偏移量Y+0x10的指针指向的指针表P则有Z个指针成员。

结构关系展示

现在让我们关注指针表中第一个指针指向的元素,如无意外,它应该表示第一列,我们看看它的内存结构:

如图所示,这个元素的结构看上去和列数的结构极度相似,偏移量0x0处是11,表示行数,然0x10处是一个有11个有效元素的指针……

我之所以在前面说了解程序知识和数据结构很重要就是因为这个了,如果你了解数组的有关知识,就会意识到这个列和行都是类数组结构,他们的组合相当于一个交错数组。

在这里,列的指针表可以理解成一个数组,它的每个元素都可以理解成一个数组,举例来说,上图蓝色高亮的数据,就是第1列第11行的元素的值。

结合游戏我们分析一下这一系列数字的含义,这一系列数字依次是9,9,1,12,12,12,1,9,9,9,9:

游戏,红色是对应数字

由此我们可以看出,这个值表示的当前格子的状态,显而易见,1~8表示格子周围的雷数,9表示未点开的格子,而12表示没有雷的格子。

然而遗憾的是,这些内容对我们而言帮助不大。所以我们收回目光,看看Board结构偏移量0x58的指针指向的结构里有什么信息:

Board+0x58

我们惊喜的发现,这里的内存布局和它的“前辈”格外相似,唯一比较费解的是,记录第1列每一行元素的信息的指针指向的地址的结构(黑框圈出的部分)跟我们预期的不太一样,不是以4字节为单位的有序的列表。

我们直接在内存浏览器中看看它的样子:

内存浏览

我们看到,一开始是0,然后是1,然后是一串零,结合游戏的状态,我们可以猜测它或许是以1字节为单位的。那么,什么信息可能不是0就是1呢?当然是有没有雷啦。

黑框处应该有雷

我们看这组元素,第二个元素的值是1,如果我们理解正确,一旦我们点击上图黑框的单元格,游戏即刻爆炸失败,试一试吧。如果觉得不放心,可以换一列尝试。

点了一下,果然炸了……

所以这意味着什么呢?这意味着我们可以了解每一个地雷的位置,如果你会编程序的话,大可以据此写一个自动扫雷工具~

5 最后的工作

还记得大明湖畔那个偏移量0x38的数值吗?

就是第3节提到的标注为unknown的那个地址,扫雷会拿它跟1对比。现在我们看看它是什么东西吧。

首先,当然是看看是什么访问了这个地址,我们找到了下面的结果:

这些地址访问了偏移量0x38的地址

上面蓝色高亮标记的地址都是拿它跟1比较,所以我们会发现,它的值有可能是1,3,或者4。

我们修改它的值试试,先试0和2……因为有134没有道理没有0和2。

先是0,改成0后游戏无法点击,时间也不再增加。猜测0表示暂停。改成2也一样,那么说明0和2的值可能是无效的。

老老实实实验别的吧。改成3,直接弹出胜利窗口,看来3表示胜利状态。而改成4则弹出失败窗口……

另外我们注意到,当胜利或失败窗口弹出后,这个值变成了2。

所以最终我们确定这个值就是用于保存游戏当前状态,直接修改它就可以实现随意完成或失败了。这个不用做成脚本,直接当成地址就好,更自由,想要追求显示效果可以设置它的下拉模式。方法和效果如下图。

右键单击项目,选择黑框中的菜单

设置下拉模式

实际效果

最后,完整的CE表格如下:

代码块
clike
自动换行
复制代码
<?xml version="1.0" encoding="utf-8"?>
<CheatTable CheatEngineTableVersion="34">
  <CheatEntries>
    <CheatEntry>
      <ID>45</ID>
      <Description>"游戏信息"</Description>
      <Options moHideChildren="1" moManualExpandCollapse="1" moDeactivateChildrenAsWell="1"/>
      <LastState Value="" Activated="1" RealAddress="026F86A0"/>
      <ShowAsSigned>0</ShowAsSigned>
      <GroupHeader>1</GroupHeader>
      <Address>Minesweeper.exe+AAA38</Address>
      <Offsets>
        <Offset>0</Offset>
      </Offsets>
      <CheatEntries>
        <CheatEntry>
          <ID>46</ID>
          <Description>"游戏状态"</Description>
          <DropDownList ReadOnly="1" DescriptionOnly="1" DisplayValueAsItem="1">1:游戏中
2:结束窗口
3:胜利
4:失败
</DropDownList>
          <LastState Value="2" RealAddress="026F86D8"/>
          <ShowAsSigned>0</ShowAsSigned>
          <VariableType>Byte</VariableType>
          <Address>+38</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>47</ID>
          <Description>"扫雷信息"</Description>
          <Options moHideChildren="1" moManualExpandCollapse="1" moDeactivateChildrenAsWell="1"/>
          <LastState Value="" Activated="1" RealAddress="074169C0"/>
          <ShowAsSigned>0</ShowAsSigned>
          <GroupHeader>1</GroupHeader>
          <Address>+18</Address>
          <Offsets>
            <Offset>0</Offset>
          </Offsets>
          <CheatEntries>
            <CheatEntry>
              <ID>48</ID>
              <Description>"雷数"</Description>
              <LastState Value="13" RealAddress="074169C8"/>
              <ShowAsSigned>0</ShowAsSigned>
              <VariableType>4 Bytes</VariableType>
              <Address>+8</Address>
            </CheatEntry>
            <CheatEntry>
              <ID>57</ID>
              <Description>"行数"</Description>
              <LastState Value="11" RealAddress="074169CC"/>
              <ShowAsSigned>0</ShowAsSigned>
              <VariableType>4 Bytes</VariableType>
              <Address>+C</Address>
            </CheatEntry>
            <CheatEntry>
              <ID>56</ID>
              <Description>"列数"</Description>
              <LastState Value="12" RealAddress="074169D0"/>
              <ShowAsSigned>0</ShowAsSigned>
              <VariableType>4 Bytes</VariableType>
              <Address>+10</Address>
            </CheatEntry>
            <CheatEntry>
              <ID>55</ID>
              <Description>"已消耗的标记"</Description>
              <LastState Value="0" RealAddress="074169D4"/>
              <ShowAsSigned>0</ShowAsSigned>
              <VariableType>4 Bytes</VariableType>
              <Address>+14</Address>
            </CheatEntry>
            <CheatEntry>
              <ID>54</ID>
              <Description>"已开启的格子"</Description>
              <LastState Value="66" RealAddress="074169D8"/>
              <ShowAsSigned>0</ShowAsSigned>
              <VariableType>4 Bytes</VariableType>
              <Address>+18</Address>
            </CheatEntry>
            <CheatEntry>
              <ID>53</ID>
              <Description>"时间"</Description>
              <LastState Value="78" RealAddress="074169E0"/>
              <ShowAsSigned>0</ShowAsSigned>
              <VariableType>Float</VariableType>
              <Address>+20</Address>
            </CheatEntry>
            <CheatEntry>
              <ID>62</ID>
              <Description>"难度"</Description>
              <DropDownList ReadOnly="1" DescriptionOnly="1" DisplayValueAsItem="1">1:初级
2:中级
3:高级
4:自定义
</DropDownList>
              <LastState Value="4" RealAddress="074169E4"/>
              <ShowAsSigned>0</ShowAsSigned>
              <VariableType>Byte</VariableType>
              <Address>+24</Address>
            </CheatEntry>
            <CheatEntry>
              <ID>63</ID>
              <Description>"格子状态"</Description>
              <Options moHideChildren="1" moManualExpandCollapse="1" moDeactivateChildrenAsWell="1"/>
              <LastState Value="" RealAddress="07361910"/>
              <ShowAsSigned>0</ShowAsSigned>
              <GroupHeader>1</GroupHeader>
              <Address>+50</Address>
              <Offsets>
                <Offset>0</Offset>
              </Offsets>
              <CheatEntries>
                <CheatEntry>
                  <ID>64</ID>
                  <Description>"列数"</Description>
                  <LastState Value="12" RealAddress="07361910"/>
                  <ShowAsSigned>0</ShowAsSigned>
                  <VariableType>Byte</VariableType>
                  <Address>+0</Address>
                </CheatEntry>
                <CheatEntry>
                  <ID>66</ID>
                  <Description>"列列表(偏移量8)"</Description>
                  <LastState Value="" RealAddress="0736CE70"/>
                  <ShowAsSigned>0</ShowAsSigned>
                  <GroupHeader>1</GroupHeader>
                  <Address>+10</Address>
                  <Offsets>
                    <Offset>0</Offset>
                  </Offsets>
                  <CheatEntries>
                    <CheatEntry>
                      <ID>70</ID>
                      <Description>"行数"</Description>
                      <LastState Value="11" RealAddress="07360CF0"/>
                      <ShowAsSigned>0</ShowAsSigned>
                      <VariableType>Byte</VariableType>
                      <Address>+0</Address>
                      <Offsets>
                        <Offset>0</Offset>
                      </Offsets>
                    </CheatEntry>
                    <CheatEntry>
                      <ID>72</ID>
                      <Description>"行列表(偏移量4)"</Description>
                      <DropDownList ReadOnly="1" DescriptionOnly="1" DisplayValueAsItem="1">1:周围1个雷
2:周围2个雷
3:周围3个雷
4:周围4个雷
5:周围5个雷
6:周围6个雷
7:周围7个雷
8:周围8个雷
9:未开启
12:无雷
</DropDownList>
                      <LastState Value="12" RealAddress="073D8E30"/>
                      <ShowAsSigned>0</ShowAsSigned>
                      <VariableType>Byte</VariableType>
                      <Address>+0</Address>
                      <Offsets>
                        <Offset>0</Offset>
                        <Offset>10</Offset>
                      </Offsets>
                    </CheatEntry>
                  </CheatEntries>
                </CheatEntry>
              </CheatEntries>
            </CheatEntry>
            <CheatEntry>
              <ID>73</ID>
              <Description>"地雷分布"</Description>
              <Options moHideChildren="1" moManualExpandCollapse="1" moDeactivateChildrenAsWell="1"/>
              <LastState Value="" Activated="1" RealAddress="07360B90"/>
              <ShowAsSigned>0</ShowAsSigned>
              <GroupHeader>1</GroupHeader>
              <Address>+58</Address>
              <Offsets>
                <Offset>0</Offset>
              </Offsets>
              <CheatEntries>
                <CheatEntry>
                  <ID>74</ID>
                  <Description>"列数"</Description>
                  <LastState Value="12" RealAddress="07360B90"/>
                  <ShowAsSigned>0</ShowAsSigned>
                  <VariableType>Byte</VariableType>
                  <Address>+0</Address>
                </CheatEntry>
                <CheatEntry>
                  <ID>75</ID>
                  <Description>"列列表(偏移量8)"</Description>
                  <LastState Value="" RealAddress="0736BA38"/>
                  <ShowAsSigned>0</ShowAsSigned>
                  <GroupHeader>1</GroupHeader>
                  <Address>+10</Address>
                  <Offsets>
                    <Offset>8</Offset>
                  </Offsets>
                  <CheatEntries>
                    <CheatEntry>
                      <ID>76</ID>
                      <Description>"行数"</Description>
                      <LastState Value="11" RealAddress="07360D50"/>
                      <ShowAsSigned>0</ShowAsSigned>
                      <VariableType>Byte</VariableType>
                      <Address>+0</Address>
                      <Offsets>
                        <Offset>0</Offset>
                      </Offsets>
                    </CheatEntry>
                    <CheatEntry>
                      <ID>77</ID>
                      <Description>"行列表(偏移量1)"</Description>
                      <DropDownList ReadOnly="1" DescriptionOnly="1" DisplayValueAsItem="1">0:普通
1:地雷
</DropDownList>
                      <LastState Value="0" RealAddress="07361210"/>
                      <ShowAsSigned>0</ShowAsSigned>
                      <VariableType>Byte</VariableType>
                      <Address>+0</Address>
                      <Offsets>
                        <Offset>0</Offset>
                        <Offset>10</Offset>
                      </Offsets>
                    </CheatEntry>
                  </CheatEntries>
                </CheatEntry>
              </CheatEntries>
            </CheatEntry>
          </CheatEntries>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
    <CheatEntry>
      <ID>58</ID>
      <Description>"附加功能"</Description>
      <Options moHideChildren="1" moManualExpandCollapse="1" moDeactivateChildrenAsWell="1"/>
      <LastState Value="" RealAddress="00000000"/>
      <GroupHeader>1</GroupHeader>
      <CheatEntries>
        <CheatEntry>
          <ID>59</ID>
          <Description>"时间冻结"</Description>
          <LastState/>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
"Minesweeper.exe"+2B753:
  nop 8


 
 
[DISABLE]
"Minesweeper.exe"+2B753:
  //addss xmm0,[Minesweeper.exe+14454]
  db F3 0F 58 05 F9 8C FE FF
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>60</ID>
          <Description>"开局直接过关"</Description>
          <LastState/>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
"Minesweeper.exe"+320BD:
  nop 2

[DISABLE]
"Minesweeper.exe"+320BD:
  //jne Minesweeper.exe+320CB
  db 75 0C
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>61</ID>
          <Description>"点击后直接过关"</Description>
          <LastState/>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
alloc(newmem,2048,"Minesweeper.exe"+320B6)
label(originalcode)


newmem:
  //eax目前是开启的方块数+雷数
  //这里比较eax和雷数,如果相等则说明开启的方框数是0
  cmp eax,dword ptr [r11+08]
  je originalcode
  // 如果不相等,直接跳到目标地址:
  jmp Minesweeper.exe+320BF
originalcode:
  imul edx,[r11+0C]
  cmp edx,eax
  jne Minesweeper.exe+320CB

"Minesweeper.exe"+320B6:
  jmp newmem

[DISABLE]
dealloc(newmem)
"Minesweeper.exe"+320B6:
  //imul edx,[r11+0C]
  db 41 0F AF 53 0C
</AssemblerScript>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
  </CheatEntries>
  <UserdefinedSymbols/>
</CheatTable>
复制成功

6 小结

我花了几个小时来描述修改小小扫雷的过程,即使已经省略了不少内容,依然用了5000字才将将完成。如果是更大型的游戏,可以琢磨的地方就更多了。所以游戏修改绝不只是为了打游戏爽快以获取廉价的满足感,也可以锻炼我们的观察力、想象力与创造力,还能让我们更好的理解程序的运转方式。

以及让生活充满乐趣——我写了一个半后台运行的总是让扫雷失败的程序,打算明天悄悄装同事电脑上,跟她比赛。。。P:)