It’s Cheating! Learning to Hack Games and Write Cheats with a Simple Example
Computer games open up new worlds for us—and the world of cheats is one of them. Today, we’ll go from theory to practice and write our own cheat. If you want to learn how to hack executable files, this can be a great exercise.
Types of Cheats and Common Tactics
There are several types of cheats, which can be grouped as follows:
- External – Cheats that run in a separate process. If you hide your external cheat by loading it into another process’s memory, it becomes a hidden external cheat.
- Internal – Cheats that are injected directly into the game’s process using an injector. After being loaded into the game’s memory, the cheat’s entry point is called in a separate thread.
- Pixelscan – Cheats that use screen images and pixel patterns to extract information from the game.
- Network proxy – Cheats that use network proxies to intercept client-server traffic, allowing you to gather or modify information as needed.
There are three main tactics for modifying game behavior:
- Modifying game memory: Using OS APIs to find and change memory regions containing important data (like health or ammo).
- Simulating player actions: The app mimics player actions, such as clicking the mouse at specific locations.
- Intercepting game traffic: The cheat sits between the game and the server, intercepting and modifying data to trick the client or server.
Most modern games are made for Windows, so our examples will focus on that platform.
Writing a Simple Game in C#
It’s best to learn about cheats through practice. We’ll write a small game to experiment with. I’ll use C#, but I’ll keep the data structure similar to C++. In my experience, cheating in C# games is very easy.
The game is simple: press Enter and you lose. Not very fair, right? Let’s try to change the rules.
Let’s Start Reverse Engineering
We have the game’s executable file. Instead of the source code, we’ll study the app’s memory and behavior.
Understanding Game Behavior
Each time you press Enter, the player’s health drops by 15. The starting health is 100.
We’ll use Cheat Engine to study memory. This app helps you find variables in an app’s memory and is also a good debugger. Restart the game and attach Cheat Engine to it.
- First, search for all values of 85 in memory.
- Press Enter in the game; health drops to 70. Filter the values.
- Once you find the right value, change it and press Enter to check the result.
The problem is, after restarting the game, the value will be at a different address. Filtering every time is pointless. Instead, use AOB scanning (Array Of Bytes).
Due to ASLR (Address Space Layout Randomization), the player structure will be at a new location each time. To find it, you need a signature—a set of bytes that don’t change in the structure, which you can search for in memory.
After a few more Enter presses, health drops to 55. Find the value in memory and open the region it’s in. The highlighted byte is the start of our int32
value: 37 00 00 00
(which is 55 in decimal).
Copy a small region of memory and paste it into Notepad for analysis. Restart the app, find the value again, and compare the memory regions. The goal is to find bytes near the signature that don’t change. The fewer bytes in the signature, the faster the scan. For example, 01 00 00 00
is too common; 03 00 00 01 00 00 00
is better, but still not unique. Try ED 03 00 00 01 00 00 00
for uniqueness.
Once you have a unique signature, note the offset to get the start address, not just the health address. Save the signature for later.
The External Cheat Lifecycle
Using OpenProcess
, external cheats get a handle to the target process and make changes (patching code or reading/writing variables in memory) using ReadProcessMemory
and WriteProcessMemory
. Since data is dynamically allocated, you can use AOB scanning to find addresses each time.
The external cheat lifecycle:
- Find the process ID.
- Get a handle to the process with the required permissions.
- Find addresses in memory.
- Patch code if needed.
- Draw a GUI if needed.
- Read or modify memory as needed.
Writing an External Cheat for Our Game
To call WinAPI functions from C#, use P/Invoke. You can find ready-to-use declarations on pinvoke.net. Here are the main functions:
OpenProcess
ReadProcessMemory
WriteProcessMemory
VirtualQueryEx
(to enumerate memory regions)
Define a MemoryRegion
class to store the base address, size, and protection of each region. Then, enumerate all memory regions, checking their state and protection, and add them to a list.
To scan for a pattern, create an interface for pattern parts (known and unknown bytes), parse the pattern string, and implement fast memory scanning (avoid LINQ for performance).
Once you find the pattern in memory, calculate the player’s health address, read and write its value, and demonstrate the cheat in action by setting health to int.MaxValue
.
Testing
Run your cheat, then start the game. Everything should work: pressing Enter in the game will show that the cheat is working and health is not decreasing.
Writing Your First Injector
There are many ways to make a process load your code: DLL Hijacking, SetWindowsHookEx, but we’ll start with the simplest and most well-known: LoadLibrary. This function makes the target process load a DLL.
Steps:
- Get the DLL name from the user and check if it exists.
- Get the process name and find its ID.
- Open a handle to the process with full access.
- Convert the DLL path to bytes and allocate memory in the target process.
- Write the DLL path into the process’s memory.
- Get the address of
LoadLibraryA
usingGetProcAddress
andGetModuleHandle
. - Create a remote thread in the process at the
LoadLibraryA
address, passing the DLL path as an argument.
Now your injector is ready. Test it after writing a simple DLL.
Writing the Internal Cheat Base
Switch to C++. Start with the DLL entry point, which takes three parameters: HINSTANCE
(library handle), DWORD
(reason for call), and LPVOID
(reserved). On DLL_PROCESS_ATTACH
, show a message box for testing.
Next, create a singleton class for your cheat logic. In the entry point, initialize and run the cheat when the DLL is loaded.
To find a pattern in memory, enumerate all memory regions (using VirtualQuery
), and for each, scan for the pattern. Use a vector of int
to store pattern data, with -1
for wildcards. If you find the pattern, save its address.
Be careful with memory protection flags when scanning regions. Make sure to include all readable and writable pages, not just executable ones. If you miss regions, you might not find your pattern.
Once you find the player base, save it and use it in your run()
function to set the player’s health to INT_MAX
every 100 ms.
Testing the Internal Cheat
Start the game, inject the DLL, and press Enter a few times. The cheat works—your health doesn’t decrease!
Conclusion
Any game element processed on your computer can be modified or even removed. For better or worse, game companies don’t always care about anti-cheat, leaving the door open for us, the cheaters.