⬆️ ⬇️

The study of the puzzle game "Minesweeper" (continued)

We continue our study of the game "Minesweeper" from Microsoft.



This article is a continuation of the first article .



What will be discussed:

1) Hacking based on buffer overflow

2) Hacking game mines

3) Study the architecture of the game.

')

I



Open the game, followed by CE (Cheat Engine). Join the game process:



image







and then look for the value of the open cells of the field at the moment:







further we look for the instruction (instructions) which somehow interact with the found address. To do this, add the address found in the AddressList, right-click and select "Find out what accesses this address". An additional window will open. Go to the game and click on some cell:







It turns out that 3 instructions interact with the address (the first and the third are similar, but the addresses are different). Last time, as some remember, we were interested in just those instructions that were written to memory directly (ASM-command "mov"), that is, the first and third, but this time we will focus on the second instruction, which is also like the rest, writes in memory, but in a slightly different way. It increments some value in memory as many times as many cells of the field have been opened (in this case it is one cell).

Ok, this instruction is quite appropriate in order to overflow the buffer, which stores the number of open cells in the field at the moment. If we look in the debugger how the instruction works, then we immediately turn our attention to the following sequence of commands:







at first it may seem that this chain is somehow connected with checking the “inside” of the cell, that is, determining whether the player has poked the mine with a mine. I will say that nothing like this is not observed. You can check it yourself by putting BreakPoint on the first comparison, for example. Then when you click on a regular cell, it will work, but it will not work when you click on a cell with a mine. From this it follows that the definition of the inside of the cell is determined somewhere else.



In this case, the only interesting thing is the increment command:



inc [rcx + 18]



We try to change this command so that when a new cell is opened, not one is added, but, for example, the maximum integer of the type INTEGER, that is, 4294967295 (HEX = FFFFFFFF):







Then we go into the game and start saping (for clarity, you can start a new game and select a larger field):







As you can see, buffer overflow works in our favor, but not to the full. At the first click on the field, more than half of all the cells immediately opened (in this case, more than 128 cells, as you can see). Playing on we will still be able to stumble on a mine and "explode." When losing the game and the subsequent demonstration of all the mines, we note that along with that half of the dropout cells, half of all the mines were eliminated. This, of course, facilitates the passing of the game, but not completely - by more than half.



II



Now we will consider in what way it is possible to “manually” interact with mines.



To do this, restart the game (close the game, then open). And again we attach to its process from CE. In order to find the instruction that interacts with the number of mines in the game, you must first find the address in the memory where the integer value of the number of mines is stored. General algorithm:



Search memory address with the number of mines -> Search and study instructions, working with value -> debugging



1) Looking for the current number of minutes in memory. To do this, you need to do an algorithm for finding the value similar to that used to search for the current number of open cells in the game field. It's easy to find the address in 2-3 searches: first select the “Novice” difficulty level (10 min) and look for the number 10 in memory, then change the difficulty level, for example, to “Professional” (99 min) and look for the number 99 in memory. Repeating it several times, you get something like this:







One address. Add it to AddressList and see what interacts with it. To do this, restart the (F2) game:







Yeah, 4 instructions fell right away. We are interested only in those who work with memory. There are two of them: the first and the third, but the third does not write the value in memory, but reads it, so we are not interested in it at the moment. Investigate the first instruction in disassembler:







Hmm, very interesting ... here is the initialization of many values. There is a theory that we are pleased with the function that is triggered at the very start of the game, which is engaged in the initialization of all in-game values. Select the current function (right click on the instructions, select "Select current function" in the menu):







The function is quite small. If you scroll a little lower, it can be noticed. Ok, let's try to put the breakpoint at the beginning of the function:







Then go into the game and start a new game (F2). The breakpoint will work, hence this function is performed at the start of a new game. The theory is confirmed! Now we return to the “mov” instruction related to the number of mines. The instruction can be otdebazhit, changing its second parameter - the value recorded at the address in the place where the current number of minutes is:







In this case, the number of mines corresponding to the current level of complexity has been replaced by four. In other words, we changed the number of mines to 4. Go to the game, start a new game:







As you can see, the number of mines has changed. Playing on:







Cool :) The problem with mine is solved.



III



The final stage will be the study of the architecture of the program code of the game. First, let's imagine the architecture of any standard application. It turns out about the following:



1) Connection of libraries used in the program

2) Class descriptions and implementation of their methods

3) Entry point. (In C, for example, this is a function of main ())



Actually, surprisingly, according to the same principle, the Minesweeper application is built.

Consider the pseudocode characterizing the internal architecture of the game:



class Application {...} // application class

class Game {



***



void gameStart () {...}

void gameLose () {...}

void gameWin () {...}



void createField () {}



void Init (params) {...}



***



}



int Main ()

{

Game game;

game.gameStart ();

}





As you remember, in paragraph II of this article we found the function that is performed when starting the game. It can be assumed that its real code is not far from the beginning of the main function Main() . Then somewhere above should be the source code of the classes.



Now we investigate it under the disassembler. A little above the function we found earlier, we notice the following thing:







These comments can only mean one thing - certain logical blocks that perform their functions. This may not necessarily be the architecture of the game class "Game". It can also be normal functions (higher level language). After reading the comments, you can easily understand what one or another logical block of data is responsible for.



For example, it is easy to understand that the [RandSeed] block is responsible for generating a pseudo-random number, which will then be used to set random coordinates of the mines. Block [Mines] is used to generate random coordinates min. And so on.



That, in fact, is all that concerns the game "Minesweeper".

In the future, other games will be explored, but already more serious and more complex level :)

Source: https://habr.com/ru/post/167829/



All Articles