The Hacking of Dark Souls III Continues with Abomnification
Hello everyone once again. The final bit of our hacking of Dark Souls III shall now begin. At the conclusion of this article, the game will have the necessary minimum changes in order for me to be able to certify it as Omnified, therefore making it available for streaming on my Twitch channel. Today we’ll be implementing my Abomnification system, one of my craziest game-neutral systems, which will cause NPCs to constantly change shape and size randomly.
I will eventually have an Omnified Design article specifically dedicated to my Abomnification system. How the system works and what it does it is truly interesting, and I think appreciation for it can only be gained when learning about all of its features and capabilities. I also need to make a video about Abomnification on my YouTube, and I promise to do so one day soon. Until then, however, read on for a very brief explanation regarding what the Abomnification system does.
Abomnification System: Basic Explanation
The Abomnification system, when implemented, causes all entities other than the player to change shape and size, often in a continuous manner that results in an experience akin to what one might see after ingesting a moderate amount of LSD.
Size and shape change is achieved through smooth animation between an origin scale and a target scale. This process is referred to as morphing. When registered with the Abomnification system, a creature is assigned a randomly determined morphing mode, of which there are (at the time of writing) three different kinds. Also assigned is a randomly determined amount of morphing steps, which influences the number of steps that occur between each morph (i.e. how fast they shape shift).
The Abomnification system causes the gameplay experience to be absurd, hilarious, and also greatly increases the difficulty in unforeseen ways. Indeed, in combination with the other Omnified systems, it really sends the difficulty level to insane levels, as you can no longer read enemies reliably, and you even have trouble hitting the enemy sometimes.
Checking for Easy Scaling Support
Unlike the Apocalypse system and Predator system implementations for this game, implementation of the Abomnification system can sometimes be more complex and require multiple initiation points. Whether or not multiple initiation points are required is dependent on whether the game engine has built in support for changing the scale of of character models.
The design article on the Abomnification system will go into more detail on this, but simply put, the Abomnification system needs to do two things: 1) generate new scaling parameters for a particular creature according to its assigned morphing mode, and 2) change the character model’s dimensions to reflect these new scaling parameters.
In many games, the character’s height, width, and depth multipliers are available as floats located nearby where the character’s location coordinates are stored in memory. When Omnifying a game like that, implementation of the Abomnification system is incredibly easy. However, not all games have easy scaling support built into creature data structures, and Dark Souls III is one of those games (actually, all FromSoft games are like this).
When we don’t have easy scaling support available, we need to find the code responsible for drawing character model matrices, typically where the skeleton of the creature is being constructed. This is an advanced reverse engineering requirement, however I have gotten good at it, and I’m going to show you how I did it when hacking Dark Souls III.
Finding the Character Model Drawing Code
The simplest way to find the code responsible for drawing the character model is to find values that are being written to when the model is changing in ways that can be searched. So, for Dark Souls III, we’re going to take our player character and try to find data being written to when there are changes to his or her height.
So how might we go about effectuating a change in height? Well, after playing around a bit, we can see that simply blocking causes the height to decrease. When we stop blocking, the neutral pose is resumed and height increases back to its original value.
Because the character ducks their head when ducking, they essentially become shorter. The code responsible for drawing the character model will need to be involved in order for this to occur. Therefore, we can use the action of blocking and unblocking to locate this code.
To go about our search, we will need to bind some hotkeys in Cheat Engine. In particular, we need to bind “Next Scan-Decreased Value” to a key and “Next Scan-Increased Value” to another key. We need to do this because if we try to block and then Alt-Tab to do a search for “Decreased Values” (since the height has gone down), the character will stop blocking! Therefore we need to conduct the search entirely while the game has focus.
So, to start things off, let’s do a search for all floats of unknown initial values. We are searching for floats as the matrices that define character model skeletons typically are of this value type.
This will result in around 1.8 billion floats or so. So yeah…we got our work cut out for us. The next step is to go into the game, and hold down the Block button, causing the character’s height to decrease. While blocking, we then press the key to initiate a “Next Search” for decreased values.
So that’s some progress. We got the number of results down from 1.8 billion to 1.8 million. Which is…great! Right? We have a ways to go though. At this point we will want to release the Block button, causing our character to resume his or her normal height. We then press the appropriate key to initiate a “Next Search” for increased values.
We then repeat the above process again and again until we get the number of found results to as low of a number as possible. Be aware that, given the nature of what we’re looking for, we will still have lots and lots of results we’re going to have go through even if we get the results list reduced to as fine of a list as possible.
In my case, I was able to get the number of results down to around 13,000. This is actually an OK amount! We can work with this; not everything is going to be handed to us on a silver platter. What we want to do now is scroll down in the results list until we find a range of addresses that live in a space in memory that looks like an appropriate place for character data.
We can get an idea of what an appropriate place for character data might look like by seeing where the player’s root structure exists. In the case of hacking Dark Souls III, it looks like we want to look at values starting at memory addresses of around 0x7FF400000000.
So we scroll down to values stored around this region, and we then look for values that might look like they’re related to the height. So, when the character is in a neutral pose, I want to find values that are around 1.0, although not necessarily exactly that amount (you probably won’t ever find anything like that). Most important, a number that is positive and not too different in value from 1.0.
We don’t need to be perfect at all in our guessing here; the character model drawing code is going to be responsible for drawing a plethora of different values here. When we find one that looks like a reasonable value, we add it to our address list and then right click it and choose “Find out what writes to this address”.
Typically there is only going to be one instruction writing to values related to a character model skeletal structure, and we see that to be the case here as well.
Just looking at the solitary instruction displayed to us, this is looking good (based on previous experience I’ve had) and may indeed be the character model drawing code we’re looking for. Let’s take a closer look at the surrounding code by clicking on the “Show disassembler” button.
Just by looking at this code, I can tell, from previous experience, that this may very well indeed be the character model drawing code. If you take a look at the instructions writing to the memory, you can see that there are three double quadword writes to [rdx], [rdx+10], and [rdx+20]. These values being written to make up what it is essentially a 3×4 matrix, which is probably the most common type of data used to express a particular skeletal structure.
Is there a quick way we can validate that this is indeed the character model drawing code? Yes! Let’s right click on one of the instructions:
…and choose “Replace with code that does nothing”. If this is the model drawing code, then removing this code, which we determined is related to changes in the height, will result in changes to the character’s height no longer occurring.
If we go back to the game and try blocking now, the pose the character makes will be markedly different from when the code was enabled:
This confirms that we found code responsible for drawing the character’s model. By virtue of the fact that disabling one of the instructions prevents the character’s height from updating in the game when it should, we can manipulate the values in that very same instruction to increase or decrease the character’s height.
Indeed, if we write some code that doubles the values found in the xmm1 register (which is being written to [rdx+10]), we see the following, very satisfying result:
Hooray! We can effectuate changes to our character scale in a game that doesn’t have have built in scaling support! This is a very difficult reverse engineering goal that we just achieved, so let’s give ourselves a big ol’ pat on the back for real!
Now that we found the needed code, it’s time to get to writing some code. Or is it? Nope, a bit more disassembly is required, although what remains isn’t as difficult. If we’re lucky, that is.
Figuring Out Where to Store the Morph Scale ID
One topic that the design article on the Abomnification system will discuss is the need to associate what is known as a morph scale ID to each and every creature being morphed. Abomnification achieves the morphing of a creature’s model by storing a large number of configuration parameters that are specific to that creature. These are stored in sandboxed, safe, memory; however, we need some way to tell where in the memory sandbox to look for a particular creature.
We achieve an association between a creature and a place in the memory sandbox by giving that creature a morph scale ID, and storing it somewhere we can find within the creature’s data structures. All we require are four bytes of data that we can write to in one of these structures. Previous implementations of the Abomnification system required us to store all the morphing related data somewhere in the game’s memory, however that was a very difficult requirement to meet.
Even finding somewhere to write just four bytes to in the memory used by the game can be difficult! Some games use memory they allocate very optimally; that is, they use every single bit and byte in whatever they allocate. Other games are more liberal, and leave wide swathes of unused memory in between parts that actually see some use.
And even if the memory doesn’t look like it is used, it might not be possible to write to that memory, or perhaps it might lead to instability after an indeterminate amount of time. Trying to store arbitrary data in some other program’s defined data structures is not a task for the weak hearted!
Fortunately for you, I have several techniques which aid us in finding the perfect place to store some data, and I will walk you through the process we’ll be using for hacking the Abomnification system into Dark Souls III right now!
I like to store the morph scale ID in the same place creature coordinates are stored, as the initiation point for the Abomnification system is probably going to be somewhere where creature coordinates are being processed. So let’s take a look at a large number of enemy physics module structures, and see if there’s a row of data we can manipulate successfully without causing the game to crash.
I first locate an enemy’s coordinates, and then I see what code is accessing those coordinates. I go with the most commonly executing one, as this is probably going to be some map-wide entity coordinate polling function which should be reading coordinates from all creatures.
The code shown above is such a function, that I believe will be accessing the coordinates of all enemies on the map. Let’s right click on the main instruction and then choose to see what addresses are being accessed, like is shown on the image. We will then be greeted with, hopefully, a large amount of coordinate addresses.
Let’s now open up a phatty “Structure dissect” window with all the enemy coordinates loaded into it. We can do this by highlighting all the addresses in the window that just appeared (save for the first address, which in our case is the player’s coordinates), and then right clicking and choosing to “Open dissect data with selected addresses”. After a number of prompts we’ll be greeted with the desired phatty dissection window:
So now that we have a bunch of sample data loaded, we need to find a good place to store our ID. I like to find a place in the structure that has a bunch of 0’s surrounding it, which gives me a little bit of assurance that there won’t be any overlapping data being written into our spot.
Just finding a place surrounded by a bunch of empty memory is by no means any kind of guarantee that it’ll work out in the end, however. In fact, everything we’re about to do doesn’t perfectly guarantee us a good spot to store our data, but it does give us near perfect assurance.
Once we find a suitable place, we want to double click on the values in the row and change it to a random value for each column (with each column representing a different creature), like 444 or something noticeable. We’ll first want to see if we can change the value of even one of the columns, as sometimes you’ll have code constantly running which will prevent you from modifying the data and will immediately set it back to 0.
If we’re able to change the value of one column, let’s then see if we can change the values for all the columns loaded. Sometimes you’ll be able to change the value for most of the structure instances, until you get to the last one, and it’ll stubbornly refuse your efforts. That is annoying, but we must dust our shoulders off and continue on to the next row in that case.
Once we find a row where we can store our random value of 444 on, we should then try to move around and interact with some of the creatures. Maybe get them to attack you, or whack them. Get them to move. We want to see then if all the values on our designated row remain at our magic value.
If so, a final test is needed! We want to see if our data storage causes any issues during an area reload. I’ve encountered crashing bugs many, many times that would be caused simply by leaving some bytes of data in a spot in memory that didn’t seem to be used at all during normal gameplay.
After following the described process above for our efforts in hacking the Abomnification system into Dark Souls III, we first settled on the offset of 0xDC (as shown in the pictures), but later drifted to the perfect place: 0x39C.
As a reminder, the offset being spoken of here exists inside the SprjChrPhysicsModule, which houses the sources of truth for creature coordinates. So, with everything said and done, we’ll be storing our morph scale ID at SprjChrPhysicsModule+0x39C.
Now that we have a place to store Abomnification-specific information in our creature data, we need to figure out one last thing before we can start writing code — something which sometimes is the most difficult of the reverse engineering problems we must solve when implementing the Abomnification system.
Figuring Out How To Get the Morph Scale ID From Drawing Code
One of the most difficult problems that arises from hacking the Abomnification system into any game, and not just Dark Souls III, is how we go about fetching creature data from code as abstract as where the character model drawing functionality typically resides. Typically, when we are as far down the stack as we are when calculating matrix values, we’re surrounded by structures populated with matrix data. Nothing that looks like the structures we see looking down from the top of a root data structure can be found nearby.
How difficult it is to find creature data depends on the game, but it usually is never pretty. Specifically, I remember trying to find creature data while in Dark Souls II’s drawing function being a huge chore. Well, let’s see how awful it is in the case of hacking the Abomnification system into Dark Souls III.
This is going to require a big ol’ bag of random reverse engineering tricks. So let me walk you through the process I’ll be using now. Pay attention!
I first place a breakpoint at the place the drawing code lives (“DarkSoulsIII.exe”+D279FC) and see what data structures are being pointed to by the CPU’s registers. If we’re lucky we’ll find something here that will work. If we’re unlucky we’ll have to go look at the stack. If we’re really unlucky we’ll have to deal with a polymorphic array of data, which is definitely not fun to deal with in assembly.
Speaking of handling polymorphic data in assembly, something we sometimes have to do when injecting into areas of code such as the the one we’re talking about now, I have a video that discusses this very topic! So if you have to do it for Dark Souls III, or any future game, you may want to refer to the following:
So going through all the registers, I noticed that the r12 register was consistently pointing to a data structure of unknown form (meaning there’s no formal definition of it in the RTTI) that itself consistently would point to the root structure of the enemy being rendered.
From here, we can easily find the morph scale ID, which will reside in the physics module that we know is pointed to by the creature’s root structure. So, at least from the outset here, we have figured out how we’ll be finding the pertinent creature information from the drawing code. Specifically, we’ll be going down this chain of data:
r12 -> 0x58 (EnemyIns) -> 0x2298 (SprjChrPhysicsModule) -> 0x39C (morph scale ID)
We now have everything we need in order to write our code. We have several hooks to write, but we’ll be starting with the Abomnification initiation point code first.
Implementing the Abomnification Initiation Point Code
As we discussed earlier, some games require only an Abomnification initiation point hook in order to achieve Abomnification. In the case of hacking the Abomnification system into Dark Souls III, we need to write two separate hooks: 1) the Abomnification initiation point hook, and 2) the Abomnification application hook.
The initiation point hook is responsible for calling game-neutral code, which in turn updates the scaling parameters for the particular creature we’re initiating the system on behalf of. Normally, we then take these updated scaling parameters and then update the creature’s live scaling values and effectuate a shape/size change.
Because Dark Souls III lacks easy scaling support, we cannot perform the latter step described above. This will be handled by the Abomnification application code. All we need to do for the initiation point in Dark Souls III is initiate a call to the Abomnification system which will update parameters we’ll be using once execution is inside our own scaling function.
Taking all of this into consideration, implementing the initiation point for the Abomnification system is a rather simple affair. We just need to figure out the perfect place to hook the initiation point into.
I prefer to hook the initiation point into code that is processing enemy coordinates. I do this because these enemy-wide polling functions execute at a frequency that results in typically smooth animations for the enemies. I usually implement the hook into code that is reading the enemy coordinates, but in the case of Dark Souls III, we’re actually going to implement the hook into the update location code (which writes to the coordinates), shown here:
The initiation point is typically not implemented here, as the update location code typically only is executed when the creature is moving. However, as we saw during the hacking of the Predator system into Dark Souls III, the update location code fires continuously.
So let’s get to code writing!
Abomnification Initiation Hook – Template
// Initiates the Abomnification system, generating new scaling parameters // for a creature. // rbx: Target physics module. define(omnifyAbomnificationHook,"DarkSoulsIII.exe"+9D2360) assert(omnifyAbomnificationHook,66 0F 7F B3 80 00 00 00) alloc(initiateAbomnification,$1000,omnifyAbomnificationHook) registersymbol(omnifyAbomnificationHook) initiateAbomnification: pushf initiateAbomnificationOriginalCode: popf movdqa [rbx+00000080],xmm6 jmp initiateAbomnificationReturn omnifyAbomnificationHook: jmp initiateAbomnification nop 3 initiateAbomnificationReturn:
All this code needs to do is execute the main Abomnification function named executeAbomnification, which is made publicly accessible in my Omnified framework found in the Omnified.lua file residing in the same directory. The parameters for this function are as follows:
- The address to the creature’s morph scale ID.
That’s it! As you can see, it’s incredibly easy to implement the Abomnification system in a game that has built-in easy scaling support. For our purposes, we can find the address to the creature’s morph scale ID at [rbx+0x39C] (since rbx will be pointing to the creature’s physics module).
The only thing we need to keep in mind is to preclude any chance that the player character will undergo Abomnification initiation, as this is only supposed to be affecting enemy character models.
That being said, this is so simple, let’s just take a look at the final code for the Abomnification initiation point right here and now!
Abomnification Initiation Hook – Complete
// Initiates the Abomnification system, generating new scaling parameters // for a creature. // rbx: Target physics module. define(omnifyAbomnificationHook,"DarkSoulsIII.exe"+9D2360) assert(omnifyAbomnificationHook,66 0F 7F B3 80 00 00 00) alloc(initiateAbomnification,$1000,omnifyAbomnificationHook) registersymbol(omnifyAbomnificationHook) initiateAbomnification: pushf // Ensure that the player coordinates pointer has been initialized. push rax mov rax,playerCoords cmp [rax],0 pop rax je initiateAbomnificationOriginalCode // Ensure that the we aren't about the Abomnify the player. push rax mov rax,playerCoords cmp rbx,[rax] pop rax je initiateAbomnificationOriginalCode // Load the address to the morph scale ID and then call the Abomnification system. push rax lea rax,[rbx+39C] push rax call executeAbomnification pop rax initiateAbomnificationOriginalCode: popf movdqa [rbx+00000080],xmm6 jmp initiateAbomnificationReturn omnifyAbomnificationHook: jmp initiateAbomnification nop 3 initiateAbomnificationReturn:
That’s it! Since there is no easy scaling support, we can’t update the creature’s scaling parameters here, we must do so in our own custom scaling function: our Abomnification application hook, which we’ll be writing right now.
Implementing the Abomnification Application Code
Because there is no easy scaling support in Dark Souls III, we must implement our own. The previous sections of this article stated, in quite some detail, where this scaling support needs to be injected into, as well as how it’ll be retrieving the data it needs in order to do its job.
The hook will be located inside the primary character model drawing code, which we identified earlier to be located at “DarkSoulsIII.exe”+D279FC. Let’s take a look at the initial template of the code we’ll be filling in:
Abomnification Application Hook – Template
// Applies the Abomnification system. // [rax]: Width matrix values // [rax+10]: Height matrix values // [rax+20]: Depth matrix values define(omnifyApplyAbomnificationHook, "DarkSoulsIII.exe" + D279FC) assert(omnifyApplyAbomnificationHook, 0F 28 00 4B 8D 14 76) alloc(applyAbomnification,$1000, omnifyApplyAbomnificationHook) registersymbol(omnifyApplyAbomnificationHook) applyAbomnification: applyAbomnificationOriginalCode: movaps xmm0,[rax] lea rdx,[r14+r14*2] jmp applyAbomnificationReturn omnifyApplyAbomnificationHook: jmp applyAbomnification nop 2 applyAbomnificationReturn:
The values pointed to by the rax register are what the drawing code sources. These values end up getting stored by the drawing code into the data structure pointed to by the rdx register. So in order to effectuate changes to the character’s model, all we need to do is change all the values pointed to by the rax register before normal execution resumes.
To do this, we’re going to find the creature data, and specifically its morph scale ID, by looking at the r12 register, as was previously discussed. Now, since this is a low level rendering function, we should expect (and indeed, it is the case) that the data we’ll be dealing with is slightly polymorphic. This means essentially that we cannot always assume that the r12 register will point to a data structure where we can find the EnemyIns root structure.
So we’ll need to employ the use of the Omnified framework’s checkBadPointer function, which will determine if a loaded address leads to a valid place in memory or not. For more information about how that function works, check out the video on polymorphic data I embedded above!
With all of that said, let’s write the first part of our hook, which is mainly responsible for stack preservation of values and performing a number of data integrity checks to ensure we can pull creature data from memory without crashing the game.
Abomnification Application Hook – First Steps
applyAbomnification: pushf // Ensure that the player root structure pointer has been initialized. push rax mov rax,player cmp [rax],0 pop rax je applyAbomnificationOriginalCode // Backing up a few registers we'll be using to perform calculations // on the original matrix values with updated scaling parameters. sub rsp,10 movdqu [rsp],xmm0 sub rsp,10 movdqu [rsp],xmm1 push rbx push rcx // We take a look at the address stored at the place we expect to // find the enemy root structure, and see if it is a valid pointer. mov rbx,[r12+58] lea rcx,[rbx] call checkBadPointer cmp ecx,0 jne applyAbomnificationExit // We then further check to see if the root structure address differs // from the player's own root structure. mov rcx,player cmp rbx,[rcx] je applyAbomnificationExit // We do one final data integrity check to ensure that the loaded // loaded structure is indeed a valid enemy root structure by checking // if there is a physics module at the expected place in memory. mov rcx,rbx mov rbx,[rcx+2298] lea rcx,[rbx+39C] call checkBadPointer cmp ecx,0 jne applyAbomnificationExit // Now that we know our data is good, we want to check if a morph scale // ID has actually been assigned yet by the Abomnification system. mov rcx,[rbx+39C] cmp rcx,0 je applyAbomnificationExit // This should never happen, but we want to ensure we aren't going to access // memory beyond the allocated bounds of our sandbox region. cmp ecx,#999 ja applyAbomnificationExit // Each creature has morph scale data that takes up a total of 48 bytes. // So we want to multiply the size of each section by our ID to get the // offset to apply to our base morphScaleData, which will give us the // location to our creature-specific morph scale data. push rax mov rax,rcx mov rcx,#48 mul rcx mov rcx,rax pop rax mov rbx,morphScaleData add rbx,rcx
At the conclusion of the above code, we’ve verified that it is an enemy being rendered, and more importantly, we have that enemy’s Abomnified scaling data pointed to by the rbx register. We then need to modify the creature’s live scaling data with the Abomnified scaling data found at the following locations:
- [rbx+4]: The Abomnified width scale.
- [rbx+8]: The Abomnified height scale.
- [rbx+C]: The Abomnified depth scale.
We do that by taking those Abomnified scales, multiplying the appropriate character model matrix values by them, and then committing those results to memory.
Let’s write up that portion of the code now.
Abomnification Application Hook – Scale Adjustment
applyAbomnificationScaleAdjustment: // We want to then do one final check to ensure that some sort of morph // scale data has actually been initialized for the creature. mov rcx,[rbx+4] cmp rcx,0 je applyAbomnificationExit // We load the scaling parameter for the creature's width, and then multiply // the live width matrix values by it. movss xmm0,[rbx+4] shufps xmm0,xmm0,0 movdqu xmm1,[rax] mulps xmm1,xmm0 movdqu [rax],xmm1 // We load the scaling parameter for the creature's height, and then multiply // the live height matrix values by it. movss xmm0,[rbx+8] shufps xmm0,xmm0,0 movdqu xmm1,[rax+10] mulps xmm1,xmm0 movdqu [rax+10],xmm1 // We load the scaling parameter for the creature's depth, and then multiply // the live depth matrix values by it. movss xmm0,[rbx+C] shufps xmm0,xmm0,0 movdqu xmm1,[rax+20] mulps xmm1,xmm0 movdqu [rax+20],xmm1
It is my hope that the code is documented well enough that it causes you no confusion! With the matrix values now updated, we just need to do a little cleanup, and then resume normal execution of the rendering function.
Abomnification Application Hook – Cleanup
applyAbomnificationExit: // Restore backed up values. pop rcx pop rbx movdqu xmm1,[rsp] add rsp,10 movdqu xmm0,[rsp] add rsp,10 applyAbomnificationOriginalCode: popf movaps xmm0,[rax] lea rdx,[r14+r14*2] jmp applyAbomnificationReturn
And that’s it, we’re done hacking both the Abomnification initiation and application hooks into Dark Souls III!
But…Does It Work!?
You’re god damned right it works. After saving the above code and injecting it into my Dark Souls III game, I was greeted by this horrific and terrible sight when running into the first enemy in the game:
We can see above here a creature that has had the non-uniform dynamic morphing mode applied to it. Do be aware that the animation is actually smoother in the game — working 60fps into an animated gif would probably explode the file size, sorry!
Abomnification system in in. Here is the complete code to the Abomnification application hook:
Abomnification Application Hook – Complete
// Applies the Abomnification system. // [rax]: Width matrix values // [rax+10]: Height matrix values // [rax+20]: Depth matrix values define(omnifyApplyAbomnificationHook, "DarkSoulsIII.exe" + D279FC) assert(omnifyApplyAbomnificationHook, 0F 28 00 4B 8D 14 76) alloc(applyAbomnification,$1000, omnifyApplyAbomnificationHook) registersymbol(omnifyApplyAbomnificationHook) applyAbomnification: pushf // Ensure that the player root structure pointer has been initialized. push rax mov rax,player cmp [rax],0 pop rax je applyAbomnificationOriginalCode // Backing up a few registers we'll be using to perform calculations // on the original matrix values with updated scaling parameters. sub rsp,10 movdqu [rsp],xmm0 sub rsp,10 movdqu [rsp],xmm1 push rbx push rcx // We take a look at the address stored at the place we expect to // find the enemy root structure, and see if it is a valid pointer. mov rbx,[r12+58] lea rcx,[rbx] call checkBadPointer cmp ecx,0 jne applyAbomnificationExit // We then further check to see if the root structure address differs // from the player's own root structure. mov rcx,player cmp rbx,[rcx] je applyAbomnificationExit // We do one final data integrity check to ensure that the loaded // loaded structure is indeed a valid enemy root structure by checking // if there is a physics module at the expected place in memory. mov rcx,rbx mov rbx,[rcx+2298] lea rcx,[rbx+39C] call checkBadPointer cmp ecx,0 jne applyAbomnificationExit // Now that we know our data is good, we want to check if a morph scale // ID has actually been assigned yet by the Abomnification system. mov rcx,[rbx+39C] cmp rcx,0 je applyAbomnificationExit // This should never happen, but we want to ensure we aren't going to access // memory beyond the allocated bounds of our sandbox region. cmp ecx,#999 ja applyAbomnificationExit // Each creature has morph scale data that takes up a total of 48 bytes. // So we want to multiply the size of each section by our ID to get the // offset to apply to our base morphScaleData, which will give us the // location to our creature-specific morph scale data. push rax mov rax,rcx mov rcx,#48 mul rcx mov rcx,rax pop rax mov rbx,morphScaleData add rbx,rcx applyAbomnificationScaleAdjustment: // We want to then do one final check to ensure that some sort of morph // scale data has actually been initialized for the creature. mov rcx,[rbx+4] cmp rcx,0 je applyAbomnificationExit // We load the scaling parameter for the creature's width, and then multiply // the live width matrix values by it. movss xmm0,[rbx+4] shufps xmm0,xmm0,0 movdqu xmm1,[rax] mulps xmm1,xmm0 movdqu [rax],xmm1 // We load the scaling parameter for the creature's height, and then multiply // the live height matrix values by it. movss xmm0,[rbx+8] shufps xmm0,xmm0,0 movdqu xmm1,[rax+10] mulps xmm1,xmm0 movdqu [rax+10],xmm1 // We load the scaling parameter for the creature's depth, and then multiply // the live depth matrix values by it. movss xmm0,[rbx+C] shufps xmm0,xmm0,0 movdqu xmm1,[rax+20] mulps xmm1,xmm0 movdqu [rax+20],xmm1 applyAbomnificationExit: // Restore backed up values. pop rcx pop rbx movdqu xmm1,[rsp] add rsp,10 movdqu xmm0,[rsp] add rsp,10 applyAbomnificationOriginalCode: popf movaps xmm0,[rax] lea rdx,[r14+r14*2] jmp applyAbomnificationReturn omnifyApplyAbomnificationHook: jmp applyAbomnification nop 2 applyAbomnificationReturn:
Dark Souls III Is Now Omnified!
That’s right folks, with our hacking of the third game-neutral Omnified system into the game complete, Dark Souls III is now officially Omnified.
Now, this doesn’t mean we won’t be writing some additional challenging hacks for the game, but it does mean that the game has been hacked enough that it is now eligible for streaming live on my official Twitch stream. If I do any additional hacking for this game, expect to see some additional articles on the topic here on my hackpad!
Please catch the insane Omnified gameplay live on there, and if you don’t make it, I’ll be sure to upload the streams to my YouTube for your future perusal!
Thanks for your interest! Implementing the Abomnification system delves into some rather difficult reverse engineering topics, and it is my hope that this article has been both informative and useful for you.
Until next time, keep it real everybody. 🙂