Now that the intro is complete, let's dive right into the dirty deets of game hacking. In any program, once the user hits the execute/compile button, the program's high-level language is converted into assembly then translates those individual instructions into binary for the machine to read and execute at a lower level. This is important because we need to step in at the low-level language level in order to gain sight into what's going on and make the necessary modifications to achieve our desired outcome. You don't need to be able write full applications in x86 or ARM, but you will need to be able to read it and understand how the machine uses registers to store values in memory. Given that, you will also need to know how and what pointers are used for in C/C++. I'll touch on it a bit, but I won't teach a full class on memory.
Before we jump in, a pointer is a variable that stores the address of another variable. Why not store the same value in a new variable? Well that's inefficient and it wastes memory space. Pointers allow us to access a specific address in memory quickly without having to iterate through all of the memory space looking for a specific value. In reference to game hacking, it's necessary to find the value and the location of a particular in-game resource in order to change that value. Check out Wikipedia if you need more info.
As with any type of hacking, we need to conduct reconnaissance to find resources/information of value that can be used to our gain. How do we do this? Play the damn game. Enjoy it for what the devs originally intended. Doing this will allow you to identify the commodities and processes of value like the other players. I, personally, view each game as an individual country. Think about it. The world has an active infrastructure that it's inhabitants need in order to function. There's an economy that includes some form of trade, a way for players to communicate, skills and services that occur in exchange for something of value, personal skills for people to develop, businesses that sell products, and even an underground market that exists outside of the natural markets. Having an advantage in any of these fields can yield major in-game profit. Once we identify a particular value that we want to attack, we enter the research and weaponization phase, which is where we are currently at in the kill-chain.
The game I'll be doing a quick Proof-Of-Concept on is Enter the Gungeon. It's a fairly fun rogue-like shooter that isn't the easiest of games. Naturally, it frustrated me a little too much and I hit a point where I just wanted to win. In this game, like most offline, single-player games, money is a bit of a barrier to get ahead earlier on in a run, so this will be our target. Here I fired up Cheat Engine since I don't have to worry about anti-cheat. Did an initial scan on the one shell (money) I possessed. Cool, there's thousands of results as 1 found just about everywhere in this virtual memory space. After gaining some shells and repeating the scan off of the previous results, I narrowed the address down to 1 result. I changed the value to 5000 just to make sure it was the correct address then waited for it to update in game
Nice, were successful at finding the current temporary address that stores our target value. Now we must dive through layers of pointers to reach our base address. The base address will be the static address that we can always access this value at. Since the pointers will point to a new address in memory space on execution of the application, we absolutely need to find a static address in order to maintain persistence on the value. There are several different approaches to finding the base address that the guys over at guided hacking can take you through. The OG way of doing this is finding what accesses this address on update of the value, going to the register that is either added or moved into our current value position, then verifying that in the live memory view with the address of that value and repeating the process until we end up at our desired base address. However, on this run through, I wanted to test out the pointer scan function of Cheat Engine. Initially after the scan finished I had around 104000 results. I then closed the game and reran the pointer scan on the previous set. When I use "previous set," I mean cheat engine saves the scan data to a file and we can use the previous pointers on a new session of the game to verify they are pointing to the correct address.
After narrowing the selection down to less than a 100 results, I finally started testing individual base addresses to see what would stick. From the image below, you can see which address I ended up sticking with and all the offsets associated with each pointer. Since multilevel pointers can be very time consuming to map, this process exponentially sped things up. SO! Now that we have our base address which is the address at which mono.dll is loaded in the game's virtual memory plus the sum of the offsets. In the PE info, we can see the address of mono.dll with the "preferred imagebase" value is 7FFB726A0000. Sweet, we've done it. We have a static address that doesn't change if we restart the game. What's next?
Alright, so now we have our base address, but let's say, hypothetically, there was an anti-cheat that would pull our machine's process list (yes they pull that and sometimes save it) and finds we're running cheat engine. BOOM, banhammered. We need to externally access this process's memory space and rewrite the value at the base address. This is going to happen in C++. Break out the Mountain Dew and Cheetos boys and girls, we're programming.
To make this program I just followed Rake's tutorial from Guided Hacking on how to write a program to do what we need. Linked here. I made a few modifications that were suggested in the comments of the tutorial in order to grab the base address of a DLL dynamically by just typing in the name of it instead of using the hard-coded value we found. I won't post the full code since it's already over on the page I linked, but let's explain the process.
First, we hook into the program by using the procId and the CreateToolhelp32Snapshot method. This method "Takes a snapshot of the specified processes, as well as the heaps, modules, and threads used by these processes" per the almighty MSDN. Noice, we retrieved the processId, but now we also have to get the base address of your DLL. For this, we'll take another snapshot using TH32CS_SNAPMODULE, TH32CS_SNAPMODULE32, and procId as the parameters. The former parameter includes all modules from our process and the latter includes all 32-bit modules when called from a 64-bit process. Here we can access any of the modules that are called and loaded into the game, perfect for our "mono.dll" module. Once we get both address we can now open a handle to the process since we're ready to make an active modification to the game. Open up the process the resolve the pointer by using the series of offsets listed above. Yes, all 7 levels. To resolve, we have to read the process memory at the location specified to return to the use it. This is just to show what is currently stored there. Then, write to that location the new value for the number of shells we want and read it one more time after to ensure it was actually written. As shown below, we can see the output of the program I made and the in-game result:
Dope, we've done it. The game's memory has been exploited and we now have a program that will be able to change the money to whatever we want anytime we play the game. Congrats, we've reached level 1 game hacker.
A very poignant question is "What happens when a new update comes out for the game?" Well, you're about 95% likely to have to re-do the process just completed since the base code has been changed and the compilation is now very different. Game hacking is very "devs-VS-hackers." Hackers will always find a way to exploit the game because they only need to be correct one time, where devs need to be correct all the time. It's part of what I find so fun about the process.