CE扫雷20211031

棋盘范围
根据开发的经验可以猜测存储器存储棋盘位置时应该将每个位置当做数组来存储,即(行数,列数)。所以找到棋盘(1,1)和最后一个格子的存储位置就可以找到整个棋盘的范围。
寻找棋盘范围是一个很基础的CE修改器分析扫雷的案例,也是逆向分析扫雷软件,反汇编扫雷软件的一个基础操作,找到棋盘的范围后可以在范围内看到个别与其他格子不一样取值的特殊格子,在扫雷软件上找到对应的格子进行试验,发现这些特别的格子正是炸弹,在CE修改器的帮助下很容易就能看到每次刷新后地雷的位置。
特别的,在几次对棋盘首地址的查询后,发现扫雷棋盘的首地址是不变的,都是在01005361处储存,只有尾地址随着难度的变化而调整位置,这也给我们研究分析扫雷软件提供了便捷。
1.找到首地址
点击棋盘(1,1)处,搜索“未知的初始值”(关掉快速搜索,否则搜不出),重开一局再次点击棋盘(1,1)处,结果相同(一共有空,1,2,3四种结果)则搜索“未变动的数值”,若结果不同则搜索“变动的数值”,最终找到基址0100535E,这个地址不一定是真正的首地址,需要打开内存区域,通过制造事件验证,如图:
在这里插入图片描述
图2.1 找到首地址
在这里插入图片描述
图2.1.1 找到首地址流程1
2.右键找到“浏览相关内存区域”,点击(1,1),如果可以看到有数据变成红色并变化的最靠前的地址(在CE修改器中变红表示数据发生变化,绿则代表数据是不变的),则找到棋盘的起始地址01005361,再次点击最后一个格子,如果可以看到有数据变成红色并变化的最靠后的地址,则找到尾地址,初级中级高级同理,如图:
在这里插入图片描述
图2.2 内存浏览器
在这里插入图片描述
图2.2.1 找到首地址2
在寻找中级或者高级的棋盘时,往往会因为地址过于靠后而窗口太小而在点击各自的一瞬间找不见尾地址,这就导致了无意义的多次试验。对于这个问题,我在进行点击最后一格之前一般会提前估算其位置,棋盘的首地址是不变的,而且棋盘所含格子数在扫雷的界面中也是可视的,可以在01005361的基础上加格子数(十六制加法),提前找到最后一格的大致位置。(初级:99 中级:1616 高级:16*30),实验结果如下:
初级棋盘: 01005361-01005469
中级棋盘: 01005361-01005550
高级棋盘: 01005361-0100555E

雷数,笑脸,计时器基址寻找
这三个值都可以通过查找变化的值来查找地址,雷数、笑脸、计时都是变化的值,都可以通过搜索变化的值来搜索他们的地址,且搜索出的值就是基址,本身是比较简单的一项逆向搜索,重点在于找到这三个值变化的时刻。
雷数
地雷数在一局中是不会改变的,但是在改变难度的时候雷数会改变(初级:10个 中级:40个 高级:99个),可以在改变难度后搜索变化的值,在难度不变的时候搜索不变的值,最终筛选出基址。
选择初级、中级、高级中的一个,搜索所含雷值,搜索出三个值,因为搜索出的值较少,(本实验中筛出的数据较少,若是筛出的数据较多,则要多次重复搜索,在改变难度后搜索“变化的值”,在难度不变的时候多次搜索不变的值,直到筛到剩一个值)所以直接将三个数据一起拉入工作区,改变其中一个数据的数值并再次点击棋盘(此时必须再次点击棋盘刷新雷数,否则保持原值不变),观察左上显示雷数的数值是否变为和输入的值相同。若是相同,则可以确定该地址为雷数基址(本实验中三个值查找出的地址都显示为绿色,即三个地址都是基址,所以不需要再查找每个地址的偏移值。若是查找出的地址为黑色,则必须找到该值真正的基址,通过查找“是什么访问了这个地址”或者“是什么改变了这个地址”来查找上一级地址,重复多次相同的操作后找到绿色的地址,这是查找基址的任务才完成);若不同,则继续测试下一个地址。本实验中将这三个基址都改为“1”雷数,以此可以快速验证雷数是否修改成功,最终找到的雷数基址为010056A4
在这里插入图片描述
图3.1 雷数修改

笑脸:
通过分析可得“笑脸”一共有四种表情,分别是在平时普通的笑脸,胜利时戴墨镜的脸,失败时悲伤的脸,点击格子时张嘴的脸。分析出这四种表情以后,就可以通过改变笑脸的表情来搜索变化的值找到控制笑脸的数值(通过编写程序的经验,对于这类只有三四个选项的变量时,往往会编写一个flag变量,即if(flag==?)print(?),每个值代表一个选项,而且根据经验而谈,这类flag值一般都设为个位数,即0,1,2,3…通过对扫雷的逆向分析推导出该变量的编码),找到值后,就可以找到基址。
由于雷数太多导致实验时间太长,所以提前修改我们上一个找的雷数值,将其修改为1,这样可以加速试验的时间。开始时笑脸不变,搜索“未知的数值”,在胜利时笑脸带墨镜,踩雷时表情变化,(在搜索笑脸时最好搜索胜利时的墨镜脸和失败时的悲伤脸,点击时的张口脸由于太快可能CE修改器无法捕捉到,不过可以在测试一个地址是否为笑脸基址时使用,这个数值比较少见可以代表改变表情的特征)表情变化时搜索“变化的数值”,表情不变时搜索“未改变的数值”,重复多次,找到两个结果,修改结果观察笑脸表情是否变化(此处需要再次点击格子刷新),若变化,则该地址为笑脸基址;若没有变化则继续测试下一个地址。最终找到笑脸基址01005160,如图:
在这里插入图片描述
图3.2 笑脸修改

(3)计时器:

计时器无法搜索精确数值,只能通过它的特性来查找,经过观察可得,计时器在不断增长,一次我们得知可以查找“增加的数值”,要注意的是当点击一个格子时计时器才开始计时,在重置棋盘之后到点击第一个格子之前这段时间里计数器一直为0,此时若是可选数据有很多,可以精确搜索0,来筛选数据。
搜索“未知的初始值”,点击一个格子,开始计时后搜索“增加的数值”,多次搜索,若是找到与页面计时器同步增长的数值,那该地址即为计时器基址;若是班花不同的值则不是,最后找到基址0100579C,如图:
在这里插入图片描述
图3.3 计时器修改

分析雷存放算法
在整个扫雷软件的项目中,布雷算法是其中一个很重要的函数,它主要决定了每一局中雷所放的位置,它被主函数调用的时机也决定了扫雷游戏中存雷的算法。分析他的算法对于理解扫雷游戏的运行有很大帮助。
首先我们要知道布雷函数被主函数调用的时机,根据观察可得,打开扫雷游戏之后,自动生成初级扫雷游戏,那么软件会在这个时候就调用布雷函数将雷的位置定好了吗?我做了一个实验:打开扫雷软件点击固定位置的一个格子,点击一个格子后就立即点击笑脸重置棋盘,再次点击上次点击的格子,多次实验(最少实验50次)。
在这里插入图片描述
在这里插入图片描述
图4.1 测试调用时机
经过50次实验,每一次点击的格子都是安全地带,50次中没有一次第一次就踩到地雷。如果主函数在最初就调用布雷函数,那么一次就踩到雷的概率应该是40/256(中级棋盘),而实际测试出的结果是0/50,大概率说明我们一开始的假设——认为在棋盘初始化后就已经调用布雷函数的观点是错误的,所以我又做了第二个实验,测试第二次是否能踩到雷。和第一个实验的流程相同,同样是选取了两个固定的格子,进行了50次实验,总结实验数据后发现第二次踩雷的概率基本与理论上的概率拟合(40/255)。通过两次实验的结果比对,可以得出一个结论:主函数在第一次点击棋盘上任意一个格子之前没有调用布雷函数,也就是说无论玩家第一次点击棋盘上任一格子都不会踩到雷,因为此时的棋盘上还不存在地雷,而在玩家点击第一个格子之后到点击第二个格子之前已经调用布雷函数,这时棋盘上的地雷才布置好。
此时我们知道了布雷函数在主函数的调用时机是在点击第一个格子之后,那么在布雷的时候就要避免将雷布置在第一个格子上,否则在玩家点击第二个格子之后会直接按踩雷结算。那么就需要在布雷函数中传递一个表示当前第一个格子的坐标的参数。我们先假设棋盘上的每一个格子位置都是由二维数组存储的,这样在传参时只需传递横坐标的值和纵坐标的值即可。
经过第二次实验我发现每次重置后地雷的布置基本上没有规律可循,我们先假设每次都是随机布置地雷位置,那么我们可以用srand函数产生随机数种子,以便在之后随机分配横纵坐标。以中级难度为例,我们需要提前知道需要多少雷数以及棋盘的长和宽,所以在调用布雷函数时也要把所需雷数和棋盘的长宽传参过来。知道雷数后设一个循环函数知道所需雷数全部布置好。同时要注意需要跳过在前文中提到的第一次点击格子位置,我所猜测的扫雷布雷算法的循环体结构如下:
在这里插入图片描述
图4.3 猜测代码——循环体
其中row和col为第一次点击格子的横纵坐标,m_uMineNum为所需地雷数,m_uYNum和m_uXNum分别为棋盘的长和宽。因为随机数的产生没有限制范围,所以会出现超过已有棋盘范围的可能,为了解决这个问题,我对产生的随机数取余,使其永远在棋盘限制的范围内。对于调过第一个格子的问题,我采用条件语句加continue跳过本次循环的语句来解决,经过编译证实其与原版扫雷软件的效果相同。
现在我们已经找到了符合要求数量的雷的位置,接下来我们需要做的是埋雷,根据经验我们需要在循环体中设置一个埋雷语句。做这个工作时我们要注意随机数会出现重复数据的现象,我们不需要在已经埋好雷的位置再次埋入新雷,所以我们需要避开已经埋过雷的位置。我所编写的猜测代码如下:
在这里插入图片描述
图4.4 猜测代码——埋雷
ATTRIB_MINE为地雷的标识码,如果一个格子的标志值uAttrib与ATTIB_MINE相同的话,这个格子处埋着地雷。我使用了一个条件语句避开已经埋过雷的格子。将循环体的index放入这个条件语句中是因为只有找到一个没有埋过雷的格子才算埋好雷,若是将index放在条件语句之外,会导致还没有埋够所需要的雷数就已经停止循环,无法达到需求。
到这里为止猜测布雷函数的基本功能已经编写完毕,经过实验验证,用这个猜测函数做出来的扫雷游戏与原版扫雷游戏规则与功能基本相似,可以证明扫雷软件的布雷算法基本与我们所猜测的一致。

扫雷游戏软件思维导图
点击工具栏的“游戏”,在等级区可以选择难度,选择初级显示的是99的棋盘和10个地雷,选择中级显示的是1616的棋盘和40个地雷,选择高级显示的是16*30的棋盘和99个地雷,选择自定义可以弹出一个弹窗自主输入长度、宽度和雷数。
点击工具栏的“游戏”,取消“颜色”的选择,整个界面变为灰白色。
点击工具栏的“游戏”,选择“声音”,玩游戏时音效出现。
点击工具栏的“游戏”,选择“扫雷英雄榜”,可以查看每个难度最高纪录,选择左下角“重新计数”,所有记录清零。
点击工具栏的“游戏”,选择开局,重置棋盘。左键点击棋盘任意一个格子给这个格子插上旗子,同时显示的雷数变少,当所有雷所在的格子被插上旗子,则判断胜利。
右键点击棋子任意一个格子,如果是第一次点击格子,不会踩到雷。之后再点击格子,如果这个格子里是雷,那么判定失败;如果这个格子周围8个格子中有雷,那么显示周围雷的个数(只有1,2,3这三种可能);如果这个格子周围无雷,自动打开周围8个格子。若是周围8个格子的外围有雷则隐藏含雷的格子,打开其他无雷的格子,以此类推。
在这里插入图片描述
图5.1 扫雷思维导图

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>