Target Information #
- Architecture: x86_32
- OS: Windows
- Game: Minesweeper
Objective #
Find a way to reveal all the mines
Tools #
- x32dbg
Process #
How does the game draw the mines?
My objective is to reveal all mines in the start of a game. I presumed a good place to start would be to look at function calls related to the games graphics, but this idea fell flat. There are at least +25 functions being used from the GDI32 library (Graphics library the game uses). I quickly got overwhelmed by information and realized I needed a new approach to narrow the search.
While playing the game, I noticed when you click on a mine it would trigger an explosion sound then the game would display all the mines. So, with that in mind I formulated a new question…
How does the game call the function for the explosion sound?
I figured if I can find the function responsible for playing sounds, I should be able to traverse the game code from that point of reference. Because remember, during gameplay the logic is as follows:
Click on a mine > play audio > display mines
OR
click on a mine > display mines > play audio
I’m not entirely sure which function comes first because game execution makes it seem simultaneously. Using a debugger will be helpful here, since it will allow us to look over each instruction, line by line.
Using x32dbg, I begin the search for intermodular calls. This will provide me with a listing of all OS functionality the game uses. The search is still broad at this point, but I am looking for anything that might point me in the right direction.
Address=010038C8
Disassembly=call dword ptr ds:[<&PlaySoundW>]
Destination=<winmm.PlaySoundW> (71E82D80)
Address=010038E6
Disassembly=call dword ptr ds:[<&PlaySoundW>]
Destination=<winmm.PlaySoundW> (71E82D80)
Address=01003937
Disassembly=call dword ptr ds:[<&PlaySoundW>]
Destination=<winmm.PlaySoundW> (71E82D80)
These disassembly instructions look interesting. Let’s set a breakpoint on these addresses to see how the game uses <PlaySoundW>
, hopefully it relates to the explosion noise I’ve been hearing when clicking on a mine.
Reverse Engineering #
Now, with the breakpoints set, all we have to do is play the game with the intent of clicking on a mine and see if anything gets triggered.
Address 0x010038C8 #
Address=010038C8
Disassembly=call dword ptr ds:[<&PlaySoundW>]
Destination=<winmm.PlaySoundW> (71E82D80)
The breakpoint on this address was not triggered at all during gameplay. For now, I will skip this function and move on to the next two.
Address 0x010038E6 #
Address=010038E6
Disassembly=call dword ptr ds:[<&PlaySoundW>]
Destination=<winmm.PlaySoundW> (71E82D80)
Again, the breakpoint was not triggered during gameplay, moving on to the last function.
Address 0x01003937 #
Address=01003937
Disassembly=call dword ptr ds:[<&PlaySoundW>]
Destination=<winmm.PlaySoundW> (71E82D80)
Breakpoint for Address 0x01003937
gets triggered during gameplay due to the clock ticking audio. I had to unbreak this address or else the game is unplayable due to pausing every second. Out of the 3 addresses, this breakpoint was the only that was broken.
It would appear that the 3 addresses in question were of no help. But, I did see something interesting from our 3rd breakpoint.
|
|
The assembly bit above is a function frame and the last line 0x1003937
is our 3rd breakpoint location that we triggered. In this frame, we can see conditional branching to function <PlaySoundW>
, you can also see the different sets of arguments being passed for a specific condition. Let’s organize the data into a flow chart to get a different visual.
I set more breakpoints on lines 11,15, and 19 to test each one, I picked these lines because they are arguments being passed to the function call.
Line 11 gets triggered from clicking on a mine.
Line 15 never gets triggered during gameplay (I’m assuming this is for a win condition, since the only way I’ve been playing is by playing to lose).
Line 19 gets triggered from game clock audio.
This is the game state with a triggered breakpoint on line 11 (the lose condition). Visually, I know now that the reveal mines function is called before <PlayingSoundW>
.
|
|
Line 2 is the beginning of the function, let’s get references to this line. By doing this, we will find where we previously were before calling Function 0x010038ED
|
|
The results are 3 different addresses (Think of conditional jumps to 0x010038ED
). Again, we have to step into each code construct and find out what they do.
Reference 1 #
|
|
Using the same process, I set a breakpoint on line 2 0x1002FE0
the game halts execution due to the game clock ticking. What this tells me is that the subsequent lines of instructions have nothing to do with the game lose condition. So, we can stop our search here and exclude this path of logic. Which leads us with two more conditional logic paths for us to examine.
Reference 2 #
|
|
In this code segment, there is multiple calls (Lines 12,18,26,32).
We can exclude line 32 because that path leads us to <PlaySoundW>
.
To check each call we will set a breakpoint on the first one and then step-over it. Stepping-over the function will keep us from entering another function frame. We just want to see the functionality, not the details.
I set a breakpoint on line 12 0x01003497
and play the game to get the lose condition.
Game halts execution before calling the function and we can see only one mine being displayed. I step-over the function, but one mine is still displayed.
I move onto the next call, line 18. The results are what I wanted to see…
Finding revealMines() #
Bingo! Looks like we found the function!
The call to winmine_xp.1002F80
is the function responsible for revealing the mines. For a quick test, you can set the EIP to address 0x01002F80
and the debugger will execute the function.
Using, C++ I made a DLL containing a function pointer to the address of revealMines() and injected the DLL into the game’s process.