首页
社区
课程
招聘
[原创]扫雷分析
发表于: 2011-12-29 17:37 13905

[原创]扫雷分析

2011-12-29 17:37
13905

【文章标题】: 扫雷分析
【文章作者】: loongzyd
【软件名称】: Windows Xp自带扫雷游戏
【下载地址】: XP自带
【编写语言】: VC++ 6.0
【使用工具】: OD,IDA
【操作平台】: Windows XP
【作者声明】: 老师布置的若干课程设计题目之一,这个题目和逆向比较紧密,所以就选了这个题目(自动扫雷),水平有限,请大家指出。

01005340数据:存放雷区的初始值
   01005330数据:雷的数量(010056A4数据同样也是雷的数量)
   01005334数据:当前界面的长度x
   01005338数据:当前界面的宽度y
   01005118数据:用户点击格子的Y坐标
   0100511c数据:用户点击格子的X坐标
   010057A0数据:不是雷的个数
   010057A4数据:貌似记录是否为用户第一次点击,第一次的话申请时钟(用户点击次数)
   01005144数据:经过处理的WM_LBUTTONDOWN的wParam:key indicator
   01005798数据:记录格子周围8个格子都不是雷的格子的坐标的数组的下一个存放下标。  
   0100579c数据:计时器数据
   010056A8数据:当前雷区X长度
   010056AC数据:当前雷区Y长度
   01005194数据:剩下没有标记雷的个数
sub_1002ED5的作用是设置雷区的初始值:
01002ED5  /$  B8 60030000            MOV EAX,360                              ;  雷区的"总面积"为0x360
01002EDA  |>  48                     /DEC EAX         
01002EDB  |.  C680 40530001 0F       |MOV BYTE PTR DS:[EAX+1005340],0F        ;  1005340开始的0x360区域全部初始为0x0f
01002EE2  |.^ 75 F6                  \JNZ SHORT winmine_.01002EDA
01002EE4  |.  8B0D 34530001          MOV ECX,DWORD PTR DS:[1005334]           ;  长度X
01002EEA  |.  8B15 38530001          MOV EDX,DWORD PTR DS:[1005338]           ;  宽度Y
01002EF0  |.  8D41 02                LEA EAX,DWORD PTR DS:[ECX+2]             ;  长度+2
01002EF3  |.  85C0                   TEST EAX,EAX
01002EF5  |.  56                     PUSH ESI
01002EF6  |.  74 19                  JE SHORT winmine_.01002F11
01002EF8  |.  8BF2                   MOV ESI,EDX
01002EFA  |.  C1E6 05                SHL ESI,5                                ;  宽度左移5位
01002EFD  |.  8DB6 60530001          LEA ESI,DWORD PTR DS:[ESI+1005360]
01002F03  |>  48                     /DEC EAX
01002F04  |.  C680 40530001 10       |MOV BYTE PTR DS:[EAX+1005340],10        ;  设定雷区行边界:0x10(表示已经出了雷区)
01002F0B  |.  C60406 10              |MOV BYTE PTR DS:[ESI+EAX],10
01002F0F  |.^ 75 F2                  \JNZ SHORT winmine_.01002F03
01002F11  |>  8D72 02                LEA ESI,DWORD PTR DS:[EDX+2]             ;  宽度+2
01002F14  |.  85F6                   TEST ESI,ESI
01002F16  |.  74 21                  JE SHORT winmine_.01002F39
01002F18  |.  8BC6                   MOV EAX,ESI
01002F1A  |.  C1E0 05                SHL EAX,5                                ;  (宽度+2)左移5位
01002F1D  |.  8D90 40530001          LEA EDX,DWORD PTR DS:[EAX+1005340]
01002F23  |.  8D8408 41530001        LEA EAX,DWORD PTR DS:[EAX+ECX+1005341]
01002F2A  |>  83EA 20                /SUB EDX,20
01002F2D  |.  83E8 20                |SUB EAX,20
01002F30  |.  4E                     |DEC ESI
01002F31  |.  C602 10                |MOV BYTE PTR DS:[EDX],10                ;  设定雷区列边界:0x10(表示已经出了雷区)
01002F34  |.  C600 10                |MOV BYTE PTR DS:[EAX],10
01002F37  |.^ 75 F1                  \JNZ SHORT winmine_.01002F2A
01002F39  |>  5E                     POP ESI
01002F3A  \.  C3                     RETN
接下来将雷的个数存放在dword_1005330处,然后就是部署地雷的相关部分:
010036C7  |> /FF35 34530001 PUSH DWORD PTR DS:[1005334]
010036CD  |. |E8 6E020000   CALL winmine_.01003940
010036D2  |. |FF35 38530001 PUSH DWORD PTR DS:[1005338]
010036D8  |. |8BF0          MOV ESI,EAX
010036DA  |. |46            INC ESI                                  ;  随机产生的雷区的横排值(X坐标)
010036DB  |. |E8 60020000   CALL winmine_.01003940
010036E0  |. |40            INC EAX
010036E1  |. |8BC8          MOV ECX,EAX                              ;  随机产生的雷区的竖排值(Y坐标)
010036E3  |. |C1E1 05       SHL ECX,5
010036E6  |. |F68431 405300>TEST BYTE PTR DS:[ECX+ESI+1005340],80    ;  如果该坐标已经设定为雷,则重新产生随机坐标
010036EE  |.^ 75 D7         JNZ SHORT winmine_.010036C7
010036F0  |. |C1E0 05       SHL EAX,5
010036F3  |. |8D8430 405300>LEA EAX,DWORD PTR DS:[EAX+ESI+1005340]
010036FA  |. |8008 80       OR BYTE PTR DS:[EAX],80                  ;  设定该坐标为雷(0x0f->0x8f)
010036FD  |. |FF0D 30530001 DEC DWORD PTR DS:[1005330]               ;  还需要部署的雷的个数减1
01003703  |.^\75 C2         JNZ SHORT winmine_.010036C7
01003705  |.  8B0D 38530001 MOV ECX,DWORD PTR DS:[1005338]
0100370B  |.  0FAF0D 345300>IMUL ECX,DWORD PTR DS:[1005334]
01003712  |.  A1 A4560001   MOV EAX,DWORD PTR DS:[10056A4]
01003717  |.  2BC8          SUB ECX,EAX
01003719  |.  57            PUSH EDI
0100371A  |.  893D 9C570001 MOV DWORD PTR DS:[100579C],EDI           ;  赋值为0
01003720  |.  A3 30530001   MOV DWORD PTR DS:[1005330],EAX           ;  雷的个数
01003725  |.  A3 94510001   MOV DWORD PTR DS:[1005194],EAX           ;  雷的格数
0100372A  |.  893D A4570001 MOV DWORD PTR DS:[10057A4],EDI           ;  赋值为0
01003730  |.  890D A0570001 MOV DWORD PTR DS:[10057A0],ECX           ;  不是0的个数
01003736  |.  C705 00500001>MOV DWORD PTR DS:[1005000],1
010031DD  |.  A1 18510001   MOV EAX,DWORD PTR DS:[1005118]           ;  上次点击格子的X数
010031E2  |.  3BD0          CMP EDX,EAX
010031E4  |.  8B0D 1C510001 MOV ECX,DWORD PTR DS:[100511C]           ;  上次点击格子的Y数
010031EA  |.  57            PUSH EDI
010031EB  |.  8B7D 0C       MOV EDI,DWORD PTR SS:[EBP+C]
010031EE  |.  75 08         JNZ SHORT winmine_.010031F8              ;  这次点击和上次点击是否在同一行
010031F0  |.  3BF9          CMP EDI,ECX                              ;  这次点击和上次点击是否在同一列
010031F2  |.  0F84 1F020000 JE winmine_.01003417                     ;  如果说两次左键点击的格子相同,函数就退出
010031F8  |>  833D 44510001>CMP DWORD PTR DS:[1005144],0             ;  如果不是MK_SHIFT 函数就跳转往后执行
010031FF  |.  53            PUSH EBX
01003200  |.  56            PUSH ESI
01003201  |.  8BD8          MOV EBX,EAX
01003203  |.  8BF1          MOV ESI,ECX
01003205  |.  8915 18510001 MOV DWORD PTR DS:[1005118],EDX           ;  记录当前用户点击的格子的列数
0100320B  |.  893D 1C510001 MOV DWORD PTR DS:[100511C],EDI           ;  记录用户当前点击格子的行数
01003211  |.  0F84 80010000 JE winmine_.01003397
010033D7  |.  3B15 34530001 CMP EDX,DWORD PTR DS:[1005334]           ;  判断当前点击的列数是否越界
010033DD  |.  7F 36         JG SHORT winmine_.01003415
010033DF  |.  3B3D 38530001 CMP EDI,DWORD PTR DS:[1005338]           ;  判断当前点击的行数是否越界
010033E5  |.  7F 2E         JG SHORT winmine_.01003415
010033E7  |.  C1E7 05       SHL EDI,5
010033EA  |.  8A8417 405300>MOV AL,BYTE PTR DS:[EDI+EDX+1005340]
010033F1  |.  A8 40         TEST AL,40                               ;  判断点击的格子对应的内存数据是高29位是否为1
010033F3  |.  75 20         JNZ SHORT winmine_.01003415
010033F5  |.  24 1F         AND AL,1F                                ;  保留低位
010033F7  |.  3C 0E         CMP AL,0E
010033F9  |.  74 1A         JE SHORT winmine_.01003415               ;  判断对应内存诗句是否为0x0e
010033FB  |.  8B3D 1C510001 MOV EDI,DWORD PTR DS:[100511C]           ;  用户当前点击的格子的行数
01003401  |.  8B35 18510001 MOV ESI,DWORD PTR DS:[1005118]           ;  用户当前点击格子的列数
01003407  |.  57            PUSH EDI
01003408  |.  56            PUSH ESI
01003409  |.  E8 5DFDFFFF   CALL winmine_.0100316B
0100316B  /$  8B4424 08     MOV EAX,DWORD PTR SS:[ESP+8]             ;  点击的格子的行数
0100316F  |.  8B4C24 04     MOV ECX,DWORD PTR SS:[ESP+4]             ;  点击的格子的列数
01003173  |.  C1E0 05       SHL EAX,5
01003176  |.  8D9408 405300>LEA EDX,DWORD PTR DS:[EAX+ECX+1005340]
0100317D  |.  8A02          MOV AL,BYTE PTR DS:[EDX]                 ;  点击格子对应的内存单元数据
0100317F  |.  33C9          XOR ECX,ECX
01003181  |.  8AC8          MOV CL,AL
01003183  |.  83E1 1F       AND ECX,1F
01003186  |.  83F9 0D       CMP ECX,0D
01003189  |.  75 05         JNZ SHORT winmine_.01003190              ;  如果低8位为D则不跳转
0100318B  |.  6A 09         PUSH 9
0100318D  |.  59            POP ECX                                  ;  ecx初值为9
0100318E  |.  EB 07         JMP SHORT winmine_.01003197
01003190  |>  83F9 0F       CMP ECX,0F
01003193  |.  75 02         JNZ SHORT winmine_.01003197              ;  如果低位为F则不跳转
01003195  |.  33C9          XOR ECX,ECX
01003197  |>  24 E0         AND AL,0E0                               ;  保留字节的高8位
01003199  |.  0AC1          OR AL,CL
0100319B  |.  8802          MOV BYTE PTR DS:[EDX],AL                 ;  更新格子对应的内存数据
0100319D  \.  C2 0800       RETN 8
.text:010037E1 sub_10037E1     proc near               ; CODE XREF: sub_1001BC9+43Cp
.text:010037E1                 mov     eax, dword_1005118
.text:010037E6                 test    eax, eax
.text:010037E8                 jle     loc_10038B6     ; 点击的Y坐标
.text:010037EE                 mov     ecx, dword_100511C
.text:010037F4                 test    ecx, ecx        ; 点击的X坐标
.text:010037F6                 jle     loc_10038B6
.text:010037FC                 cmp     eax, dword_1005334 ; 界面的长度(X)
.text:01003802                 jg      loc_10038B6
.text:01003808                 cmp     ecx, dword_1005338 ; 界面的宽度(Y)
.text:0100380E                 jg      loc_10038B6
.text:01003814                 push    ebx
.text:01003815                 xor     ebx, ebx
.text:01003817                 inc     ebx
.text:01003818                 cmp     dword_10057A4, 0 ; 判断是否为用户第一次点击
.text:0100381F                 jnz     short loc_100386B
.text:01003821                 cmp     dword_100579C, 0 ; 也是与判断是否为用户第一次点击有关
.text:01003828                 jnz     short loc_100386B
.text:0100382A                 push    ebx
.text:0100382B                 call    sub_10038ED     ; 与声音有关的相关处理
.text:01003830                 inc     dword_100579C
.text:01003836                 call    sub_10028B5     ; 与图形有关的相关处理
.text:0100383B                 push    0               ; lpTimerFunc
.text:0100383D                 push    3E8h            ; uElapse
.text:01003842                 push    ebx             ; nIDEvent
.text:01003843                 push    hWnd            ; hWnd
.text:01003849                 mov     dword_1005164, ebx
.text:0100384F                 call    ds:SetTimer     ; 用户第一次点击后,申请时钟,开始计时。
.text:01003855                 test    eax, eax
.text:01003857                 jnz     short loc_1003860 ; 用户点击格子的Y坐标
.text:01003859                 push    4               ; 如果计时器没有创建成功
.text:0100385B                 call    sub_1003950     ; 弹出对话框提示
.text:01003860
.text:01003860 loc_1003860:                            ; CODE XREF: sub_10037E1+76j
.text:01003860                 mov     eax, dword_1005118 ; 用户点击格子的Y坐标
.text:01003865                 mov     ecx, dword_100511C ; 用户点击格子的X坐标
.text:0100386B
.text:0100386B loc_100386B:                            ; CODE XREF: sub_10037E1+3Ej
.text:0100386B                                         ; sub_10037E1+47j
.text:0100386B                 test    byte ptr dword_1005000, bl
.text:01003871                 pop     ebx
.text:01003872                 jnz     short loc_1003884 ; dword_1005144为0:不是MK_RBUTTON,MK_SHIFT时
.text:01003874                 push    0FFFFFFFEh
.text:01003876                 pop     ecx
.text:01003877                 mov     eax, ecx
.text:01003879                 mov     dword_100511C, ecx ; 用户点击格子的Y坐标
.text:0100387F                 mov     dword_1005118, eax ; 用户点击格子的X坐标

前半部分我们可以看到,程序判断用户点击的格子的坐标有没有超过主界面的范围。接着,判断用户是否为第一次点击格子。如果是第一次点击的话,就申请一个时钟(1S),开始计时。

.text:01003884 loc_1003884:                            ; CODE XREF: sub_10037E1+91j
.text:01003884                 cmp     dword_1005144, 0 ; dword_1005144为0:不是MK_RBUTTON,MK_SHIFT时
.text:0100388B                 jz      short loc_1003896
.text:0100388D                 push    ecx
.text:0100388E                 push    eax
.text:0100388F                 call    sub_10035B7
.text:01003894                 jmp     short loc_10038B6
.text:01003896 ; ---------------------------------------------------------------------------
.text:01003896
.text:01003896 loc_1003896:                            ; CODE XREF: sub_10037E1+AAj
.text:01003896                 mov     edx, ecx
.text:01003898                 shl     edx, 5
.text:0100389B                 mov     dl, byte_1005340[edx+eax] ; 用户点击的坐标对应的内存单元值
.text:010038A2                 test    dl, 40h         ; 判断29位是否为1
.text:010038A5                 jnz     short loc_10038B6
.text:010038A7                 and     dl, 1Fh
.text:010038AA                 cmp     dl, 0Eh
.text:010038AD                 jz      short loc_10038B6
.text:010038AF                 push    ecx
.text:010038B0                 push    eax             ; 传递的为用户点击的坐标的X,Y值
.text:010038B1                 call    sub_1003512     ; 该坐标不是雷的时,将相应内存单元的数据修改为0x40+? ?为周围雷的个数
.text:010038B6
.text:010038B6 loc_10038B6:                            ; CODE XREF: sub_10037E1+7j
.text:010038B6                                         ; sub_10037E1+15j ...
.text:010038B6                 push    dword_1005160
.text:010038BC                 call    sub_1002913     ; 图形操作相关
.text:010038C1                 retn
.text:010038C1 sub_10037E1     endp
.text:01003512 sub_1003512     proc near               ; CODE XREF: sub_10037E1+D0p
.text:01003512
.text:01003512 arg_0           = dword ptr  4
.text:01003512 arg_4           = dword ptr  8
.text:01003512
.text:01003512                 mov     eax, [esp+arg_4]
.text:01003516                 push    ebx             ; 点击的Y坐标
.text:01003517                 push    ebp
.text:01003518                 push    esi
.text:01003519                 mov     esi, [esp+0Ch+arg_0] ; 点击的X坐标
.text:0100351D                 mov     ecx, eax
.text:0100351F                 shl     ecx, 5
.text:01003522                 lea     edx, byte_1005340[ecx+esi]
.text:01003529                 test    byte ptr [edx], 80h
.text:0100352C                 push    edi
.text:0100352D                 jz      short loc_1003595 ; 如果点击的不是雷
前半部分程序判断用户点击的格子是为为雷(高30位为1)。

用户鼠标左键点击的格子不是雷:

首先,我们来看用户点击的不是雷的情况:
.text:01003595 loc_1003595:                            ; CODE XREF: sub_1003512+1Bj
.text:01003595                 push    eax             ; 如果点击的不是雷
.text:01003596                 push    esi             ; 传入的参数分别为:点击雷格子的Y坐标和X坐标
.text:01003597                 call    sub_1003084
.text:0100359C                 mov     eax, dword_10057A4 ; 目前确定不是雷的个数
.text:010035A1                 cmp     eax, dword_10057A0 ; 不是雷的总个数
.text:010035A7                 jnz     short loc_10035B0

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 6
支持
分享
最新回复 (14)
雪    币: 434
活跃值: (20)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
先回帖 在看帖 表示 已经好久没有出来活动了
表示很喜欢原创的东西 汇编语言 我还是要加强= =
2011-12-29 18:50
0
雪    币: 380
活跃值: (138)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
3
支持,学习了
2011-12-29 20:35
0
雪    币: 1015
活跃值: (235)
能力值: ( LV12,RANK:440 )
在线值:
发帖
回帖
粉丝
4
老师说我理解错他的意思了,本来他是想让我们通过读取像素来判断每个格子周围的雷的个数,然后写算法来模拟扫雷
这个囧了,幸好他说结果对了就行,不然就纠结了
2011-12-30 17:49
0
雪    币: 207
活跃值: (26)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
汗~前几天我也刚好分析了扫雷
2011-12-30 18:30
0
雪    币: 274
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
不错不错
2011-12-31 13:24
0
雪    币: 2325
活跃值: (1058)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
7
这个要支持下
2011-12-31 15:00
0
雪    币: 73
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
这下不用写算法了,直接就知道雷在哪了
2011-12-31 18:33
0
雪    币: 57
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
我写了个汇编版的。

.586
.model flat, stdcall
option casemap :none

include		windows.inc
include		String.inc	
include		user32.inc
includelib	user32.lib
include		kernel32.inc
includelib	kernel32.lib


.code

inject_start label dword

	call @F				;自定位。
@@:
	pop ebx
	sub ebx,@B
	call [ebx+fix_call]		;因为是hook retn之前的call,所以先补上这个call再执行自己的代码.
					;执行call之后发现ebx不会改变.
					;不用再次自定位
	mov [ebx+current_lie],0         ;每次刚开始时初始化为第1行,第0列
	mov [ebx+current_hang],1
	
	
	mov ecx,ds:[1005338h];总行数
loop_outer:	
	push ecx
	
	mov ecx,ds:[1005334h];总列数
loop_inner:	
	push ecx
	
	inc [ebx+current_lie]
	mov ecx,[ebx+current_hang]  ;取出当前行
	mov edx,ecx
	mov eax,[ebx+current_lie]   ;取出当前列
	
	shl edx,5
	mov dl,byte ptr [edx+eax+1005340h]      ;相关代码在这个call 1003512h 之前
	test dl,40h ;判定是否已经打开过了                ;是不是雷是在call里判断的
	jnz @F                                                       ;要实现自动扫,我们就把雷的判断也移到外面来
	test dl,80h ;判定是否为雷                            ;是雷的话就不call
	jne @F
	and dl,1Fh  ;没细看汇编了,不管他判定什么,照抄过来
	cmp dl,0Eh
	je @F

	push ecx
	push eax
	call dword ptr [ebx+call_addr]
@@:		
	pop ecx  ;继续扫描下一列
	loopd loop_inner
	
	inc [ebx+current_hang]		;之前都不会改变ebx,可以不用再次自定位
	mov [ebx+current_lie],0
	pop ecx  ;继续扫描下一行
	loopd loop_outer
	
	retn
	
current_hang	dd 1		;初始化为第1行,第0列
current_lie	dd 0
call_addr	dd 1003512h     ;call地址
fix_call	dd 1002913h     ;hook的retn前一行,先call他再执行自己的代码

inject_end label dword
inject_length equ offset inject_end- offset inject_start    
                                           		   
.data
pid	dd 0
hook	db 0E9h ;jmp
        dd 0 

.code
start proc
	LOCAL hProcess,virtual_addr,len
	mov len,inject_length
	invoke FindWindow,$CTA0("扫雷"),NULL
	invoke GetWindowThreadProcessId,eax,offset pid
	invoke OpenProcess,PROCESS_ALL_ACCESS,0,pid
	mov hProcess,eax
	invoke VirtualAllocEx,hProcess,NULL,1,MEM_COMMIT,PAGE_EXECUTE_READWRITE
	mov virtual_addr,eax
	invoke WriteProcessMemory,hProcess,virtual_addr,offset inject_start,len,NULL
	mov eax,virtual_addr    ;计算jmp的偏移. 目的地-当前-5
	sub eax,10038bch
	sub eax,5
	mov dword ptr [hook+1],eax
	invoke WriteProcessMemory,hProcess,10038bch,offset hook,5,NULL       
	invoke CloseHandle,hProcess
	invoke ExitProcess,0
	
start endp
	
end start
	

上传的附件:
2012-1-2 11:11
0
雪    币: 190
活跃值: (40)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
很好很强大~`
2012-1-2 15:56
0
雪    币: 1737
活跃值: (110)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
哈哈,好啊,就得支持这种分析系统自带的程序的东东~~
2012-1-3 14:03
0
雪    币: 30
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
强烈支持一下,一直在看理论还没有实践过,先支持下楼主。。。
2012-1-4 09:56
0
雪    币: 210
活跃值: (143)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
强帖留名。。。。
2012-1-4 17:02
0
雪    币: 324
活跃值: (113)
能力值: ( LV15,RANK:280 )
在线值:
发帖
回帖
粉丝
14
扫雷还有个有意思的事情。雷使用伪随机数生成,整个游戏过程中只有一次调用srand。只要游戏一开始,n局以后雷的布局都是可以预测的。
2012-1-4 20:05
0
雪    币: 44
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
fyd
15
经常玩扫雷游戏,下载好好学习学习!谢谢啊!
2012-3-3 17:45
0
游客
登录 | 注册 方可回帖
返回