walk in winmine

原创 2012年03月26日 16:55:57

貌似下午很无聊,就玩了下winmine.

用OD(OllyICE v1.10 HA_second)加载winmine.停在了入口.庆幸下,标准的WinMainCRTStartup.

01003E21 > $  6A 70         push    70
01003E23   .  68 90130001   push    01001390
01003E28   .  E8 DF010000   call    0100400C
01003E2D   .  33DB          xor     ebx, ebx
01003E2F   .  53            push    ebx                                 ; /pModule => NULL
01003E30   .  8B3D 8C100001 mov     edi, dword ptr [<&KERNEL32.GetModul>; |kernel32.GetModuleHandleA
01003E36   .  FFD7          call    edi                                 ; \GetModuleHandleA
...

so直接向下滚鼠标,找到传说中的GetStartupInfo.

01003F68   .  50            push    eax                                 ; /pStartupinfo
01003F69   .  FF15 90100001 call    dword ptr [<&KERNEL32.GetStartupInf>; \GetStartupInfoA
...
01003F89   >  50            push    eax
01003F8A   .  56            push    esi
01003F8B   .  53            push    ebx
01003F8C   .  53            push    ebx
01003F8D   .  FFD7          call    edi
01003F8F   .  50            push    eax
01003F90   .  E8 5BE2FFFF   call    010021F0 ;WinMain
...
01003F9F   .  56            push    esi                                 ; /status
01003FA0   .  FF15 94110001 call    dword ptr [<&msvcrt.exit>]          ; \exit

在下面发现exit.在二者之间找call,显然,010021F0应该是WinMain了.点击下WinMain(010021F0),回车进入,随便翻了下,创建窗口什么什么的,并没有好东西找到.猜测扫雷是随机生成的,所以应该会调用rand一类的函数,所以,在command栏>bp rand,然后F9直接跑着.果然停住了.

76D6C070 >  E8 59E5FFFF     call    76D6A5CE
76D6C075    8B48 14         mov     ecx, dword ptr [eax+14]
76D6C078    69C9 FD430300   imul    ecx, ecx, 343FD
76D6C07E    81C1 C39E2600   add     ecx, 269EC3
76D6C084    8948 14         mov     dword ptr [eax+14], ecx
76D6C087    8BC1            mov     eax, ecx
76D6C089    C1E8 10         shr     eax, 10
76D6C08C    25 FF7F0000     and     eax, 7FFF
76D6C091    C3              retn

看下栈

0006FE70   01003946  返回到 winmine.01003946 来自 msvcrt.rand
0006FE74   010036D2  返回到 winmine.010036D2 来自 winmine.01003940

返回到winmine.01003946.直接回车到01003946.看来应该是个好玩的函数.

_01003940 PROC N
call rand
cdq
idiv    dword 
ptr [esp+4]
mov     eaxedx
retn    4
_01003940 ENDP

还原成C

__declspec(naked) int _3940_rnd(int n)
{
	return rand() % n;
}

 先不管有什么用.F4到01003946.F8直到回到上一个函数.向上滚一下.

0100367A  /$  A1 AC560001   mov     eax, dword ptr [10056AC]
0100367F  |.  8B0D A8560001 mov     ecx, dword ptr [10056A8]
...
0100368A  |.  3B05 34530001 cmp     eax, dword ptr [1005334]
01003690  |.  893D 64510001 mov     dword ptr [1005164], edi
01003696  |.  75 0C         jnz     short 010036A4
01003698  |.  3B0D 38530001 cmp     ecx, dword ptr [1005338]
...
01003746  |.  E8 05E2FFFF   call    01001950
0100374B  |.  5F            pop     edi
0100374C  |.  5E            pop     esi
0100374D  |.  5B            pop     ebx
0100374E  \.  C3            retn

访问了几个全局变量.[10056AC],[10056A8],[1005334],[1005338],[1005164]...把rand的断点删了.不过,为了让思路更清晰,给0100367A下个bp.然后随便给上面找到的某全局变量下个硬断,我下了[1005338].Ctrl+F2重新开始.然后在01002C09停住.

01002BF9  |.  6A 1E         push    1E
01002BFB  |.  56            push    esi
01002BFC  |.  56            push    esi
01002BFD  |.  6A 03         push    3
01002BFF  |.  A3 A8560001   mov     dword ptr [10056A8], eax
01002C04  |.  A3 38530001   mov     dword ptr [1005338], eax
01002C09  |.  E8 19FFFFFF   call    01002B27
01002C0E  |.  6A 03         push    3
01002C10  |.  57            push    edi
01002C11  |.  57            push    edi
01002C12  |.  57            push    edi
01002C13  |.  A3 AC560001   mov     dword ptr [10056AC], eax
01002C18  |.  A3 34530001   mov     dword ptr [1005334], eax
01002C1D  |.  E8 05FFFFFF   call    01002B27
...


good lucky!一直在call 01002B27,然后把eax给某全局变量.其实隐约可以猜到什么了,进去看看.

01002B27  /$  55            push    ebp
01002B28  |.  8BEC          mov     ebp, esp
01002B2A  |.  51            push    ecx
01002B2B  |.  8D45 FC       lea     eax, dword ptr [ebp-4]
01002B2E  |.  50            push    eax                                 ; /pBufSize
01002B2F  |.  8D45 08       lea     eax, dword ptr [ebp+8]              ; |
01002B32  |.  50            push    eax                                 ; |Buffer
01002B33  |.  8B45 08       mov     eax, dword ptr [ebp+8]              ; |
01002B36  |.  6A 00         push    0                                   ; |pValueType = NULL
01002B38  |.  6A 00         push    0                                   ; |Reserved = NULL
01002B3A  |.  FF3485 D05000>push    dword ptr [eax*4+10050D0]           ; |ValueName
01002B41  |.  C745 FC 04000>mov     dword ptr [ebp-4], 4                ; |
01002B48  |.  FF35 50590001 push    dword ptr [1005950]                 ; |hKey = D4
01002B4E  |.  FF15 00100001 call    dword ptr [<&ADVAPI32.RegQueryValue>; \RegQueryValueExW
...


从注册表里读取数据,然后再给全局变量!直接在ValueName下断点.删除硬件断点,Ctrl+F2,重新开始.F9到断点处.
第一次

01002B3A  |.  FF3485 D05000>push    dword ptr [eax*4+10050D0]           ; |ValueName = "AlreadyPlayed"

eax是索引,4是指针长度,猜测10050D0应该是个字符串表,保存注册表里每个文件的名字.
然后继续第二次ValueName = "Height",读取失败,然后把一种类似于默认值的东西返回了.写入到全局变量.

[56A8]=[5338]=Height(高) //返回了9 记得扫雷默认大小么
[56AC]=[5334]=Witdh(宽) //也是返回9
[56A0]=Difficulty(难度?)
[55A4]=Mines(雷数量) //返回了A,默认10个雷
[56B0]=Xpos
[56B4]=Ypos
[56B8]=Sound
[56BC]=Mark
[56C0]=Tick
[56C4]=Menu


接下来就是一些记录了

[56CC]=Time1 //0x3E7 也就是999
[56D0]=Time2 //999
[56D4]=Time3 //999

测试下,把56D4改成15,运行下,高级变成了15秒...然后是记录的名字

[56D8]=Name1(Unicode)
[5718]=Name2(Unicode)
[5758]=Name3(Unicode)

找到了这些以后,go on,记得之前下过的bp么,F9后停在了这里.把汇编代码copy出来.简单修改下

_2ED5_initTable PROC
  
mov     eax360 ;eax = 0x360
L001:
  
dec     eax ;while(eax--)
  
mov     byte ptr [eax+1005340], 0F ;_byte_5340[eax] = 0xf;
  
jnz L001
  
;...make a circle & set state 0x10 with Width5334 & Heigth5338
  
retn
_2ED5_initTable ENDP

_36A7_initMineTable PROC
  
mov     eax, [Width56AC]
  
mov     ecx, [Heigth56A8]
  
push    ebx
  
push    esi
  
push    edi
  edi 
0
  
cmp     eax, [Width5334]
  
mov     dword ptr [1005164], edi ;dw_1005164 = 0
  
jnz L013
  
cmp     ecx, [Heigth5338]
  
jnz L013
  
push    4
  
jmp L014
L013
:
  
push    6
L014:
  
;ebx = (Width5334 != Width56AC || Heigth5338 != Heigth56A8)? 6: 4;
  
pop     ebx
  
mov     [Width5334], eax ;Width5334 = Width56AC
  
mov     [Heigth5338], ecx ;Heigth5338 = Heigth56A8
  
call    _2ED5_initTable ;initialize _byte_5340[360](5340-569F)
  
mov     eax, [Mines]
  
mov     dword ptr [1005160], edi ;dw_1005160 = 0
  
mov     [temp_Mines], eax ;temp_Mines = Mines
L021:  ;do {
  
push    [Width5334]
  
call    _3940_rnd
  
push    [Heigth5338]
  
mov     esieax
  
inc     esi ;esi = _3940_rnd(Width5334) + 1;
  
call    _3940_rnd
  
inc     eax ;eax = _3940_rnd(Heigth5338) + 1;
  
mov     ecxeax ;ecx = eax
  
shl     ecx5
  
test    byte ptr [ecx+esi+1005340], 80
  
jnz L021 ;if(_byte_5340[ecx][esi] & 0x80) coutinue;
  
shl     eax5
  
lea     eaxdword ptr [eax+esi+1005340]
  
or      byte ptr [eax], 80 ;_byte_5340[eax][esi] |= 0x80
  
dec     [temp_Mines]
  
jnz L021 ;} while(--temp_Mines);
  
mov     ecx, [Heigth5338]
  
imul    ecx, [Width5334]
  
mov     eax, [Mines]
  
sub     ecxeax
  
push    edi
  
mov     dword ptr [100579C], edi ;[_Tick_579C]=0 //测试出来的.
  
mov     [temp_Mines], eax
  
mov     dword ptr [1005194], eax ;[_Mines_5194] = [Mines];
  
mov     dword ptr [10057A4], edi ;[_57A4] = 0
  
mov     dword ptr [10057A0], ecx ;[_TableSize_57A0] = [Heigth5338] * [Width5334]
  
mov     dword ptr [1005000], 1
  
call    0100346A ;DrawMines
  
push    ebx ;flags
  
call    01001950
  
pop     edi
  
pop     esi
  
pop     ebx
  
retn
_36A7_initMineTable ENDP

其实程序到这里就差不多了,用_initTable初始化下表,然后用两个Rnd()来随机生成雷,在雷的标记是0x80,空的标记是0xF,其实可以跟一下DFS,我这里就不跟了,就简单说下,如果一个块周围有雷,那么它会被设置成0x40 | 周围雷的个数.

接下来的图片凑字数


感谢OD(OllyDbg v1.10),记事本,QQ截图工具,对本文的大力支持.
完全不知道自己在干什么.

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

in_order_walk 中序遍历的非递归实现源码

#include "stdafx.h" /* ** 数据结构:链表实现的二叉树,在内存中分配的连续区域模拟数组实现的栈,栈中存放的是树结点的指针(地址) ** ** ** */ #include ...

【IAP支付之一】In-App Purchase Walk Through 整个支付流程

1. 适用情况 想使用In-App Purchase(以下简称IAP)完成App内付费前,先确定需求是不是能用这个方案来满足。 除了IAP以外,还有支付宝SDK、信用卡等其他方式完成软件内付费。 ...

内支付三:In-App Purchase Walk Through 整个支付流程

目录(?)[-] 1 适用情况2 购买及发放虚拟产品流程3虚拟产品 虚拟产品的分类关于自动更新订阅品更新周期组Auto-Renewable Subscription Duration Famili...

扫雷源码--winmine.rc

//Microsoft Developer Studio generated resource script. // //#include "resource.h" #define APSTUDIO...
  • fyzqzpd
  • fyzqzpd
  • 2011年12月26日 12:01
  • 650

在“开始”“运行”中输入cmd, calc, winmine是干什么?

我们经常在“开始”,“运行”中输入cmd, calc, winmine(每次输入一个), 实际上就是运行了3个.exe文件,分别是cmd.exe, calc.exe和winmine.exe. 这三个....
  • stpeace
  • stpeace
  • 2012年12月06日 11:29
  • 3036

windows10上winmine安装程序

  • 2016年09月27日 19:09
  • 816KB
  • 下载

C#SNMP_WALK举例

  • 2015年07月05日 23:29
  • 5KB
  • 下载

[python]怎么样用Python读取一个目录树-os.walk入门

原文: http://pythoncentral.org/how-to-traverse-a-directory-tree-in-python-guide-to-os-walk/         ...

winmine2.0

  • 2006年03月16日 00:00
  • 89KB
  • 下载

扫雷(WinMine)

  • 2006年03月16日 00:00
  • 96KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:walk in winmine
举报原因:
原因补充:

(最多只允许输入30个字)