Posts Minesweeper - Revealing the Mines
Post
Cancel

Minesweeper - Revealing the Mines

We all know about Minesweeper. Its all about random numbers (pseudorandom though). Is it possible to win every time ?
Yes it is. Let’s check it out

Image0

Open the binary in x32dbg.
From 0x10036C2, things become interesting.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
;   ....
    mov dword ptr ds:[<n_mines>], eax

put_mines:

    push dword ptr ds:[0x1005334]    ; argument
    call rand_mod                    ; rand() % argument

    push dword ptr ds:[0x1005338]
    mov esi, eax
    inc esi
    call rand_mod

    inc eax
    mov ecx, eax
    shl ecx, 0x5

    test byte ptr ds:[ecx + esi + <mines>], 0x80
    jne put_mines

    shl eax, 0x5
    lea eax, dword ptr ds:[eax + esi + <mines>]
    or byte ptr ds:[eax], 0x80

    dec dword ptr ds:[<n_mines>]
    jne put_mines
;   ...

Dump the memory at 0x1005338 and run the executable. You can see that the 0x1005338 is an 2D array where each row is 32 bytes.
The field is bounded by bytes of value 0x10.
Mines correspond to 0x8F and other areas are marked by 0xf bytes.

Now we can just read the memory at 0x1005338 and find out the position of mines :)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tlhelp32.h>

#pragma comment(lib, "user32")

typedef LONG (WINAPI* NT_ROUTINE) (HANDLE);

int main(int argc, char** argv)
{
    int n_mines = 0, width, height, i;
    char* base;
    char row[32];
    HWND hWnd, hProcSnap, hProcess = (HANDLE)-1;
    char* exe_name;
    PROCESSENTRY32 pInfo;
    HANDLE hNt;
    NT_ROUTINE NtSuspendProcess, NtResumeProcess;

    exe_name = "winmine.exe";
    if (argc == 2)
    {
        exe_name = strlwr(argv[1]);
    }

    // already loaded, just return module base
    hNt = LoadLibrary("ntdll.dll");
    NtSuspendProcess = (NT_ROUTINE) GetProcAddress(hNt, "NtSuspendProcess");
    NtResumeProcess = (NT_ROUTINE) GetProcAddress(hNt, "NtResumeProcess");

    hProcSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    Process32First(hProcSnap, &pInfo);
    do
    {
        if (strstr(strlwr(pInfo.szExeFile), exe_name))
        {
            hProcess = OpenProcess(PROCESS_SUSPEND_RESUME | PROCESS_VM_READ, FALSE, pInfo.th32ProcessID);
            printf("[*] Found Minesweeper (%s) ... PiD = %d\n", pInfo.szExeFile, pInfo.th32ProcessID);
            break;
        }
    } while (Process32Next(hProcSnap, &pInfo));

    CloseHandle(hProcSnap);
    if (! hProcess)
    {
        printf("[-] Failed to open process !!\n");
        exit(2);
    }
    else if (hProcess == (HANDLE)-1)
    {
        printf("[ Usage ]\n    %s [minesweeper_binary_name]\n", *argv);
        printf("\n[ Note ]\n    Execute this program only after starting minesweeper\n");
        printf("    If no argument is provided, the program searches for WinMine.exe\n");
        printf("\n[~ x0r19x91 ~]\n");
        exit(0);
    }

    printf("[*] Waiting for Minesweeper window to appear ...\n");
    while (!(hWnd = FindWindow("Minesweeper", "Minesweeper")));
    printf("[*] HWnd : %p\n", (long*) hWnd);

    while (!n_mines)
        ReadProcessMemory(hProcess, (char*) 0x1005330, &n_mines, 4, NULL);

    NtSuspendProcess(hProcess);
    ReadProcessMemory(hProcess, (char*) 0x1005334, &width, 4, NULL);
    ReadProcessMemory(hProcess, (char*) 0x1005338, &height, 4, NULL);

    printf("[*] # mines : %d\n", n_mines);
    printf("[*] Dimensions : %d x %d\n", height, width);
    printf("[ Note ] X represents Mine\n");
    printf("[*] Mine Layout ...\n");
    base = (char*) 0x1005360;

    while (height--) {
        ReadProcessMemory(hProcess, base, row, 32, NULL);
        base += 0x20;
        i = 1;
        do {
            if ((row[i] & 0xffu) == 0x8f)
                row[i] = 'X';
            else
                row[i] = '.';
        } while (row[++i] != 0x10);
        printf("%.*s\n", width, row+1);
    }

    NtResumeProcess(hProcess);
    WaitForSingleObject(hProcess, -1);
    CloseHandle(hProcess);
}

Image1

This post is licensed under CC BY 4.0 by the author.