***DISCLAIMER - Intellectual Rights***
I wrote this 100% from scratch. If you are going to post this on another forum I would like to be credited by having a link to this thread at the top of your post.
Why Assault Cube?
Assault Cube is a a very good game to start off with in respect to learning reverse engineering. Why? Because it's free! And as if that wasn't enough of a reason, it also has no anti-cheat client (as of this post), is only ~40MB in size, is fully open-source, and does not force dev-build. Force dev-build simply means that you can modify the source code, compile it, and run your modified version in multi-player. However, in these tutorials, I'm not going to take advantage of the fact that this game doesn't force dev-build because that's not going to help you learn reverse engineering. You can download the game here.
Now keep in mind that after following this tutorial you will be able to apply the method to get infinite health, armor, and grenades.
These are the end-objectives that this tutorial will accomplish and the knowledge that you can take away from this tutorial.
- Finding a variable in memory
- Modifying the variable's value in memory
- Re-assembling the game code to do what you want (in this tutorial we will NOP a DEC instruction used for decreasing the ammo count)
First things first, open up the game. You should change the game to windowed-mode to make it easy for alt-tabbing in and out from the game. You can do this either by going through the menu (escape-key) or by pressing alt+enter. Windowed-mode is a must have for any game that you attempt to reverse.
Next step is to open up Cheat Engine. Cheat Engine is a free lightweight memory scanner. There are many different alternatives such as TSearch, so it all boils down to your personal preference. I'll be using Cheat Engine in this tutorial.
You need to tell Cheat Engine to look at the Assault Cube game process. To do this, click on the computer with a magnifying glass icon that I have highlighted in the screenshot below.
Select the Assault Cube process, which is named ac_client.exe, and then hit Open.
Now this is where we can begin searching for the ammo count in the memory. Obviously the amount of ammo will depend on the gun you have selected and the amount of bullets you have already used.
Tabbing over to Assault Cube, we can see the the max clip size is 20 bullets for the default weapon.
The stuff on the right side of Cheat Engine allows us to specify parameters for what we're searching for. For our purpose, we really only care about 3 fields; Value, Scan Type, and Value Type.
Enter 20 in the Value field to specify that we're looking for a variable whose current value is 20. Make sure that Scan Type is set to Exact Value and Value Type is set to 4 bytes. What do these two fields mean? Well we know the exact value of ammo remaining, hence the Exact Value option. Now Value Type is a little bit more tricky. You basically either have to have access to the game's source code, or better yet, have prior programming experience and know about the different primitive data types and the amount of memory each variable takes up. Now keep in mind that the amount of space in memory allocated for each type of variable will differ slightly from one environment, language, or operating system to the other. For our purpose, you can make the assumption that the vast majority of video games are written in C++ and refer to that language's data types. Take a look at this resource if you're not familiar with how much memory each type of primitive variable takes up.
The goal here is to figure out what type of variable is used to store the amount of ammo remaining in the clip. Integers are the most common variable type for keeping track of whole (non-decimal) numbers. Declared integers will take on the signed form by default, unless otherwise specified as unsigned by the programmer. It's very likely the the programmers of Assault Cube used an integer data type to store the ammo amount, and integers take up 4 bytes (or 32 bits) in C++.
So in a nutshell, we're telling Cheat Engine to search for an integer (4 bytes), whose exact value is 20.
Now the search is going to return a lot of results (each result consist of a memory address and the value at that address). Odds are astronomical there is only one 4-byte chunk in memory whose value can be interpreted as 20. So now we have to narrow down the amount of search results so we can find the memory address that counts the value of ammo remaining by making a couple of subsequent follow-up searches.
Go in game and shoot off 3 bullets. Then go back into Cheat Engine and enter 17 as our new value to search for and hit Next Scan. What Next Scan does is create a new scan for a new value in memory from the list of memory addresses returned from our original first scan.
Now do this a couple more times until you narrow down the number of memory addresses down to a small workable number, such as 2.
Tip: If you like using keyboard shortcuts like alt-tab for going back into Cheat Engine, you can directly start typing numbers to change the Value field and hit enter to do Next Scan.
Now I've got my addresses narrowed down to 2 (keep in mind that your addresses will be different than mine because they change dynamically every time you run the game). Double click on each memory address in the search results to add them to your active memory list down below.
What you have to do now is figure out exactly which memory address corresponds to ammo. Double click on the value of each address and change it to a different number. Then look back in the game to see if the ammo on-screen changed. Do this until you have figured out which memory address represents the ammo count.
For me, the memory address for ammo was 02798E4C (remember that yours WILL be different).
Now open up OllyDbg. OllyDbg is a very useful and powerful tool for debugging and re-assembling program instructions, or code.
Tell OllyDbg to debug the Assault Cube process by going to
File -> Attach
When you first attach OllyDbg it will pause the target process to press F9 to resume the game.
Note: Some games have anti-debug protection and will not allow you to attach a debugger to the game. In this case you'll have to find a tool that will reset the debug port.
Select the Assault Cube process.
Now the next part isn't necessary, but it's a good habit to get in to. Press alt-e to open up the Executable Modules and double click the ac_client module to tell Olly to jump to the actual game module instructions in view.
Your screen should look like this. You'll see a couple of important windows.
- Disassembler (Top Left Window). This displays all of the instructions of the program.
- Registers (Top Right Window).
- Memory Dump (Bottom Left Window). This displays all of the memory values of the program.
- Stack (Bottom Right Window).
The two windows that we're mainly interested in are the Disassembler and the Memory Dump.
In the memory dump tell Olly to jump to the memory address that we found in Cheat Engine that holds the value of ammo. To do this right click anywhere in the Memory Dump window and select Go to->Expression.
Enter the memory address that we found in Cheat Engine that keeps track of ammo.
For me, the address was 02798E4C.
Now looking at the memory dump we see 14. What the heck is 14? Where is our ammo count? Well by default, Olly will display the memory dump in hexadecimal. Each individual character represents a 2-byte hex value. 14 is actually the 4-byte hex representation of 20 in decimal form (I had reloaded my clip before going into Olly).
We can tell Olly to show us the values in the memory dump as actual integers by right clicking and selecting Unsigned Decimal.
Now this makes a lot more sense. We see a value of 20 at our address, which is the current amount of ammo left. You can go in game and shoot off a couple of bullets and see the value change in Olly.
Note: Olly doesn't auto-refresh like Cheat Engine does, so you have to click on something within Olly, like a neighboring address, to get Olly to re-sample the memory in the memory dump.
So now we have found where ammo is stored in the memory. Now what? Well the goal of this tutorial is to get unlimited ammo. There are two approaches to achieve this:
The first option is incredibly inefficient and the second option is a much more elegant solution. So the obvious choice is approach #2.
- Continuously write some generic value to this memory address to make sure that the ammo never hits 0.
- Find what writes to and changes the value at this memory address, and tell it to stop.
What we need to do is set a breakpoint at the memory address that holds the value of ammo and see what writes to this memory address. Right click on our address and select Hardware, on write -> Dword.
Dword just means that we're looking at a 4-byte, or 32-bit quantity, which is exactly the size in memory of the variable that stores ammo (signed integer).
To remove a hardware breakpoint you go to Debug->Hardware breakpoints.
Here you can see all of the hardware breakpoints that you have set, and can delete any if you want. (We will do this later).
Go back into the game and fire off a shot. Now go back into Olly. If Olly hits the breakpoint more than once, you can press F9 to tell Olly to resume the program and go to it's next instruction (until it hits the breakpoint again).
You will see that Olly took us to address 0x0045B761 (yours WILL be different). This is the address of the instruction that writes to (changes the value of the variable) that stores the ammo count.
0045B761 8D7424 24 LEA ESI, DWORD PTR SS:[ESP+24]
Let's break this down into three parts. The first column is the virtual memory address. In the second column you see the ocodes represented byte by byte (this is what the computer looks reads and understands). Each set of two characters is equal to one byte in memory. The third column shows the assembler mnemonics, which is just a translation of the opcodes so that we, the user, can read and understand them.
Let's take a look at the 3rd part.
LEA ESI, DWORD PT SS:[ESP+24]
The LEA command simply copies the result of the second operand to the first operand (each operand is separated by a comma). ESP stands for Extended Stack Pointer and always points to the last element used on the stack. ESI stands for Extended Source Index and is typically used for copying bytes/dwords. You can think of the ESI register like a pointer in C++.
So this is saying
- Grab the value of the 4 byte pointer in the stack segment ("SS:" part) that is 0x24 away from the value in the ESP register.
- Copy the value from step 1 into the ESI register.
The instruction on the line above is actually of most interest to us.
0045B75F FFOE DEC DWORD PTR DS:[ESI]
What this is saying is to decrement the value of what the DWORD (4 byte) pointer in the data-segment stored in the ESI register points to.
Now we could change the instruction that decrements the ammo count to do nothing right now, but instead, we're going to go one step further. We want to make this future proof and write a program that will automatically do this for us. The problem is that we can't just write down the memory address that we found using Cheat Engine because the ammo value memory address is a Dynamic Memory Address (DMA), meaning that it will change every time we run the game.
So what we have to do is create a signature pattern that we can use for finding the address of the instruction that decrements the ammo count. A signature pattern is essentially a unique pattern of instructions in the memory which we can use for finding this specific instruction again.
To create a signature you must select at least 2 instruction lines.
Right click and select Make Sig->Test Sig.
Note: You must have installed the Sig Maker Olly plugin by dropping it in the Olly folder
Now you will see signature details with 4 important parameters.
Make sure that this signature is unique in the sense that it will only return one address. Hit scan to verify this. If you get back more than one address, go back and select more lines of instructions and try again.
- Signature - the actual pattern of instructions (in hex) byte by byte. Each "/" denotes the next byte and the "x" denotes hexadecimal.
- Mask - A "x" denotes that we care about this byte in our pattern. A "?" denotes that we don't care about this particular byte in the pattern.
- Base Address - Where to start searching for the pattern in the memory.
- Scan Size - How far out from the base address to search?
Now we can finally go back and finally achieve our desired unlimited ammo. Go to the address that had the DEC instruction. We're going to re-assemble the program to do nothing instead of decrement the value.
We're going to replace
DEC DWORD PTR DS:[ESI]
"NOP" simply means No Operation. And this does exactly what it sounds like. The processor will do nothing at this particular instruction.
Make sure that Fill with NOP's is checked off. This is because whatever we replace the current instruction with must match in size to the old one. The op-code (the middle column of the instruction line between the memory address and the instruction command) for the DEC instruction is FFOE and 2-bytes in size. The op-command for a NOP command is 90 and 1-byte in size. Selecting Fill with NOP's will automatically put two NOP's to match the op-code size.
This is what our instruction looks like now in the assembler. We're almost done! Just remove the hardware breakpoint and go back in game and test out your unlimited ammo.
I'll write up another tutorial soon to explain how to write a program in C++ that will do this for you.