Hacking, Coding and Gaming | @[email protected]

In this blog post I'll show how to make a "map hack" for Warcraft 2 - that is, a means of revealing the whole game map without the use of cheats, or manually exploring the game world. Needless to say this type of hack can give you quite an advantage in multiplayer strategy games. I've decided to target the GoG re-release of Warcraft 2 as I enjoy retro gaming and hopefully this will avoid any legal threats. It also has the benefit of providing "windowed" mode. Debugging full screen games can be frustrating if/when the game crashes or memory scanner / debugger pauses the process and your mouse cursor is "stuck" or you can't get to out of the now frozen game.

In order to make a hack we have figure out what logic is currently in place that we want to change or bypass, and how we want to change it. In Warcraft 2 - as with many strategy games - there is a world/map in which we move characters around and a "fog of war" obscuring areas not yet discovered. Areas already discovered but no longer "in range" are still shown but shaded over (and enemy units in that area NOT shown). By scanning the game's memory for changing values as we move a unit in and out of an area we are able to find the code responsible for drawing the map tiles and forcing it to always draw the whole map:

1. Setting things up:

Start a game, pick an area of the map to use (in my screen shots I chose bottom right), and move a unit in to the area to explore it:

Be sure you only run scans once the unit has finished moving and the shroud has updated - include the rounded "border" that's drawn around visible areas. Perform an "unknown" byte scan (which returned 25 million addresses for me), and spend some time selecting different units and scrolling around the map while doing "unchanged" scans.

2. Finding the code that draws the map

Move the unit in/out of the are you've chosen, doing a "changed" search, a few times. Also make sure you move your unit 1 space left or right and do an "unchanged" search, and the same moving it 1 space up or down, to filter out its location from the memory addresses being returned. I was able to get the number of resulting memory addresses down to 220:

Select memory addresses that appear to be in the same memory range with similar values, and "lock" their values while your unit's revealing the area, then move your unit out of the area. If you've selected the correct memory addresses then moving your mouse over the area should redraw some of it (note not-shaded block below)

Pick one of the frozen addresses and use Cheat Engine's "Find out what writes to this address" or a debugger's "break point on memory write" to find the code modifying this memory address. You should find the code below:

0042D4EA     B8 10101010    MOV EAX,10101010

3. Hacking the code

In a perfect world we'd use a debugger to step through the code, figure out the logic being applied, the meaning of that "10101010" (perhaps a bit-mask applied to bytes instead of bits?)... or we could just use trial and error. As it turns out simply changing the value being put in EAX (which is then written to memory) to "0" causes the whole map to be drawn:

0042D4EA     B8 00000000    MOV EAX,0

This works pretty well, but enemy units and goldmines are not displayed on the mini-map (but are on the big map). Scrolling around the code we've just changed we see another value being put in to EAX:

0042D4FC     83C8 FF        OR EAX,FFFFFFFF

Changing it to:

0042D4FC     83C8 00        OR EAX,0

Causes the mini-map to reflect the other objects :)

4. My “War2 Maphack” source + exe

I re-purposed my previous Diablo 1 "Wirt Spy" AutoIt code to perform the maphack changes above and put them on GitHub with an .exe: https://github.com/hypn/game-hacks/tree/master/Warcraft%202%20-%20GoG

I also made a Frida version after some gentle nudging from @leonjza who's done some cool game hackery stuff with Frida in the past. My Frida code is also on GitHub, you can run it with:

frida "Warcraft II BNE_dx.exe" -l war2-frida-maphack.js

And it looks like this: