扫雷游戏逆向分析

EN…最近项目做完,然后心血来潮,找了一个老游戏玩一下,是的,就是扫雷,然后发现吧,这游戏要是可以一键完成那就好了。主要还是为了练习一下逆向

效果演示

在这里插入图片描述

本文主要是分享逆向的思路,技术难点其实不难,主要是思路

目标地址在这里:扫雷游戏

我们的目标 ———— 实现一键完成游戏

目标细分

  1. 找到入口
  2. 找到需要的功能模块
  3. 调用现有的模块实现我们需要的功能

整个是这么的结构,记得把事件监听器的祖先去掉,剩下的就是跟自己相关的事件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9mQmubXl-1664426203894)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8968ff5513124be393437b6ee991d732~tplv-k3u1fbpfcp-watermark.image?)]

在此处下断点,为什么?
因为我们不知道作者是通过哪个来触发雷区的,所以要判断他是通过onmousedown事件来处理触发雷区,还是通过鼠标的onmouseup来触发雷区,然后思路就是要去跟触发雷区前的那个事件,因为你出发完了之后,你就跟不到她调用的堆栈了,调试起来就很难了。

在这里插入图片描述

可以看到在onmousedown断点的时候,地图没有被标记颜色,但是onmouseup的时候,是已经处理完了,所以我们的入口点就在onmousedown,顺着这个继续往下跟。

开始调试分析

随便点击一个位置,单步跟进去,然后看到这里又几个判断,在每个判断里面下断点

在这里插入图片描述

  var k = Math['floor']((g['clientX'] - h['left']) / 25)
  var l = Math['floor']((g['clientY'] - h['top']) / 25)

在这里插入图片描述

上面这两行代码,获取到的是当前点击的方块坐标,这个我们先记录一下,单步继续往下

o0o(k, l)

最后走到了这一步,说明这个函数是一个关键的函数,如果不确定可以多验证几次,继续往里走

在这里插入图片描述

可以看到,这个函数是一个递归调用,我们先把其他断点去掉在以下几个地方打断点

在这里插入图片描述

然后刷新一下页面,这里我们不单步调试,直接执行,为什么,主要是看他对canvas在干什么,因为你们看‘drawImage’,这个基本就是对canvas的操作,如果你一开始就直接单步调试的话,木有目的性,效率太慢了,

可以看到下图演示,通过前面单独点击和这里初始化的效果,可以判断出,这个函数是每次点击都会操作的,里面肯定有我们需要的东西

在这里插入图片描述

我们先把断点去掉,然后让他初始化完毕,再重新打上断点,来单步调试一下这个的作用。

我们先点击一个有雷的区域,会进到下面这个函数

f17()

在他的上一行,是一个给canvas绘图的方法
在这里插入图片描述

我们验证一下,改成gfs[1],点是雷块的时候,就变成了标记旗帜,我们记录下这个方法

ctx['drawImage'](gfs[1], k * 25, l * 25) // 可以给块绘成旗帜图案

在这里插入图片描述

并且当 “d31[l][k][1] == 1” 的时候会进到进到这个判断,判断是一个雷区,这一步我们找到了雷的判定规则

我们看一下d31里面是什么东西,这里我们自己写一个hook代码,去hook他,也可以自己改源码来打印结果

在这里插入图片描述

这里我们自己写一个代码,我们利用目前获取到的信息,将所有雷的区域标记出来

function gameHook () {
    for (let l = 0; l < Y; l++) {
      for (let k = 0; k < X; k++) {
        if (d31[l][k][1] == 1) {
          ctx['drawImage'](gfs[1], k * 25, l * 25)
        }
      }
    }
}

在这里插入图片描述

保存好代码,然后保存,刷新页面,然后再控制台执行我们自己的代码,看到了吧,这些就是有地雷的区域,你也可以自己点击验证一下,验证完了之后,记得把你刚刚改的 gfs[1] 改回原来的值,原来是 gfs[2],这样不会干扰到我们后面的调试

在这里插入图片描述

到了这一步,我们还需要解决,当他标记出来之后,我们还需要给他的雷计数赋值,这里的数量是没有变化的,所以我们还要找他的赋值功能。

在这里插入图片描述

通过前面,我们知道,ctx[‘drawImage’](gfs[1], k * 25, l * 25)这个的功能是标志旗帜的功能,扫雷这个游戏,他一般只有标记的时候才会出现旗帜的符号,我们全局搜一下这个方法

在这里插入图片描述

他在这两个地方,出现,我们在这里下两个断点,然后我们在canvas上右击标记,看下他是怎么运行的

在这里插入图片描述

在这里插入图片描述

右击标记之后,是进到了这个函数,我们单步往下走

在这里插入图片描述

进到crm(–RM)处,会有一个闭包,这里我们不管,直接在最后下断点,然后运行,可以看到,雷的数量就少了一个,所以我们先不用管里面的细节,直接把这个方法拿来用

在这里插入图片描述

crm(–RM)还有一个RM,这个一看就肯定是雷的数量,要是不确定,可以自己去验证一下。然后根据这个特性,我们继续完善一下自己的程序,保存更新,刷新页面,运行函数

function gameHook() {
    for (let l = 0; l < Y; l++) {
        for (let k = 0; k < X; k++) {
            if (d31[l][k][1] == 1) {
                ctx['drawImage'](gfs[1], k * 25, l * 25)
                if (RM) {
                    crm(--RM)
                }
            }
        }
    }
}

下图可以看到,运行最新的程序之后,他的计数已经全部为0了,此时基本算是完成了找雷和给雷计数的操作,还剩下一个操作就是把没有雷的块也要点开以及让游戏完成
在这里插入图片描述

这一步,将不是雷的块全部点开,保存代码,刷新运行,可以看到下面的效果

function gameHook() {
    for (let l = 0; l < Y; l++) {
        for (let k = 0; k < X; k++) {
            if (d31[l][k][1] == 1) {
                ctx['drawImage'](gfs[1], k * 25, l * 25)
                if (RM) {
                    crm(--RM)
                }
            } else {
                ctx['drawImage'](gfb[d31[l][k][2]], k * 25, l * 25)
            }
        }
    }
}

在这里插入图片描述

最后我们要让成功的笑脸出现,还记得我们之前调试的时候,点击雷区,出现一个ends的方法么

在这里插入图片描述

在这里插入图片描述

这个方法应该是一个游戏结束的方法,我们去看一下有几个地方调用了他,能调用他的地方,有以下几种情况

  1. 踩到雷区了
  2. 游戏通过了

我们踩到雷的地方已经找到了,就还要找游戏通过的时候,全局搜一下方法

可以看到,被d64和scs函数调用了,以及我们刚刚的f17,我们主要关注d64和f17,分别在这两个里面下断点

在这里插入图片描述

在这里插入图片描述

当我们清空的时候,首先会进到d64的函数中,那么说明他这个是初始化的时候调用的,而且从代码上看,他里面是在初始化一些事件,由此可以确定,这个不是我们要找的,那就只剩下一个scs的调用了

在这里插入图片描述

我们只留下一个scs的断点,然后自定义一个数量,然后玩一下游戏,让游戏成功完成,看他会不会进到我们预期的断点里面

在这里插入图片描述

当我们把最后一个块点掉之后,顺利进到了断点位置,可以确定,这个位置就是我们需要的
在这里插入图片描述

然后我们分析一下他的源码

在这里插入图片描述

有两个地方是会出现alert,而且第一个的判断是当他不为雷的时候,就提示,以及当总雷数RM不为0的时候提示,那么我们只需要把提示后半部分提取出来就行,我们不考虑其他,修改我们自己的程序就是这样子,保存刷新页面,然后运行

function gameHook() {
    for (let l = 0; l < Y; l++) {
        for (let k = 0; k < X; k++) {
            if (d31[l][k][1] == 1) {
                ctx['drawImage'](gfs[1], k * 25, l * 25)
                d31[l][k][0] = 2
                if (RM) {
                    crm(--RM)
                }
            } else {
                ctx['drawImage'](gfb[d31[l][k][2]], k * 25, l * 25)
            }
        }
    }

    crm(0)
    $('face')['src'] = gif[1]
    up()
}

最后的成果演示

在这里插入图片描述

本文主要是提供分析思路,技术难点上的话乜有太多,都是在CV,有什么不懂的地方可以交流,今天就到这里啦

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值