Goals
In our previous lesson, we hacked our gold by modifying a variable in memory. Memory modification like this is powerful, but it also has limitations. More complex hacks will often require you to modify the game’s code. For example, imagine if we wanted to create a hack that would allow us to recruit units for no gold. One way to do this would be to constantly monitor our gold and manually increase it whenever we recruited a unit. This would also require you to constantly look for any new units added to the game and that unit’s cost. An easier approach would be to modify the game’s code to never decrease a player’s money when recruiting.
Viewing a game’s code as it is running is known as debugging. Understanding and modifying that code to do what you want is known as reversing. You do not have to debug a game to reverse it, but it is very helpful if you can.
Tools Involved
To debug a game, you use a tool known as a debugger. The first step in debugging is “attaching” the debugger to the game you want to debug. Once it’s attached, you are able to view the game’s code in memory. You are also able to pause execution of the game, change the game’s code, and modify registers. We will see examples of these actions in future lessons.
Debuggers can cause unintended side-effects. For example, if you change the game’s code incorrectly, the game can crash. Depending on the game, this can freeze your computer’s display. This is another reason to always separate your hacking machine from your personal machine.
There are many debuggers, but some well-known ones include IDA and gdb. Other debuggers, like WinDbg and OllyDbg, are often mentioned but no longer maintained. In this series, we will be using an open-source debugger named x64dbg. Like any other tool, it’s more important to know the fundamentals than the tool. The same approach we will learn while using x64dbg can be applied to any debugger.
Disassembly and Debugging
After attaching a debugger to a game, the debugger will display the game’s code. However, this is not the original game’s code. Like we discussed in the first lesson, games are usually programmed in a high-level language, like C++. However, the executable running on our computer only contains the opcodes for the CPU to execute. This lack of the original code is what makes reversing difficult. Often games will contain thousands of these opcodes.
When assembling a program, each line of assembly code is converted to an opcode. Disassembly is the process in which opcodes are converted back to assembly. Normally disassembly and debugging are used interchangeably, especially when reversing a game. However, they can be done separately. It is possible to disassemble a program without debugging it. This is known as static analysis and is commonly done when reversing malware. It is also possible to debug a program without disassembling it. A common example of this would be debugging a program that you have written. In this case, you have the original code and the disassembly is unneeded.
It is possible to partially recreate the original high-level code from the disassembly. This is known as decompiling. However, disassembly is always representative of the code executing, while decompiling is not. Decompilers are often forced to guess at the original structure of the code. While these tools can be helpful, they can also lead you down false paths. In these lessons, we will not cover decompiling.
Assembly
When debugging and reversing a game, you will mainly be dealing with assembly. Assembly is similar to the first language we covered in the Computer Fundamentals lesson. Each instruction in assembly does one thing, such as add or subtract. It is not necessary to know every assembly instruction to reverse a game.
While it may appear daunting, any assembly code can be understood by going through it one instruction at a time. When debugging, this is known as stepping through the disassembly. Often there are many instructions in a game that are not critical to understand while reversing. For example, the CPU has to do several instructions when adding numbers that have decimal values. If we are only interested in the result of this addition, we can skip over many of these instructions. Understanding which instructions can be skipped comes with experience.