貌似下午很无聊,就玩了下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 eax, edx
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 eax, 360 ;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 esi, eax
inc esi ;esi = _3940_rnd(Width5334) + 1;
call _3940_rnd
inc eax ;eax = _3940_rnd(Heigth5338) + 1;
mov ecx, eax ;ecx = eax
shl ecx, 5
test byte ptr [ecx+esi+1005340], 80
jnz L021 ;if(_byte_5340[ecx][esi] & 0x80) coutinue;
shl eax, 5
lea eax, dword 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 ecx, eax
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截图工具,对本文的大力支持.
完全不知道自己在干什么.