Cheating Minesweeper - by Sunshine

1 - Introduction

Today we gonna have a look at the Windows game 'Minesweeper'. Let's see if we can make it that we always win. :-)

Tools used: Ollydbg (I use Shadow's MOD from version 1.10)
  A C Compiler (if you wanna recompile my loader)
  Knowledge of Windows API and 32bit Assembly (!)

You find Minesweeper in your system32 folder in your Windows directory, the file is called winmine.exe (if you installed these games with your windows installation). My file version of winmine.exe is 5.1.2600.0 (I have WinXP with service pack 2 (German)), so if you have another version, the offsets and memory locations can be different.

2 - First look at the target...

We have a gamefield of choosable width and height, also the number of mines we can define. Goal is to open all fields without a mine; as soon as we select a mine, we loose. Note that a field is uncovered when we release the mousebutton and not if we press it; that means we have to look at the WM_LBUTTONUP message.
Let's think how we would code the gamefield as a programmer (at least as I would do it :-) : as data structure we would probably use a one- or two-dimensional array of some type, perhaps an integer, defining each possible state with a special value (e.g MINE=0, EMPTY=1 and so on...). That means at start of a new game a memory block of special size (most probably proportional to width*height of gamefield) is allocated and mines are placed either randomly or with help of an special algorithm. On every release of the left mousebutton, we look if the cursor is over a single field, then we look at the corresponding element in our array. If it was a mine -> gameover, otherwise we would calculate the number to inform the player about near mines. Finally we repaint the gamefield and that's it.
Ok, with that knowledge in mind we can start the real part...

3 - Let's begin...

Ok, start Ollydbg, open a backup copy of winmine.exe and begin tracing:

01003E21 > $ 6A 70        PUSH 70                       :: Entrypoint
01003E23 . 68 90130001    PUSH winmine.01001390
01003E28 . E8 DF010000    CALL winmine.0100400C         :: install SE Handler

After some checks and commandline parsing we step into the following call which starts the real game:

01003F8B . 53             PUSH EBX
01003F8C . 53             PUSH EBX
01003F8D . FFD7           CALL EDI                      :: kernel32.GetModuleHandleA
01003F8F . 50             PUSH EAX ; |Arg1
01003F90 . E8 5BE2FFFF    CALL winmine.010021F0         :: <- Main call (step into)

Stepping into this call, we see normal startup code like initiation of common controls and loading icons and cursors; after that we see that a window class is registered with 'RegisterClass', the main window is created and the application enters the message loop. So we have to find out where the messages are processed. As we know, the address of the window procedure is part of the WNDCLASS structure passed to the RegisterClass call. Have a look:

0100225A   897D B4        MOV DWORD PTR SS:[EBP-4C],EDI
0100225D   C745 B8 C91B0  MOV DWORD PTR SS:[EBP-48],winmine.01001BC9 <- lpfnWndProc
01002264   897D BC        MOV DWORD PTR SS:[EBP-44],EDI
01002267   897D C0        MOV DWORD PTR SS:[EBP-40],EDI

01002283   8D45 B4        LEA EAX,DWORD PTR SS:[EBP-4C]  :: eax = ptr to wndclass
0100228B . 50             PUSH EAX ;                     :: push that pointer
01002292 . FF15 CC100001  CALL DWORD PTR DS:[<&USER32.RegisterClassW>];RegisterClassW

For the beginners: How do we know that address 1001BC9 is our wanted window procedure? Well, very often it's a hardcoded address like in our case. But to be more sure, we can look at the definition of the WNDCLASS structure in MSDN. There we see that the pointer to the window procedure is the second member in this structure: here in our case the first member is in [EBP-4C], so in [EBP-48] must be second (the first member is of type UINT and so 4 bytes long).

01001FDF > 33FF           XOR EDI,EDI ; Cases 202 (WM_LBUTTONUP) ....
01001FE1 . 393D 40510001  CMP DWORD PTR DS:[1005140],EDI
01001FE7 . 0F84 BC010000  JE winmine.010021A9
01001FED > 893D 40510001  MOV DWORD PTR DS:[1005140],EDI
01001FF3 . FF15 D8100001  CALL DWORD PTR DS:[<&USER32.ReleaseCapture>]
01001FF9 . 841D 00500001  TEST BYTE PTR DS:[1005000],BL
01001FFF . 0F84 B6000000  JE winmine.010020BB
01002005 . E8 D7170000    CALL winmine.010037E1         :: Process Game AI
0100200A . E9 9A010000    JMP winmine.010021A9

The call at 1002005 is the only one which is processed when a mousebutton is released, so here must be processed the game logic. Step into it...

010037E1 $ A1 18510001    MOV EAX,DWORD PTR DS:[1005118]  :: selected field in X
010037E6 . 85C0           TEST EAX,EAX
010037E8 . 0F8E C8000000  JLE winmine.010038B6
010037EE . 8B0D 1C510001  MOV ECX,DWORD PTR DS:[100511C]  :: field in Y direction
010037F4 . 85C9           TEST ECX,ECX
010037F6 . 0F8E BA000000  JLE winmine.010038B6
010037FC . 3B05 34530001  CMP EAX,DWORD PTR DS:[1005334]  :: size of playfield in X
01003802 . 0F8F AE000000  JG winmine.010038B6
01003808 . 3B0D 38530001  CMP ECX,DWORD PTR DS:[1005338]  :: size of playfield in Y
0100380E . 0F8F A2000000  JG winmine.010038B6
01003814 . 53             PUSH EBX

At 1005118 the selected field in x-direction is saved, in 100511C the one in y-direction. (Note: To find that out, you can rightclick one of these lines and select 'Find reference to address constant'. Only at three locations a value is written to this address; with some effort we find out that at 10031DD/10031E4 on every mouse move the fields under the cursor are written to these locations).
Both values are checked for validity: if they are greater than zero and smaller than the actual width/height.
Some lines below we have:

01003818    833D A4570001 00 CMP DWORD PTR DS:[10057A4],0  :: first field clicked?
0100381F |. 75 4A            JNZ SHORT winmine.0100386B

After some runnings, it's easy to find out that at 10057A4 a flag is stored saving if it's the first player's try of current game. If so, we don't jump but do some initialization things like starting the game time. Going on, we discover following lines:

0100388D |. 51            PUSH ECX               :: selected row
0100388E |. 50            PUSH EAX               :: selected column
0100388F |. E8 23FDFFFF   CALL winmine.010035B7

Seems promising at first, but this call is not always executed. Step over it and see what coming next:

01003896 > \8BD1           MOV EDX,ECX          :: edx = row of selected field
01003898 . C1E2 05         SHL EDX,5            :: row = row * 32
0100389B . 8A9402 40530001 MOV DL,BYTE PTR DS:[EDX+EAX+1005340]  
                           -> dl = 1005340 + (row * 32) + column
010038A2 . F6C2 40         TEST DL,40
010038A5 . 75 0F           JNZ SHORT winmine.010038B6
010038A7 . 80E2 1F         AND DL,1F
010038AA . 80FA 0E         CMP DL,0E
010038AD . 74 07           JE SHORT winmine.010038B6
010038AF . 51              PUSH ECX                 :: push row
010038B0 . 50              PUSH EAX                 :: push column
010038B1 . E8 5CFCFFFF     CALL winmine.01003512    :: game logic
010038B6 > FF35 60510001   PUSH DWORD PTR DS:[1005160]
010038BC . E8 52F0FFFF     CALL winmine.01002913    :: paint routine

Ok here is the important part: main purpose of the call at 10038BC is painting the changed gamefield to screen. The call at 10038B1 does the main game logic, e.g it checks again if it's the first try (if so it handles a special case as you may have noticed that on your first try you never hit a mine) and calculates the new field, dependent if the player hit a mine or not.
But let's have a look at the lines in front of this call: the selected column and the selected row multiplied by 32 are added to an address constant. And I can tell you, this calculation also happens quite often inside the game logic call. Follow 1005340 in dump and .... we see this:

It looks as we have found the memory location where the gamefield data is stored. If you want you can change the width, height and number of mines in Minesweeper to become sure that this is really the actual gamefield data.... and yes it is :-)
Well, in fact that's all we have to know: cause the address is a constant, this data is always(!) stored at 1005340 which makes things much more easier than to handle with pointers to changeable memory places. To access a special field, you just do: field = 0x1005340 + (row * 32) + column. 0x8F means there is a mine, 0x0F means it's still uncovered, 0x40 it's uncovered.... the rest you can find out by yourself if you want but it's not necessary...
So what to do know? First I thought of patching, but it seems a bit complicated to me. We would have to check if the player has hit a mine and if so we could redirect the program flow that the field would stay uncovered. But that means inserting additional code into a cave, but what's more a problem is the fact that we have to change also the paint routine in this case (I tried some patching and got some strange graphic errors...). And we have seen that several game states/options etc. are saved somewhere in memory (e.g. is it the first player's try? Is a mouse button currently pressed? And so on...). Probably we would have to take a look at them, too in order to keep them valid.

That's why I have decided to code a little loader which doesn't change the Minesweeper file in any form. The loader starts minesweeper (or attaches a running instance), reads periodically the memory at our found address (0x1005340) and paints the current gamefield, including all mines of course. Have a look at the source, it's quite easy and well commented. Here a screenshot where you can see the loader in action:

I hope you found this little article and my source useful :-) C u again...
Questions, criticism? Mail me!

Sunshine, March 2006

This site is part of Sunshine's Homepage