【文章标题】: 扫雷分析
【文章作者】: 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
[培训]科锐逆向工程师培训第53期2025年7月8日开班!