The Hacking of Dark Souls III Continues with Predator Speed
Greetings all! The hacking of Dark Souls III shall continue now! At the conclusion of this article, we will be that much closer to certifying Dark Souls III as Omnified. If you read my previous article, you would know that we have overhauled Dark Souls III’s damage system already with the implementation of my Apocalypse system. Today, we’ll be implementing my Predator system, giving enemies a deadly speed boost.
One of these days I will definitely be getting around to publishing some articles that go into great depth in explaining how each of my game-neutral systems work. The Predator system will absolutely be one of those systems covered by one of these articles, and when it exists, I will link it here. Until then, however, you can watch the video below for a general overview.
The Predator system is a general purpose AI movement enhancer. The aim of the Predator system is to make enemies move faster to kill your ass quicker! It factors in a number of factors related to the enemy to maximize effectiveness of the boost applied. I recommend watching the video to understand what it does in detail (or wait for that article)!
Finding the Movement Application Code
Much like what was required in the hacking of the Apocalypse system into Dark Souls III, in order to hack in the Predator system, we need to create the appropriate initiation point. The initiation point of the Predator system needs to be located in the area of game code where movement offsets are being applied to working memory holding the creature’s current coordinates.
Working memory is a term I use to refer to memory that is being used to temporarily hold a value, prior to that value being committed to the memory used to store the creature’s actual location. In all the games I’ve disassembled, these places in code are always very distinct, and many times very, very far apart.
A movement offset represents the amount of movement on a particular axis. The “movement application” code is where this offset is being applied to coordinate values based on the creature’s current coordinates. The “location update” code is where these updated coordinates get committed back to the memory that holds the creature’s current coordinates.
As the general overview article on the Predator system will emphasize, it is extremely important (99% of the time) that initiation point be injected into the movement application code and not the location update code. The Apocalypse system had a similar requirement (damage application code, not health update code), but you can get away with skirting that requirement much more with the Apocalypse system than you can with the Predator system.
Failure to adhere to this requirement when implementing the Predator system can cause horrific results; improper placement of the initiation point might lead to creatures phasing out existence, or just bouncing all over the place. This is because there is a lot of physics code running in games these days, and this code needs to be aware of where the creature is moving to — a simple dirty speed hack, written at the place of location update, can often lead to disaster.
Finding the movement application code can sometimes be very difficult. Let’s see how difficult it is to find when hacking Dark Souls III! To start things off, let’s find the location update code by looking for code that writes to our player’s coordinates.
An opcode watch window will pop open. We see some entries in there already — hopefully more will show up after we move our character around, which we proceed to do. Hmm…not seeing any more show up. Move around some more? Nooooo…
Unfortunately, in Dark Souls III, our location update code is a constantly running function. In many (most?) games, the location update code will only execute in response to actual player movement. However, as we can see here, that is not always the case. This can complicate disassembly and debugging, and we must be conscious of this fact when actually implementing Predator.
We will see how this complicates disassembly shortly. Let’s click on that “Show disassembler” button to pop open the memory viewer, and see what we got.
Here is our location update code. The rbx register holds the character physics module (identified as the SprjChrPhysicsModule in the first Dark Souls III hacking article I wrote). The memory offset 0x80 is where the location coordinates for the player begins. The updated coordinates are found in the xmm6 register. Our job is to find out where the calculation for the value in xmm6 occurs.
Now, don’t forget, we don’t care about our location update code, we care about the location update code for other creatures. We can do a quick check to see if other NPC’s use this code by finding a moving one and then right clicking the instruction and choosing “Find out what addresses this instruction accesses”.
We can see multiple addresses being updated, so we know that our efforts thus far are not in vain. In some games, the location update code for the player is only for the player, and separate code exists for creatures. Sometimes there are multiple location update codes for different kinds of creatures, however this is rare.
So it is now time to find the movement application code, where movement offsets are being applied to a current coordinate value, for Dark Souls III. This can be a very difficult task to do, however I have a number of techniques I have developed that will aid you in this task.
Fortunately for me (and maybe unfortunately for you, as you don’t get to learn about them), I don’t need to share any of these techniques yet because the movement application code is very easy to find in this game! Let’s go back to our location update code. Now…scroll up just a bit.
Just a few lines above the location update code, we can some packed floating point arithmetic operations (subps, mulps, addps). These lines of code very suspiciously look like code you’d find in the movement application code!
Here’s where the “constantly executing location update code” makes disassembly a bit tricky. We need to verify that this code is movement application code. We will know if it is the movement application code if we see the original coordinates in one of the XMM registers and smaller values representing the movement offsets being added to it.
However, because this code is constantly executing, simply throwing a breakpoint on the code will just stop execution, preventing us from moving and checking the values. The easiest way around this (other than setting a conditional breakpoint, which I’m not too sure how to do when XMM registers are involved), is to throw a breakpoint on one of the instructions, bind a hotkey to “Debug->Run” in the options, and then constantly hit that hotkey while in the game and while trying to move forward.
When I did this, I got the following values to show up in the XMM registers with a breakpoint on the instruction found at DarkSoulsIII.exe+9D2336:
As a reminder, the instruction the breakpoint was placed on is:
addps xmm6,xmm1
Looking above, the player’s current coordinates are stored in xmm1 (verified by looking at our the values returned by the playerCoords pointer we created in the first hacking article for Dark Souls III). And indeed, we can see values that are most certainly movement offsets in the xmm6 register. If the player stops moving, these values become 0.
So we found our movement application code! This was very easy! Dare I say that this was the easiest movement application code I’ve ever had to find! Don’t think it is always this easy! It most certainly is not!
It is here that we will inject our initiation point for the Predator system. The initiation code for the Predator system is a bit more complicated than the Apocalypse system, so let’s get to writing it. The sooner it’s done, the sooner we can enjoy the beauty of enemies rushing in at high speed to destroy us!
Hacking the Predator Initiation Hook Into Dark Souls III
Time to write the initiation point for the Predator system. We’ll be injecting it into the bit of code we had the breakpoint on during our disassembly. Here is the starting template for our initiation hook:
Predator Initiation Hook – Template
// Initiates the Predator system. define(omnifyPredatorHook, "DarkSoulsIII.exe" + 9D2336) assert(omnifyPredatorHook, 0F 58 F1 E8 E2 FC E0 FF) alloc(initiatePredator,$1000,omnifyPredatorHook) registersymbol(omnifyPredatorHook) initiatePredator: initiatePredatorOriginalCode: addps xmm6,xmm1 call DarkSoulsIII.exe+7E2020 jmp initiatePredatorReturn omnifyPredatorHook: jmp initiatePredator nop 3 initiatePredatorReturn:
Our code will go under the initiatePredator label, and it needs to do whatever it has to so that
addps xmm6,xmm1
ends up using the movement offsets produced by the Predator system. So let’s take a breather real quick and talk about just exactly we need to do.
As of now there is only a single “public facing” component of the Predator system we need to be concerned with. It is responsible for modifying the movement offsets of non-player creatures, and it is accessible through the executePredator function. This function requires the following parameters to be provided:
- The player’s X, Y, and Z coordinate values.
- The target enemy’s X, Y, and Z coordinate values.
- The target enemy’s dimensional scales (height, width, and depth).
- The target enemy’s X, Y, and Z movement offsets.
All of these values are expected to be in floating point form — which is no big deal in the case of Dark Souls III, since it seems to use all floats for its coordinate values. Further discussion regarding what these parameters are, and how to pass them to the function, will be covered in the Predator-specific article. That aside, we have ourselves here an opportunity to see a real case of how we might go about invoking it, in this case for Dark Souls III.
The return values of the Predator system are stored in the eax, ebx, and ecx registers. These registers will hold the updated X, Y, and Z movement offsets respectively. So, we’ll be updating the contents of the xmm6 register with those values when all is said and done.
Some Things to Keep in Mind…
We will want to ensure that it is not the player whose coordinates are being updated before calling the Predator system. I have not added player speed governance functionality into the Predator system yet; for now, it is strictly for enemy movement. Since I usually add a player speed hook for each game, I really should add some sort of player component, and hell I’ll get around to doing it sometime soon…OK!?
We must also bear in mind that our player coordinate pointer (playerCoords), which we will be using to check for target creature identity, may very well not be initialized yet at the time of the location update code executing. So we must make sure that is initialized before even deigning to think about calling the Predator function!
Everything else that we need should be right here at our fingertips. The only other thing that is a bit of a stumbling block here is the requirement that the data being passed is in the correct form. In particular, dumping an XMM register’s value onto the stack as a parameter directly from the register is not supported for the fourth parameter. This will be discussed in greater detail in the Predator-specific article — for now I’ll settle with simply showing you how to pass movement offset values originating from an XMM register as a parameter correctly.
Let’s take a look at the first bit of code we’ll be writing. This code will be responsible for checking that the needed pointers have undergone initialization, that the player isn’t the one moving, and for performing the needed preservation of data onto the stack.
Predator Initiation Hook – First Steps
initiatePredator: pushf // Ensure that the player coordinates pointer has been initialized. push rax mov rax,playerCoords cmp [rax],0 pop rax je initiatePredatorOriginalCode // Ensure that the entity moving is not the player. push rax mov rax,playerCoords cmp rbx,[rax] pop rax je initiatePredatorOriginalCode // Backing up a few SSE registers we'll be using to hold // values for some parameters we'll be passing. sub rsp,10 movdqu [rsp],xmm0 sub rsp,10 movdqu [rsp],xmm1 // Backing up the registers that are used by the Predator // system to hold its return values. push rax push rbx push rcx
Here we ensure that playerCoords is pointing to some real place in memory. If it hasn’t been initialized it’ll simply have a bunch of zeroes assigned to it. We then check if the address contained in rbx, which is responsible for holding the target creature’s physics module address, is the same as what playerCoords points to. If it is the same, we bail out!
We want to make sure we preserve whatever data is stored in the rax, rbx, and rcx registers, as these will be overwritten with the Predator system’s return values. After all this, we got everything we need in order to prepare parameters for passing to the Predator system’s main function.
Predator Initiation Hook – Function Execution
initiatePredatorExecute: // We construct an identity matrix, full of just 1's to pass // as the dimensional scale matrix parameter as this game lacks // true scale. More info can be found in Predator overview article! movss xmm0,[identityValue] shufps xmm0,xmm0,0 // Dereference the player coordinates pointer, and then // push the player's X, Y, and Z coordinates to the stack // as the first parameter. mov rax,playerCoords mov rcx,[rax] push [rcx+80] push [rcx+88] // Push the enemy's X, Y, and Z coordinates to the stack // as the second parameter. push [rbx+80] push [rbx+88] // We make room for 16 bytes on the stack and dump the identity // matrix values onto the stack directly from xmm0 as the third parameter. // Although we only need to pass values for height, width, and depth, // the Predator system expects this parameter to take up 16 bytes. sub rsp,10 movdqu [rsp],xmm0 // The Predator system expects the movement offsets to be provided // as if they were being pushed onto the stack directly from memory. // The following code accomplishes this by putting xmm6's high words // onto the xmm1 register and then moving the xmm6 register and then // the xmm1 register onto the stack as quadwords. movhlps xmm1,xmm6 sub rsp,8 movq [rsp],xmm6 sub rsp,8 movq [rsp],xmm1 // With the fourth parameter passed we execute the Predator system. call executePredator
The bulk of code in our hook deals with the preparing and pushing of parameters onto the stack. The Predator system takes care of everything else for us however! The only remaining code to write is the code that will process the return values of the Predator system and update the movement offsets that Dark Souls III will be using in its own movement application code.
Predator Initiation Hook – Return Value Processing and Cleanup
initiatePredatorCleanup: // Make some temporary room on the stack to store the returned values // from the Predator system onto for loading into the xmm6 register. sub rsp,10 movups [rsp],xmm6 mov [rsp],eax mov [rsp+4],ebx mov [rsp+8],ecx movups xmm6,[rsp] add rsp,10 // Restore backed up values. pop rcx pop rbx pop rax movdqu xmm1,[rsp] add rsp,10 movdqu xmm0,[rsp] add rsp,10
That’s it! If everything goes well, we will have been successful in hacking in the Predator system into Dark Souls III! As was the case with implementing the Apocalypse system into Dark Souls III, external references to the Omnified framework found in the Omnified.lua library file are required. If you’re still scratching your head on that one, it will be covered by an Omnified Design article in the near future!
But…Does It Work!?
I can proudly say that this actually worked when I injected it into the game…first try! That really doesn’t happen too often; although, it is happening more and more! Enemies in Dark Souls III now close in on the player with terrifying speed and accuracy.
Don’t believe me? Here’s a little peek into our almost Omnified Dark Souls III, now powered by the Predator system:
This enemy normally moves quite slowly…and is now so fast that I’m getting scared just thinking about my eventual Omnified Dark Souls III playthrough on my stream. Predator system is in.
Here is the complete code required for our successful hacking of the Predator system into Dark Souls III:
Predator Initiation Hook – Complete
// Initiates the Predator system. // xmm6: Movement offsets. // rbx: Target physics module. define(omnifyPredatorHook, "DarkSoulsIII.exe" + 9D2336) assert(omnifyPredatorHook, 0F 58 F1 E8 E2 FC E0 FF) alloc(initiatePredator,$1000,omnifyPredatorHook) alloc(identityValue,8) registersymbol(omnifyPredatorHook) initiatePredator: pushf // Ensure that the player coordinates pointer has been initialized. push rax mov rax,playerCoords cmp [rax],0 pop rax je initiatePredatorOriginalCode // Ensure that the entity moving is not the player. push rax mov rax,playerCoords cmp rbx,[rax] pop rax je initiatePredatorOriginalCode // Backing up a few SSE registers we'll be using to hold // values for some parameters we'll be passing. sub rsp,10 movdqu [rsp],xmm0 sub rsp,10 movdqu [rsp],xmm1 // Backing up the registers that are used by the Predator // system to hold its return values. push rax push rbx push rcx initiatePredatorExecute: // We construct an identity matrix, full of just 1's to pass // as the dimensional scale matrix parameter as this game lacks // true scale. More info can be found in Predator overview article! movss xmm0,[identityValue] shufps xmm0,xmm0,0 // Dereference the player coordinates pointer, and then // push the player's X, Y, and Z coordinates to the stack // as the first parameter. mov rax,playerCoords mov rcx,[rax] push [rcx+80] push [rcx+88] // Push the enemy's X, Y, and Z coordinates to the stack // as the second parameter. push [rbx+80] push [rbx+88] // We make room for 16 bytes on the stack and dump the identity // matrix values onto the stack directly from xmm0 as the third parameter. // Although we only need to pass values for height, width, and depth, // the Predator system expects this parameter to take up 16 bytes. sub rsp,10 movdqu [rsp],xmm0 // The Predator system expects the movement offsets to be provided // as if they were being pushed onto the stack directly from memory. // The following code accomplishes this by putting xmm6's high words // onto the xmm1 register and then moving the xmm6 register and then // the xmm1 register onto the stack as quadwords. movhlps xmm1,xmm6 sub rsp,8 movq [rsp],xmm6 sub rsp,8 movq [rsp],xmm1 // With the fourth parameter passed we execute the Predator system. call executePredator initiatePredatorCleanup: // Make some temporary room on the stack to store the returned values // from the Predator system onto for loading into the xmm6 register. sub rsp,10 movups [rsp],xmm6 mov [rsp],eax mov [rsp+4],ebx mov [rsp+8],ecx movups xmm6,[rsp] add rsp,10 // Restore backed up values. pop rcx pop rbx pop rax movdqu xmm1,[rsp] add rsp,10 movdqu xmm0,[rsp] add rsp,10 initiatePredatorOriginalCode: popf addps xmm6,xmm1 call DarkSoulsIII.exe+7E2020 jmp initiatePredatorReturn omnifyPredatorHook: jmp initiatePredator nop 3 initiatePredatorReturn: identityValue: dd (float)1.0
And that’s a wrap for the Predator system everybody.
The remaining Omnified system to be implemented is the Abomnification system! It will have its own article written for it as I hack it into the game in the near future.
Thanks once again for reading everybody. If you have any questions, let me know! And, as I must always state, don’t forget that the only place to catch this Omnified action live is on my official Twitch stream: https://twitch.tv/omni
Keep it real everybody. š
-Omni