好友
阅读权限10
听众
最后登录1970-1-1
|
最近开始正经学破解了,应老师要求,尝试破解微软经典的扫雷小游戏,实现一秒内通关。实验环境是win11,以及od
要实现软件的破解,我认为先得玩一下看看大致思路:
显然是一个非常典型的Windows窗口程序,点击小方格就可以开始扫雷并且计时了,要在 一秒 之内扫完所有雷我猜可以选择冻结时间,但似乎非常不妥,另一种方法应该是在点击之后快速将所有没有雷的地方触发,这样应该更符合要求
使用OllyDbg打开程序看看,根据吾爱破解论坛的前人文章的指点,所有Win操作系统上的窗口程序最开始都会给自己注册一个窗口类,根据注册窗口类(RegisterClassW),以此为切入点想必会很快找到窗口运行代码核心。
微软提供了该函数的一些信息,按图索骥,我们得到了其数据结构。
注册窗口需要一个包含窗口类属性的结构体,而该结构体的第二位WNDPROC即是该回调函数的指针,处理的结构体就在里面,使用OD进入。
先得到注册窗口函数的地址,但发现出了一点问题。
似乎是不在该程序领空,不知道该怎么办了,遂直接根据前人经历直接找到了函数所在位置。
下断点,运行:
能看见堆栈区域的lpwndClass的地址,右键在数据窗口跟随就能看见WNDCLASSW的具体内容,其中4-8字节就是窗口回调函数的指针,选择在反汇编窗口跟随。
一路向下执行,发现到达了DefwindowsProc函数处,查询发现这是应用程序未处理任何输入时进入的函数,说明主处理函数就在上面。
向上查询果然能发现一大堆swich case表,以及VM_LBUTTON等字样,应该是主处理函数没错,根据我最初的思路,在左键点击时触发所有非炸弹区域,所以前往左键处理处查看逻辑。大概是这么一团。
最开始两句是switch的判断部分,无需在意。看了半天发现不大对,游戏在执行过程中是在左键抬起来的时候才真正执行了点击这一过程,不然就是这个样子,按下去但没起来,所以到VM_LBUTTONUP处寻找函数
。
能看见在[01005140]处数据与edi(此时为0)比较后判定是否跳进010021A9(Def函数),即在1005140这里的全局变量如果为零0就不管了,我猜测是判断是否抬起的一个标志位,此时不会是游戏胜负判定,抬起在不论胜负的时候还要处理别的事。随后是一个释放鼠标的一个API调用。随后的test和je我开始认为应该是判断Release的返回值是否正确,但想了一下ebx似乎是保留的寄存器,bl应该不会发生函数里面的变化,所以我猜测其是判定105000的某一个位是否为零用的如果为零,就会从je跳转走,可能是某个判断游戏胜利或者失败的部分。
最后的010037E1应该是核心代码。
在游戏中有一个比较经典的就是递归展开,就是当点击处附近没有雷时递归展开周围的未解开的格子从这方面,很容易能想到一种破解方式就是解除递归展开的限制,一次性把所有没有雷的格子全部展开,似乎是最好的一个方式。不过先回到这段代码上。
最始这段代码基本上都跳往了函数末尾,比较的还多是数据区的一些东西,多半是一些对常量设置检查,可以掠过。
随后是一个看着十分重要的函数调用:SetTimer,
显然就是函数随意点击某个节点后才开始的计时器部分,随后是对返回值的处理,盲猜点击处理函数就在下面不远。仔细一看发现直到函数末尾基本上都是短跳转,只有两个除外:
先进010035B7看看:
双层循环,我猜十有八九跟要找的递归展开有关,往上翻翻,可以找到一个关键点,eax * 32(shl eax , 5),根据前人的经验,这个游戏把地图分成了32*32大小的一块块地图,此时左移五位显然是为了计算某个点的下标(行 + 列 * 32,或者反过来),汇编层面应该是没有所谓的双层数组可以绕过这种叠加法的大概(
此时可以确定eax是行或者列,反正是方形地图,大抵无所谓
显然esi就是对应的列或者行,下面对40h的与应该是检查某个标记位的,暂且不明,似乎为零了就直接跳到结尾了,暂时猜测为已经解开或者是雷的标记位,循环的判定如下,看着像把里面所有点都遍历一遍,edi每次加一,esi每一加一排,也就是32,而ebx作为单纯的判定限度作用,只加了1。
看循环体,一眼就能看见只有两个长跳转:先进01002EAB看看。一进来就是注册表的什么东西,下面全是pushcall,都是push一些常量,然后调用同一个函数,连格子的坐标都没看出来用,应该是来错了,先去另一个看看。下面是另一个:
也是pushcall一大堆,但是看见了edi,esi,怎么也说明多半跟坐标相关,稍微看看数据构成,发现push的参数不是一些不明所以的常数而是一些非常固定的寄存器:
从上而下输入的参数分别是(esi,edi – 1),(esi,edi),(esi,edi + 1),(esi + 1,edi – 1),(esi + 1,edi+ 1),(esi + 2,edi – 1)(esi + 2,edi)(esi + 2,edi + 1).这八个下标完全是连在一起的八个格子,加上上面调用的一次,就构成了一个点和它周围的八个点。
显然,这01003008里面藏着这个游戏的核心,这上面说到的逻辑完全符合点击一个格子并揭开后根据情况(如果没有雷在周围,即不需要打印数字,开始递归)查看并且揭开周围八个格子情况的逻辑。有相当大把握01003008里的就是揭开格子和计算周围雷数的核心代吗。
一进01003008,就看见几个非常熟悉的操作,使用shl和add计算下标,再把下标对应的数取出来做后续判断。所有揭开格子的操作都会经过这一步,可谓是元操作了,直接在函数开头下断点,开始调试。
随便点了一个格子,刚好触发递归展开,能看见周围格子从上至下,从左至右一个一个的被揭开。
说明没找错地方,再次观察代码结构:
能看见下标对应的数值给了eax,随后比较判定也是针对eax,下标地址构成为esi + 1005340,这个1005340应该就是存储格子状态的地区基址,在数据窗口查找z:
能看见批量的0F围着一个似乎是方形的场地,前面提到,计算基址的行列计算会乘32,此处竖向排列的10也是隔一排出现一次(一排16个字符)似乎是作为边界存在,仔细观察就能发现一些异常的节点:3 * 3的由4开头的数组成的方形,以及稀少的8F,我猜这个4开头的就是解开的格子,与扫雷界面对照:
显然,4开头的就是被揭开的格子,后八位是周围地雷数,那8F是什么呢,点一下炸了,应该是炸弹的意思。综上,可以得知高八位的第一位是炸弹,第二位是被揭开。 低八位是F表是未被揭开,其他的表示周围雷数,应该是如此。观察下面的几个跳转:
显然就是判断是否已经被揭开(40),是否是墙(10),是否是旗子(0E,这是我猜的,随后进入揭开流程
这里进去发现是查看周围雷数的,返回如果是有雷就跳过,即进入下一个循环,我尝试在这里改一下,让就算是雷也照常进入递归揭开流程
发现把雷也揭开了,真是抽象,所以在上面再另作修改
最后在此处把对旗子的判定改成了对雷的判定,如果是雷自动跳过揭开流程,但周围有雷,继续揭开流程
|
免费评分
-
查看全部评分
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|