揭開「掃雷(WinMine)」的秘密
揭開「掃雷(WinMine)」的秘密
Written by Black White 「掃雷(WinMine)」大家都是熟悉的,我相信凡是玩過電腦的人很少 沒有玩過「掃雷」這個遊戲的。 但微軟做的這個「掃雷」遊戲中隱藏著一 些秘密,我估計知道的人並不多。 我以前聽到過這樣的傳說:「掃雷」在被輸入某個密碼的情況下,你就 可以輕易知道哪個是地雷,哪個不是地雷。 我正是想證明這個傳說是否真的存在才花了N個小時對掃雷程序進行了 分析,最後發現這個傳說確實是真的,並且還發現了一些另外的秘密。 我分析的目標是Windows98下面的掃雷程序,程序名為「winmine.exe」, 該程序存放在C:\Windows這個資料夾下面,它的長度是24059字元,與它一 起的還有一個ini文件叫winmine.ini。 要分析這樣一個看起來並不大的EXE程序其實並不容易,因為把它反匯 編(unassemble/disassemble)出來的程式碼仍舊是很長的。 我決定採用靜態反 彙編與動態跟蹤相結合的辦法來對它進行一個比較徹底的分析。我使用的靜 態反彙編工具是俄羅斯人Ilfak Guilfanov寫的IDA Pro,該工具軟體的主頁 是http://www.datarescue.com。動態跟蹤工具當然是SoftICE了。 以下這段程式碼是用IDA Pro反彙編出來的,它是WinMine的消息處理程序: :03EE WindowProc: :03EE enter 22h, 0 :03F2 push si :03F3 mov ax, [bp+0Ch] ; AX=WMSG :03F6 dec ax :03F7 dec ax :03F8 jz WM_DESTROY ; WM_DESTROY=2 :03FC dec ax :03FD jz WM_MOVE ; WM_MOVE=3 :03FF sub ax, 3 :0402 jz WM_ACTIVATE ; WM_ACTIVATE=6 :0406 sub ax, 9 :0409 jz WM_PAINT ; WM_PAINT=0Fh :040D sub ax, 7 :0410 jz WM_ENDSESSION ; WM_ENDSESSION=16h :0414 sub ax, 0EAh ; ;這裡對是否為鍵盤消息進行判斷 :0417 IS_WM_KEYDOWN?: ; WM_KEYDOWN=100h :0417 jz WM_KEYDOWN ; 若是鍵盤消息則轉WM_KEYDOWN :041B sub ax, 11h :041E jz WM_COMMAND ; WM_COMMAND=111h :0422 dec ax :0423 jz WM_SYSCOMMAND ; WM_SYSCOMMAND=112h :0425 dec ax :0426 jz WM_TIMER ; WM_TIMER=113h :042A sub ax, 0EDh ; ;這裡對是否為滑鼠移動消息進行判斷 :042D jz WM_MOUSEMOVE ; WM_MOUSEMOVE=200h ; 若是滑鼠移動則轉WM_MOUSEMOVE :0431 dec ax :0432 jz WM_LBUTTONDOWN ; WM_LBUTTONDOWN=201h :0436 dec ax :0437 jz WM_LBUTTONUP ; WM_LBUTTONUP=202h :043B dec ax :043C dec ax :043D jz WM_RBUTTONDOWN ; WM_RBUTTONDOWN=204h :0441 dec ax :0442 jz WM_LBUTTONUP ; WM_RBUTTONUP=205h :0446 dec ax :0447 dec ax :0448 jz WM_MBUTTONDOWN ; WM_MBUTTONDOWN=207h :044C dec ax :044D jz WM_LBUTTONUP ; WM_MBUTTONUP=208h :0451 sub ax, 9 :0454 jz WM_ENTERMENULOOP ; WM_ENTERMENULOOP=211h :0458 dec ax :0459 jz WM_EXITMENULOOP ; WM_EXITMENULOOP=212h :045D OtherMessages: :045D jmp GotoDefWindowProc :0460 ; ----------------------------------------------------------------------- 以上這段程式碼的作用就是對各種消息進行判斷並根據不同消息轉到不同 的分支執行。 這裡我們就重點關注其中的鍵盤消息與滑鼠移動消息的分支轉 移。對於鍵盤消息WM_KEYDOWN,程序將轉移到以下程式碼: 語法:
:0569 WM_KEYDOWN: ; 當有鍵被按下時,轉到此處執行 可能自然轉移到位址05FA處執行。當然,我後來就設法強制改變數MenuFlag 的值,然後看程序繼續執行之後會有什麼後果,結果發現當該變數的值改成1 時功能表居然消失了,而改成2時則功能表重現。但這樣仍舊沒有從根本上解決問 題,因為我仍舊不知道這個變數的值在什麼情況下會自然發生變化,比如在什 麼情況下,MenuFlag的值會等於2。 要搞清楚變數MenuFlag的值究竟在什麼情況下發生變化的,就應想辦法 瞭解這個變數有沒有在程序的其它地方被引用。 這一點用IDA Pro可以輕鬆解 決,因為IDA Pro在反彙編時會指出某個變數在程序中的哪些地方被引用,這 個叫做Cross Reference(交叉引用)。根據MenuFlag的交叉引用,我就找到了 以下這段程式碼與MenuFlag的賦值有關: ;這些是相關的資料定義 :004E aWinmine_ini db 'winmine.ini',0 :005A aDifficulty db 'Difficulty',0 :0065 aMines db 'Mines',0 :006B aHeight db 'Height',0 :0072 aWidth db 'Width',0 :0078 aXpos db 'Xpos',0 :007D aYpos db 'Ypos',0 :0082 aSound db 'Sound',0 :0088 aMark db 'Mark',0 :008D aMenu db 'Menu',0 :0092 aTick db 'Tick',0 :0097 aColor db 'Color',0 :009D aTime1 db 'Time1',0 :00A3 aName1 db 'Name1',0 :00A9 aTime2 db 'Time2',0 :00AF aName2 db 'Name2',0 :00B5 aTime3 db 'Time3',0 :00BB aName3 db 'Name3',0 :00C1 align 2 ;從C語言角度來理解,從位址00C2開始定義的是一個游標 ;陣列,不妨取名為IniItemPtr。 ;其中IniItemPtr[0]等於字串串"Difficulty"的首位址; ; IniItemPtr[1]等於字串串"Mines"的首位址; ; IniItemPtr[2]等於字串串"Height"的首位址; ; ...... ; IniItemPtr[6]等於字串串"Sound"的首位址; ; IniItemPtr[8]等於字串串"Menu"的首位址; ; IniItemPtr[9]等於字串串"Tick"的首位址; ; ...... :00C2 IniItemPtr dw offset aDifficulty ; "Difficulty" :00C4 dw offset aMines ; "Mines" :00C6 dw offset aHeight ; "Height" :00C8 dw offset aWidth ; "Width" :00CA dw offset aXpos ; "Xpos" :00CC dw offset aYpos ; "Ypos" :00CE dw offset aSound ; "Sound" :00D0 dw offset aMark ; "Mark" :00D2 dw offset aMenu ; "Menu" :00D4 dw offset aTick ; "Tick" :00D6 dw offset aColor ; "Color" :00D8 dw offset aTime1 ; "Time1" :00DA dw offset aName1 ; "Name1" :00DC dw offset aTime2 ; "Time2" :00DE dw offset aName2 ; "Name2" :00E0 dw offset aTime3 ; "Time3" :00E2 dw offset aName3 ; "Name3" ;-------------------------------------------------------------- ;以下函數用來從winmine.ini讀取各項的值,如Width、Menu、Sound、Tick :2072 ReadWinMineIni proc near :2072 push 2 ; 2是游標陣列IniItemPtr的上標, ; IniItemPtr[2]的位址=2*2+C2=00C6 ; 00C6 dw offset aHeight; "Height" :2074 push 8 :2076 push 8 :2078 cmp word_2464, 1 :207D sbb ax, ax :207F and ax, 9 :2082 add ax, 10h :2085 push ax :2086 call sub_1FBE ; 讀取winmine.ini中Height的值 :2089 mov word_2558, ax :208C mov Height, ax ;-------------------------------------------------------------- :208F push 3 ; 3*2+C2=00C8 ; 00C8 dw offset aWidth ; "Width" :2091 push 8 :2093 push 8 :2095 push 1Eh :2097 call sub_1FBE :209A mov word_255A, ax :209D mov Width, ax :20A0 push 0 ; 0*2+C2=00C2 ; 00C2 IniItemPtr dw offset aDifficulty :20A2 push 0 :20A4 push 0 :20A6 push 3 :20A8 call sub_1FBE ; 讀取winmine.ini中Difficulty的值 :20AB mov word_2554, ax ;-------------------------------------------------------------- :20AE push 1 ; 1*2+C2=00C4 ; 00C4 dw offset aMines ; "Mines" :20B0 push 0Ah :20B2 push 0Ah :20B4 push 3E7h :20B7 call sub_1FBE ; 讀取Mines的值 :20BA mov word_2556, ax ;-------------------------------------------------------------- :20BD push 4 ; 4*2+C2=00CA ; 00CA dw offset aXpos ; "Xpos" :20BF push 50h ; 'P' :20C1 push 0 :20C3 push 400h :20C6 call sub_1FBE ; 讀取Xpos的值 :20C9 mov word_255C, ax ;-------------------------------------------------------------- :20CC push 5 ; 5*2+C2=00CC ; 00CC dw offset aYpos ; "Ypos" :20CE push 50h ; 'P' :20D0 push 0 :20D2 push 400h :20D5 call sub_1FBE ; 讀取Ypos的值 :20D8 mov word_255E, ax ;-------------------------------------------------------------- :20DB push 6 ; 6*2+C2=00CE ; 00CE dw offset aSound ; "Sound" :20DD push 0 :20DF push 0 :20E1 push 3 :20E3 call sub_1FBE ; 讀取Sound的值 :20E6 mov SoundFlag, ax ;-------------------------------------------------------------- :20E9 push 7 ; 7*2+C2=00D0 ; 00D0 dw offset aMark ; "Mark" :20EB push 1 :20ED push 0 :20EF push 1 :20F1 call sub_1FBE ; 讀取Mark的值 :20F4 mov word_2562, ax ;-------------------------------------------------------------- :20F7 push 9 ; 9*2+C2=00D4 ; 00D4 dw offset aTick ; "Tick" :20F9 push 0 :20FB push 0 :20FD push 1 :20FF call sub_1FBE ; 讀取Tick的值 :2102 mov TickFlag, ax ;-------------------------------------------------------------- :2105 push 8 ; 8*2+C2=00D2 ; 00D2 dw offset aMenu ; "Menu" :2107 push 0 :2109 push 0 :210B push 2 :210D call sub_1FBE ; 讀取Menu的值 :2110 mov MenuFlag, ax ;-------------------------------------------------------------- :2113 push 0Bh ; B*2+C2=00D8 ; 00D8 dw offset aTime1 ; "Time1" :2115 push 3E7h :2118 push 0 :211A push 3E7h :211D call sub_1FBE ; 讀取Time1的值 :2120 mov word_256A, ax ;-------------------------------------------------------------- :2123 push 0Dh ; D*2+C2=00DC ; 00DC dw offset aTime2 ; "Time2" :2125 push 3E7h :2128 push 0 :212A push 3E7h :212D call sub_1FBE ; 讀取Time2的值 :2130 mov word ptr dword_256C, ax ;-------------------------------------------------------------- :2133 push 0Fh ; F*2+C2=00E0 ; 00E0 dw offset aTime3 ; "Time3" :2135 push 3E7h :2138 push 0 :213A push 3E7h :213D call sub_1FBE ; 讀取Time3的值 :2140 mov word ptr dword_256C+2, ax ;-------------------------------------------------------------- :2143 push 0Ch ; C*2+C2=00DA ; 00DA dw offset aName1 ; "Name1" :2145 push ds :2146 push offset byte_2570 ; LPSTR :2149 call sub_204A ; 讀取Name1的值 ;-------------------------------------------------------------- :214C push 0Eh ; E*2+C2=00DE ; 00DE dw offset aName2 ; "Name2" :214E push ds :214F push offset byte_25B0 ; LPSTR :2152 call sub_204A ; 讀取Name2的值 ;-------------------------------------------------------------- :2155 push 10h ; 10*2+C2=00E2 ; 00E2 dw offset aName3 ; "Name3" :2157 push ds :2158 push offset byte_25F0 ; LPSTR :215B call sub_204A ; 讀取Name3的值 ;-------------------------------------------------------------- :215E mov ax, word_2530 :2161 mov word_2568, ax :2164 or ax, ax :2166 jz loc_2175 :2168 push 0Ah ; A*2+C2=00D6 ; 00D6 dw offset aColor ; "Color" :216A push ax :216B push 0 :216D push 1 :216F call sub_1FBE ; 讀取Color的值 :2172 mov word_2568, ax ;-------------------------------------------------------------- :2175 loc_2175: :2175 cmp SoundFlag, 3; 若Sound不等於3則不理它 :217A jnz locret_2182 :217C call EnableSound ; 若Sound等於3則開聲音 :217F mov SoundFlag, ax :2182 :2182 locret_2182: :2182 retn :2182 ReadWinMineIni endp ;-------------------------------------------------------------- ;-------------------------------------------------------------- ;以下這個函數sub_1FBE被上面的函數ReadWinMineIni使用。 ;函數sub_1FBE的作用是讀取winmine.ini文件中某一項的值。 :1FBE sub_1FBE proc near :1FBE :1FBE :1FBE arg_0 = word ptr 4 :1FBE arg_2 = word ptr 6 :1FBE arg_4 = word ptr 8 :1FBE arg_6 = word ptr 0Ah :1FBE :1FBE enter 4, 0 :1FC2 push si :1FC3 push ds :1FC4 push offset byte_2532 ; LPCSTR :1FC7 mov bx, [bp+arg_6] ; BX=游標陣列IniItemPtr的上標 :1FCA add bx, bx ; 上標*2 :1FCC push ds :1FCD push IniItemPtr[bx] ; 等於某一項名的首位址 :1FD1 push [bp+arg_4] ; int :1FD4 push ds :1FD5 push offset aWinmine_ini ; 指向"winmine.ini" :1FD8 mov si, bx :1FDA call GETPRIVATEPROFILEINT ; 讀取某一項的整數值 :1FDF cmp ax, [bp+arg_0] :1FE2 jg loc_1FFD :1FE4 push ds :1FE5 push offset byte_2532 ; LPCSTR :1FE8 mov bx, si :1FEA push ds :1FEB push IniItemPtr[bx] ; LPCSTR :1FEF push [bp+arg_4] ; int :1FF2 push ds :1FF3 push offset aWinmine_ini ; LPCSTR :1FF6 call GETPRIVATEPROFILEINT :1FFB jmp short loc_2000 :1FFD ; ----------------------------------------------------------------------- :1FFD loc_1FFD: :1FFD mov ax, [bp+arg_0] :2000 loc_2000: :2000 cmp ax, [bp+arg_2] :2003 jge loc_200A :2005 mov ax, [bp+arg_2] :2008 jmp short loc_2045 :200A ; ----------------------------------------------------------------------- :200A loc_200A: :200A push ds :200B push offset byte_2532 ; LPCSTR :200E mov bx, [bp+arg_6] :2011 add bx, bx :2013 push ds :2014 push IniItemPtr[bx] ; LPCSTR :2018 push [bp+arg_4] ; int :201B push ds :201C push offset aWinmine_ini ; LPCSTR :201F mov si, bx :2021 call GETPRIVATEPROFILEINT :2026 cmp ax, [bp+arg_0] :2029 jg loc_2042 :202B push ds :202C push offset byte_2532 ; LPCSTR :202F push ds :2030 push IniItemPtr[si] ; LPCSTR :2034 push [bp+arg_4] ; int :2037 push ds :2038 push offset aWinmine_ini ; LPCSTR :203B call GETPRIVATEPROFILEINT :2040 jmp short loc_2045 :2042 ; ----------------------------------------------------------------------- :2042 loc_2042: :2042 mov ax, [bp+arg_0] :2045 loc_2045: :2045 pop si :2046 leave :2047 retn 8 :2047 sub_1FBE endp 我把上面程式碼中的第一個函數取名為ReadWinMineIni是因為它的作用就 是讀取掃雷程序的winmine.ini文件中的各項。winmine.ini文件中允許包含 的各項包括: Difficulty Mines Height Width Xpos Ypos Sound Mark Menu Tick Color Time1 Name1 Time2 Name2 Time3 Name3 開啟winmine.ini文件看一下,發現裡面並不包括上面列出的所有項, 其中以下三項是沒有的: Sound Menu Tick 好,那就試著把這3項給它加上。在用記事本或者其它文本編輯器開啟 winmine.ini之後,加上以下3行: Sound=3 Menu=1 Tick=1 現在再重新雙按winmine.exe執行掃雷。我們首先會發現掃雷的功能表消 失了,這是Menu的作用;當你開始挖雷之後,隨著秒數的增加,你會聽到 「滴滴」的聲音,這個就是Tick的作用;當你不小心挖爆一個地雷時,你會 聽到「嘟啊嘟啊」的聲音,這個就是Sound的作用。 接下去再來試試功能鍵的作用:當你按F6時,功能表重新出現了;當你按 F5時功能表消失;當你按F4時聲音消失,再按F4聲音重新開啟。 小結一下,Sound、Menu、Tick這3項的值代表的含義如下: Sound=3 開啟聲音; Sound=2 關閉聲音; Menu=1 隱藏功能表; Menu=2 顯示功能表; Tick=0 關閉「滴滴」聲; Tick=1 開啟「滴滴」聲; F4、F5、F6這3個功能鍵的作用如下: F4 開啟/關閉聲音; F5 隱藏功能表; F6 顯示功能表; 現在,再回過頭來關注一下在輸入了正確密碼"XYZZY"之後怎樣輕易地 獲知滑鼠所指的位置下面是否有地雷。 那就再來看一段程式碼,這段程式碼與滑鼠移動的消息有關: :06A9 WM_MOUSEMOVE: ; 當滑鼠移動時轉到此處執行 :06A9 cmp LButtonDownFlag, 0 ; 若滑鼠左鍵沒有按下,則 :06AE jz IsPasswordOk ; 轉IsPasswordOk判斷密碼 ;若滑鼠移動時左鍵被按下,則繼續執行 :06B0 test byte ptr word_2376, 1 :06B5 jz loc_773 :06B9 mov ax, [bp+6] :06BC add ax, 4 :06BF shr ax, 4 :06C2 push ax :06C3 mov ax, [bp+8] :06C6 sub ax, 27h :06C9 shr ax, 4 :06CC push ax :06CD :06CD loc_6CD: :06CD call sub_1154 :06D0 jmp GotoDefWindowProc :06D3 ; --------------------------------------------------------------------------? :06D3 ; 當滑鼠移動時左鍵沒有按下,則轉到此處執行 :06D3 IsPasswordOk: :06D3 cmp PassCount, 0 ; 若已輸入密碼字串的個數為0, :06D8 jz GotoDefWindowProc ; 則轉預設消息處理,不理會 :06DC cmp PassCount, 5 ; 若已輸入密碼字串的個數≠5 :06E1 jnz loc_6E9 ; 則轉loc_6E9 ; ;此時,已輸入密碼字串個數=5,即密碼輸入正確 :06E3 test byte ptr [bp+0Ah], 8 ; 若滑鼠移動同時Ctrl鍵按下 :06E7 jnz CtrlIsHeldDown ; 則轉CtrlIsHeldDown ; 注意這裡需要兩個條件同時 ; 成立:密碼正確、Ctrl按下 ;注意這裡有兩種情形: ;(1) 如果輸入密碼字串個數不等於5時轉到此處(回顧一下前面的程式碼, ; 當輸入正確密碼之後再按Shift鍵會使PassCount=20) ;(2) 如果輸入密碼字串個數等於5但Ctrl沒有按下時也轉到此處 :06E9 loc_6E9: :06E9 cmp PassCount, 5 ; 若密碼字串個數小於等於5 :06EE jle GotoDefWindowProc ; 則轉預設消息處理。 ; 情形(2)符合此條件,所以 ; 在滑鼠移動時若Ctrl鍵沒有 ; 按下則不予理會。 ;凡屬以下兩種情形之一,則轉此處執行: ;(A) 密碼輸入正確(字串個數=5)並且滑鼠移動時Ctrl鍵被按下 ;(B) 密碼輸入正確(字串個數>5): 輸入完正確密碼後按一次Shift鍵使PassCount=20 :06F2 :06F2 CtrlIsHeldDown: :06F2 mov ax, [bp+6] ; AX=coordinate X :06F5 add ax, 4 :06F8 shr ax, 4 :06FB mov CoordinateX, ax ; 計算X坐標 :06FE mov cx, [bp+8] ; CX=Coordinate Y :0701 sub cx, 27h :0704 shr cx, 4 :0707 mov CoordinateY, cx ; 計算Y坐標 :070B or ax, ax :070D jle InvalidCoordinate :070F or cx, cx :0711 jle InvalidCoordinate :0713 cmp ax, Width ; 判斷X坐標有否超過寬度 :0717 jg InvalidCoordinate :0719 cmp cx, Height ; 判斷Y坐標有否超過高度 :071D jle ValidCoordinate :071F InvalidCoordinate: :071F jmp GotoDefWindowProc :0722 ; ----------------------------------------------------------------------- ;若坐標正確則轉此處執行 :0722 ValidCoordinate: :0722 call GETDESKTOPWINDOW ; 取得桌面視窗的句柄(handle) :0727 push ax :0728 call GETDC :072D mov [bp-2], ax :0730 push ax :0731 push 0 :0733 push 0 :0735 mov si, CoordinateY ; 判斷滑鼠所指 :0739 shl si, 5 ; 位置下面是否 :073C mov bx, CoordinateX ; 有地雷, :0740 test byte ptr [bx+si+460h], 80h ; 若沒有地雷, :0745 jz it_is_not_a_mine ; 則轉 ; ;若有地雷則轉此處執行 :0747 it_is_a_mine: ; AX=0, DX=0 :0747 xor ax, ax ; RGB=0表示黑色 :0749 cwd ; means to show a black dot :074A jmp short ShowDot ; 在視窗左上角顯示一個黑點 :074C ; ----------------------------------------------------------------------- :若沒有地雷則轉此處執行 :074C it_is_not_a_mine: :074C mov ax, 0FFFFh ; AX=0FFFFh, DX=00FFh :074F mov dx, 0FFh ; RGB=255表示白色 :074F ; 在視窗左上角顯示一個亮點 :0752 ShowDot: :0752 push dx :0753 push ax :0754 call SETPIXEL ; 畫一個點! :0759 call GETDESKTOPWINDOW; 重新獲取桌面視窗句柄 :075E push ax :075F push word ptr [bp-2] :0762 call RELEASEDC ; 釋放DC :0767 jmp GotoDefWindowProc :076A ; -----------------------------------------------------------------------[/ 通過對上面這段滑鼠移動消息處理程式碼的分析,我們可以得出以下結論: 在正確輸入5個字串的密碼"XYZZY"之後,如果想在滑鼠移動時知道當前 滑鼠所指位置底下是否有地雷,可以有兩個辦法: 1 當移動滑鼠時,左手按住Ctrl鍵不要放; 2 直接按一下Shift鍵,以後移動滑鼠時不需要按住Ctrl鍵 不管是哪種辦法,在滑鼠移動時,你只要仔細觀察桌面左上角有沒有黑 點,如果有黑點則表示滑鼠底下是地雷,如果是亮點則滑鼠底下沒有地雷。 要注意第2種辦法中的Shift是一個開關鍵,按奇數次開啟探查功能,按偶數 次關閉探查功能。 在完成了上述分析之後,我發現只有在Windows3.1下面才能實現地雷探 查功能,而在Windows98下面則不行,也就是說,在正確輸入密碼之後,我看 不到桌面視窗左上角有黑點。 後來發現毛病出在GETDESKTOPWINDOW這個API上面。掃雷程序原先是執行 在Windows3.1上面的,它是NE格式的EXE,而不是現在一般的PE格式。即它是 一個16位的Windows程序,而非32位的Windows程序。所以它存在了一個相容 性的問題,原先在Windows3.1的桌面視窗上可以畫點,但在Windows98或者更 高版本的Windows XP上面則不行。我想正是由於這個原因,這個傳說中的掃雷 密碼才慢慢失傳而不為人所知。 那麼,現在該怎麼辦? 辦法還是有的,只能改程序了。只要把上面這段 滑鼠移動消息處理程式碼中的兩個API使用GETDESKTOPWINDOW改成另外一個API 使用GETACTIVEWINDOW就可以了。GETACTIVEWINDOW的意思就是獲取當前活動 視窗的句柄,當你在玩掃雷時,活動視窗當然就是WinMine的視窗了。 所以, 這樣一來,當我們開啟地雷探查功能時,我們看到的黑點與亮點不再顯示在 桌面視窗的左上角,而是在掃雷視窗的左上角。 具體位置請看下圖: 掃雷左上角 要改NE格式的EXE程序並不是件容易的事,因為我對這種格式並不熟悉。 後來是先到下面這個位址下載了一份NE格式我的文件: http://www.wotsit.org/filestore/windoc.zip 仔細研讀了許久,終於設法把winmine.exe修理好了。修改步驟如下: 用UltraEdit或者類似的EXE文件編輯器開啟winmine.exe,搜尋以下16進 制串: 02 00 1E 01 並替換為: 02 00 3C 00 實際只改了兩個字元,即把1E改成3C,把01改成00。 總結一下,「掃雷」除了有探查地雷的密碼,還有Menu、Sound、Tick等 秘密。 如果你想試驗Menu、Sound和Tick的效果,請用記事本或其它文本編輯器 開啟winmine.ini,增加以下3行並儲存: Sound=3 Menu=1 Tick=1 執行「掃雷」程序,按F4可以關閉/開啟聲音,按F6顯示功能表,按F5隱藏功能表。 如果你想試驗探查地雷的功能,請先按上面提到的步驟修改winmine.exe。 修改完之後執行「掃雷」程序,按順序輸入"XYZZY"這5個字母,然後按一下 Shift鍵放掉或者按住Ctrl鍵不放,同時移動滑鼠,觀察「掃雷」視窗左上角, 如果有黑點則滑鼠底下是地雷,若是亮點則滑鼠底下沒地雷。 「多羅羅羅」,啊,終於掃完了。 P.S.:附件是修改過的winmine.exe和winmine.ini的壓縮包。 |
高呀....利害
|
真是天才
|
好強啊!!拍拍手
|
好屌阿
|
所謂的破解就是這麼一回事
|
真是太崇拜了!
|
所有時間均為台北時間。現在的時間是 07:28 PM。 |
Powered by vBulletin® 版本 3.6.8
版權所有 ©2000 - 2024, Jelsoft Enterprises Ltd.
『服務條款』
* 有問題不知道該怎麼解決嗎?請聯絡本站的系統管理員 *