Reverse Engineering Dark Souls
GITHUB REPO
Note: While this write-up includes technical details that could potentially be misused in Dark Souls PvP, I urge you not to cheat in games. This project was conducted purely for educational purposes.
Controls
- F1 - Toggle Infinite Health
Introduction
This project involved reverse-engineering Dark Souls Remastered to manipulate values in memory, a process that involved several challenges and learning opportunities. Throughout this write-up, I’ll outline the key steps I took, some obstacles I encountered, and how I ultimately succeeded in creating a pointer chain to track the health value. One of the main challenges involved attaching a debugger using DBVM, which can occasionally lead to system crashes, but overcoming these difficulties made the experience invaluable. I’m looking forward to exploring similar projects, such as infinite stamina or damage multipliers, in the future.
Requirements
For this project, I used a copy of Dark Souls Remastered and Cheat Engine. Although Cheat Engine is often associated with game cheats, it’s actually a powerful and sophisticated tool for reverse engineering. Its simplicity in allowing memory modifications is not a flaw but rather a testament to its effectiveness as a reverse-engineering tool.
Since I assume you’re already familiar with Dark Souls Remastered and general game mechanics, I won’t be going into game details such as menus, locations, or stats. Similarly, I won’t be focusing too much on the specifics of Cheat Engine itself, as this write-up is more about the steps I took to reverse-engineer the game than a detailed tutorial on the software.
Finding the Health Value Memory Address
To get started, I opened both Dark Souls Remastered and Cheat Engine, then attached Cheat Engine to the game process (DARK SOULS™: REMASTERED or DarkSoulsRemastered.exe) using the computer icon at the top left [1]. I created a character and moved to Firelink Shrine since it provided easy access to both a bonfire and enemies for testing.
From there, I found my current health value in the game [2] and entered it into Cheat Engine’s "Value" field [3], leaving the scan type set to "Exact Value." Since I suspected the health value was stored as a 4-byte integer (common in C++), I used that as a starting point. A 4-byte integer occupies 32 bits of memory (4 bytes = 32 bits), and in most cases, this type of value would work. If not, other common types such as float or double might be worth testing.
After clicking "New Scan" and "First Scan" [4], Cheat Engine presented a list of matching addresses, each representing potential memory locations for the health value. To narrow it down, I took some damage in-game (courtesy of a skeleton) and used "Next Scan" to search for the updated health value [5].
At this point, I knew that some of these addresses might be tied to display values rather than the actual health value, so I started experimenting by modifying the values slightly. Doing this allowed me to track which addresses had an effect on the in-game health, but I had to be cautious as drastic changes could cause the game to crash.
Finding the Health Pointer Chain
Once I had the health value address, I knew that it wouldn’t stay valid if I closed and reopened Dark Souls. This was because the health value was stored on the heap, a dynamically allocated section of memory that changes between game runs. To address this, I needed to find a series of pointers that led to the health value address, which is commonly known as a pointer chain. This approach would allow me to reliably locate the health value address every time the game was restarted.
The next step was attaching a debugger to Dark Souls. I used DBVM, as mentioned earlier, but it comes with some risks, like potential system crashes. A regular debugger would likely be detected by the game and cause it to crash, so DBVM was my only real option. As a side note, if you’re experimenting with any of this, it’s essential to run the game in 'Offline Mode' to avoid being banned. You can learn more about DBVM from the Cheat Engine Wiki.
To locate the health pointer chain, I needed to find the code accessing the health value’s memory address. I right-clicked the health value in Cheat Engine and selected "Find out what writes to this address" [1]. When prompted to attach a debugger, I clicked "Yes" to both the DBVM warning and the debugger prompt. Once the debugger was running, I quickly took some damage in-game to trigger a memory write, then stopped the debugger.
This brought up a window showing the 'Counts' and 'Instructions' [2]. The 'Count' column indicated how many times the code was executed, while the 'Instruction' column showed the memory location, hex opcodes, and disassembly. I double-clicked on the instruction with the fewest calls (or the one triggered by the damage I took) to focus on the function most likely responsible for processing damage to the player. A new window displayed a more detailed disassembly[3] and the stack frame at the time the instruction was executed [4]. For those curious about how stack frames work in x86-64 architecture, Eli Bendersky’s article, especially the 'Registers galore' section, provides some excellent information.
Cheat Engine suggested the pointer value that would allow me to track the health address [5], but I opted to find it manually for better understanding.
The instruction I focused on looked like this: mov [rbx+000003E8],eax
. Here, the value in the eax
register was being written to rbx+000003E8
, which corresponded to the address 0D51B538
.
rbx+000003E8 = 0D51B538
. By subtracting the offset 3E8
, I calculated the base pointer: rbx = 0D51B538 - 000003E8 = 0D51B150
.
Of course, I could have simply used the value in the rbx
register as Cheat Engine suggested, but I chose the manual approach to reinforce my understanding of how memory addresses and pointers work.
At this point, I had the base pointer for health, but I wasn’t quite done. The base pointer was still located on the heap, meaning it would be invalid after restarting the game. What I needed was a pointer on the stack, something like DarkSoulsRemastered.exe+???????
, which would remain stable across sessions.
With this in mind, I searched for the base pointer 0D51B150
in Cheat Engine, checking the 'Hex' box and starting a scan for any memory addresses that pointed to it. This scan returned many results. To narrow them down, I tried "Next Scan" a few times but knew this wouldn’t do much. At this stage, I had to dig deeper. Time for some sleuthing.
One method I considered was going through each value individually and using "Find out what accesses this address" on each one, carefully reading through the instructions and disassembly to determine their relevance. While this can work, it’s incredibly tedious and time-consuming, so I only use this method when I’ve exhausted all other options.
Instead, I scanned through the list of pointers and noticed something interesting: most of the values began with 0 or 2, but one started with 1. This seemed like a promising lead. I ran "Find out what accesses this address" on that value and noticed that it triggered many instructions like cmp [rdx], rax
, with rdx
equal to the pointer 01C4E4D40
. Since the offset was 0, this was equivalent to cmp [rdx + 0], rax
, which is important to note when constructing pointer chains.
I saved this value and ran another scan using it. This time, one of the pointers was green, indicating a stack address. A key find! I saved this pointer, and with it, I began building a pointer chain that would automatically lead me to the correct health address, even between game sessions.
As I investigate and trace through the pointers, I usually keep track of them by writing something like this in a notepad:
[DarkSoulsRemastered.exe + 1A31768] = 01C4E4D40
[01C4E4D40 + 0] = 0D51B150
[0D51B150 + 3E8] = Health value
But what does DarkSoulsRemastered.exe + 1A31768
mean?
DarkSoulsRemastered.exe
refers to the game’s main executable, which is loaded into memory when the game starts. Each executable or module has a base address, which is the starting point in memory where it’s loaded. Modules can also include Dynamic Link Libraries (DLLs), which are external files that the game loads to provide additional functionality.
When we write DarkSoulsRemastered.exe + 1A31768
, we’re using the base address of the game’s executable and adding the offset 1A31768
to find a specific memory address within the game.
In this case, DarkSoulsRemastered.exe + 1A31768
points to the memory address 01C4E4D40
, and from there, the pointer chain leads us to the health value. If we wanted to access memory from other modules, we could do so in a similar way by referencing the module name, such as OtherModule.dll + Offset
.
From this, I can construct the pointer chain in Cheat Engine:
Challenges in Finding Pointer Chains
Anti-Cheat Mechanisms: Many online or competitive games include anti-cheat mechanisms designed to prevent tampering with memory values. These mechanisms can detect when a debugger is attached, and games might crash or behave unpredictably if external programs like Cheat Engine attempt to modify in-game memory. Additionally, some anti-cheat software will actively block memory scanning and debugging tools, making it significantly harder to find and manipulate pointer chains.
Code Obfuscation: Some games employ code obfuscation techniques to make reverse engineering more difficult. This could involve obfuscating memory structures or instructions so that they don’t follow predictable patterns. For example, the game might randomize or encrypt pointer values, or use dynamically generated code that changes each time the game runs. This makes it much harder to find reliable pointer chains, as the memory layout may be entirely different from one session to the next.
Games Using Interpreted Languages: Games built using interpreted languages like Java or C# can also pose additional challenges. In these cases, memory allocation can be handled by a virtual machine (VM), which adds an extra layer of abstraction. The VM may move memory values around during runtime through techniques like garbage collection, making it difficult to track the actual pointer chains because memory addresses might change unpredictably.
Deliberate Pointer Obfuscation: Some games intentionally obscure their pointer chains as a defense against memory tampering. For example, the game might cycle through a list of pointers, with only one being valid at any given time. Accessing the invalid ones could result in incorrect values or even crash the game with a null reference exception or memory access violation. These rotating pointers make it extremely difficult to reliably locate the correct pointer chain, requiring advanced techniques and a deeper understanding of the game's memory architecture to bypass.
Multi-Level Pointer Chains: In more complex games, the pointer chain might not be as simple as one or two levels deep. You may encounter pointer chains that span several levels, with each pointer leading to another memory address that, in turn, points to the next one. This means that you’ll need to recursively trace through each level of pointers to reach the final value. Multi-level pointer chains can be very time-consuming to identify and map out correctly, especially in larger, more complex games.
Dynamic Memory and ASLR: Address Space Layout Randomization (ASLR) is a security feature used by many modern operating systems to randomly arrange the positions of key data areas, such as the base of executables and libraries in memory. This means that each time the game runs, the memory addresses of key components may change, making it harder to locate fixed pointers or pointer chains across different sessions. ASLR forces the reverse engineer to use techniques like base address calculation or signature scanning to find stable pointers.
To be continued...