飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 7024|回复: 14

[转贴] 揭开“扫雷(WinMine)”的秘密

[复制链接]
  • TA的每日心情
    慵懒
    2019-3-12 17:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

    发表于 2010-5-13 10:43:01 | 显示全部楼层 |阅读模式
    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键有关的部分:
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2019-3-12 17:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

     楼主| 发表于 2010-5-13 10:43:48 | 显示全部楼层
    代码  
    :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
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2019-3-12 17:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

     楼主| 发表于 2010-5-13 10:47:12 | 显示全部楼层
    我把上面代码中的第一个函数取名为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的窗口了。所以,
    这样一来,当我们开启地雷探查功能时,我们看到的黑点与亮点不再显示在
    桌面窗口的左上角,而是在扫雷窗口的左上角。具体位置请看下图:



    BlackWhite

    I'm a nutcracker
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2019-3-12 17:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

     楼主| 发表于 2010-5-13 10:48:45 | 显示全部楼层
    要改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的压缩包。
                  (全文完)
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2019-3-12 17:25
  • 签到天数: 3 天

    [LV.2]偶尔看看I

     楼主| 发表于 2010-5-13 14:05:26 | 显示全部楼层
    标 题: 【分享】微软扫雷PEDIY尝试
    作 者: sungy
    时 间: 2008-12-24,13:41:59
    链 接: http://bbs.pediy.com/showthread.php?t=79338

    【文章标题】: 微软扫雷PEDIY尝试
    【文章作者】: 浮海观云
    【作者邮箱】: cc_sz@163.com
    【作者主页】: www.ifgoogle.com
    【作者QQ号】: 250341858
    【软件名称】: windows自带
    【软件大小】: 120K
    【下载地址】: 电脑上就有啊
    【加壳方式】: 无
    【保护方式】: 无
    【编写语言】: VC
    【使用工具】: OD,Winhex,ResHacker.exe
    【操作平台】: WINDOWS
    【软件介绍】: 扫雷就是电脑上那个扫雷啊
    【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
    --------------------------------------------------------------------------------
    【详细过程】
          扫雷程序原来就有隐藏的作弊模式(xyzzy),现在通过PEDIY来修改一下玩个新花样,功力有限只做点小改动,剪刀加糨糊
      让扫雷程序可以发声音报警。
          先用OD分析程序,找到下面几处作弊点:
          1,可以秒表停止的地方
      01002FE0  /$  833D 64510001>CMP DWORD PTR DS:[1005164],0
      01002FE7  |.  74 1E         JE SHORT winmine.01003007
      01002FE9  |.  813D 9C570001>CMP DWORD PTR DS:[100579C],3E7
      01002FF3  |.  7D 12         JGE SHORT winmine.01003007
      01002FF5      FF05 9C570001 INC DWORD PTR DS:[100579C]               ;  加1秒的地方
      01002FFB  |.  E8 B5F8FFFF   CALL winmine.010028B5
      01003000  |.  6A 01         PUSH 1
      01003002  |.  E8 E6080000   CALL winmine.010038ED
      01003007  \>  C3            RETN
      
          2,下面比较连续输入的字符是不是"XYZZY",是则计数加1,否则变0,当DWORD PTR DS:[1005154]=5时进入作弊等
      待模式,以后按下shift键可以开关作弊
      01001CBF  |.  66:8B0C45 34500001   MOV CX,WORD PTR DS:[EAX*2+1005034]                  ;  作弊码XYZZY(宽字符)
      01001CC7  |.  66:2B4D 10           SUB CX,WORD PTR SS:[EBP+10]
      01001CCB  |.  40                   INC EAX
      01001CCC  |.  66:F7D9              NEG CX
      01001CCF  |.  1BC9                 SBB ECX,ECX
      01001CD1  |.  F7D1                 NOT ECX
      01001CD3  |.  23C8                 AND ECX,EAX
      01001CD5      890D 54510001        MOV DWORD PTR DS:[1005154],ECX                      ;  当DS:[1005154] 里的值为5时,进入作弊模式
      01001CDB      E9 C9040000          JMP winmine.010021A9
      
      010020C1  |> \A1 54510001   MOV EAX,DWORD PTR DS:[1005154]           ;  作弊是否
      
      01001D40  |> \833D 54510001>CMP DWORD PTR DS:[1005154],5             ;  Case 10 of switch 01001C9D
      01001D47  |.  0F8C 5C040000 JL winmine.010021A9
      01001D4D  |.  8335 54510001>XOR DWORD PTR DS:[1005154],14            ;作弊开关
      01001D54  |.  E9 50040000   JMP winmine.010021A9
      
      01001CA0  |. /0F84 9A000000 JE winmine.01001D40                      ;  是不是按下了shif键(ASCII-16)
      
          3,原来的作弊方法是在屏幕的左上角画一个象素的黑/白点
      01002126  |.  57            PUSH EDI                                 ; /hWnd
      01002127  |.  FF15 2C110001 CALL DWORD PTR DS:[<&USER32.GetDC>]      ; \GetDC
      0100212D  |.  8B0D 18510001 MOV ECX,DWORD PTR DS:[1005118]
      01002133  |.  8BF0          MOV ESI,EAX
      01002135  |.  A1 1C510001   MOV EAX,DWORD PTR DS:[100511C]
      0100213A  |.  C1E0 05       SHL EAX,5
      0100213D  |.  8A8408 405300>MOV AL,BYTE PTR DS:[EAX+ECX+1005340]     ;0F没雷,8F有雷
      01002144  |.  24 80         AND AL,80
      01002146  |.  F6D8          NEG AL
      01002148  |.  1BC0          SBB EAX,EAX
      0100214A  |.  25 010000FF   AND EAX,FF000001
      0100214F  |.  05 FFFFFF00   ADD EAX,0FFFFFF                          ;EAX值决定画黑点还是白点
      01002154  |.  50            PUSH EAX                                 ; /Color
      01002155  |.  57            PUSH EDI                                 ; |Y
      01002156  |.  57            PUSH EDI                                 ; |X
      01002157  |.  56            PUSH ESI                                 ; |hDC
      01002158  |.  FF15 58100001 CALL DWORD PTR DS:[<&GDI32.SetPixel>]    ; \SetPixel
      0100215E  |.  56            PUSH ESI                                 ; /hDC
      0100215F  |.  57            PUSH EDI                                 ; |hWnd
      01002160  |.  FF15 28110001 CALL DWORD PTR DS:[<&USER32.ReleaseDC>]  ; \ReleaseDC
      01002166  |.  EB 41         JMP SHORT winmine_.010021A9
      
          4,看一个播放声音的地方
      010038ED  /$  833D B8560001>CMP DWORD PTR DS:[10056B8],3
      010038F4  |.  75 47         JNZ SHORT winmine.0100393D
      010038F6  |.  8B4424 04     MOV EAX,DWORD PTR SS:[ESP+4]
      010038FA  |.  48            DEC EAX                                  ;  Switch (cases 1..3)
      010038FB  |.  74 2A         JE SHORT winmine.01003927
      010038FD  |.  48            DEC EAX
      010038FE  |.  74 15         JE SHORT winmine.01003915
      01003900  |.  48            DEC EAX
      01003901  |.  75 3A         JNZ SHORT winmine.0100393D
      01003903  |.  68 05000400   PUSH 40005                               ;  Case 3 of switch 010038FA
      01003908  |.  FF35 305B0001 PUSH DWORD PTR DS:[1005B30]              ;  winmine.01000000
      0100390E  |.  68 B2010000   PUSH 1B2
      01003913  |.  EB 22         JMP SHORT winmine.01003937               ;  跳去播放炸雷声音
      01003915  |>  68 05000400   PUSH 40005                               ;  Case 2 of switch 010038FA
      0100391A  |.  FF35 305B0001 PUSH DWORD PTR DS:[1005B30]              ;  winmine.01000000
      01003920  |.  68 B1010000   PUSH 1B1
      01003925  |.  EB 10         JMP SHORT winmine.01003937               ;  播放另一种怪声
      01003927  |>  68 05000400   PUSH 40005                               ;  Case 1 of switch 010038FA
      0100392C  |.  FF35 305B0001 PUSH DWORD PTR DS:[1005B30]              ;  winmine.01000000
      01003932  |.  68 B0010000   PUSH 1B0                                 ;  秒表嘀哒声
      01003937  |>  FF15 68110001 CALL DWORD PTR DS:[<&WINMM.PlaySoundW>]  ;  WINMM.PlaySoundW
      0100393D  \>  C2 0400       RETN 4                                   ;  Default case of switch 010038FA
      
      现在来改一下功能,PEDIY一下。可以增加一下菜单命令添加作弊选项,我用reshacker
        POPUP "游戏(&G)"
      {
        MENUITEM "开局(&N)\tF2",  510
        MENUITEM SEPARATOR
        MENUITEM "初级(&B)",  521
        MENUITEM "中级(&I)",  522
        MENUITEM "高级(&E)",  523
        MENUITEM "自定义(&C)...",  524
              MENUITEM "探雷器",  525             (自己添加一项,编译保存)
        MENUITEM SEPARATOR
        MENUITEM "标记(?)(&M)",  527
        MENUITEM "颜色(&L)",  529
        MENUITEM "声音(&S)",  526
        MENUITEM SEPARATOR
        MENUITEM "扫雷英雄榜(&T)...",  528
        MENUITEM SEPARATOR
        MENUITEM "退出(&X)",  512
      }
      
      这样游戏下拉菜单中就多出了一项作弊模式“探雷器”,用OD载入修过的”winmine.exe“,在下面一行下断点
      01001DBC  |> \0FB745 10     MOVZX EAX,WORD PTR SS:[EBP+10]           ;  Case 111 (WM_COMMAND) of switch 01001D5B
      01001DC0  |.  B9 10020000   MOV ECX,210                              ;扫雷英雄榜
      01001DC5  |.  3BC1          CMP EAX,ECX
      01001DC7  |.  0F8F 0F010000 JG winmine.01001EDC
      01001DCD  |.  0F84 FF000000 JE winmine.01001ED2
      01001DD3  |.  3D FE010000   CMP EAX,1FE                              ;开局
      01001DD8  |.  0F84 EA000000 JE winmine.01001EC8
      01001DDE  |.  3BC6          CMP EAX,ESI
      01001DE0  |.  0F84 B7000000 JE winmine.01001E9D
      01001DE6  |.  3D 08020000   CMP EAX,208
      01001DEB  |.  0F8E B8030000 JLE winmine.010021A9
      01001DF1  |.  3D 0B020000   CMP EAX,20B
      01001DF6  |.  7E 61         JLE SHORT winmine.01001E59                ;初级从这里跳走
      01001DF8  |.  3D 0C020000   CMP EAX,20C
      01001DFD  |.  74 50         JE SHORT winmine.01001E4F
      01001DFF  |.  3D 0E020000   CMP EAX,20E
      01001E04  |.  74 20         JE SHORT winmine.01001E26
      01001E06  |.  3D 0F020000   CMP EAX,20F
      01001E0B  |.  0F85 98030000 JNZ winmine.010021A9
      01001E11  |.  33C0          XOR EAX,EAX
      01001E13  |.  3905 BC560001 CMP DWORD PTR DS:[10056BC],EAX
      01001E19  |.  0F94C0        SETE AL
      01001E1C  |.  A3 BC560001   MOV DWORD PTR DS:[10056BC],EAX
      01001E21  |.  E9 24010000   JMP winmine.01001F4A
      
      运行winmine.exe,打开游戏菜单,选择“扫雷器”后,程序断在“01001DBC”(自己设定的断点) ,那么EAX将被赋值为:
      020D(十进制525)。下面增加一行判断如果等于525 就打开作弊模式。看了一下在这地方加点代码也麻烦,反正玩初级的
      是不太会玩的就把初级的变成作弊模式算了。
      01001E59  |> \8B45 10       MOV EAX,DWORD PTR SS:[EBP+10]         ;初级跳到这里设置参数
      01001E5C  |.  05 F7FDFFFF   ADD EAX,-209                          ;209就是521,EAX在这里变成了0
      01001E61  |.  66:A3 A056000>MOV WORD PTR DS:[10056A0],AX
      01001E67  |.  0FB7C0        MOVZX EAX,AX
      01001E6A  |.  8D0440        LEA EAX,DWORD PTR DS:[EAX+EAX*2]
      01001E6D  |.  C1E0 02       SHL EAX,2
      01001E70  |.  8B88 10500001 MOV ECX,DWORD PTR DS:[EAX+1005010]     ;初级的是十个雷吧?
      01001E76  |.  890D A4560001 MOV DWORD PTR DS:[10056A4],ECX         ;雷数放到DS:[10056A4]
      01001E7C  |.  8B88 14500001 MOV ECX,DWORD PTR DS:[EAX+1005014]     ;9行
      01001E82  |.  8B80 18500001 MOV EAX,DWORD PTR DS:[EAX+1005018]     ;9列
      01001E88  |.  890D A8560001 MOV DWORD PTR DS:[10056A8],ECX
      01001E8E  |.  A3 AC560001   MOV DWORD PTR DS:[10056AC],EAX
      01001E93  |.  E8 E2170000   CALL winmine.0100367A
      01001E98  |.  E9 AD000000   JMP winmine.01001F4A                   ; 改为:jmp 01004a76
      
      改动方案:在程序中找一块空白处(如:01004a76),添加代码:
      
      01004A76      C705 54510001>MOV DWORD PTR DS:[1005154],5
      01004A80    ^ E9 C5D4FFFF   JMP winmine.01001F4A
      
      现在程序已经实现,打开一下初级模式就开通了作弊模式,以后我们只要按‘SHIFT’键就可以打开关闭“黑白点”提示了!
      
      接下来,改成声音报警的
      方案1,用系统的BEEP,优点是没有声卡也能听到或者说没有耳机也能听到声音?、、
      改成下面的代码,原代码看上面的第3条,优点
      0100214F  |.  05 FFFFFF00               ADD EAX,0FFFFFF                          ;  修改下面的
      01002154      85C0                      TEST EAX,EAX
      01002156      75 51                     JNZ SHORT winmine.010021A9
      01002158      05 00010000               ADD EAX,100
      0100215D      90                        NOP
      0100215E      50                        PUSH EAX
      0100215F      50                        PUSH EAX
      01002160      FF15 C411B176             CALL DWORD PTR DS:[<&KERNEL32.Beep>]     ;  kernel32.Beep
      01002166  |.  EB 41                     JMP SHORT winmine.010021A9
      01002168  |>  393D 4C510001             CMP DWORD PTR DS:[100514C],EDI
      0100216E  |.^ 0F85 EAFAFFFF             JNZ winmine.01001C5E
      
       方案2,用程序中使用的WINMM.PlaySoundW
      修改代码如下:
      
      0100213D      8A8408 40530001            MOV AL,BYTE PTR DS:[EAX+ECX+1005340]                ;  0F没雷与8F有雷
      01002144      04 71                      ADD AL,71
      01002146      85C0                       TEST EAX,EAX
      01002148      75 5F                      JNZ SHORT winmine.010021A9
      0100214A      68 05000400                PUSH 40005
      0100214F      FF35 305B0001              PUSH DWORD PTR DS:[1005B30]                         ;  winmine.01000000
      01002155      68 B2010000                PUSH 1B2
      0100215A      FF15 68110001              CALL DWORD PTR DS:[<&WINMM.PlaySoundW>]             ;  WINMM.PlaySoundW
      01002160      90                         NOP
      01002161      90                         NOP
      01002162      90                         NOP
      01002163      90                         NOP
      01002164      90                         NOP
      01002165      90                         NOP
      
      然后保存修改内容到可执行文件,用winhex修改对应的字节就行了。
      运行程序享受一下PEDIY的成果吧。爽吗?。。。。不爽?那声音是不是太烦人了,戴着声音听那爆炸声可不是种享受,换
      一下喜欢的音乐吧,哈哈。
      打开ResHacker.exe替换增加一下WAVE资源Action->add a new resource,选择自己喜欢的wav文件,资料类型为WAVE,资源
      名称为435(1B3),然后把下面的代码改一下:
      01002155      68 B2010000                PUSH 1B2    改成push 1B3 就是自己喜欢的声音啦。
      好了,先写到这吧。
      终于过了吧PEDIY的ying,估计只有初学者感兴趣就发到这里吧。有耐心看完的说明你有时间,特别是有耐心或者是同情
      心。附近中是我改好的一个,无聊时可以拿来玩玩。

    评分

    参与人数 1飘云币 +40 收起 理由
    月之精灵 + 40 您的贴子很精彩,希望能再次分享!

    查看全部评分

    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-5-13 17:51:35 | 显示全部楼层
    技术差太远
    我是肉鸟型的- -!
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2016-4-28 06:28
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2010-5-13 21:46:34 | 显示全部楼层
    好象没有啊
    PYG19周年生日快乐!
  • TA的每日心情
    慵懒
    2015-10-9 11:25
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2010-5-13 22:38:13 | 显示全部楼层
    楼主的水平真高
    PYG19周年生日快乐!

    该用户从未签到

    发表于 2010-5-13 23:27:11 | 显示全部楼层
    楼主的冲劲比地雷还强悍,我准备拜师了
    另:快去搞定 VMP ,你行的师傅
    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2016-11-23 19:02
  • 签到天数: 1 天

    [LV.1]初来乍到

    发表于 2010-5-15 23:15:06 | 显示全部楼层
    完全看不懂。
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表