Hacking, Coding and Gaming | @[email protected]

In the game "Diablo" (the first/original one) there is a character named "Wirt" who sells magic items... but he only sells one at a time, and in order to just see what he currently has for sale you have to pay him 50 gold. Back in 2008 I made a "hack" which tells you the name of the item Wirt has for sale - saving you the 50 gold and time spent walking to him. It worked by reading a fixed memory address and displaying a Windows MessageBox popup, and was coded in AutoIt for simplicity.

"Diablo 1" has recently been made available on GoG.com, with some fixes and improvements. When I tried using my "hack" with on it, it didn't seem work - so I went about re-creating it this weekend (only to end up with the same memory address my original code had, and my original .exe working fine). I also spent some time figuring out how to display the price of the item for sale - something I hadn't gotten right previously. This blog post serves as a kind of "how to" for re-creating this "hack" which I've named "Wirt Spy"...

Tools used:

Part 1 - Finding the item name:

Start by walking to Wirt and paying to see the item he has for sale (eg: "Red Buckler") :

Using the memory scanner do a "string" search (case insensitive to be safe) for that item's name:

You should get two results, the first is the item's name in memory and the second is the string show in the user interface. Starting a new game should cause the second address to go blank while the first still shows a string value, which always matches what Wirt's selling - making our item name address 0x0069F195.

Part 2 - finding the item price:

This part's more tricky... doing an integer search for the price displayed (eg: "2340") should return some results, but none are for the "source price" - some will be unrelated variables in memory, and one is just the price shown in the user interface. By opening and closing the Wirt buy window, and creating or re-loading games, we can see the found memory addresses change and that only one address matches the price shown on screen (when talking to Wirt), memory address 0x006A01F0.

The goal is to be able to read the price WITHOUT talking to Wirt, in which case the price isn't in memory. To get the source address we need to work backwards from how the displayed price is read.

Using OllyDbg:

  1. attach to the Diablo.exe process ("File" menu)
  2. make sure it hasn't suspended the game (press F9 a few times and make sure it says "Running" in the bottom right hand corner of OllyDbg)
  3. make sure you're in the Diablo executable (click the "E" button in the toolbar and choose "diablo.exe" in the list)
  4. go to the memory address we got above (click in the bottom/memory panel in OllyDbg then press CTRL+G and paste "006A01F0")
  5. right click on the memory address, choose "Breakpoint" and "Memory, on write"
  6. view or attempt to buy the item Wirt has for sale and OllyDbg should pause the game and show "Paused" in the bottom right corner

OllyDbg will detect the game making changes to the memory address we found, which is for the price label shown to the user. If OllyDbg stops on "OR DWORD..." press F9 to keep running - we're not interested in this code path (it will probably stop here twice). When OllyDbg stops on the following code we're in the right place:

MOV DWORD PTR DS:[ECX+69FBD8],EDX

The value we want - the item's price - is actually in the EDX register at this point and gets moved in to the memory address we found. We need to see how the value got in to EDX. Sometimes you can simply scroll up in OllyDbg, but in this case we're in a short function which doesn't seem to set EDX. Press CTRL+F9 to "Execute till return" (run to the end of this function) then press F8 to perform the return - this takes out of the function we were just in, so we can see what happened before it was called, you should see something like this:

The code is:

004599BC A1 E0F16900 MOV EAX,DWORD PTR DS:[69F1E0]
004599C1 8BD0 MOV EDX,EAX
004599C3 ...
004599C5 D1FA SAR EDX,1
004599C7 03D0 ADD EDX,EAX

Here's what the code does:

  1. reads a value from memory address 0x69F1E0 in to the EAX register (eg: EAX=0x618)
  2. copies the EAX register's value in to the EDX register (eg: EDX=0x618)
  3. bit shifts EDX to the right once (halving it, eg: EDX=0x30C)
  4. adds EAX to EDX (eg: 0x618 + 0x30C = 0x924 = 2340)

So it's adding 50% to the memory address's price. I'm not sure if it's some kind of obfuscation, or just Wirt's mark up on each item's base price, but there you have it... the price of Wirt's item is at memory address 0x69F1E0 - it just needs 50% added.

3. My "Wirt Spy" hack source + exe

While I'm not suggesting you should run the .exe files I've created, the code for my original and updated "Wirt Spy" hacks are available on GitHub. It simple performs a memory read or two and pops up a message box with the item name and price :)