October 17, 2016

Cheating at Heroes of Might and Magic 3 with WinDbg


Introduction

I decided I wanted to learn how to use WinDbg. I’ve read good comments about it online and learning it would make sense since I want to play a little bit with some Windows internals at some point. As a starting point, I decided I was going to try to debug the “Heroes of Might and Magic 3” (HoMM3) game in order to figure out how to increase the number of troops controlled by my hero. What I found out, is that the game already provides a nice “Cheat” menu, with many cool options that allow, among other things, the addition of new troops to my army.

This is then going to be a post that describes my the process on how to activate this “Cheat” menu while describing what I learned about WinDbg in a step by step fashion. See it as a practical introduction to WinDbg if you will.

For those that want to follow along, the game can be purchased here.

The environment used for this exercise consists of:

  • Windows 7 Professional SP1
  • WinDbg 6.12.0002.633 AMD64
  • Resource Hacker
  • Heroes of Might and Magic 3

Target audience

The following list describes the traits of those who might be interested in reading this post:

  • You have a little bit of programming experience with lower level languages (like C or Assembly).
  • You know almost nothing about windows development.
  • You know almost nothing about debugging windows applications.
  • You find game hacking interesting.
  • You want to learn some basics of WinDbg.
  • You just find this stuff interesting and want to see the end result.
  • You played a bunch of Heroes of Might and Magic 3 and want a way to cheat on it by your own merits!
  • You just like to read.

Installing WinDbg

As of this writing (10/20/2016), installing WinDbg on Windows 7 SP1 is not 100% intuitive. That is because Windows 10 is out, and the main installer distributed on Microsoft’s website does not really work with Windows 7.

I decided then to write this quick section here to explain how to install WinDbg on Windows 7.

Go to the Windows SDK archives page here: Windows SDK Archive

Search for the “Windows 7 and .NET Framework 4” entry in the table and download that one! Direct link.

If you are having trouble with it in your system check out this article: Windows SDK Fails to Install with Return Code 5100.

In order to install the SDK, you will need the .NET Framework 4 installed. It can be downloaded here: Microsoft .NET Framework 4 (Web Installer)

After downloading the SDK installer, just execute it, and when prompted for what to install, select the “Debugging tools” and “Debugging tools for Windows”, as in the following screenshot:

Installing WinDbg Installing WinDbg

What happened

The VM I used for this is a VM where I had some other tools for reverse engineering installed, including “Resource Hacker”. While navigating around the HoMM3 folder, I just right-clicked its executable and the option to open it in “Resource Hacker” was there. I decided to give it a shot and see what was available. What I found is:

  • The game can be run in a windowed mode (I wasn’t aware of that)
  • There is a menu full of options for cheating

Cheat menu as seen in Resource Hacker Cheat menu as seen in Resource Hacker

Looking at the game’s regular menu in “Resource Hacker” I noticed that if you press F4 during the game, it will switch to windowed mode. (Note: this only works if your video card is configured to 16 bit color mode, instead of the more common 32 bit color mode)

Running the game normally, this is how the menu looks like:

Regular menu for HoMM3 Regular menu for HoMM3

My plans on hacking the game just got way easier, so I decided to give it a try and get the “Cheat” menu enabled in my game session.

Summary of the thought process

The idea of this post is to describe the thought process on how to approach such problem. I asked myself the following questions:

  • How do I load the game and get it to run from WinDbg?
  • How does the game load its menu? What function is it using for that?
  • How do I stop execution of the game when it calls the function to load the menu?
  • How do I change the parameters being passed to this function to load the “Cheat” menu?

The answers to each one of those questions, together with the steps to get to them are described in the following sections. Answering all those questions will lead to the final goal of activating the “Cheat” menu in the game.

When I first started writing this post, I had close to zero experience with Windows development and the Win32 API. I realize that the steps described are simple, but I had to start somewhere. Also, the target audience section covers me here.

Detailed Thought process

How do I load the game and get it to run from WinDbg?

This is where the basics of WinDbg start. So if you already know how to use WinDbg, feel free to skip this section.

Load the game on WinDbg using File -> Open Executable….

Find the place where the game is installed in your computer and click the Open button.

Opening the game with WinDbg Opening the game with WinDbg

At this point the Command screen will be displayed:

Command screen Command screen

The process is stopped at a breakpoint that is placed by WinDbg and that happens before the entry point of our application has been reached.

In order to get the process to continue execution, just type the command g and press Enter.

The Process should resume and stop at another breakpoint. This one is also outside of our target process and triggered by WinDbg. Just like before, type the command g and press Enter.

The game will try to start, but an error will happen. Something like this:

HoMM3 error starting HoMM3 error starting

This error occurs because HoMM3 is looking for DLLs that it cannot find. This happens because the binary load path is not set to the correct value (as if it had been copied to another folder and we tried to open it) and when it tries to look for some DLLs that have a path relative to itself, those cannot be found and the process errors out.

Look back at the screen to load the binary above. Notice there is a “Start Directory” text box that is blank. We need to fill that with the path where the binary is located. In my case, it is: C:\GOG Games\Heroes of Might and Magic 3 Complete.

Configuring the Start Path for the game Configuring the Start Path for the game

Just click OK in the error message that is displayed. You should be back in the debugger window. Click the menu Debug -> Stop Debugging, and then start over on File -> Open Executable… and this time fill the Start Directory text box.

Just run the command g twice in the Command window now, and notice that the game will start.

How does the game load its menu? What function is it using for that?

The game is now running. It is time to investigate how does the game load its menu?

Considering that the game creates a window, it might be safe to assume that there might be a function in the Win32 API responsible for loading the menu. But what function would that be?

I didn’t really know the answer for that. So I just googled around with some keywords such as “win32 api load menu resource”, which returned me these two nice links:

The first one describes what a MENU resource is, and the second one talks about the LoadMenu function.

It seems that LoadMenu is the function used to load a menu resource that is inside a binary. Here is what the documentation says about it:

HMENU WINAPI LoadMenu(
  _In_opt_ HINSTANCE hInstance,
  _In_     LPCTSTR   lpMenuName
);

hInstance [in, optional]
    Type: HINSTANCE
        A handle to the module containing the menu resource to be loaded.
lpMenuName [in]
    Type: LPCTSTR
        The name of the menu resource. Alternatively, this parameter can 
        consist of the resource identifier in the low-order word and zero
        in the high-order word. To create this value, use the MAKEINTRESOURCE 
        macro.

One thing about the Win32 API is that functions seem to always (or almost always) have two different versions: a Unicode one and an ASCII one. So, for the LoadMenu function, the real function names are either LoadMenuA (ASCII version) or LoadMenuW (Unicode version).

With this information in hand, the next question is waiting for an answer!

How do I stop execution of the game when it calls the function to load the menu?

If you have any experience at all with programming and debugging, you know the answer is “Set a breakpoint”. So, how to set a breakpoint on WinDbg?

In order to be able to set a breakpoint on functions in the Win32 API, first we need to configure WinDbg to retrieve the debugging symbols from a server and to store them locally (well, storing them locally is not necessary but it saves a lot of time). For that, go to File -> Symbol File Path… and in the window that just popped up, enter the following value:

  • SRV*c:\symbols*http://msdl.microsoft.com/download/symbols

This tells WinDbg to get the symbols from Microsoft’s server and to store them in the c:\symbols folder locally.

In the command screen, type the command .reload, so that WinDbg will reload the currently loaded modules and download the debugging symbols for them. This might take a while.

Per the documentation, the LoadMenu functions can be found in the User32 lib, meaning that they are in the User32.dll file. WinDbg offers the x command to check for loaded symbols. The syntax is:

x [Options] Module!Symbol

So, trying the following:

x user32!*LoadMenu*

And the response should be something like:

0:000> x user32!*LoadMenu*
00000000`76b943bc USER32!__ClientLoadMenu = <no type information>
00000000`76b94b74 USER32!CommonLoadMenu = <no type information>
00000000`76ba4eef USER32!LoadMenuA = <no type information>
00000000`76b94391 USER32!LoadMenuW = <no type information>

So now we know that these symbols have been loaded as expected. All we need to do is set a breakpoint for them (since we don’t know what function HoMM3 is using, we set a breakpoint for both). The command for setting up breakpoints is bp.

0:000> bp LoadModuleA
0:000> bp LoadModuleW

At this point we need to restart the debugging session since the game is already running and the menu has already been loaded.

Use the command .restart or go to Debug -> Restart to restart the debugging session. The debugger will break for the first time:

0:000> .restart
WARNING: Whitespace at start of path element
WARNING: Whitespace at start of path element
CommandLine: "C:\GOG Games\Heroes of Might and Magic 3 Complete\Heroes3.exe"
Starting directory: C:\GOG Games\Heroes of Might and Magic 3 Complete
WARNING: Whitespace at start of path element
Symbol search path is:  ;SRV*c:\symbols-bkp*http://msdl.microsoft.com/download/symbols
Executable search path is: 
ModLoad: 00000000I`00400000 00000000`006b6000   image00000000`00400000
ModLoad: 00000000I`772b0000 00000000`77459000   ntdll.dll
ModLoad: 00000000I`77490000 00000000`77610000   ntdll32.dll
ModLoad: 00000000I`74c50000 00000000`74c8f000   C:\Windows\SYSTEM32\wow64.dll
ModLoad: 00000000I`743c0000 00000000`7441c000   C:\Windows\SYSTEM32\wow64win.dll
ModLoad: 00000000I`74c40000 00000000`74c48000   C:\Windows\SYSTEM32\wow64cpu.dll
(7c8.7c4): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x30:
00000000`7735cb60 cc              int     3

Try to set the breakpoints:

0:000> bp LoadMenuA
Bp expression 'LoadMenuA' could not be resolved, adding deferred bp
0:000> bp LoadMenuW
Bp expression 'LoadMenuW' could not be resolved, adding deferred bp

As you can see, the debugger says that the function name could not be resolved. This happens because the User32 (user32.dll) module has not yet been loaded.

Just run the g command once so that the other modules are loaded:

0:000> g
ModLoad: 00000000`77090000 00000000`771af000   WOW64_IMAGE_SECTION
ModLoad: 00000000`76400000 00000000`76510000   WOW64_IMAGE_SECTION
ModLoad: 00000000`77090000 00000000`771af000   NOT_AN_IMAGE
ModLoad: 00000000`771b0000 00000000`772aa000   NOT_AN_IMAGE
ModLoad: 00000000`76400000 00000000`76510000   C:\Windows\syswow64\kernel32.dll
ModLoad: 00000000`76640000 00000000`76686000   C:\Windows\syswow64\KERNELBASE.dll
ModLoad: 00000000`749a0000 00000000`749a9000   C:\Windows\SysWOW64\VERSION.dll
ModLoad: 00000000`752d0000 00000000`7537c000   C:\Windows\syswow64\msvcrt.dll
ModLoad: 00000000`69d90000 00000000`69dc2000   C:\Windows\SysWOW64\WINMM.dll
ModLoad: 00000000`76510000 00000000`76610000   C:\Windows\syswow64\USER32.dll
ModLoad: 00000000`76f70000 00000000`77000000   C:\Windows\syswow64\GDI32.dll
ModLoad: 00000000`76240000 00000000`7624a000   C:\Windows\syswow64\LPK.dll
ModLoad: 00000000`762b0000 00000000`7634d000   C:\Windows\syswow64\USP10.dll
ModLoad: 00000000`751b0000 00000000`75250000   C:\Windows\syswow64\ADVAPI32.dll
ModLoad: 00000000`76350000 00000000`76369000   C:\Windows\SysWOW64\sechost.dll
ModLoad: 00000000`76b40000 00000000`76c30000   C:\Windows\syswow64\RPCRT4.dll
ModLoad: 00000000`74ff0000 00000000`75050000   C:\Windows\syswow64\SspiCli.dll
ModLoad: 00000000`74fe0000 00000000`74fec000   C:\Windows\syswow64\CRYPTBASE.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\GOG Games\Heroes of Might and Magic 3 Complete\mss32.dll - 
ModLoad: 00000000`21000000 00000000`21058000   C:\GOG Games\Heroes of Might and Magic 3 Complete\mss32.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\GOG Games\Heroes of Might and Magic 3 Complete\smackw32.dll - 
ModLoad: 00000000`10000000 00000000`1001b000   C:\GOG Games\Heroes of Might and Magic 3 Complete\smackw32.dll
ModLoad: 00000000`69e70000 00000000`69f57000   C:\Windows\SysWOW64\DDRAW.dll
ModLoad: 00000000`69e60000 00000000`69e66000   C:\Windows\SysWOW64\DCIMAN32.dll
ModLoad: 00000000`76c30000 00000000`76dcd000   C:\Windows\syswow64\SETUPAPI.dll
ModLoad: 00000000`76690000 00000000`766b7000   C:\Windows\syswow64\CFGMGR32.dll
ModLoad: 00000000`77000000 00000000`7708f000   C:\Windows\syswow64\OLEAUT32.dll
ModLoad: 00000000`75050000 00000000`751ac000   C:\Windows\syswow64\ole32.dll
ModLoad: 00000000`76610000 00000000`76622000   C:\Windows\syswow64\DEVOBJ.dll
ModLoad: 00000000`6dad0000 00000000`6dae3000   C:\Windows\SysWOW64\dwmapi.dll
ModLoad: 00000000`73a50000 00000000`73a57000   C:\Windows\SysWOW64\WSOCK32.dll
ModLoad: 00000000`76f30000 00000000`76f65000   C:\Windows\syswow64\WS2_32.dll
ModLoad: 00000000`76630000 00000000`76636000   C:\Windows\syswow64\NSI.dll
ModLoad: 00000000`75380000 00000000`75fca000   C:\Windows\syswow64\SHELL32.dll
ModLoad: 00000000`76ed0000 00000000`76f27000   C:\Windows\syswow64\SHLWAPI.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\GOG Games\Heroes of Might and Magic 3 Complete\binkw32.dll - 
ModLoad: 00000000`00230000 00000000`0025b000   C:\GOG Games\Heroes of Might and Magic 3 Complete\binkw32.dll
*** WARNING: Unable to verify checksum for C:\GOG Games\Heroes of Might and Magic 3 Complete\IFC20.dll
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\GOG Games\Heroes of Might and Magic 3 Complete\IFC20.dll - 
ModLoad: 00000000`00260000 00000000`00283000   C:\GOG Games\Heroes of Might and Magic 3 Complete\IFC20.dll
(7c8.7c4): WOW64 breakpoint - code 4000001f (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
ntdll32!LdrpDoDebuggerBreak+0x2c:
77530f3b cc              int     3

At this point, User32.dll has been loaded and we can try to set the breakpoint again:

0:000:x86> bp LoadMenuA
breakpoint 2 redefined
0:000:x86> bp LoadMenuW
breakpoint 8 redefined

Now, just continue execution using the g command:

0:000:x86> g
ModLoad: 6dfa0000 6dfec000   C:\Windows\SysWOW64\apphelp.dll
ModLoad: 74680000 7470d000   C:\Windows\AppPatch\AcLayers.DLL
ModLoad: 6e2c0000 6e2d7000   C:\Windows\SysWOW64\USERENV.dll
ModLoad: 73900000 7390b000   C:\Windows\SysWOW64\profapi.dll
ModLoad: 74ca0000 74cf1000   C:\Windows\SysWOW64\WINSPOOL.DRV
ModLoad: 74660000 74672000   C:\Windows\SysWOW64\MPR.dll
ModLoad: 76250000 762b0000   C:\Windows\SysWOW64\IMM32.DLL
ModLoad: 766c0000 7678c000   C:\Windows\syswow64\MSCTF.dll
ModLoad: 73af0000 73b70000   C:\Windows\SysWOW64\uxtheme.dll
Breakpoint 2 hit
USER32!LoadMenuA:
76544eef 8bff            mov     edi,edi

As you will notice, the execution will break twice in the LoadMenuA function. This means that two different menus are being loaded. Just execute the g command when the breakpoints are hit to continue execution and get the game to start.

How do I change the parameters being passed to this function to load the “Cheat” menu?

So, looking at the parameters for LoadMenuA we can see that the second one identifies the menu that is being loaded. It will either be a string with the menu name, or the menu identifier. This is an x86 binary we are dealing with here, and the Windows convention for parameter passing in this case is that they should be passed on the stack.

So, let’s look at the stack as soon as the process stops in the first break point for LoadMenuA. In order to do this, we can use the db command and give it the address that we want to print:


0:000:x86> db @esp
0018fe94  0f db 4e 00 00 00 40 00-6e 00 00 00 fd 95 69 00  ..N...@.n.....i.
0018fea4  00 00 40 00 00 00 00 00-10 27 00 00 fc fe 18 00  ..@......'......
0018feb4  a3 7a 4f 00 00 00 00 00-00 00 00 00 00 e0 fd 7e  .zO............~
0018fec4  d0 fe 18 00 05 b3 61 00-b0 b7 6a 00 88 ff 18 00  ......a...j.....
0018fed4  f1 8e 61 00 0d 00 00 00-3f 79 61 00 00 00 00 00  ..a.....?ya.....
0018fee4  00 00 00 00 00 e0 fd 7e-80 6c 61 00 f0 4d 40 00  .......~.la..M@.
0018fef4  65 00 00 00 65 00 00 00-88 ff 18 00 94 a3 61 00  e...e.........a.
0018ff04  00 00 40 00 00 00 00 00-bf 2e 75 00 0a 00 00 00  ..@.......u.....

The @esp value, means that we should use the address stored in the esp register.

Looking at the stack then, we see these values at the top:

  • 0f db 4e 00 at address 0x0018fe94 : This is the return address
  • 00 00 40 00 at address 0x0018fe98 : This is the first argument for LoadMenuA
  • 6e 00 00 00 at address 0x0018fe9c : This is the second argument for LoadMenuA

From the LoadMenuA signature, we know that the second argument is the one we are interested in. If we take the value of that argument (0x6E) and convert it to decimal we get 110. Looking at the menu resource in Resource Hacker we have:

110 MENU
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
{
POPUP "&File"
{
	MENUITEM "&Quit",  40140
}
POPUP "&Display"
{
	MENUITEM "&Full Screen (F4)",  40009
}
POPUP "&Help"
{
	MENUITEM "On Line &Manual",  40052
	MENUITEM SEPARATOR
	MENUITEM "&About",  40053
}
}

As we can see, the identifier of the regular menu is 110. So, let’s look at the menu that contains the Cheat menu:

111 MENU
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
{
POPUP "&Cheat"
{
	POPUP "Free artifacts"
	{
		MENUITEM "Spellbook",  45000
		MENUITEM "Holy Grail",  45002
		MENUITEM "Ballista",  45004
		MENUITEM "Ammo Cart",  45005
		MENUITEM "First-Aid Tent",  45006
		MENUITEM "Centaurs Axe",  45007
		MENUITEM "Blackshard of The Dead Knight",

... [Shortened for brevity]

We can see that the identifier for this menu is 111. It should be just a matter of changing the second parameter for LoadMenuA from 0x6E (110) to 0x6F (111) to get the application to load the Cheat menu.

This change can be made with the command ed, as shown below:

0:000:x86> ed 0018fe9c 0x6f
0:000:x86> db @esp
0018fe94  0f db 4e 00 00 00 40 00-6f 00 00 00 fd 95 69 00  ..N...@.o.....i.
0018fea4  00 00 40 00 00 00 00 00-10 27 00 00 fc fe 18 00  ..@......'......
0018feb4  a3 7a 4f 00 00 00 00 00-00 00 00 00 00 e0 fd 7e  .zO............~
0018fec4  d0 fe 18 00 05 b3 61 00-b0 b7 6a 00 88 ff 18 00  ......a...j.....
0018fed4  f1 8e 61 00 0d 00 00 00-3f 79 61 00 00 00 00 00  ..a.....?ya.....
0018fee4  00 00 00 00 00 e0 fd 7e-80 6c 61 00 f0 4d 40 00  .......~.la..M@.
0018fef4  65 00 00 00 65 00 00 00-88 ff 18 00 94 a3 61 00  e...e.........a.
0018ff04  00 00 40 00 00 00 00 00-bf 2e 75 00 0a 00 00 00  ..@.......u.....

Notice now that the value in the stack has been changed from 0x6E to 0x6F.

At this point, just run the g command to resume execution. Another breakpoint will be triggered for the LoadMenuA function. Inspect the stack, check what parameter is being passed to it and fix it in the same way as we just did. Notice that this second menu being loaded, is the menu used when a battle starts in the game. You will notice that it has a different menu identifier, and Resource Hacker should show you a second Cheat menu as well.

After that, just run the g command again to resume execution and see the game start this time.

Game with the Cheat menu Game with the Cheat menu

Summary of WinDbg commands

Here is then the summary of WinDbg commands that were used and learned during this experience:

  • bp Module!Symbol - Sets a breakpoint
  • .restart - Restarts the debugging session
  • .reload - Reloads the modules already loaded and their debugging symbols
  • x Module!Symbol - Displays the symbols of a module
  • g - Continues program execution
  • db address - Prints data on address
  • ed address value - Edits the value at address and set it to value. Writes 4 bytes at a time

And the following commands, that were not used but that can be pretty useful:

  • k - Backtrace
  • uf address - Shows disassembly of address
  • uf $scopeeip - Shows disassembly of current instruction

Help can be obtained by using the .hh command.

Summary of the process

Just to reinforce and to have verything together, here is a small summary of what has been done to figure out the existence of the Cheat menu and how to enable it in the game:

  • Open the game binary in Resource Hacker and realize there is a Cheat menu
  • Get the game to run in windowed mode
  • Load the game in WinDbg
  • Figure out how to get it to start
  • Figure out how the menu is loaded
  • Figure out the menu identifiers
  • Stop execution at the start of the function that loads the menu
  • Edit the memory to change the value of the menu identifier being passed to it
  • Resume execution of the program

Closing thoughts

This was just a small introduction to WinDbg and game hacking. While it is pretty interesting to use the debugger to perform this task, ideally we don’t want to have the debugger running while playing since it interferes with performance and is not practical to do every time the game is started.

In the next article, we will go through the process of patching the binary to always load the Cheat menu instead. It should be short and simple!

Aftermath Discovery

It came to my attention that this cheat menu can also be enabled via a command line argument (/ncwgrail) that can be passed to the application when it is started.

I still believe that doing it with the debugger is a nice feat, and that being able to patch the binary to automatically enable this menu will also be a cool thing to do.