In the previous lessons, we explored techniques to hack Wesnoth, including changing memory (such as gold) and changing code (such as recruiting units). However, all of these changes only persisted until we closed Wesnoth. To regain these hacks upon reopening the game, we would then have to repeat the initial process in a memory scanner or debugger.
This is both tedious and impossible to distribute to a larger audience. However, since we can now defeat DMA, we know that any memory we need to change is always in a static location. Because of this, we can create a set of instructions that contains the changes we wish to make. Creating this set of instructions in a way that a computer can understand is known as programming. By programming hacks, we can create programs that can be executed and will automatically change the memory we care about. We can also distribute these programs to other people who want to experiment with our hacks.
Programming, as a whole, is too large of a topic to comprehensively cover in these lessons. Instead, we will focus on the subset of programming that is relevant for creating hacks.
In the Computer Fundamentals lesson, we briefly covered programming languages. Programming languages allow code to be written in a human-readable form. This code is then translated down to instructions that a CPU can understand. There are many programming languages, and they can be broken down into roughly two categories: what they execute on, and how they execute.
Programming languages can create code that either executes directly on a CPU or executes through an interpreter. An interpreter works by dynamically translating the initial code into a form that the CPU can understand. These two types are known as compiled languages and interpreted languages, respectively. Programming languages can also either execute instructions in order (top to bottom), or through the declaration and resolution of functions. These types are known as imperative and functional, respectively.
A language can be classified by applying those two modifiers. For example, C is a compiled, imperative language. Java is an interpreted, imperative language. Haskell is an example of a compiled, functional language. Interpreted languages can be compiled as well, often by bundling the interpreter and the initial instructions together.
There is no correct or best language. Some languages are better suited for different purposes, but all languages can achieve every purpose. However, when programming game hacks, we have several restrictions that limit our choice of language. The language we pick needs to support three main features:
- Direct access to the Windows API
- Modification other applications’ memory
- Loaded and executed on the CPU
All of these requirements will be explained later, but they basically exclude interpreted languages. In addition, languages that don’t allow direct memory access, such as Java, are excluded.
There are several languages that support the three criteria above. These include C and C#, as well as compiled versions of python. However, C++ offers the best combination of high-level language features (such as classes and strings) and low-level direct access to memory. This makes C++ ideal for programming game hacks.
C++ is a compiled, imperative language. It is a relatively difficult language to learn, but we will only need to understand a subset of its features to create game hacks. One of its most important features, for us, is the ability to create pointers that can directly modify memory addresses.
Pointers are another complex topic that we will only cover briefly. Pointers are a type of variable that point to another section of memory. For example, take the following C++ code:
int x = 5;
int *y = &x;
In C++, a * represents a pointer declaration. The & returns the address of a variable. So, after executing this code, the variable y points to the variable x. Consider the following code:
*y = 6;
This code will dereference (or get the address it points at) y and then assign that value to 6. After this code executes, the variable x will also be 6, since this was the value that y points to.
Applying this to game hacking, let’s say we find a gold value at
0x12345678 and this value is not dynamically allocated. If we
were to load our C++ program into the game’s address space, we could use a
pointer to modify the value of the gold:
int *gold = (int*)0x12345678;
*gold = 999;
After executing, the gold value at
0x12345678 will now be set
to 999. Pointers give us a large amount of control over a game’s memory,
but they can be hard to understand. We will explore them more in following
Types of Hacks
There are three main types of game hacks that can be programmed. These are:
- External executables
- Injected DLL’s (dynamic-link libraries)
- Custom wrappers
Each of these has its own use-case. External executables are stand-alone programs that can be executed normally. These executables use functions built into Windows, known as Application Programming Interfaces (API’s), to read and modify memory of another executable. By contrast, injected DLL’s need to be loaded into the game’s memory in some way. Once loaded, they execute within the memory of the game and can directly access the game’s memory through pointers. Custom wrappers are used when creating hacks that target the game’s drawing libraries, such as DirectX and OpenGL. By loading a custom version of these libraries that “wrap” the original functionality, we can cause the game’s drawing logic to be altered.