最近几天闲来无事,便拿扫雷游戏来练练手。其实扫雷游戏挺简单,对汇编感兴趣的朋友不妨看看。
先做准备工作。
- 操作系统:Windows XP with SP2
- 调试工具:Windbg
- 开发工具:Visual Studio 2008
- Windows XP SP2 Symbols
先看winmine.exe细节情况。
用dumpbin看之,发现该程序导入表简单,没有使用MFC和C++的dll,估计是用C写成,呵呵,标准SDK程序。细察之,发现其使用了msvcrt.dll中srand和rand两个函数。估计是用来生成随机的地雷的,决定就从这着手了。另外,该程序只有三个section:.text,.data,.rsrc。果然是中规中矩,毕竟这种小游戏MS也没啥可费力弄的。
然后启动windbg,打开可执行文件C:/windows/system32/winmine.exe。windbg自动停在起始处。下断点,bp msvcrt!srand和bp msvcrt!rand。
g运行,断点msvcrt!srand命中,kv查看堆栈,发现其为
ChildEBP RetAddr Args to Child 0007fea4 01003ac4 0000a9b6 7c80b529 000a1eff msvcrt!srand (FPO: [Non-Fpo]) 0007fee0 7c930970 7c99e4c0 7c8021b5 7c802011 winmine!InitConst+0x14 (FPO: [Uses EBP] [0,1,4]) 0007fee8 7c8021b5 7c802011 7c80b529 000a1eff ntdll!RtlReleasePebLock+0xf (FPO: [0,0,0]) 0007feec 7c802011 7c80b529 000a1eff 00000000 kernel32!GetStartupInfoA+0x2a7 (FPO: [Non-Fpo]) 0007ff18 0007ffc0 01003f95 01000000 00000000 kernel32!GetStartupInfoA+0x2b9 (FPO: [Non-Fpo]) WARNING: Frame IP not in any known module. Following frames may be wrong. 0007ffc0 7c816d4f 00011970 7c9318f1 7ffd7000 0x7ffc0 0007ffe0 7c8399f2 7c816d58 00000000 00000000 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo]) 0007ffe8 00000000 00000000 00000000 01003e21 kernel32!TerminateProcess+0x9 (FPO: [Non-Fpo])
可知winmine的WinMain中调用InitConst函数(从名称猜测可能为初始化常量),InitConst又调用了srand。
反汇编WinMain,
0:000> uf winmine!WinMain winmine!WinMain: 010021f0 55 push ebp 010021f1 8bec mov ebp,esp 010021f3 83ec4c sub esp,4Ch 010021f6 8b4508 mov eax,dword ptr [ebp+8] 010021f9 53 push ebx 010021fa 56 push esi 010021fb 57 push edi 010021fc a3305b0001 mov dword ptr [winmine!hInst (01005b30)],eax 01002201 e8aa180000 call winmine!InitConst (01003ab0) 01002206 33db xor ebx,ebx 01002208 43 inc ebx 01002209 33ff xor edi,edi 0100220b 837d1407 cmp dword ptr [ebp+14h],7 0100220f 740c je winmine!WinMain+0x2d (0100221d) winmine!WinMain+0x21: 01002211 837d1402 cmp dword ptr [ebp+14h],2 01002215 893d385b0001 mov dword ptr [winmine!bInitMinimized (01005b38)],edi 0100221b 7506 jne winmine!WinMain+0x33 (01002223) winmine!WinMain+0x2d: 0100221d 891d385b0001 mov dword ptr [winmine!bInitMinimized (01005b38)],ebx winmine!WinMain+0x33: 01002223 8d45f8 lea eax,[ebp-8] 01002226 50 push eax 01002227 c745f808000000 mov dword ptr [ebp-8],8 0100222e c745fcfd160000 mov dword ptr [ebp-4],16FDh 01002235 ff151c100001 call dword ptr [winmine!_imp__InitCommonControlsEx (0100101c)] 0100223b 6a64 push 64h 0100223d ff35305b0001 push dword ptr [winmine!hInst (01005b30)] 01002243 ff15ac100001 call dword ptr [winmine!_imp__LoadIconW (010010ac)] 01002249 8b0d305b0001 mov ecx,dword ptr [winmine!hInst (01005b30)] 0100224f 68007f0000 push 7F00h 01002254 57 push edi 01002255 a3285b0001 mov dword ptr [winmine!hIconMain (01005b28)],eax 0100225a 897db4 mov dword ptr [ebp-4Ch],edi 0100225d c745b8c91b0001 mov dword ptr [ebp-48h],offset winmine!MainWndProc (01001bc9) 01002264 897dbc mov dword ptr [ebp-44h],edi 01002267 897dc0 mov dword ptr [ebp-40h],edi 0100226a 894dc4 mov dword ptr [ebp-3Ch],ecx 0100226d 8945c8 mov dword ptr [ebp-38h],eax 01002270 ff15bc100001 call dword ptr [winmine!_imp__LoadCursorW (010010bc)] 01002276 53 push ebx 01002277 8945cc mov dword ptr [ebp-34h],eax 0100227a ff1560100001 call dword ptr [winmine!_imp__GetStockObject (01001060)] 01002280 8945d0 mov dword ptr [ebp-30h],eax 01002283 8d45b4 lea eax,[ebp-4Ch] 01002286 bea05a0001 mov esi,offset winmine!szClass (01005aa0) 0100228b 50 push eax 0100228c 897dd4 mov dword ptr [ebp-2Ch],edi 0100228f 8975d8 mov dword ptr [ebp-28h],esi 01002292 ff15cc100001 call dword ptr [winmine!_imp__RegisterClassW (010010cc)] 01002298 6685c0 test ax,ax 0100229b 0f849a000000 je winmine!WinMain+0x14b (0100233b) winmine!WinMain+0xb1: 010022a1 68f4010000 push 1F4h 010022a6 ff35305b0001 push dword ptr [winmine!hInst (01005b30)] 010022ac ff15d4100001 call dword ptr [winmine!_imp__LoadMenuW (010010d4)] 010022b2 68f5010000 push 1F5h 010022b7 ff35305b0001 push dword ptr [winmine!hInst (01005b30)] 010022bd a3945a0001 mov dword ptr [winmine!hMenu (01005a94)],eax 010022c2 ff1560110001 call dword ptr [winmine!_imp__LoadAcceleratorsW (01001160)] 010022c8 894514 mov dword ptr [ebp+14h],eax 010022cb e8f2080000 call winmine!ReadPreferences (01002bc2) 010022d0 a1885b0001 mov eax,dword ptr [winmine!dypAdjust (01005b88)] 010022d5 8b0d205b0001 mov ecx,dword ptr [winmine!dyWindow (01005b20)] 010022db 8b152c5b0001 mov edx,dword ptr [winmine!dxWindow (01005b2c)] 010022e1 57 push edi 010022e2 ff35305b0001 push dword ptr [winmine!hInst (01005b30)] 010022e8 03c8 add ecx,eax 010022ea 57 push edi 010022eb 57 push edi 010022ec 51 push ecx 010022ed 8b0d905a0001 mov ecx,dword ptr [winmine!dxpBorder (01005a90)] 010022f3 03d1 add edx,ecx 010022f5 52 push edx 010022f6 8b15b4560001 mov edx,dword ptr [winmine!Preferences+0x14 (010056b4)] 010022fc 2bd0 sub edx,eax 010022fe a1b0560001 mov eax,dword ptr [winmine!Preferences+0x10 (010056b0)] 01002303 52 push edx 01002304 2bc1 sub eax,ecx 01002306 50 push eax 01002307 680000ca00 push 0CA0000h 0100230c 56 push esi 0100230d 56 push esi 0100230e 57 push edi 0100230f ff155c110001 call dword ptr [winmine!_imp__CreateWindowExW (0100115c)] 01002315 3bc7 cmp eax,edi 01002317 a3245b0001 mov dword ptr [winmine!hwndMain (01005b24)],eax 0100231c 7507 jne winmine!WinMain+0x135 (01002325) winmine!WinMain+0x12e: 0100231e 68e8030000 push 3E8h 01002323 eb11 jmp winmine!WinMain+0x146 (01002336) winmine!WinMain+0x135: 01002325 53 push ebx 01002326 e825f6ffff call winmine!AdjustWindow (01001950) 0100232b e8e4070000 call winmine!FInitLocal (01002b14) 01002330 85c0 test eax,eax 01002332 750e jne winmine!WinMain+0x152 (01002342) winmine!WinMain+0x144: 01002334 6a05 push 5 winmine!WinMain+0x146: 01002336 e815160000 call winmine!ReportErr (01003950) winmine!WinMain+0x14b: 0100233b 33c0 xor eax,eax 0100233d e984000000 jmp winmine!WinMain+0x1d6 (010023c6) winmine!WinMain+0x152: 01002342 ff35c4560001 push dword ptr [winmine!Preferences+0x24 (010056c4)] 01002348 e898190000 call winmine!SetMenuBar (01003ce5) 0100234d e828130000 call winmine!StartGame (0100367a) 01002352 53 push ebx 01002353 ff35245b0001 push dword ptr [winmine!hwndMain (01005b24)] 01002359 ff1534110001 call dword ptr [winmine!_imp__ShowWindow (01001134)] 0100235f ff35245b0001 push dword ptr [winmine!hwndMain (01005b24)] 01002365 ff1558110001 call dword ptr [winmine!_imp__UpdateWindow (01001158)] 0100236b 8b3554110001 mov esi,dword ptr [winmine!_imp__GetMessageW (01001154)] 01002371 893d385b0001 mov dword ptr [winmine!bInitMinimized (01005b38)],edi 01002377 eb2b jmp winmine!WinMain+0x1b4 (010023a4) winmine!WinMain+0x189: 01002379 8d45dc lea eax,[ebp-24h] 0100237c 50 push eax 0100237d ff7514 push dword ptr [ebp+14h] 01002380 ff35245b0001 push dword ptr [winmine!hwndMain (01005b24)] 01002386 ff1550110001 call dword ptr [winmine!_imp__TranslateAcceleratorW (01001150)] 0100238c 85c0 test eax,eax 0100238e 7514 jne winmine!WinMain+0x1b4 (010023a4) winmine!WinMain+0x1a0: 01002390 8d45dc lea eax,[ebp-24h] 01002393 50 push eax 01002394 ff154c110001 call dword ptr [winmine!_imp__TranslateMessage (0100114c)] 0100239a 8d45dc lea eax,[ebp-24h] 0100239d 50 push eax 0100239e ff1548110001 call dword ptr [winmine!_imp__DispatchMessageW (01001148)] winmine!WinMain+0x1b4: 010023a4 57 push edi 010023a5 57 push edi 010023a6 8d45dc lea eax,[ebp-24h] 010023a9 57 push edi 010023aa 50 push eax 010023ab ffd6 call esi 010023ad 85c0 test eax,eax 010023af 75c8 jne winmine!WinMain+0x189 (01002379) winmine!WinMain+0x1c1: 010023b1 e886020000 call winmine!CleanUp (0100263c) 010023b6 393d5c510001 cmp dword ptr [winmine!fUpdateIni (0100515c)],edi 010023bc 7405 je winmine!WinMain+0x1d3 (010023c3) winmine!WinMain+0x1ce: 010023be e8e8090000 call winmine!WritePreferences (01002dab) winmine!WinMain+0x1d3: 010023c3 8b45e4 mov eax,dword ptr [ebp-1Ch] winmine!WinMain+0x1d6: 010023c6 5f pop edi 010023c7 5e pop esi 010023c8 5b pop ebx 010023c9 c9 leave 010023ca c21000 ret 10h
可看到其主要作各种初始化工作,呵呵,套路了,比如InitCommonControlsEx,load各种资源,创建窗口,建消息循环等。
有意思的是这行01002201 e8aa180000 call winmine!InitConst (01003ab0)
,调用InitConst。
另外发现winmine!Preferences,从其使用方法来看,猜测winmine!Preferences为一个用户参数的结构。
程序开始时调用010022cb e8f2080000 call winmine!ReadPreferences (01002bc2)
读用户参数。程序结束时调用010023be e8e8090000 call winmine!WritePreferences (01002dab)
写用户参数。
0:000> uf winmine!InitConst winmine!InitConst: 01003ab0 51 push ecx 01003ab1 55 push ebp 01003ab2 56 push esi 01003ab3 57 push edi 01003ab4 ff1584100001 call dword ptr [winmine!_imp__GetTickCount (01001084)] 01003aba 0fb7c0 movzx eax,ax 01003abd 50 push eax 01003abe ff15ac110001 call dword ptr [winmine!_imp__srand (010011ac)] 01003ac4 59 pop ecx 01003ac5 6a20 push 20h 01003ac7 33ed xor ebp,ebp 01003ac9 68a05a0001 push offset winmine!szClass (01005aa0) 01003ace 45 inc ebp 01003acf 55 push ebp 01003ad0 e812ffffff call winmine!LoadSz (010039e7) 01003ad5 6a20 push 20h 01003ad7 68e05a0001 push offset winmine!szTime (01005ae0) 01003adc 6a07 push 7 01003ade e804ffffff call winmine!LoadSz (010039e7) 01003ae3 6a20 push 20h 01003ae5 68405b0001 push offset winmine!szDefaultName (01005b40) 01003aea 6a08 push 8 01003aec e8f6feffff call winmine!LoadSz (010039e7) 01003af1 8b350c110001 mov esi,dword ptr [winmine!_imp__GetSystemMetrics (0100110c)] 01003af7 6a04 push 4 01003af9 ffd6 call esi 01003afb 40 inc eax 01003afc 6a0f push 0Fh 01003afe a3805b0001 mov dword ptr [winmine!dypCaption (01005b80)],eax 01003b03 ffd6 call esi 01003b05 40 inc eax 01003b06 6a06 push 6 01003b08 a3345b0001 mov dword ptr [winmine!dypMenu (01005b34)],eax 01003b0d ffd6 call esi 01003b0f 40 inc eax 01003b10 6a05 push 5 01003b12 a3845b0001 mov dword ptr [winmine!dypBorder (01005b84)],eax 01003b17 ffd6 call esi 01003b19 40 inc eax 01003b1a a3905a0001 mov dword ptr [winmine!dxpBorder (01005a90)],eax 01003b1f 8d44240c lea eax,[esp+0Ch] 01003b23 50 push eax 01003b24 6850590001 push offset winmine!g_hReg (01005950) 01003b29 33ff xor edi,edi 01003b2b 57 push edi 01003b2c 6819000200 push 20019h 01003b31 57 push edi 01003b32 57 push edi 01003b33 57 push edi 01003b34 6840130001 push offset winmine!`string' (01001340) 01003b39 6801000080 push 80000001h 01003b3e ff1510100001 call dword ptr [winmine!_imp__RegCreateKeyExW (01001010)] 01003b44 85c0 test eax,eax 01003b46 7520 jne winmine!InitConst+0xb8 (01003b68) winmine!InitConst+0x98: 01003b48 55 push ebp 01003b49 57 push edi 01003b4a 57 push edi 01003b4b 6a11 push 11h 01003b4d e8d5efffff call winmine!ReadInt (01002b27) 01003b52 ff3550590001 push dword ptr [winmine!g_hReg (01005950)] 01003b58 8bf0 mov esi,eax 01003b5a ff1514100001 call dword ptr [winmine!_imp__RegCloseKey (01001014)] 01003b60 3bf7 cmp esi,edi 01003b62 0f8557010000 jne winmine!InitConst+0x20f (01003cbf) winmine!InitConst+0xb8: 01003b68 53 push ebx 01003b69 6a19 push 19h 01003b6b 6a09 push 9 01003b6d 5e pop esi 01003b6e 56 push esi 01003b6f 56 push esi 01003b70 6a02 push 2 01003b72 e89bfeffff call winmine!ReadIniInt (01003a12) 01003b77 6a1e push 1Eh 01003b79 56 push esi 01003b7a 56 push esi 01003b7b 6a03 push 3 01003b7d a3a8560001 mov dword ptr [winmine!Preferences+0x8 (010056a8)],eax 01003b82 e88bfeffff call winmine!ReadIniInt (01003a12) 01003b87 6a03 push 3 01003b89 57 push edi 01003b8a 57 push edi 01003b8b 57 push edi 01003b8c a3ac560001 mov dword ptr [winmine!Preferences+0xc (010056ac)],eax 01003b91 e87cfeffff call winmine!ReadIniInt (01003a12) 01003b96 bee7030000 mov esi,3E7h 01003b9b 56 push esi 01003b9c 6a0a push 0Ah 01003b9e 6a0a push 0Ah 01003ba0 55 push ebp 01003ba1 66a3a0560001 mov word ptr [winmine!Preferences (010056a0)],ax 01003ba7 e866feffff call winmine!ReadIniInt (01003a12) 01003bac bb00040000 mov ebx,400h 01003bb1 53 push ebx 01003bb2 57 push edi 01003bb3 6a50 push 50h 01003bb5 6a04 push 4 01003bb7 a3a4560001 mov dword ptr [winmine!Preferences+0x4 (010056a4)],eax 01003bbc e851feffff call winmine!ReadIniInt (01003a12) 01003bc1 53 push ebx 01003bc2 57 push edi 01003bc3 6a50 push 50h 01003bc5 6a05 push 5 01003bc7 a3b0560001 mov dword ptr [winmine!Preferences+0x10 (010056b0)],eax 01003bcc e841feffff call winmine!ReadIniInt (01003a12) 01003bd1 6a03 push 3 01003bd3 57 push edi 01003bd4 57 push edi 01003bd5 6a06 push 6 01003bd7 a3b4560001 mov dword ptr [winmine!Preferences+0x14 (010056b4)],eax 01003bdc e831feffff call winmine!ReadIniInt (01003a12) 01003be1 55 push ebp 01003be2 57 push edi 01003be3 55 push ebp 01003be4 6a07 push 7 01003be6 a3b8560001 mov dword ptr [winmine!Preferences+0x18 (010056b8)],eax 01003beb e822feffff call winmine!ReadIniInt (01003a12) 01003bf0 55 push ebp 01003bf1 57 push edi 01003bf2 57 push edi 01003bf3 6a09 push 9 01003bf5 a3bc560001 mov dword ptr [winmine!Preferences+0x1c (010056bc)],eax 01003bfa e813feffff call winmine!ReadIniInt (01003a12) 01003bff 6a02 push 2 01003c01 57 push edi 01003c02 57 push edi 01003c03 6a08 push 8 01003c05 a3c0560001 mov dword ptr [winmine!Preferences+0x20 (010056c0)],eax 01003c0a e803feffff call winmine!ReadIniInt (01003a12) 01003c0f 56 push esi 01003c10 57 push edi 01003c11 56 push esi 01003c12 6a0b push 0Bh 01003c14 a3c4560001 mov dword ptr [winmine!Preferences+0x24 (010056c4)],eax 01003c19 e8f4fdffff call winmine!ReadIniInt (01003a12) 01003c1e 56 push esi 01003c1f 57 push edi 01003c20 56 push esi 01003c21 6a0d push 0Dh 01003c23 a3cc560001 mov dword ptr [winmine!Preferences+0x2c (010056cc)],eax 01003c28 e8e5fdffff call winmine!ReadIniInt (01003a12) 01003c2d 56 push esi 01003c2e 57 push edi 01003c2f 56 push esi 01003c30 a3d0560001 mov dword ptr [winmine!Preferences+0x30 (010056d0)],eax 01003c35 6a0f push 0Fh 01003c37 e8d6fdffff call winmine!ReadIniInt (01003a12) 01003c3c 68d8560001 push offset winmine!Preferences+0x38 (010056d8) 01003c41 6a0c push 0Ch 01003c43 a3d4560001 mov dword ptr [winmine!Preferences+0x34 (010056d4)],eax 01003c48 e83afeffff call winmine!ReadIniSz (01003a87) 01003c4d 6818570001 push offset winmine!Preferences+0x78 (01005718) 01003c52 6a0e push 0Eh 01003c54 e82efeffff call winmine!ReadIniSz (01003a87) 01003c59 6858570001 push offset winmine!Preferences+0xb8 (01005758) 01003c5e 6a10 push 10h 01003c60 e822feffff call winmine!ReadIniSz (01003a87) 01003c65 8b35b0100001 mov esi,dword ptr [winmine!_imp__GetDesktopWindow (010010b0)] 01003c6b ffd6 call esi 01003c6d 50 push eax 01003c6e ff152c110001 call dword ptr [winmine!_imp__GetDC (0100112c)] 01003c74 55 push ebp 01003c75 57 push edi 01003c76 8bd8 mov ebx,eax 01003c78 6a18 push 18h 01003c7a 53 push ebx 01003c7b ff1530100001 call dword ptr [winmine!_imp__GetDeviceCaps (01001030)] 01003c81 33c9 xor ecx,ecx 01003c83 83f802 cmp eax,2 01003c86 0f95c1 setne cl 01003c89 51 push ecx 01003c8a 6a0a push 0Ah 01003c8c e881fdffff call winmine!ReadIniInt (01003a12) 01003c91 53 push ebx 01003c92 a3c8560001 mov dword ptr [winmine!Preferences+0x28 (010056c8)],eax 01003c97 ffd6 call esi 01003c99 50 push eax 01003c9a ff1528110001 call dword ptr [winmine!_imp__ReleaseDC (01001128)] 01003ca0 833db856000103 cmp dword ptr [winmine!Preferences+0x18 (010056b8)],3 01003ca7 5b pop ebx 01003ca8 750a jne winmine!InitConst+0x204 (01003cb4) winmine!InitConst+0x1fa: 01003caa e813fcffff call winmine!FInitTunes (010038c2) 01003caf a3b8560001 mov dword ptr [winmine!Preferences+0x18 (010056b8)],eax winmine!InitConst+0x204: 01003cb4 5f pop edi 01003cb5 5e pop esi 01003cb6 5d pop ebp 01003cb7 83c404 add esp,4 01003cba e9ecf0ffff jmp winmine!WritePreferences (01002dab) winmine!InitConst+0x20f: 01003cbf 5f pop edi 01003cc0 5e pop esi 01003cc1 5d pop ebp 01003cc2 59 pop ecx 01003cc3 c3 ret
可发现其就是从注册表中读取各种参数,
重要的是三组值
雷场宽度:winmine!xBoxMac和winmine!Preferences+0xc
雷场高度:winmine!yBoxMac和winmine!Preferences+0x8
雷场雷数:winmine!Preferences+0x4
好,下面就要看看它是如何初始化雷场的了
(待续)