• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            小默

            [zz]ASM - 揭開“掃雷(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:   ; 當有鍵被按下時,轉到此處執行

            :0569               mov     ax, [bp+0Ah]

            :056C               cmp     ax, 75h         ; AL==75h (F6 Key)

            :056F               jz      IsF6Key         ; 若是F6鍵則轉IsF6Key

            :0573               ja      CheckPassword

            :0575               sub     al, 10h         ; AL==10h (Shift Key)

            :0577               jz      IsShiftKey      ; 若是Shift鍵則轉IsShiftKey

            :057B               sub     al, 0Bh         ; AL==1Bh (Esc Key)

            :057D               jz      IsEscKey        ; 若是Esc鍵則轉IsEscKey

            :057F               sub     al, 58h ; 'X'   ; AL==73h (F4 Key)

            :0581               jz      IsF4Key         ; 若是F4鍵則轉IsF4Key

            :0583               dec     al              ; AL==74h (F5 Key)

            :0585               jz      IsF5Key         ; 若是F5鍵則轉IsF5Key

            ;

            ;若不是以上這些鍵,則接下去判斷輸入的是否為密碼

            :0587 CheckPassword:

            :0587               cmp     PassCount, 5    ; 若密碼字符個數大于等于5,

            :058C               jge     GotoDefWindowProc ; 則不理它

            ;若已經輸入的密碼字符個數小于5,則繼續判斷

            :0590               mov     al, [bp+0Ah]    ; AL=剛輸入的字符

            :0593               mov     bx, PassCount   ; BX=已輸入的字符個數

            :0597               cmp     byte ptr password[bx], al ; "XYZZY"

                                ; 判斷剛輸入的字符是否為正確的密碼字符

            :059B               jnz     ClearPassword   ; 如果不正確則清除輸入

            :059D               inc     PassCount       ; 如果正確,則密碼字符個數+1

            :05A1               jmp     GotoDefWindowProc

            :05A4 ;

            -----------------------------------------------------------------------

            :05A4 IsEscKey:     ; 這里是對Esc鍵進行處理

            :05A4               or      byte ptr word_2376, 4

            :05A9               push    hwndMain

            :05AD               push    large 112F020h

            :05B3               push    0

            :05B5               push    0

            :05B7               call    POSTMESSAGE

            :05BC               jmp     GotoDefWindowProc

            :05BF ;

            -----------------------------------------------------------------------

            :05BF IsF4Key:      ; 這里是對F4鍵進行處理,它的功能就是開/關聲音。

            :05BF               cmp     SoundFlag, 1      ; 若聲音標志小于等于1,則

            :05C4               jle     GotoDefWindowProc ; 不理它,轉缺省消息處理

            :05C8               cmp     SoundFlag, 3      ; 若聲音標志不等于3(等于2),

            :05CD               jnz     SoundFlagIs2      ; 即無聲時,則開聲音。

            ;若聲音標志等于3,即有聲時,則關聲音

            :05CF SoundFlagIs3:

            :05CF               call    DisableSound      ; 開聲音

            :05D2               mov     SoundFlag, 2      ; 若原先有聲,則設成無聲

            :05D8               jmp     GotoDefWindowProc

            :05DB ;

            -----------------------------------------------------------------------

            :05DB SoundFlagIs2:

            :05DB               call    EnableSound       ; 關聲音

            :05DE               mov     SoundFlag, ax     ; 若原先無聲,則設成有聲

            :05E1               jmp     GotoDefWindowProc

            :05E4 ;

            -----------------------------------------------------------------------

            :05E4 IsF5Key:      ; 這里是對F5鍵進行處理,它的功能是隱藏菜單。

            :05E4               cmp     MenuFlag, 0     ; 若菜單標志為0則不理它

            :05E9               jz      LetsGotoDefWindowProc

            ;若菜單標志不等于0,則隱藏菜單

            :05EB HideMenu:                             ; 1 means to hide menu

            :05EB               push    1               ; 參數1表示隱藏菜單

            :05ED ToHideShowMenu:

            :05ED               call    HideShowMenu    ; 調用隱藏/顯示菜單函數

            :05F0               jmp     GotoDefWindowProc

            :05F3 ;

            -----------------------------------------------------------------------

            :05F3 IsF6Key:      ; 這里是對F6鍵進行處理,它的功能是顯示菜單。

            :05F3               cmp     MenuFlag, 0     ; 若菜單標志為0則不理它

            :05F8               jz      LetsGotoDefWindowProc

            ;若菜單標志不等于0,則顯示菜單

            :05FA               push    2               ; 參數2表示顯示菜單

            :05FC               jmp     short ToHideShowMenu

            :05FE ;

            -----------------------------------------------------------------------

            :05FE IsShiftKey:   ; 這里是對Shift鍵進行處理,它的功能是對PassCount

            ;                   ; 這個變量值進行切換,若原值為5則變成20(14h),若

            ;                   ; 原值為20(14h),則變成5。

            :05FE               cmp     PassCount, 5

            :0603               jl      LetsGotoDefWindowProc

            :0605               xor     byte ptr PassCount, 14h

            :060A               jmp     GotoDefWindowProc

            :060D ;

            -----------------------------------------------------------------------

            :060D ClearPassword:

            :060D               mov     PassCount, 0

            :0613               jmp     GotoDefWindowProc

            :0616 ;

            -----------------------------------------------------------------------

            :0616 WM_DESTROY:

            :0616               push    hwndMain

            :061A               push    1

            :061C               call    KILLTIMER

            :0621               push    2

            :0623               push    0

            :0625               push    0

            :0627               call    sub_1734

            :062A               push    0

            :062C               call    POSTQUITMESSAGE

            :0631 WM_ENDSESSION:

            :0631               cmp     word_23AC, 0

            :0636               jnz     loc_63B

            :0638 LetsGotoDefWindowProc:

            :0638               jmp     GotoDefWindowProc

            :063B ;

            ------------------------------------------------------------------------

            --

            ?

            ;這里是與password有關的一個變量及一個數組

            :0034 PassCount     dw 0

            :0036 password      db 'XYZZY',0

             

                上面這段代碼是對鍵盤輸入進行處理,主要涉及到以下這些鍵:

                    F4、F5、F6、Shift、其它鍵

            其中F4的作用是開關聲音,F5的作用是隱藏菜單,F6的作用是顯示菜單,

            Shift鍵的作用是對變量PassCount的值進行切換,它的實際作用將在后面

            部分分析。其它鍵其實就是用來輸入密碼的鍵,比如英文字母A到Z,數字

            鍵0到9等。根據上面代碼,我們已經知道,那傳說中的密碼就是:

                    XYZZY

            這樣5個字母。當你在玩“掃雷”時,只要連續輸入這5個字母,那么掃雷

            的阿里巴巴之門就從此為你打開。

             

                現在先暫時不提在輸入了密碼之后如何去“照”出哪個是地雷,哪個

            不是地雷。這里先講一下F4、F5、F6以及Shift鍵的功能是如何分析出來的

            的。

             

                事實上,如果光是根據上面的代碼是根本無法確定這些鍵的功能的,因為

            IDA Pro的功能就算再強,它也不能達到理解代碼甚至猜測代碼作用的地步。

            上面代碼中所涉及到的變量名如PassCount、password、SoundFlag、MenuFlag

            都是我根據分析手工加上去的,另外代碼中提到的一些函數、標號名如:

                DisableSound、EnableSound、HideShowMenu

                CheckPassword、ClearPassword、GotoDefWindowProc

            也都是我根據自己的理解加上的。只有那些全部是大寫字母組成的函數名如:

                POSTMESSAGE、KILLTIMER、POSTQUITMESSAGE

            才是IDA Pro分析出來的。

             

                要確定這些鍵的功能肯定需要進行動態跟蹤。起先用SoftICE跟蹤以上

            這段代碼時,仍舊看不出這些鍵的作用,因為總是在跟蹤到一定時候就會發

            現某些相關變量的初值為0。例如,摘錄上面代碼中與F6鍵有關的部分:

            :05F3 ;

            -----------------------------------------------------------------------

            :05F3 IsF6Key:      ; 這里是對F6鍵進行處理,它的功能是顯示菜單。

            :05F3               cmp     MenuFlag, 0     ; 若菜單標志為0則不理它

            :05F8               jz      LetsGotoDefWindowProc

            ;若菜單標志不等于0,則顯示菜單

            :05FA               push    2               ; 參數2表示顯示菜單

            :05FC               jmp     short ToHideShowMenu

            :05FE ;

            -----------------------------------------------------------------------

            在跟蹤到地址05F3時,我發現MenuFlag這個變量的值一直是0,這樣程序就不

            可能自然轉移到地址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"之后,如果想在鼠標移動時知道當前

            鼠標所指位置底下是否有地雷,可以有兩個辦法:

                ① 當移動鼠標時,左手按住Ctrl鍵不要放

                ② 直接按一下Shift鍵,以后移動鼠標時不需要按住Ctrl鍵

                不管是哪種辦法,在鼠標移動時,你只要仔細觀察桌面左上角有沒有黑

            點,如果有黑點則表示鼠標底下是地雷,如果是亮點則鼠標底下沒有地雷。

            要注意第②種辦法中的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鍵不放,同時移動鼠標,觀察“掃雷”窗口左上角,

            如果有黑點則鼠標底下是地雷,若是亮點則鼠標底下沒地雷。

                “多羅羅羅”,啊,終于掃完了。

            posted on 2009-12-26 14:29 小默 閱讀(1359) 評論(0)  編輯 收藏 引用 所屬分類: Language

            導航

            統計

            留言簿(13)

            隨筆分類(287)

            隨筆檔案(289)

            漏洞

            搜索

            積分與排名

            最新評論

            閱讀排行榜

            久久精品无码专区免费| 99久久婷婷免费国产综合精品| 97r久久精品国产99国产精| 久久久久久久久久久精品尤物| 无码任你躁久久久久久| 久久精品夜色噜噜亚洲A∨| 国产亚州精品女人久久久久久 | 色偷偷888欧美精品久久久| 狠狠88综合久久久久综合网| 久久亚洲精品成人av无码网站| 久久精品国产亚洲av水果派| 久久精品国产亚洲AV无码娇色| 久久亚洲中文字幕精品有坂深雪| 久久99国产综合精品| 亚洲精品国产成人99久久| 久久久久亚洲AV无码去区首| 伊人色综合久久天天网| 少妇久久久久久久久久| 国产精品一区二区久久不卡| 久久亚洲综合色一区二区三区| 久久激情五月丁香伊人| 亚洲狠狠婷婷综合久久蜜芽 | 久久精品黄AA片一区二区三区| 国产亚洲精久久久久久无码| 国产高清国内精品福利99久久| 久久97久久97精品免视看秋霞| 久久久国产视频| 99国产精品久久| 亚洲欧美成人久久综合中文网 | 久久91精品国产91| 久久精品一区二区三区不卡| 色婷婷久久久SWAG精品| 国产精品久久久久久久久| 久久精品无码一区二区日韩AV| 久久精品国产99国产精品导航| 久久91精品久久91综合| 久久婷婷五月综合97色直播| 99国产欧美久久久精品蜜芽| 久久久久久久久66精品片| 四虎国产精品免费久久5151| 一本色道久久88精品综合|