Sunday, January 8, 2012

Minesweeper patch: Stop the timer

In this post I will demonstrate how to patch the Minesweeper game: the task will be to stop the timer from incrementing its count.

The task requires some basic assembly language knowledge. If you don't have that knowledge, then Art of Assembly is a great book to start with.

The executable I will be working on is Windows XP version of Minesweeper, called winmine.exe. The file is also available here (MD5=9c45d38b74634c9ded60bec640c5c3ca), download it and rename it to winmine.exe.

The symbols file is required as well. You can download it using symchk command-line tool (which is part of Debugging Tools for Windows), with the below parameters:
symchk /s SRV*c:\symbols* /if winmine.exe
I will use IDA disassembler (freeware version available here) for generating the assembly source code from winmine.exe executable. To simplify the demonstration, I'm loading the symbols file (winmine.pdb) to IDA as well. Otherwise, function names, for example, will have meaningless names in generated code.

Discovering the code to patch

IDA generates plenty of assembly source code for the winmine.exe executable. Function names is a reasonable place to start browsing at. By examining the functions list I found the DoTimer() function.

Let's see its content:
.text:01002FE0 _DoTimer@0   proc near        ; CODE XREF: MainWndProc(x,x,x,x)+1A3 p
.text:01002FE0         cmp   _fTimer, 0
.text:01002FE7         jz   short locret_1003007
.text:01002FE9         cmp   _cSec, 3E7h
.text:01002FF3         jge   short locret_1003007
.text:01002FF5         inc   _cSec
.text:01002FFB         call  _DisplayTime@0 ; DisplayTime()
.text:01003000         push  1
.text:01003002         call  _PlayTune@4   ; PlayTune(x)
.text:01003007 locret_1003007:             ; CODE XREF: DoTimer()+7 j
.text:01003007                     ; DoTimer()+13 j
.text:01003007         retn  

Two variables are being used here: _fTimer and _cSec. The _cSec variable is most probably the one that holds the seconds counter, since it is first compared to 3E7h (decimal 999 - which is game's maximum seconds), and then its value incremented by 1. Let's find all _cSec usages in the program (in IDA, right click on _cSec symbol, and select Jump to xref to operand):

_cSec variable's value incremented twice: in DoTimer() and in DoButton1Up() functions. The inc instruction in DoButton1Up() function is called only once in the beginning (once the first cell selected/opened). All the subsequent counter increments executed by DoTimer() function. In order to hold the timer zeroed need to remove both inc instructions.

Patching the program

Patching the program is as simple as replacing instructions with another instructions. The only thing to note here is that replaced and replacing instructions must have the same total opcode byte size.

In IDA, go to the first inc _cSec instruction. Click on the instruction to select it. From Edit menu, select Patch program and then click on Change byte. This will open a dialog with 16 byte values.
By default, Patch program menu is disabled. To enable it, close IDA and go to its installation folder. Open cfg folder, then find and open idagui.cfg file. Find the DISPLAY_PATCH_SUBMENU key and change its value to YES. Re-launch IDA, and verify that Patch program submenu is available.
The inc _cSec instruction is 6 bytes long. The replacing instruction should do nothing - which is exactly what NOP instruction does. nop is 1 byte long: thus, one inc instruction should be replaced with 6 nops.

Therefore, only 6 first bytes (FF 05 9C 57 00 01) in dialog should be replaced to six nop instructions (90 90 90 90 90 90). Same steps should be done for the second inc _cSec occurrence. 

Once instructions replacing was done, I created a difference file. It's just a textual file that contains three columns: the offset address, the replaced byte value and the replacing byte value. 

In IDA, click on File menu, then Produce File and select Create DIF file.
You should see the following output:

This difference file is created by The Interactive Disassembler

000023F5: FF 90
000023F6: 05 90
000023F7: 9C 90
000023F8: 57 90
000023F9: 00 90
000023FA: 01 90
00002C30: FF 90
00002C31: 05 90
00002C32: 9C 90
00002C33: 57 90
00002C34: 00 90
00002C35: 01 90

The final step is to open the winmine.exe executable in your favourite hex editor (my favorite is HexEdit) and to alter the bytes. If you made many changes, and your difference file is too large for manual editing then this utility can automate this task.

The result

The patched executable will not increment its timer counter anymore:

No comments:

Post a Comment