Windows扫雷游戏
相关的例子:下载>>> 作者:Zoologist 于2008-7-19上传 

    Windows自带了一个扫雷游戏,很好玩。网上也有很多介绍作弊的方法,之前的Win98中,纪录保存在

INI文件中,XP的保存在注册表中。但是,这些方法对于我们程序员来说“太没有技术含量”了,我们要通过

编程序来“作弊”。网上也有很多“扫雷机”,今天我们就从头开始打造一个自己的扫雷机。

    Windows XP 的扫雷程序在 windows\system32\ 目录下,名字为 Winmine.exe。运行界面如下:

1.JPG (14712 字节)

    我们现用Spy++看看上面都有什么“元件”。可以看出来,界面上是一整块的,就是说我们看到的类似于

按钮的东西实际上是绘制出来的,并不是 Button 排列出来的。如果是我编写的话,没准会用很多按钮之类的。

不过,Windows中的确确实实是画出来的窗口,画出来的按钮。网上的资料说可以从随机数生成入手,我们

也从这个门。拿出OllyDBG.EXE,加载Winmine.exe,滚动到最上面,有一张“导出表”。它的用途就是调用

系统API。告诉程序,我要调用 XXX 的 API,这个API在系统的什么地方。

2.JPG (55786 字节)

    上图中,光标停在 srand ,它是VC中指定随机种子的函数,下面rand是使用这个种子生成随机数的

函数。我们使用右键弹出的菜单 Find references to 中的 Selected address 功能查找看看谁在使用srand

函数。

3.JPG (86648 字节)

    我们先看看srand函数,找到如下的程序段:

4.JPG (14714 字节)

    这段话的意思就是,用GetTickCount函数取一下当前系统的tick数,将这个作为种子,交给srand。

    再看看 rand 函数,这个函数要求输入给定生成随机数的范围:

5.JPG (14555 字节)

    再查找谁在调用这个位置,结果如下:

6.JPG (13573 字节)

     更具体的描述可以参考附件中的文章。

     应该有更简单的方法吧?可以想象一下,如果程序员编写这个游戏的时候,有很多参数,最最

简单的方法是使用 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<xValue
            jmp     nextX
    .endif
   
    invoke StdOut,addr szCr
    mov    eax,32
    sub    eax,xValue
    add    esi,eax
    inc    y
    mov    eax,y
.if    eax<yValue
    jmp    nextY
.endif

exitprog:   

    invoke    wsprintf,addr szBuffer,CTXT(".....程序结束,回车键退出")
    invoke StdOut,offset szBuffer   
   
    ;暂停显示,回车键关闭
    invoke StdIn,addr szBuffer,sizeof szBuffer
    invoke ExitProcess,0
   
end START

                       程序运行结果

wpe2.jpg (73210 字节)

        我们可以看出,程序给出了地雷的分布。在后面,我们还会继续介绍对扫雷程序的修改。



欢迎访问AoGo汇编小站:http://www.aogosoft.com
下一篇>>>