Log in

View Full Version : Reversing our M$ game: Minesweeper


ZaiRoN
September 6th, 2003, 18:35
Hi All,
I would like to present something unusual for this area, a game reversing (a M$ game reversing ).
In my mind I was thinking to modify the program in order to show where the bombs are. This is not the classical "game cheating" but something a little bit hard, that will imply not only a reverse approach but also a programming aspect.

Talking with Kayaker, he suggeted some tasks:
1. Give option to user to use the "cheat" feature. Add a new menu item or an hotkey (or why not both of them), something that will let you to switch from the normal to the cheat mode and viceversa.
2. Try to understand how (and where) the program stores the position of the bombs.
3. Show where the bombs are as a *mouse-over* effect as the cursor moves over each grid square. How does the program unhide the grid windows? Show them briefly as the mouse cursor passes by. Or use a tooltip or other way of signifying there's a hidden mine down there.
4. Package your cheat in a dll module, and add the new functionality patch in memory with only the code that will call the export function(s).

More or less it's all here. We will use the standard 2k/XP (english) version of the game as a target but everyone can play with his own version. I take a look at different versions of this game, they are similar and you should not have problems :-)

Before leaving you, I would like to suggest these two tutorials as a support for the beginning of the project:
http://www.woodmann.net/fravia/menusspa.htm
http://www.woodmann.net/fravia/TracePlus_MenuPatch.html

There are some interesting aspects involved in this project so, don't be shy and don't hesitate to ask something; there are many people willing to help you

Regards,
ZaiRoN

Bengaly
September 14th, 2003, 14:04
http://codeproject.com/csharp/minememoryreader.asp?target=minesweeper

ZaiRoN
September 14th, 2003, 14:26
Hi Bengaly,
I know this article and would have post the link in the future . It's a nice article but it's only a simple process memory reader :-p

Ciao,
ZaiRoN

Bengaly
September 14th, 2003, 18:03
Hi ZaiRon,
hehe yeah i know =)
you challenge is very good practice, was just reffering to this article of some will want to know stuff faster

ughugh
September 15th, 2003, 04:10
I used to play Minesweeper quite a lot in the Windows 3.1 days (I did the expert on ~100 seconds), so forgive me for remembering this cheat..:

http://www.eeggs.com/items/470.html

Kayaker
September 15th, 2003, 14:00
Quote:
Originally posted by ughugh
forgive me for remembering this cheat..:
http://www.eeggs.com/items/470.html


Lol, that's a good one. Sure enough there's this stupid little pixel at 0,0 on your desktop which blinks on and off as you pass over a mine. Start up a winmine game, type 'xyzzy' and press Enter, then press one of the Shift buttons and if you squint really hard you can see it working.

Now try setting a breakpoint on SetPixel and do the same and you can see which code is called to perform this amazing MS trick, heh. If you have the Windows Debug symbols installed, under ..\symbols\exe is the winmine dbg version which can be loaded into Softice and it's a bit easier to follow the 'xyzzy' Eegg. I always wondered how people found Easter eggs...

Well this is great, but has anyone except ZaiRoN given a go at reversing this thing or is the challenge to find how many cheats already exist on the net?

Kayaker

Bengaly
September 15th, 2003, 17:00
Quote:
but has anyone except ZaiRoN given a go at reversing this thing or is the challenge to find how many cheats already exist on the net?


hi Kayaker,
yeah i give it a shot, for now i found a useless function in minesweeper and removed it's code to inject the caller code of the dll, it works ok, i guess i now need to play with the app and see how things works in order to hook it up with the dll.
dunno how much time it will take me.
ttl

ZaiRoN
September 20th, 2003, 12:07
Hi All,

I did take a glance at the target starting from the easy part, the new menu item. In this post I will explain the way I used to add the new menu item, find the part of the window procedure where menu items are processed and finally write the first dll function.

Add new menu item
As always happens, this is the most easy part of the reversing session. Load the file into your preferred resource editor and add the new item under the menu section. You have to assign an identifier (a number...) to the new item, remember that it must be a number that is not used by the other items.

Find the point where the program checks whether a menu item is been pressed
This task is useful because we need to add/manage the new menu item, the special mode option. If you check the 'special mode', the game will show you the bomb, otherwise the game will remain the same.
There are many ways to solve this second point.
- You can use Spy++, a little application that gives you a view of many informations about a process that is running on your system. Run Minesweeper and then run Spy++. You will see a list of the running processes, find Minesweeper process and double click on it. A dialog will show you some informations about the game, one of them is the window procedure.
- Another way to locate the window procedure is to use Ida. This is more or less the way explained by +Spath in his tutorial.
Load the file into ida and press ctrl-p; a dialog appears. This dialog contains the functions used by the program and, we are interested in WinMain function, the one used to define the window class. This is useful because from here you can understand where the window procedure is. The program uses RegisterClassW function to register the window class:

ATOM RegisterClass(
CONST WNDCLASS *lpWndClass // address of structure with class data
);

and:

typedef struct _WNDCLASS {
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS;

This structure contains the window attributes; we are interested in lpfnWndProc because it points to the window procedure. Look at the disasm Ida gave you and take the address we were looking for.

Ok, we now know where the window procedure is.
It's time to use a debugger, Ollydbg. Load the program and jump to the window procedure, we need to set a breakpoint. Remembering that we are trying to find the part of the procedure which check whether a menu item is clicked, we need to break if and only if a menu item is clicked. When an item is clicked, a specific WM_COMMAND message is sent to the procedure. This specific message contains the menu item identifier in the loworder word of the wParam parameter.
Take a look at the first part of the window procedure:
Code:
0100180A PUSH EBP
0100180B MOV EBP,ESP
0100180D SUB ESP,40
01001810 MOV ECX,DWORD PTR SS:[EBP+C] <-- ecx is the message (i.e. WM_COMMAND)
01001813 PUSH EBX

If we want to catch the click over one of the menu item we need to set a conditional breakpoint (Shift+F2) at 1001810. The condition I have used is:
[EBP+0Ch]==111H && [EBP+10H]==209H
where '[EBP+0Ch]==111H' checks for the WM_COMMAND message and '[EBP+10H]==209H' checks whether the 'Beginner' menu item is been clicked (you can use your preferred menu item :-)). Run the program and click on the chosen item, Olly breaks. Steps a little and you will surely find the place where the program checks which menu items is been pressed. This piece of code is what we were looking for; later on we will add a new cmp with the id of the new item when we will patch the program in memory.

Write the first dll function
Now that we know all these informations, we can easily write the first function of the dll, I will call it changeItemStatus.
This function will check/uncheck the menuitem enabling or disabling the special mode, nothing more. To check/uncheck the item I will use CheckMenuItem function:

DWORD CheckMenuItem(
HMENU hmenu, // handle to menu
UINT uIDCheckItem, // menu item to check or uncheck
UINT uCheck // menu item flags
);

An easy function to use. It needs:
1. the handle of the menu. We don't have the handle but since the function is imported and since some of the items of the menu could be checked/unchecked, the easiest way to retrieve the handle is to put a breakpoint on this function and hit one of the checked items, for example the 'sound' item. Perfect, Olly will break showing us where the handle is stored.
2. the id of the menu. It's the number we gave to our new item
3. we are interested in two values:
MF_CHECKED (08h): sets the check-mark attribute to the checked state.
MF_UNCHECKED (00h): sets the check-mark attribute to the unchecked state.

I did write the dll in asm but no one will stop you from writing it in other languages. Here is my partial dll:

Code:
.386
.model flat,stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib

.data

.code
DllEntry proc hInstance:HINSTANCE, reasonWORD, reserved1WORD
mov eax, TRUE
ret
DllEntry Endp

; Change the status of the new item
changeItemStatus proc
.IF word ptr [ebp+10h] == 212h ; 212h is the id I used for the new item
mov ebx, 10049A2h ; byte [10049A2] stores the state of the menu item
xor byte ptr [ebx], 08h
movzx eax, byte ptr [ebx]
mov ebx, 10052BCh ; address where the menu handle is stored
invoke CheckMenuItem, dword ptr [ebx], 212h, eax
.ENDIF
ret
changeItemStatus endp

End DllEntry
The only thing that requires an explanation is the value stored at 10049A2. This byte is:
00h if the item is unchecked
08h if the item is checked

This is the first function and in my mind I was thinking to add only another function that will unhide a single cell if the special mode in on. We will see later, for the moment it's all...

ciao,
ZaiRoN

Aquatic
September 20th, 2003, 17:53
Gamehacking was a good way of learning reversing (for me). Cracks alone can be very one-dimensional, but gamehacking forces you to learn a bit of everything.

... my 2 cents

ZaiRoN
September 27th, 2003, 07:01
We need to find a way to break when a cell is clicked. Exploiting the work we have done in the previous task, I will use another conditional breakpoint at 1001810; the idea is to break when the left mouse button is pressed (and then released).
When this button is released, a WM_LBUTTONUP (202h) message is posted and so, the new condition will be: [ebp+0Ch]==202h
Set the new breakpoint and click on a cell; Olly breaks, steps a little until Olly will tell you that you have found the place:
Code:
01001C30 XOR EDI,EDI ; Cases 202 (WM_LBUTTONUP) <-- thx to Olly :-D
01001C32 CMP DWORD PTR DS:[1005160],EDI
01001C38 JE winmine.01001D4C
01001C3E MOV DWORD PTR DS:[1005160],EDI
01001C44 CALL DWORD PTR DS:[<&USER32.ReleaseCaptu> ; ReleaseCapture
01001C4A TEST BYTE PTR DS:[1005010],BL
01001C50 JE SHORT winmine.01001C5C
01001C52 CALL winmine.0100373E ; hmmm...
01001C57 JMP winmine.01001D4C ; jump at the end of the procedure
This is the code that manage the WM_LBUTTONUP command; as you can see there is a call to ReleaseCapture, a jump at the end of the procedure and a suspicious call. Maybe it's the call that will show us where the bombs are...

At this point I should tell you where the bombs are but, I prefer to signal an interesting article (thx Kayaker) because it is useless to write the same things two times.... The author shows you how to locate the code I posted above and after that, he shows where the bombs are. You can find the article at:
http://www.codeguru.com/mfc/comments/50642.shtml

See you on the next step...

ZaiRoN

ZaiRoN
October 7th, 2003, 12:26
In this post we will see how to play with the new dll; as support, I would like to add a new tutorial:
http://www.woodmann.net/fravia/kayaker_RegmonPlus.htm
Read it, it's full of interesting informations ;-)


Now that we know almost everything about the program, we have to show these damned bombs. I will try to explain you how to show them when the mouse pass over a cell; the bombs will remain showed until the end of the game.

We need to show the bombs when the mouse passes over a cell (that hide a bomb) so, we need to find where the program checks for WM_MOUSEMOVE message. Now, if you read the previous messages, you should not have problems to find the location; as you can see this type of message is not so useful for the original program, it makes a simple jump to the end of the routine.

When we will patch the program in memory we will need to change this jump into a jump to our code.
Which are the runtime modifications to apply at the program?
- a new compare instruction for the new itemmenu (remember the old message...)
- the jumps to the new code (one for the item menu and one for the wm_mousemove)
- the code necessary in order to call the dll functions
I will only speak about the last modification, the other two are very easy; if you have problem do not hesitate to ask.

How to call a dll? Practically you have to call 3 simple functions: LoadLibrary, GetProcAddress and FreeLibrary.

LoadLibrary: it's the first function to use and it maps the dll module into the address space of the calling process.
HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName // address of the string that names the executable module
);
The function returns a handle that can be used in GetProcAddress to get the address of a dll function.

GetProcAddress: returns the address of the exported dll function.
FARPROC GetProcAddress(
HMODULE hModule, // handle to DLL module (the value returned by LoadLibrary)
LPCSTR lpProcName // address of the string that names the function
);

FreeLibrary: unmaps the dll module fro the address space of the calling process
BOOL FreeLibrary(
HMODULE hLibModule // handle to DLL module (the value returned by LoadLibrary)
);

This is the code I used to call the dll and it's the code you have to inject into a cave of the original program:

Code:
010049A2: ; Initial address
specialMode BYTE 1h dup(0) ; specialMode=08h if specialMode is enable
; otherwise specialMode=00h
dllName db "zaiWinmine.dll",0 ; The name of the new DLL
; The name of the 2 exported functions:
changeItemStatus db "changeItemStatus",0 ; Changes the status of the itemmenu
revealCell db "revealCell", 0 ; Reveal a bomb under a cell

; From 'specialMode' item clicked (10018C4)
push 10019ADh ; Address from where to return to
pushad
push offset changeItemStatus ; Used by GetProcAddress
jmp @go

; From WM_MOUSEMOVE event (1001b50)
push 1001DD5h ; Address from where to return to
.IF [specialMode] == 00h ; Is the special mode enable?
pop eax ; specialMode is disable, no need to show the bomb
jmp eax
.ENDIF
pushad
push offset revealCell ; Used by GetProcAddress
jmp @go

@go:
push offset dllName ; The name of the dll
call LoadLibrary
mov esi, eax
push eax ; Handle to dll module
call GetProcAddress
call eax ; call the exported function
push esi ; Handle to dll module
call FreeLibrary
popad
pop eax
jmp eax ; Jump back to the original code

I think there is nothing to add.

Now we can write the other function of the dll, the one I called revealCell. What this function is supposed to do? Remembering that the function will be called on every single movement of the mouse (and let's suppose specialMode is enable), we have this scheme:

1. Is the pointer inside the grid?
No: quit from the function
Yes: jump to 2

2. Is the cell hiding a bomb?
No: quit from the function
Yes: reveal the bomb

The point number 1 is necessary because the bombs can not be outside the grid while the point number 2 is the final check.
The first cell (upper left corner) starts from coordinates 0x0C,0x37 and each cell is 0x10,0x10. Now that we know this information, we are able to implement the point number 1: a simple 'if' based on the coordinates of the point where the mouse has been pressed. How did you know the x and the y coordinates where the mouse has been pressed? WM_MOUSEMOVE will tell you; infact:
WM_MOUSEMOVE
fwKeys = wParam; // key flags
xPos = LOWORD(lParam); // horizontal position of cursor
yPos = HIWORD(lParam); // vertical position of cursor
Here is all that you need :-D

Point number 2: first of all, we have to check whether a cell hides a bomb; this is not a problem because we know where the program stores the grid in memory and, we will easily perform this check but... how can I reveal a bomb under a cell? The answer is inside Graphics Device Interface (GDI) library; in this specific case we will use GDI to display a bitmap. My new version of the game displays the image that has the bomb on the red background (open the program with a resource editor, you will find the image I am talking about under "Bitmap-410-1033". You can display the image you prefer taken from the proggie or created by you. To display this image I use these functions: GetDC, BitBlt, ReleaseDC.

GetDC: retrieves a handle of a display Device Context (DC) for the client area of the specified window.
HDC GetDC(
HWND hWnd // handle of window
);
This is the first function we have to call and it takes a single parameter. How do we know the handle of the window? Simple, these 3 functions are used by MineSweeper and so, the best way to understand how to use them is to bpx them and to look how they are used. This time, put a bpx on GetDC and run again the program. Ollydbg will surely break and the only thing you have to do is to take note of the address that contains the handle...

BitBlt: it clips an image from a source DC to a destination DC.
BOOL BitBlt(
HDC hdcDest,// handle to destination DC, the GetDC returned address
int nXDest, // x-coordinate of destination rectangle's upper-left corner
int nYDest, // y-coordinate of destination rectangle's upper-left corner
int nWidth, // width of destination rectangle
int nHeight,// height of destination rectangle
HDC hdcSrc, // handle to source device context
int nXSrc, // x-coordinate of source rectangle's upper-left corner
int nYSrc, // y-coordinate of source rectangle's upper-left corner
DWORD dwRop // raster operation code
);
As before, the best way for to understand how to use the function is to bpx it on Ollydbg. If you want to display another image you have to change the 6° parameter: hdcSrc.

ReleaseDC: the ReleaseDC function releases a device context
int ReleaseDC(
HWND hWnd, // handle of window
HDC hDC // handle of device context
);

Ok, it's time to past the source of the revealCell procedure (add it to the code I gave you before):
Code:
.data
oldRow db "01004998"
oldColumn db "0100499C"

; Display the image under a cell
revealCell proc
pushad

; Is the mouse pointer inside the grid ?
.IF word ptr [ebp+14h] < 0Ch || word ptr [ebp+16h] < 37h
jmp @exitRevealCell
.endif

xor eax, eax
mov ax, word ptr [ebp+14h] ; The x-coord of the mouse
sub eax, 0Ch
shr eax, 4
add eax, 1 ; The number of the column -inside the grid- where the mouse is pressed

xor ebx, ebx
mov bx, word ptr [ebp+16h] ; The y-coord of the mouse
sub ebx, 37h
shr ebx, 4
add ebx, 1 ; The number of the row where the mouse is pressed

mov edi, 1004998h
.IF ax == word ptr [edi] && bx == word ptr [edi+4]
; It's in the same cell than before, no need to display the same image again
jmp @exitRevealCell
.ENDIF

mov ecx, ebx
mov edx, ecx
shl edx, 5
mov ebx, 1005700h
add ebx, eax
add ebx,edx
; Reveal the cell if and only if there is an hidden bomb
.IF byte ptr [ebx] == 8Fh
mov edi, 1004998h
mov dword ptr [edi], eax ; Save the column
mov dword ptr [edi+4], ecx ; Save the row
push eax
push ecx
mov esi, 10052A8h ; Pointer to handle of window
push [esi] ; Handle of window
call GetDC
mov esi, eax
pop ecx
pop eax
shl ecx, 4
add ecx, 27h
shl eax, 4
sub eax, 4
mov ebx, 1005AE0h
push 0CC0020h ; SRCCOPY flag
push 0
push 0
push [ebx+48] ; Source DC, the image to display
push 10h ; Height of the rectangle
push 10h ; Width of the rectangle
push ecx ; y coord
push eax ; x coord
push esi ; Handle to destination DC
call BitBlt
mov ebx, 10052A8h
push esi ; Handle of DC
push dword ptr [ebx] ; Handle of window
call ReleaseDC
.ELSE
mov dword ptr [edi], 0
mov dword ptr [edi+4], 0
.ENDIF

@exitRevealCell:
popad
ret
revealCell endp
I think that's all...

Ciao,
ZaiRoN

evaluator
October 8th, 2003, 02:57
because of your good tries for newbies, i done gift for you.

advant
January 28th, 2004, 12:44
What is a good tutorial on learning how to add the DLL into the import table and how to add code to an exe without corrupting the image and static references to code already there?

advant
January 28th, 2004, 13:36
Also, how do you patch the memory to add a new compare for the WM_COMMAND.

thanks

Foreigner
January 29th, 2004, 08:49
There is a final tutorial for this project at http://www.woodmann.com/fravia/what_new.htm
Another tutorial that may help you is http://www.woodmann.com/fravia/kayaker_RegmonPlus.htm

Foreigner