Posts Tagged ‘mine visibility

26
Apr
09

[C++] :Minesweeper: In-game Toggle Mine Visibility 2

A much cleaner solution than what I made here: /c-minesweeper-in-game-toggle-mine-visibility/
It just uses the in game “ShowMines” function rather than having to write your own.

void __stdcall ShowMines(void)
{
	typedef void (__stdcall *ptrFn)(DWORD);
	ptrFn ptrShowMinesFn = (ptrFn)(0x01002F80);
	ptrShowMinesFn(0xE);
}

You call the function ptrShowMinesFn with one of the possible values for mines, the list can be found here:
/c-minesweeper-toggle-mine-visibility/
So, for example 0xE would display flags.

Advertisements
24
Apr
09

[C++] :Minesweeper: In-game Toggle Mine Visibility

Minesweeper stores its minefield in memory, starting at the address  0x1005360, and is set up looking like this:

>>>>>>>>>
>*---*-->
>-*----*>
>-**-*-->
>------->
>---*--->
>>>>>>>>>

With the >’s representing a 1 Byte border, the *’s being mines, and -‘s being empty spaces.
Knowing this, we can effectively create a loop that reads this memory, and checks if a square is a bomb or not.

In order to calculate the size of the loop, we need to read the width and height from the game. These are stored at the two following addresses: 0x1005334 and 0x1005338. The maximum width of the board, is 0x1E, which is 0x20-2, 2 for the border. So, we now know that we can read each column by looping along the rows between 0 and nwidth, and the columns can be calculated by adding 0x20 to the address, each pass between 0 and nheight.

So, now we have the loop sorted, we just need to check if the current address holds a mine, if so, display it to the user.

bool MinesCheat = false;

void ToggleMinesCheat()
{
	int maxX = *((int*) 0x1005334);
	int maxY = *((int*) 0x1005338);
	int address = 0x1005361;

	for(int y = 0;y<maxY;y++) {
		for (int x = 0;x<maxX;x++) {
			BYTE *xmine = (BYTE*) address+x;
			BYTE mine = 0x8F;
			if(MinesCheat)
				mine = 0x8A;
			if(*xmine==mine) {
				if(MinesCheat)
					*xmine = 0x8F;
				else
					*xmine = 0x8A;
			}
		}
		address = address + 0x20;
	}
	InvalidateRect(mhWnd, NULL, 1);
	UpdateWindow(mhWnd);
	MinesCheat = MinesCheat^true;
}

That will either loop through the memory displaying the mines location, by overwriting the memory with 0x8A (Visible Mine), or if the function has already been ran, will hide the mines again by writing the memory with 0x8F (hidden mine)

24
Apr
09

[C++] :Minesweeper: Toggle Mine Visibility

winmine.StartGame+74

010036EE   |.^\75 D7             JNZ SHORT winmine.010036C7              ;  if its not a mine, jmp back
010036F0   |.  C1E0 05           SHL EAX,5
010036F3   |.  8D8430 40530001   LEA EAX,DWORD PTR DS:[EAX+ESI+rgBlk]
010036FA       8008 80           OR BYTE PTR DS:[EAX],80                 ;  set the square to a mine
010036FD   |.  FF0D 30530001     DEC DWORD PTR DS:[cBombStart]           ;  decrease bomb count
01003703   |.^ 75 C2             JNZ SHORT winmine.010036C7              ;  jmp back if not all bombs set

The above is part of the code that is used in generating the minefield for the game. The game randomly calculates if the square is a bomb, and then at 0x010036FA, it OR’s the value of 0x80 with the default value, 0x0F, giving 0x8F.
From reversing the game further, we know that these are the possible values for the mines:
Mines Revealed

x0 = Blank Square (Pressed)
x1 = #1
x2 = #2
x3 = #3
x4 = #4
x5 = #5
x6 = #6
x7 = #7
x8 = #8
x9 = ? (Pressed)
xA = Mine
xB = Incorrect Mine
xC = Clicked Mine
xD = ? (Unpressed)
xE = Flag
xF = Blank Square (Unpressed)

Where x = 0; the square is not a mine.
Where x = 4; the square was clicked on.
Where x = 8; the square is a mine.
Where x = C; the square was a clicked on mine.

Now, we can modify the assembly at 0x010036FA to set it to one of these values to show that there is a bomb there.

bool MineVisCheat = false;

void ToggleMineVisibleCheat()
{
	int *ori_OR = ((int*) 0x010036FA);
	BYTE MOV[3];
	if(MineVisCheat) {
		MOV[0] = 0x80;
		MOV[1] = 0x08;
		MOV[2] = 0x80;
	}
	else {
		MOV[0] = 0xC6;
		MOV[1] = 0x00;
		MOV[2] = 0x8A;
	}
	DWORD dwProtection;
	VirtualProtect((LPVOID)0x010036FA, 1, PAGE_EXECUTE_READWRITE,&dwProtection);
	memcpy(ori_OR, &MOV, 3);
	VirtualProtect((LPVOID)0x010036FA, 1,dwProtection,&dwProtection);
	MineVisCheat = MineVisCheat^true;
}

Using the values above, this sets the opcode from OR BYTE PTR DS:[EAX],80 to MOV BYTE PTR DS:[EAX],8A. You would modify the image shown by changing the last byte of MOV to one of the values from the list.

010036EE   |.^\75 D7             JNZ SHORT winmine.010036C7
010036F0   |.  C1E0 05           SHL EAX,5
010036F3   |.  8D8430 40530001   LEA EAX,DWORD PTR DS:[EAX+ESI+rgBlk]
010036FA       C600 8E           MOV BYTE PTR DS:[EAX],8A
010036FD   |.  FF0D 30530001     DEC DWORD PTR DS:[cBombStart]
01003703   |.^ 75 C2             JNZ SHORT winmine.010036C7

One thing to take into account however, is that this will require you to start a new game before the effects come into place, as it overwrites the values while the grid is being drawn out.