Windows扫雷游戏
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Windows扫雷游戏
-
Windows自带了一个扫雷游戏,很好玩。网上也有很多介绍作弊的方法,之前的Win98中,纪录保存在
INI文件中,XP的保存在注册表中。但是,这些方法对于我们程序员来说“太没有技术含量”了,我们要通过
编程序来“作弊”。网上也有很多“扫雷机”,今天我们就从头开始打造一个自己的扫雷机。
Windows XP 的扫雷程序在 windows\system32\ 目录下,名字为 Winmine.exe。运行界面如下:
我们现用Spy++看看上面都有什么“元件”。可以看出来,界面上是一整块的,就是说我们看到的类似于
按钮的东西实际上是绘制出来的,并不是 Button 排列出来的。如果是我编写的话,没准会用很多按钮之类的。
不过,Windows中的确确实实是画出来的窗口,画出来的按钮。网上的资料说可以从随机数生成入手,我们
也从这个门。拿出OllyDBG.EXE,加载Winmine.exe,滚动到最上面,有一张“导出表”。它的用途就是调用
系统API。告诉程序,我要调用 XXX 的 API,这个API在系统的什么地方。
上图中,光标停在 srand ,它是VC中指定随机种子的函数,下面rand是使用这个种子生成随机数的
函数。我们使用右键弹出的菜单 Find references to 中的 Selected address 功能查找看看谁在使用srand
函数。
我们先看看srand函数,找到如下的程序段:
这段话的意思就是,用GetTickCount函数取一下当前系统的tick数,将这个作为种子,交给srand。
再看看 rand 函数,这个函数要求输入给定生成随机数的范围:
再查找谁在调用这个位置,结果如下:
更具体的描述可以参考附件中的文章。
应该有更简单的方法吧?可以想象一下,如果程序员编写这个游戏的时候,有很多参数,最最
简单的方法是使用 int x,y; 这样定义游戏中的参数... ...只要我们试验足够多,还是很容易知道
这些参数的位置的。在程序的开头,有一片看起来“貌似”数据的控件,我们观察这个位置就好了。
观察数据的具体分布是需要耐心经验和运气的,附件中的文章对于如何修改验证也描述的很详细,
在这里我就不再重复了。下面就是编写读取“地雷分布”。读取内存要用到如下的API:
ReadProcessMemory Function
Reads data from an area of memory in a specified process. The entire area to be read must be accessible or the operation fails.
读取给定进程的特定内存偏移。读取的区域必须是可以访问的,否则会失败。
BOOL WINAPI ReadProcessMemory(
__in HANDLE hProcess,
__in LPCVOID lpBaseAddress,
__out LPVOID lpBuffer,
__in SIZE_T nSize,
__out SIZE_T* lpNumberOfBytesRead
);
Parameters
hProcess
A handle to the process with memory that is being read. The handle must have PROCESS_VM_READ access to the process.
要读取内存进程的handle。必须已经使用 PROCESS_VM_READ 打开。
lpBaseAddress
A pointer to the base address in the specified process from which to read. Before any data transfer occurs, the system verifies that all data in the base address and memory of the specified size is accessible for read access, and if it is not accessible the function fails.
指向要读取内存的地址。
lpBuffer
A pointer to a buffer that receives the contents from the address space of the specified process.
取得内存内容之后的拷贝到缓冲区
nSize
The number of bytes to be read from the specified process.
要读取的字节数。
lpNumberOfBytesRead
A pointer to a variable that receives the number of bytes transferred into the specified buffer. If lpNumberOfBytesRead is NULL, the parameter is ignored.
指向已经读取到给定缓冲区的字节数,如果 lpNumberOfBytesRead 为 NULL,将会忽略这个参数。
Return Value
If the function succeeds, the return value is nonzero.
成功返回非零值。
If the function fails, the return value is 0 (zero). To get extended error information, call GetLastError.
读取失败,返回0。
我们已经知道程序使用关键数据的结构如下:
ds:[1005334] x方向上的格子数
ds:[1005338] y方向上的格子数
ds:[1005330] 地雷总数
byte flag[32 *24 ] 雷区,实际上最大是 32(X方向) 24 (Y方向)
程序清单如下:
;MASMPlus 代码模板 - 控制台程序
.386
.model flat, stdcall
option casemap :none
include windows.inc
include user32.inc
include kernel32.inc
include masm32.inc
include gdi32.inc
includelib gdi32.lib
includelib user32.lib
includelib kernel32.lib
includelib masm32.lib
include macro.asm
MAXMINER equ 768 ;30*24
.data
lpMsg db "Hello World!",0
hSweep dd 0
dcSweep dd 0
pID dd 0
pHandle dd 0
szCr db 0Dh,0Ah,0
mNum dd 0
mineBuf db MAXMINER dup(0)
.data?
szBuffer db MAX_PATH dup(?)
pAddr dd ?
xValue dd ?
yValue dd ?
x dd ?
y dd ?
.CODE
START:
invoke FindWindow,NULL, CTXT('扫雷') ;查找扫雷游戏
mov hSweep,eax
.if hSweep ==0
invoke wsprintf,addr szBuffer,CTXT("请启动‘扫雷游戏’")
invoke lstrcat,addr szBuffer,addr szCr
invoke StdOut,offset szBuffer
jmp exitprog
.endif
invoke GetWindowThreadProcessId,hSweep, addr pID
invoke OpenProcess,PROCESS_VM_READ,FALSE, pID
mov pHandle,eax
mov pAddr,10056ACh
invoke ReadProcessMemory, pHandle, pAddr, offset xValue, 4,NULL
mov
pAddr,10056A8h
invoke ReadProcessMemory, pHandle, pAddr, offset yValue, 4, NULL
mov pAddr,1005330h ;开局时的地雷总数
invoke ReadProcessMemory, pHandle, pAddr, offset mNum, 4, NULL
mov pAddr,1005361h ;地雷分布
invoke ReadProcessMemory, pHandle, pAddr,offset mineBuf, MAXMINER, NULL
invoke wsprintf,addr szBuffer,CTXT("横向:%d 纵向:%d 地雷总数 %d"),xValue,yValue,mNum
invoke StdOut,offset szBuffer
invoke StdOut,addr szCr
mov esi,offset mineBuf
xor eax,eax
mov y,eax
nextY:
xor eax,eax
mov x,eax
nextX:
mov al,[esi]
inc esi
cmp al,8Fh
jNz @f
invoke wsprintf,addr szBuffer,CTXT("1 ")
jmp gonext
@@:
invoke wsprintf,addr szBuffer,CTXT("0 ")
gonext:
invoke StdOut,offset szBuffer
inc x
mov eax,x
.if eax
.endif
invoke StdOut,addr szCr
mov eax,32
sub eax,xValue
add esi,eax
inc y
mov eax,y
.if eax
.endif
exitprog:
invoke wsprintf,addr szBuffer,CTXT(".....程序结束,回车键退出")
invoke StdOut,offset szBuffer
;暂停显示,回车键关闭
invoke StdIn,addr szBuffer,sizeof szBuffer
invoke ExitProcess,0
end START
程序运行结果
我们可以看出,程序给出了地雷的分布。在后面,我们还会继续介绍对扫雷程序的修改。