逆向扫雷笔记

    好吧我承认放暑假我懒了。。。有一个多月没更新了。前几天看到久违的“纸牌”,想到了搞一下扫雷。其实三天前我就搞定了,但是紧接着我就往学校赶了,之后又感冒发烧拖到现在。现在我的体温还没有降下去,故后面的语言组织可能有些混乱。

    之所以选择了扫雷首先是因为这是一个SDK编写程序,结构清晰、明了,可以很快的找到想要找的函数,而且游戏的运行模式也比较简单,就是在点下去的时候生 成一个“雷表”,之后获取鼠标的消息,根据雷表的信息和鼠标的消息来决定调用的函数。并且,分析一个SDK程序的汇编代码可以帮助我更好的理解 Windows程序运行的过程。

    先看看程序运行的方式。运行扫雷,在下面按下鼠标左键只会使格子变成凹陷状,不会发生任何事。当鼠标左键弹起时,就会翻开相应格子。按下鼠标右键时会在相应格子出插上旗子。知道这些就足够了。

    好了,知道了这些,可以开始动手了。首先想到的就是下消息断点。用OD载入扫雷,F9运行,用Spy++来获取窗口句柄,点上面的“W”看到相应句柄,右键下消息断点202 WM_LBOTTOMUP,结果弹出了这个对话框。。。

消息断点失败

    看来是不能直接下消息断点了。重新载入,停在这儿

01003E21 > $  6A 70         push    70
01003E23   
.  68 90130001   push    01001390
01003E28   
.  E8 DF010000   call    0100400C
01003E2D   
.  33DB       xor     ebxebx
01003E2F   .  53        push    ebx                    ; /pModule => NULL
01003E30   .  8B3D 8C100001  mov     edi, dword ptr [<&KERNEL32.GetMo>            ; |kernel32.GetModuleHandleA
01003E36   .  FFD7       call    edi                    ; \GetModuleHandleA
01003E38   .  66:8138 4D5A  cmp     word ptr [eax], 5A4D
01003E3D   
.  75 1F      jnz     short 01003E5E

    没什么用往下拉看到这儿

01003F86   \6A 0A         push    0A
01003F88   
.  58            pop     eax
01003F89   >  50            push    eax
01003F8A   .  56            push    esi
01003F8B   .  53            push    ebx
01003F8C   .  53            push    ebx
01003F8D   .  FFD7          call    edi
01003F8F   .  50            push    eax                    ; |Arg1
01003F90   .  E8 5BE2FFFF      call    010021F0                  ; \winmine.010021F0

    在01003F90   .  E8 5BE2FFFF   call    010021F0    ; \winmine.010021F0     处跟进去。期间有个小插曲,我用笔记本可以顺利的F4到那儿,但是用台式机就会在01003F17   .  FF15 8C110001 call    dword ptr [<&msvcrt.__getmainarg>;  msvcrt.__getmainargs (好像)进入死循环。全程跟踪后发现进入了异常处理,然后返回的程序开始出,反复循环。但是用台式机的虚拟机却 又可以正常F4。至今不知为什么,希望有高手看到可以给予一点指示。小弟跪谢。后来注意到台式机载入后可以F9运行,于是尝试在01003F90   .  E8 5BE2FFFF   call    010021F0   ; \winmine.010021F0 处下断点,果然可以正常断下来。

    跟进去一看就能发现是我们所熟悉的WinMain函数。往下拉找到窗口注册函数,因为我们要找的是消息处理函数,窗口注册函数调用了WNDCLASS结构的指针,而消息处理函数是WNDCLASS结果的成员。我们看到

0100228B  |.  50            push    eax                    ; /pWndClass
0100228C  |.  897D D4       mov     dword ptr [ebp-2C], edi          ; |
0100228F  |.  8975 D8       mov     dword ptr [ebp-28], esi          ; |

01002292  |.  FF15 CC100001 call    dword ptr [<&USER32.RegisterClas>; \RegisterClassW

    得知WNDCLASS地址在eax里。获取eax内容得知WNDCLASS地址为0006FE98。因为消息处理函数的地址是WNDCLASS结构的第二 个成员所以db 0006FE98 + 4h,可以看到0006FE9C  C9 1B 00 01 00 00 00 00      ?......

    所以消息处理函数的地址为01001BC9。转到01001BC9,这就是我们的消息处理函数了~

    接下来找到我们需要的API 202 WM_LBUTTONUP和204 WM_RBUTTONDOWN

01001FDF  |> \33FF          xor     ediedi      ;  Cases 202 (WM_LBUTTONUP),205 (WM_RBUTTONUP),208 (WM_MBUTTONUP) of switch 01001F5F

0100200F  |> \393D 48510001 cmp     dword ptr [1005148], edi      ;  Case 204 (WM_RBUTTONDOWN) of switch 01001F5F
然后

01001FDF  |> \33FF          xor     ediedi                         ;  Cases 202 (WM_LBUTTONUP),205 (WM_RBUTTONUP),208 (WM_MBUTTONUP) of switch 01001F5F
01001FE1  |.  393D 40510001 cmp     dword ptr [1005140], edi
01001FE7  |.  0F84 BC010000 je      010021A9
01001FED  
|>  893D 40510001 mov     dword ptr [1005140], edi
01001FF3  |.  FF15 D8100001 call    dword ptr [<&USER32.ReleaseCaptu>; [ReleaseCapture
01001FF9  |.  841D 00500001 test    byte ptr [1005000], bl
01001FFF  |.  0F84 B6000000 je      010020BB
01002005  
|.  E8 D7170000   call    010037E1
而左键弹起的执行函数就是01002005  |.  E8 D7170000   call    010037E1 ,跟进去。之后反复跟踪,发现函数

010038B1  |.  E8 5CFCFFFF   call    01003512 是无论炸死与否都调用的函数。跟进去反复跟踪和F9,发现炸死时使用了跳转

01003536  |/75 50         jnz     short 01003588

    接下来就是改变跳转位置让其不调用炸死函数。一开始我然他调到一个什么都不做返回的地方(主要堆栈平衡),运行后发现的却炸不死了,但是按过的雷看不出来,效果不好于是选择的跳到插旗函数。

0100200F  |> \393D 48510001 cmp     dword ptr [1005148], edi         ;  Case 204 (WM_RBUTTONDOWN) of switch 01001F5F
01002015  |.0F85 69FFFFFF jnz     01001F84
0100201B  
|.  841D 00500001 test    byte ptr [1005000], bl
01002021  |.  0F84 82010000 je      010021A9
01002027  
|.  393D 40510001 cmp     dword ptr [1005140], edi
0100202D  |.  74 27         je      short 01002056
0100202F  
|.  6A FD         push    -3                 ; /Arg2 = FFFFFFFD
01002031  |.  6A FD         push    -3                 ; |Arg1 = FFFFFFFD
01002033  |.  E8 9C110000   call    010031D4                 ; \winmine.010031D4
01002038  |.  FF75 14       push    dword ptr [ebp+14]         ; /lParam
0100203B  |.  891D 44510001 mov     dword ptr [1005144], ebx        ; |

    反复跟踪后发现一定会使用跳转01002059  |/0F84 09010000 je      01002168

    跳转到

01002168  |> \393D 4C510001 cmp     dword ptr [100514C], edi
0100216E  |.0F85 EAFAFFFF jnz     01001C5E
01002174  
|.  8B45 14       mov     eax, dword ptr [ebp+14]
01002177  |.  C1E8 10       shr     eax10
0100217A  
|.  83E8 27       sub     eax27
0100217D  
|.  C1F8 04       sar     eax4
01002180  
|.  50            push    eax
01002181  |.  0FB745 14     movzx   eax, word ptr [ebp+14]
01002185  |.  83C0 04       add     eax4
01002188  
|.  C1F8 04       sar     eax4
0100218B  
|.  50            push    eax
0100218C  |.  E8 BE150000   call    0100374F

    那么

01002174  |.  8B45 14       mov     eax, dword ptr [ebp+14]
01002177  |.  C1E8 10       shr     eax10
0100217A  
|.  83E8 27       sub     eax27
0100217D  
|.  C1F8 04       sar     eax4
01002180  
|.  50            push    eax
01002181  |.  0FB745 14     movzx   eax, word ptr [ebp+14]
01002185  |.  83C0 04       add     eax4
01002188  
|.  C1F8 04       sar     eax4
0100218B  
|.  50            push    eax
0100218C  |.  E8 BE150000   call    0100374F

    就是调用插旗函数的过程。然后把前面找到的跳转01003536  |/75 50    jnz    short 01003588 更改成跳到一个空的地方然后平衡堆栈弄掉堆栈里多余的东西(我当时脑子发昏pop了8次,其实只要加ebp就好了)。然后jmp到 01002174  |.  8B45 14  、mov  、eax, dword ptr [ebp+14] 保存。收工。
    接下来打开那个改过的扫雷后只需要一阵狂点可以创造记录哦(原则上不赞成)(偷笑)~

posted @ 2009-08-30 21:21  Xelloss  阅读(850)  评论(0编辑  收藏  举报