关于扫雷秒杀的文章,看雪论坛上讨论的比较多,本文的部分地址也是来自看雪别人分析的结果,在这里对前辈们做的分析表示由衷的感谢。

以下工作均在Ollydbg下实现,不会OD的请先简单的学习下OD的使用再来看本文。

本文的思路是选取一个用处不大的菜单,修改菜单消息响应函数地址,使他指向我们自己编写的一个函数中,有点像HOOK,我在这里选取的是使用帮助这个菜单,感觉没什么用处,就改这个了。

第一步,我们要找一块不用的空间来存放我们编写的代码,我这里使用的是01004A57,后面是一大段的00,所以我就用这个了,你也可以选取其他的空间,当然必须要能够存放的下我们编写的代码,不能把别人的代码给覆盖了。

在这里,我们要完成秒杀的功能,实际上就是自动完成扫雷的工作,这里有几个比较重要的地址:
0x10056AC 列的数目
0x10056A8 行的数目
【列+行*0x20+0x1005340】的值如果为8F就表示存在雷
01003512 为关键CALL,扫雷的函数地址。
编写以下代码到空白区域

01004A57  $  55            PUSH EBP
01004A58  .  8BEC          MOV EBP,ESP                          ;保存堆栈
01004A5A  .  A1 AC560001  MOV EAX,DWORD PTR DS:[10056AC];获取到列的数目
01004A5F  .  83C0 01      ADD EAX,1 ;列加1
01004A62  .  50            PUSH EAX  ;保存列到堆栈中
01004A63  .  8B0D A8560001 MOV ECX,DWORD PTR DS:[10056A8];获取行的数目
01004A69  .  83C1 01      ADD ECX,1;行加1
01004A6C  .  51            PUSH ECX ;保存行到堆栈中
01004A6D  .  6A 00        PUSH 0 ;创建一个堆栈变量,保存外层循环次数(列记次变量)
01004A6F  .  6A 00        PUSH 0 ;创建一个堆栈变量,保存内层循环次数(行记次变量)
01004A71  .  33C0          XOR EAX,EAX ;eax得到一个0值
01004A73  .  8945 F0      MOV DWORD PTR SS:[EBP-10],EAX;将0放到堆栈中,初始值
01004A76  >  8345 F0 01    ADD DWORD PTR SS:[EBP-10],1;循环开始,堆栈中变量加1
01004A7A  .  8B45 F0      MOV EAX,DWORD PTR SS:[EBP-10];取出外层循环变量放到eax中
01004A7D  .  90            NOP ;调试留下的一些空位置
01004A7E  .  90            NOP
01004A7F  .  90            NOP
01004A80  .  3945 FC      CMP DWORD PTR SS:[EBP-4],EAX;比较外层循环是否结束
01004A83  .  74 2E        JE SHORT winmine.01004AB3;循环完了就跳
01004A85  .  33C9          XOR ECX,ECX;获取一个0值
01004A87  .  894D F4      MOV DWORD PTR SS:[EBP-C],ECX;将0保存到堆栈变量中
01004A8A  >  8345 F4 01    ADD DWORD PTR SS:[EBP-C],1;堆栈变量加1,内层循环开始
01004A8E  .  8B4D F4      MOV ECX,DWORD PTR SS:[EBP-C];取出堆栈变量到ecx
01004A91  .  394D F8      CMP DWORD PTR SS:[EBP-8],ECX;比较内层循环是否结束
01004A94  .^ 74 E0        JE SHORT winmine.01004A76;结束了就调到外层循环
01004A96  .  8BF1          MOV ESI,ECX
01004A98  .  C1E6 05      SHL ESI,5;esi = esi×32
01004A9B  .  F68430 405300>TEST BYTE PTR DS:[EAX+ESI+1005340],80;看该位置是否有雷,080h就是有雷的标记
01004AA3  .^ 75 E5        JNZ SHORT winmine.01004A8A;有雷就继续循环,没有雷就挖掉
01004AA5  .  51            PUSH ECX;参数2,行
01004AA6  .  50            PUSH EAX;参数1,列
01004AA7  .  E8 66EAFFFF  CALL winmine.01003512;调用扫雷函数
01004AAC  .  8B45 F0      MOV EAX,DWORD PTR SS:[EBP-10];取出堆栈变量到eax
01004AAF  .^ EB D9        JMP SHORT winmine.01004A8A;跳到内层循环
01004AB1  .^ EB C3        JMP SHORT winmine.01004A76;跳到外层循环
01004AB3  >  5F            POP EDI;这里2个pop是为了平衡堆栈,因为原来的函数带2个参数
01004AB4  .  5E            POP ESI
01004AB5  .  C9            LEAVE
01004AB6  .  C2 0800      RETN 8;返回

这段代码的意思就是遍历雷区所有位置的方格,如果方格内部不存在雷,就调用函数01003512挖掉该位置。

由于我选取的是菜单使用帮助的消息响应,原来的函数有2个参数,因此这里最后要用2个pop,中间的NOP是我在调试的时候留下的,你可以去掉,不过要记得修改跳转语句和CALL的地址。

第二步到消息响应的位置,修改消息响应函数,将这里的 CALL 01003D76修改CALL 01004A57。
01001F03  |. /EB 0A        JMP SHORT winmine.01001F0F
01001F05  |> |6A 02        PUSH 2                                  ;  Case 24F of switch 01001EDC,这里是第二个参数
01001F07  |. |6A 01        PUSH 1;这里是第一个参数
01001F09  |. |EB 04        JMP SHORT winmine.01001F0F
01001F0B  |> |6A 00        PUSH 0                                  ;  Case 24E of switch 01001EDC
01001F0D  |. |6A 03        PUSH 3
01001F0F  |> \E8 432B0000  CALL winmine.01003D76;要将这里改成我们自己编写的CALL地址
01001F14  |.  E9 90020000  JMP winmine.010021A9

修改好了,就直接用OD复制到可执行文件,全部保存内容,就基本上做好了。
完成以上的工作后就是修改下菜单的资源了,使用PExplorer修改菜单使用帮助的标题为使用秒杀。

好了,现在可以来看看效果了,点下使用秒杀,就可以看到自己完成,时间为0,雷的自动标记也已经做好了。

附件是修改好了扫雷,可以看看效果。

上传的附件 winmine.rar