Tuesday, November 15, 2011

Map-Hack Tutorial

Map-Hack is a cheat for strategy games such as Warcraft or Starcraft, Dice n’ Slice games like Diablo or 3d shooters games like modern warfare. The cheat as most cheats is done in order to give an unfair advantage to the player who uses it. In case of Warcraft a map that usually looks like the one in the top left corner:

under cheating condition, would look like this:

or even better, like this:

Zoomed in:

The idea behind it, is to give more information on the map than what is found in the regular game play. Information like which gold mines are wealthier, and which enemy creatures are deadlier.
To create this kind of cheat one would have to go through two phases:

  1. Research, in which you need to figure out how and where the map data is stored in memory, and how to find where it is stored every time the game restarts.
  2. Developing, in which you write a code or a script to change the map data in real time.

In this tutorial I would only display the research phase up to the point of proof of concept.
To perform this research I need an Interactive Python Interpreter. This would bring up the question of which version of Python should I use.

  • Games are for Windows. So I'm gonna use Python for Windows (Better use the one from Python.org than Active-State Python).
  • I'm about to research a 32bit process so it is better, although not a must, to use 32bit Python.
  • The tool-chain I wrote is not ported to Python 3.x just yet, because I'm lazy, so Python 2.7 or 2.6 would have to serve me this time.
  • As for the Interactive Interpreter, I prefer the Dreampie.
The tool-chain is an external module, which helps in the task of memory research. The module is called NativDebugging, and is freely available at the following SVN: http://dreampie.sourceforge.net/. Install from source simply by:

python setup.py install

Or just get the installer from http://www.4shared.com/file/jE4xjHDb/NativDebugging-10win32.html? .
My NativDebugging module can be used as is, but it has extra GUI features if the PyQT module is found, so download it from:
and double click to install.

Now that I have got the entire environment set, let's get the party started. Let's start with something easy that everyone likes: Minesweeper (AKA: Winmine).

Important note: Use the old version of the minesweeper as many internal structures are different in the new Windows Vista version that looks like this:

I start by launching the game, and getting its' process id. There are many ways to get the process id, either to use the task manager or with the following command line:

tasklist /Fi "IMAGENAME eq winmine*"

Or from Python using the NativDebugging module.

>>> from NativDebugging.Win32.MemoryReader import *
>>> findProcessId('winmine')
[('winmine.exe', 376L)]

Next I want to tell the NativDebugging module what is the target.

>>> m = attach(376)

I need to get details about where in the 4GB virtual address space memory is allocated. The m.getMemoryMapByQuery method suits for the job.

>>> memMap = m.getMemoryMapByQuery ()

This operation might take some time, depending on how much memory is really used by the target process.
I start the search of the place in memory where the board of the game is set, using the technique called differential search. Differential search is a search in which one starts with a picture of all the memory, and slowly filters out things that are not what he is looking for, until he is left with a single result. In this example I'm looking for the top left square of the board. Setting up a new search is done as following:

>>> from NativDebugging.Win32.DifferentialSearch import *
>>> ds = DifferentialSearch(memMap, m)

Now I simply, change the value of the square by uncovering it

Next I filter out any memory address that  has not been changed:

>>> ds.removeUnchangedMemory()

The first filter can take up to few minutes, again depends on how much memory is used by the target process. If I wait for a little while, some of the memory would change by itself, so I can remove any memory that changed:

>>> ds.removeChangedMemory()

I continue by causing more changes to the first square and invoking the removeUnchangedMemory method.
To check how many potential memory addresses are left the "len" method on the DifferentialSearch object can be used. Filtering process is repeated until only one or two places in memory are left.
If you are following the process to this point, and all was done correctly your Python Interpreter should now look something like this:

(Click on the picture to enlarge it)

Now I can get the address that was found simply by looking at the DifferentialSearch object or by retrieving its' data in index 0.

>>> ds
0: 0x01005360: 10404040
>>> ds[0]

I save the address that was found in a new var to be used latter:

>>> board = ds[0]

The memory can be examined in various ways, here are few examples:

One can also try to write over the memory to see how the game behavior is changing.
The most useful way to look at memory dumps of maps is using the mapDisplay method, which hopefully, would show up in a new QT window as following:

All of the display parameters are accessible from both the GUI and the python object that was created with the creation of the display. I now play with the line size until the following result is reached:

Can you tell where all the mines are placed?

The interesting part in case of minesweeper is that, the map is always found in the same place in memory, so if I start a new game, and update the data (using w.updateData()), I might get something like:

In real games, the principle is quite the same, though it takes more time. The main difference is that the map is usually allocated dynamically so one would also have to find a global pointer to it, to be able to locate the map in memory quickly. I have an example of the same process done on the WarCraft II strategy game, but this blog post is long enough as it is. So maybe next time.

Future TODOs:

  • I believe that the search function could be much faster, I would love to have some help in optimizing the code.
  • The GUI has lots of room for improvements.
  • I need to write an installer, and to create a Python 3.x branch.