介绍
曾想了解“扫雷”游戏在幕后所发生的一切吗?嗯,我想过,还由此决定对其进行了研究。本文是我的研究结果,现公之于众。
主要概念
1. 使用 P/Invoke 调用 Win32 API。
2. 直接读取另一个进程的内存。
注1:本文的第一部分包括一些汇编代码,如果你不是很明白,无关要紧,这不是本文的目的,你尽可以跳过不管。然而,如果你想问我有关这些代码的
问题,非常欢迎你写信给我。
注2:本
程序是在Windows XP下测试的,所以如果它不能运行在其它的系统下,请注明该系统的信息,好让我们大家都知道。
注2之更新: 本代码现在经过修改后也能在Windows 2000下运行。谢谢Ryan Schreiber找到了Win2K下的内存地址。
第一步 – 探索 winmine.exe
如果你不是一个汇编迷,可以跳到这一步的最后,只看结论。
为了更好地了解“扫雷”幕后所发生的一切,我以一个调试器打开此文件作为开端。我个人最喜欢的调试器是Olly Debugger v1.08, 这是一个非常简单且直观的调试器。总之,我在调试器中打开winmine.exe,并查看该文件。 我发现在Import区(列出在
程序中用到的所有dll函数的区域)有下面一行:
010011B0 8D52C377 DD msvcrt.rand
这就意味着“扫雷”用到了VC运行库的随机函数,因此我认为这对我可能有帮助。我
搜索了该文件,看看到底在哪里调用了rand()函数,不过只在一个地方找到了这个函数:
01003940 FF15 B0110001 CALL DWORD PTR DS:[<&msvcrt.rand>]
接着我在这一行单步调用插入了一个断点并运行
程序。我发现每当点击笑脸图标时,一个新的布雷图就生成了。布雷图按以下步骤创建:
1. 首先,给布雷图分配一块内存区,并把所有的内存字节都设置成0x0F,说明在该单元(cell)中没有地雷。
2. 其次,按地雷数遍历每一个地雷:
2.1. 随机化 x 位置 (取值在1至宽度之间)。
2.2. 随机化 y 位置 (取值在1至高度之间)。
2.3. 设置内存块中被选中的单元的值为0x8F,这意味着在该单元中有一个地雷。
下面是原码,我已加入了一些注释,并加粗了重点部分。
010036A7 MOV DWORD PTR DS:[1005334],EAX ; [0x1005334] = 宽度(即横向格数)
010036AC MOV DWORD PTR DS:[1005338],ECX ; [0x1005338] = 高度(即纵向格数)
010036B2 CALL winmine.01002ED5 ; 生成空的内存块并进行清除
010036B7 MOV EAX,DWORD PTR DS:[10056A4]
010036BC MOV DWORD PTR DS:[1005160],EDI
010036C2 MOV DWORD PTR DS:[1005330],EAX ; [0x1005330] = 地雷的个数
; 以地雷个数进行循环
010036C7 PUSH DWORD PTR DS:[1005334] ; 把最大宽度(max width)压入栈
010036CD CALL winmine.01003940 ; Mine_Width = 随机化 x 位置 (0 至 max width-1) (即在0和max width-1之间随机选一个值)
010036D2 PUSH DWORD PTR DS:[1005338] ; 把最大高度压入栈
010036D8 MOV ESI,EAX
010036DA INC ESI ; Mine_Width = Mine_Width + 1
010036DB CALL winmine.01003940 ; Mine_Height =随机化 y 位置
&