Featured image of post Minesweeper plug-in

Minesweeper plug-in

# 1. Analysis process

# (1) Find the storage location of basic game information

Adjust the difficulty level of the Minesweeper game to change the number of mines, then fill in the new number of mines on the CE interface and click the “Scan Again” button. Repeat this operation until the number of addresses found on the left side of the CE drops to single digits. Double-click the found address to add it to the address list below the CE.

Address where the number of mines is stored: 0x01005194, 0x01005330, 0x010056A4

Find the map height storage location and change the map height from 24 to 20

The addresses where map heights are stored are: 0x01005338 and 0x010056A8

Find the map width storage location and change the map width from 30 to 26

The addresses where the map width is stored are: 0x01005334 and 0x010056AC

Find the address where the game time is stored. The first scan value is 0

After starting the game, continue to scan the increasing value until the scan result address becomes a single digit.

The address where the game time is stored is 0x0100579C

Find the location where the scanned quantity is stored, and search through continuous value addition

The address where the scanned quantity is stored is 0x010057A4

# (2) Use ollydbg to find the game timing instruction storage location

In addition to setting the hardware write breakpoint at 0x0100579C, the timing instruction storage locations are found to be 0x01003830 and 0x01002ff5. The data of these addresses can be modified in the program to 0x90 (nop instruction), so the timer increment instruction can not be executed when the game is running, thereby stopping the game timer.

# (3) The game will not end if you click on the thunder

Through debugging, it was found that the program will perform a test operation on the content of the address of the clicked position and 80, and then perform a je 0x01003595 jump. If the content of the address where the click location is located is 0x8F, the je jump will not be executed; if the content of the address where the click location is located is 0x0F, the je jump will be executed.

Through the tracking and debugging of 0x01003595, it is found that the subsequent execution is to continue the game. If 0x01003595 is not jumped, the execution will be executed to 0x01003593 and the jmp 0x010035AB command will be executed to end the game. Therefore, the instructions at 0x01003591-0x01003594 in the memory can be modified to fill with nop instructions, thereby satisfying the unconditional jump to continue the game (click to continue the game).

# (4) Automatic game winning function 1 - Determine the real memory starting address of the minefield

Among the multiple sets of addresses obtained by scanning, open the memory space corresponding to the address by right-clicking “Browse related memory area”, and then by starting a new game and clicking on the upper left corner of the minefield, determine the real memory starting address of the minefield, as well as the identification of mines and non-mines, the address is 0x01005361

The mine identification data is 0X8F, and the non-mine identification data is 0X0F.

Modify map width

Modify map height

Finally, create a new game and click on the leftmost grid in rows 1, 2, and 3 to determine the starting address of each row of the minefield, and find the correspondence between the starting address and the rows and columns of the minefield, that is, determine the number of grids in each row to be 32.

# (5) Automatic game winning function 2 - Determine the size of the minesweeper game grid

Open Spy++, click “Monitor–Log Message”, check “Hide Spy++ Options”, and drag the “Find Program Tool” to the main interface of the Minesweeper game, as shown in Figure 8-4. The displayed content includes the name of the Minesweeper window class (WNDCLASS) and the title name.

Switch to the “Message” window of Spy++, first clear all messages, and then check the two messages “WM_LBUTTONDOWN and WM_LBUTTONUP”, as shown in Figure 8-5. Click OK to return to the main interface of Spy++.

Return to the Minesweeper game interface, click on the three grids in the upper left corner, immediately to the right of the upper left corner, and immediately below the upper left corner, and view the xPos and yPos values ​​recorded by Spy++ when the mouse is clicked, to calculate the width and height of each grid.

The coordinates of the first minefield are:

xPos =14

yPos =58

In the Spy++ interface, right-click - Clear Message Log, and then repeat the above operation. You can also click several grids apart to calculate the average value of each grid as the width and height value of each grid. The width of the grid is 16 and the height of the grid is 16.

(6) Automatic game winning function 3 - automatic game based on minefield storage data

  1. Create a console project;

  2. Open the minesweeper process and obtain the row and column values ​​based on the memory address of the rows and columns of the minefield;

  3. Read the minefield data according to the minefield first address and minefield range;

  4. Place the minesweeper game interface at the top, read the value in row i and column j of the minefield, and determine whether it is a mine. If not, simulate a left-click operation of the mouse. If it is a mine, simulate a right-click operation of the mouse.

  5. According to the customer coordinate system of the Minesweeper game interface, calculate the coordinate position of the i-th row and j-th column, then move the mouse to this position and perform the mouse operation in step 3.

  6. Repeat 3-4 until all minefield data is scanned.

See the appendix for program source code and executable files.

# 2. User Manual

(1) Display basic information when the game is running (map size, total number of mines, etc.) - enter 1

Check the basic information again after modifying the map information.

After the game is running, recheck the basic information when the game is running.

(2) Timer stops - input 2

(3) Clicking on the mine will not end the game - enter 3

(4) Automatic game winning function - input 4

(5) Exit the program - enter 5

# 3. Code appendix

  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
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include<Windows.h>
#include<iostream>
using namespace std;

int width, height;
HANDLE handle;
HWND hwnd;

int init_minesweeper() {
    const char* text = "Minesweeper";
    hwnd = FindWindow(text, text);
    if (hwnd == NULL)
    {
        MessageBox(NULL, TEXT("Winmine is not found!"), TEXT("AutoMineSweeper"), MB_ICONSTOP);
        return 1;
    }
    DWORD pid;
    GetWindowThreadProcessId(hwnd, &pid);
    handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
}

void show_information() {
    int Mines_num = -1;
    int play_time = -1;
    int aleardy_mines = -1;
    ReadProcessMemory(handle, (LPVOID)0x01005330, &Mines_num, 4, NULL);
    ReadProcessMemory(handle, (LPVOID)0x01005334, &width, 4, NULL);
    ReadProcessMemory(handle, (LPVOID)0x01005338, &height, 4, NULL);
    ReadProcessMemory(handle, (LPVOID)0x0100579C, &play_time, 4, NULL);
    ReadProcessMemory(handle, (LPVOID)0x010057A4, &aleardy_mines, 4, NULL);
    printf("Map height: %d\n", height);
    printf("Map width: %d\n", width);
    printf("Total number of mines: %d\n", Mines_num);
    printf("Scanned quantity: %d\n", aleady_mines);
    printf("Game running time: %d\n", play_time);
}

void stop_timing() {
    char nop[6] = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
    int stop_time = 1;
    WriteProcessMemory(handle, (LPVOID)0x01003830, &nop, 6, NULL);
    WriteProcessMemory(handle, (LPVOID)0x01002ff5, &nop, 6, NULL);
}

void Invincible_mode() {
    char nop[4] = { 0x90,0x90,0x90,0x90 };
    WriteProcessMemory(handle, (LPVOID)0x01003591, &nop, 4, NULL);
}

int auto_minesweeper() {
    ReadProcessMemory(handle, (LPVOID)0x01005334, &width, 4, NULL);
    ReadProcessMemory(handle, (LPVOID)0x01005338, &height, 4, NULL);
    BYTE data[32 * 32] = { 0 };
    ReadProcessMemory(handle, (LPVOID)0x01005361, &data, 32 * height, NULL);

    ShowWindow(hwnd, SW_RESTORE);
    SetForegroundWindow(hwnd);
    Sleep(300);

    int width_num = 32;
    int x_pos = 14;
    int y_pos = 58;

    RECT rc;
    BOOL bRet = GetClientRect(hwnd, &rc);

    for (int y = 1; y <= height; y++)
    {
        for (int x = 1; x <= width; x++)
        {
            UINT downMsg = x_pos, upMsg = y_pos;
            if (data[width_num * (x - 1) + (y - 1)] == 0x10)
                break;
            else
            {
                if (data[(x - 1) + width_num * (y - 1)] == 0x0F)
                {
                    downMsg = MOUSEEVENTF_LEFTDOWN;
                    upMsg = MOUSEEVENTF_LEFTUP;
                }
                else
                {
                    downMsg = MOUSEEVENTF_RIGHTDOWN;
                    upMsg = MOUSEEVENTF_RIGHTUP;
                }
            }
            POINT curPos = { rc.left + x_pos + (x - 1) * 16, rc.top + y_pos + (y - 1) * 16 };
            ClientToScreen(hwnd, &curPos);
            SetCursorPos(curPos.x, curPos.y);
            mouse_event(downMsg, 0, 0, 0, 0);
            mouse_event(upMsg, 0, 0, 0, 0);
            Sleep(1);

        }
    }
    CloseHandle(handle);
    return 0;
}

void menu() {
    int option = -1;
    while (option) {
        cout << "1. Display basic information when the game is running (map size, total number of mines, etc.)\n2. Stop the timer\n3. Clicking on the mine will not end the game\n4. Automatic game winning function\n5. Exit\nSelect function:";
        cin >> option;
        if (option == 5)
            break;
        switch (option) {
        case 1:
            show_information();
            break;
        case 2:
            stop_timing();
            break;
        case 3:
            Invincible_mode();
            break;
        case 4:
            auto_minesweeper();
            break;
        }
    }
}

int main() {
    init_minesweeper();
    menu();
    cout << "Program ends..." << endl;
}
Licensed under CC BY-NC-SA 4.0
Built with hugo 🖤 Stack
版权声明:Licensed under CC BY-NC-SA 4.0「署名-非商业性使用-相同方式共享 4.0 国际」