注意到WinMain的这行0100234d e828130000 call winmine!StartGame (0100367a)
,估计在此启动游戏。
再次运行g,这次断在rand函数,查看堆栈如下。
ChildEBP RetAddr Args to Child 0007fea4 01003946 010036d2 00000009 00000000 msvcrt!rand (FPO: [0,0,0]) 0007fea8 010036d2 00000009 00000000 01005aa0 winmine!Rnd+0x6 (FPO: [1,0,0]) 0007febc 01002352 7c80b529 000a1eff 00000000 winmine!StartGame+0x58 (FPO: [0,0,3]) 0007ff1c 01003f95 01000000 00000000 000a1eff winmine!WinMain+0x162 (FPO: [Non-Fpo]) 0007ffc0 7c816d4f 00011970 7c9318f1 7ffd7000 winmine!WinMainCRTStartup+0x174 (FPO: [Non-Fpo]) 0007fff0 00000000 01003e21 00000000 78746341 kernel32!BaseProcessStart+0x23 (FPO: [Non-Fpo])
果然,是StartGame调用了Rnd,Rnd调用了rand。先反汇编看看StartGame。
0:000> uf winmine!StartGame winmine!StartGame: 0100367a a1ac560001 mov eax,dword ptr [winmine!Preferences+0xc (010056ac)] 0100367f 8b0da8560001 mov ecx,dword ptr [winmine!Preferences+0x8 (010056a8)] 01003685 53 push ebx 01003686 56 push esi 01003687 57 push edi 01003688 33ff xor edi,edi 0100368a 3b0534530001 cmp eax,dword ptr [winmine!xBoxMac (01005334)] 01003690 893d64510001 mov dword ptr [winmine!fTimer (01005164)],edi 01003696 750c jne winmine!StartGame+0x2a (010036a4) winmine!StartGame+0x1e: 01003698 3b0d38530001 cmp ecx,dword ptr [winmine!yBoxMac (01005338)] 0100369e 7504 jne winmine!StartGame+0x2a (010036a4) winmine!StartGame+0x26: 010036a0 6a04 push 4 010036a2 eb02 jmp winmine!StartGame+0x2c (010036a6) winmine!StartGame+0x2a: 010036a4 6a06 push 6 winmine!StartGame+0x2c: 010036a6 5b pop ebx 010036a7 a334530001 mov dword ptr [winmine!xBoxMac (01005334)],eax 010036ac 890d38530001 mov dword ptr [winmine!yBoxMac (01005338)],ecx 010036b2 e81ef8ffff call winmine!ClearField (01002ed5) 010036b7 a1a4560001 mov eax,dword ptr [winmine!Preferences+0x4 (010056a4)] 010036bc 893d60510001 mov dword ptr [winmine!iButtonCur (01005160)],edi 010036c2 a330530001 mov dword ptr [winmine!cBombStart (01005330)],eax winmine!StartGame+0x4d: 010036c7 ff3534530001 push dword ptr [winmine!xBoxMac (01005334)] 010036cd e86e020000 call winmine!Rnd (01003940) 010036d2 ff3538530001 push dword ptr [winmine!yBoxMac (01005338)] 010036d8 8bf0 mov esi,eax 010036da 46 inc esi 010036db e860020000 call winmine!Rnd (01003940) 010036e0 40 inc eax 010036e1 8bc8 mov ecx,eax 010036e3 c1e105 shl ecx,5 010036e6 f684314053000180 test byte ptr winmine!rgBlk (01005340)[ecx+esi],80h 010036ee 75d7 jne winmine!StartGame+0x4d (010036c7) winmine!StartGame+0x76: 010036f0 c1e005 shl eax,5 010036f3 8d843040530001 lea eax,winmine!rgBlk (01005340)[eax+esi] 010036fa 800880 or byte ptr [eax],80h 010036fd ff0d30530001 dec dword ptr [winmine!cBombStart (01005330)] 01003703 75c2 jne winmine!StartGame+0x4d (010036c7) winmine!StartGame+0x8b: 01003705 8b0d38530001 mov ecx,dword ptr [winmine!yBoxMac (01005338)] 0100370b 0faf0d34530001 imul ecx,dword ptr [winmine!xBoxMac (01005334)] 01003712 a1a4560001 mov eax,dword ptr [winmine!Preferences+0x4 (010056a4)] 01003717 2bc8 sub ecx,eax 01003719 57 push edi 0100371a 893d9c570001 mov dword ptr [winmine!cSec (0100579c)],edi 01003720 a330530001 mov dword ptr [winmine!cBombStart (01005330)],eax 01003725 a394510001 mov dword ptr [winmine!cBombLeft (01005194)],eax 0100372a 893da4570001 mov dword ptr [winmine!cBoxVisit (010057a4)],edi 01003730 890da0570001 mov dword ptr [winmine!cBoxVisitMac (010057a0)],ecx 01003736 c7050050000101000000 mov dword ptr [winmine!fStatus (01005000)],1 01003740 e825fdffff call winmine!UpdateBombCount (0100346a) 01003745 53 push ebx 01003746 e805e2ffff call winmine!AdjustWindow (01001950) 0100374b 5f pop edi 0100374c 5e pop esi 0100374d 5b pop ebx 0100374e c3 ret
该函数先判断当前雷区的高度和宽度是否和用户的所选高度和宽度一致,如果不一致就让winmine!xBoxMac = winmine!Preferences+0xc,winmine!yBoxMac = winmine!Preferences+0x8
然后该函数清扫雷区010036b2 e81ef8ffff call winmine!ClearField (01002ed5)
。反汇编ClearField函数
0:000> uf winmine!ClearField winmine!ClearField: 01002ed5 b860030000 mov eax,360h winmine!ClearField+0x5: 01002eda 48 dec eax 01002edb c680405300010f mov byte ptr winmine!rgBlk (01005340)[eax],0Fh 01002ee2 75f6 jne winmine!ClearField+0x5 (01002eda) winmine!ClearField+0xf: 01002ee4 8b0d34530001 mov ecx,dword ptr [winmine!xBoxMac (01005334)] 01002eea 8b1538530001 mov edx,dword ptr [winmine!yBoxMac (01005338)] 01002ef0 8d4102 lea eax,[ecx+2] 01002ef3 85c0 test eax,eax 01002ef5 56 push esi 01002ef6 7419 je winmine!ClearField+0x3c (01002f11) winmine!ClearField+0x23: 01002ef8 8bf2 mov esi,edx 01002efa c1e605 shl esi,5 01002efd 8db660530001 lea esi,winmine!rgBlk+0x20 (01005360)[esi] winmine!ClearField+0x2e: 01002f03 48 dec eax 01002f04 c6804053000110 mov byte ptr winmine!rgBlk (01005340)[eax],10h 01002f0b c6040610 mov byte ptr [esi+eax],10h 01002f0f 75f2 jne winmine!ClearField+0x2e (01002f03) winmine!ClearField+0x3c: 01002f11 8d7202 lea esi,[edx+2] 01002f14 85f6 test esi,esi 01002f16 7421 je winmine!ClearField+0x64 (01002f39) winmine!ClearField+0x43: 01002f18 8bc6 mov eax,esi 01002f1a c1e005 shl eax,5 01002f1d 8d9040530001 lea edx,winmine!rgBlk (01005340)[eax] 01002f23 8d840841530001 lea eax,winmine!rgBlk+0x1 (01005341)[eax+ecx] winmine!ClearField+0x55: 01002f2a 83ea20 sub edx,20h 01002f2d 83e820 sub eax,20h 01002f30 4e dec esi 01002f31 c60210 mov byte ptr [edx],10h 01002f34 c60010 mov byte ptr [eax],10h 01002f37 75f1 jne winmine!ClearField+0x55 (01002f2a) winmine!ClearField+0x64: 01002f39 5e pop esi 01002f3a c3 ret
现在可看到雷场起始于winmine!rgBlk (01005340),它先把雷场全部填0Fh,然后在所选雷区范围填10h。StartGame清完雷场后,调用Rnd来生成地雷。两次Rnd函数调用的参数分别为winmine!xBoxMac和winmine!yBoxMac。来看看Rnd的情况。
0:000> uf winmine!Rnd winmine!Rnd: 01003940 ff15b0110001 call dword ptr [winmine!_imp__rand (010011b0)] 01003946 99 cdq 01003947 f77c2404 idiv eax,dword ptr [esp+4] 0100394b 8bc2 mov eax,edx 0100394d c20400 ret 4
很明显,Rnd把rand的返回值变换到winmine!xBoxMac和winmine!yBoxMac的范围中,这样每两次调用Rnd就生成了一个地雷的坐标。然后StartGame测试该坐标是否为80h,如果不是就跳过该坐标(因为该坐标已经标注了),如果是,就把该坐标置成8Fh,表示是雷。StartGame循环这个过程共winmine!cBombStart(winmine!cBombStart初始值为winmine!Preferences+0x4)次。然后StartGame设置一些状态,启动游戏。