Hacking Crazy Damage Into Dragon’s Dogma With Apocalypse
Time to get real in our efforts to make Dragon’s Dogma Omnified. If you’ve been on top of things and happened to read the first hacking article on Dragon’s Dogma, you’ll be well aware that we did a cursory data structure analysis and created some needed pointers to the player’s health, coordinates, and other things. You might also be aware that we determined that Dragon’s Dogma is a rather strangely assembled program, with odd data organization. Here is to hoping that this doesn’t bite us on the butt today.
With the requisite data disassembled during our data structure analysis, we’re going to implement the first of our Omnified systems into Dragon’s Dogma: the Apocalypse system. If you’re unfamiliar with what that is, click here for a detailed article on the topic, or check out the video below for a nice visual summary:
As soon as the Apocalypse system has been implemented into Dragon’s Dogma, we’ll have RNG-fueled insane damage. It will become very easy to get one shot — so easy that we need to ensure that there are sufficient measures in place that will allow us to avoid the damage in this game; a game in which the ability to dodge away from an attack is not always available to the player.
We’ll worry about that later. For now, let’s get to hacking the Apocalypse system into Dragon’s Dogma so we can start enjoying those sweet, brutal results as soon as possible.
Finding Dragon’s Dogma’s Damage Application Code
In order to implement the Apocalypse system into Dragon’s Dogma, we’ll need to construct an initiation point for it in the proper location; namely, the code where damage application occurs in response to the player or an enemy taking damage. For more information on what the “damage application” code exactly is, as well as all the other related requirements, check out the relevant section in my article on the Apocalypse system.
To find the damage application code, we’ll first start by finding the health update code, which is responsible for updating our player’s health to its new value after we get smacked by a baddie. We’ll do this by figuring out what code is changing our health pointer’s value when we’re getting smacked.
Thankfully, when the opcode monitoring window appears after clicking the menu item in the above image there are no entries yet (it’s a bit annoying to deal with health update code that is constantly executing, even in response to nothing basically). So let’s go get smacked by a goblin, or whatever these enemies are, and see what we get.
OK! So a single instruction popped up here:
movss [edi+08],xmm0. Our health structure address is in the edi register, and the updated health value is being sourced from the xmm0 register. So our immediate goal will be to locate the code responsible for calculating the value that ultimately ends up in the xmm0 register. Sometimes this code is nearby the health update code, and sometimes it is incredibly far away. Let’s see if it is the former case by clicking that Show disassembler button and browsing around the nearby code.
Well today is our lucky day. Just a few lines above our health update code, we’re greeted with what can only be the damage application code. A working value of the current health is loaded into xmm0 with the code
movss xmm0,[edi+08] and then the damage is applied to the health with
subss xmm0,[esp+0C]. This instruction is indeed the damage application code.
So it looks like we’ll be implementing our initiation point on top of that subtraction instruction, but we’ll want to just make sure this is also the damage application code for when enemies are receiving damage as well. So we throw a breakpoint on one of the instructions, walk up to an enemy, smack it, and…
Very nice! We will only have to implement a single initiation point for the Apocalypse system then. The health structure of the target for the damage is found in the edi register, and the damage amount itself can be found at [esp+C]. There’s only one other bit of information we’re missing, and that’s some sort of identifier telling us the source of the damage. I am fairly confident in saying that monsters can harm each other, and we only want the Enemy Apocalypse to be executed when the player is doing the damage.
Many times finding the source of the damage while at the damage application code is very difficult — it’s going to be even more difficult here because we lack RTTI and we haven’t identified any sort of root structure type for the player. Let’s try anyway and poke around a bit.
Time Passes as Omni Digs Deep…
Well, knowing the problem was going to be rather difficult to solve, I set out to figure out how we might be able to tell when it’s the player doing the damage vs another creature. Since this is a 32-bit application, there are a limited number of registers on the CPU available to store extraneous information. So, I knew right away that if there was anything to be found, it would be somewhere on the stack.
After performing a number of tests where I would grab the stack data from me striking the enemy, and then doing the same when my companion struck an enemy, I found a place deep in the stack that tells us the information we need.
Indeed, at [esp+158], the address to the location structure for the entity doing the damage can be found. We have a pointer created that holds just that, so it will be very easy to check if it is the player doing the damage.
Hooray! That’s a big, big win for us, since being unable to isolate damage just coming from the player really degrades the experience. Now that we have everything that we need, let’s write that initiation point code.
Writing the Apocalypse Initiation Hook
Armed with everything we’ll ever need for a perfectly working Apocalypse implementation, let’s get started on writing the initiation point for it. Here’s our starting template for the initiation point hook:
Apocalypse Initiation Hook – Template
// Initiates the Apocalypse system. // edi: Health structure of damage target. // esp+C: Damage amount. // xmm0: Working health value. // esp+158: Location structure of damage source. define(omnifyApocalypseHook, "DDDA.exe" + 376F82) assert(omnifyApocalypseHook, F3 0F 5C 44 24 0C) alloc(initiateApocalypse,$1000, omnifyApocalypseHook) registersymbol(omnifyApocalypseHook) initiateApocalypse: initiateApocalypseOriginalCode: subss xmm0,[esp+0C] jmp initiateApocalypseReturn omnifyApocalypseHook: jmp initiateApocalypse nop initiateApocalypseReturn:
The original damage application code can be found under the initiateApocalypseOriginalCode label. We must do everything we need to before that code is reached to ensure that the working health value found in the xmm0 register and the damage amount found at [esp+C] are based on Apocalypse-determined values.
To understand more about what the initiation point must do, please consult the relevant section in the Apocalypse overview article on the Player Apocalypse API as well as the section on the Enemy Apocalypse API. These cover, in depth, the functions that must be called from our code, as well as the parameters we must provide to them.
We’re going to call both the Player and Enemy Apocalypse functions from this same hook; the exact module being called will be dictated by whether or not the player is the target of the damage. So at the beginning of the code, we’ll prepare the parameters that are used by both of these functions, as well as determine our path of execution.
Something to Keep in Mind…
A lot of the data we’ll be using here is found on the stack. We’ve gone over and have written down where exactly on the stack this required data can be found. However, this data will be shifting to a different place on the stack once we’re ready to load them, since the stack will be padded from necessary stack preservation efforts found in all the hacking code that I write.
So this will require a little of what I call “stack math”. Let’s do some stack math right now:
- At the start of our code, we’ll want to back up all the conditional flags before they get overwritten with the pushf instruction. This will shift all our stack data down the stack by 2 bytes.
- We’ll then want to ensure that we back up the eax and ebx registers, since those are used to hold the return values of our Apocalypse functions. This will require the push instruction. Given that we are operating within a 32-bit program here, each push will shift stack data down by 4 bytes, for a total of 8 bytes for two pushes.
- We’ll probably also want to back up another register, lets say the ecx register, to use to hold temporary values. This will be another 4 bytes the stack gets pushed down.
Adding all that up, we’ll be shifting our stack down by a total of 14 bytes in order to preserve data. That means the damage amount (found at [esp+C] normally) will now be found at [esp+1A], and our damage source identity (found at [esp+158] normally) will now be found at [esp+166].
Armed with this new knowledge, let’s write the start of our initiation point hook.
Apocalypse Initiation Hook – First Steps
initiateApocalypse: pushf // Backing up the eax and ebx registers are they will be used // to hold return values from the Apocalypse functions. push eax push ebx // Backing up a working register that will be used to hold // temporary values. push ecx // The damage amount is found at [esp+1A], its address having // shfited due to stack preservation efforts. mov eax,[esp+1A] // If the player is the target of the damage, we'll want to execute // the Player Apocalypse. Otherwise, if an NPC is receiving damage, // we'll want to ensure the player is the source of that damage. cmp [playerHealth],edi je initiatePlayerApocalypse // The location structure for the entity dealing the damage // can be found at [esp+166], its address having shifted due to // stack preservation efforts. mov ecx,[esp+166] cmp [playerLocation],ecx je initiateEnemyApocalypse jmp initiateApocalypseExit
The code above takes a few preparatory actions such as loading the damage amount into the eax register and determining whether to call the Player Apocalypse, the Enemy Apocalypse, or do nothing at all.
The next bit of code we’ll be writing is the execution of the Player Apocalypse. We have the damage amount, current working value of the player’s health, and maximum health for the player already available to us. We just need to prepare a location structure for the player aligned at the X coordinate.
Apocalypse Initiation Hook – Player Apocalypse
initiatePlayerApocalypse: // We realign the player's location structure so it begins at the // X coordinate. mov ebx,[playerLocation] lea ecx,[ebx+40] // Push the damage amount parameter. push eax // Push the working health value parameter. sub esp,4 movd [esp],xmm0 // Push the maximum health value parameter. push [edi+C] // Push the aligned coordinates struct parameter. push ecx call executePlayerApocalypse jmp initiateApocalypseUpdateDamage
Upon the completion of the code above, we will be left with an updated damage amount in the eax register and an updated working health value in the ebx register. Following this, we’ll shift execution to an area of code underneath initiateApocalypseUpdateDamage that will process the return values and put them in their proper places.
This handles damage being done to the player. For damage being done to enemies by the player, we’ll need to make use of the Enemy Apocalypse. Let’s write the code for this now.
Apocalypse Initiation Hook – Enemy Apocalypse
initiateEnemyApocalypse: // Push the damage amount parameter. push eax // push the working health value parameter. sub rsp,4 movd [esp],xmm0 call executeEnemyApocalypse
A very simple bit of code that just calls executeEnemyApocalypse and then continues on its merry little way. Much like what happens with the Player Apocalypse, the eax and ebx registers will contain the updated damage amount and working health value respectively.
All that remains now is the code that will put the return values into their proper places so that it affects the original game code, and then restore preserved data backed up on the stack.
Apocalypse Initiation Hook – Return Value Processing and Cleanup
initiateApocalypseUpdateDamage: // Take the updated damage amount and store it in its proper place. mov [esp+1A],eax // Take the updated working health value and store it in its proper // place. movd xmm0,ebx initiateApocalypseExit: pop ecx pop ebx pop eax
Again, a very simple return value processing and cleanup routine. With this bit of code done, we now have the Apocalypse system implemented into Dragon’s Dogma! The game is now officially 1/3rd Omnified. Woot de woot.
But…Does It Work?
It does work. Fabulously even! The player can now be one shot by light goblin blows, or heavily maimed, or even teleported across the map or under the ground! Conversely, the player can critical hit those bad guys for that extra Chocobo-fueled damage every now and then as well.
Only one of the Apocalypse system’s external parameters had to be tweaked in order for proper operation of Apocalypse within Dragon’s Dogma, and that’s the external parameter teleportitisDisplacementX. This parameter is a multiplier that is applied to all displacement amounts randomly generated during a teleportitis effect. The coordinate system for this game uses very large values for its coordinates, so a value of 100.0 was assigned to the multiplier in order to effectuate displacements to the player’s location that would be noticeable visually.
It may have appeared that not much work was required in order to get everything to work. And that is, for the most part, true, as that is truly the beauty of the game-neutral Omnified system approach. There was a little bit of work behind the scenes however, and that mainly has to do with the fact that Dragon’s Dogma is a 32-bit application.
Since the formalization of the Omnified process and the creation of the game-neutral Omnified framework, there has not, until now, been a 32-bit game that has been Omnified. Unfortunately, the game-neutral code found in the Omnified framework common library is purposed towards 64-bit applications. Changes had to be made to it in order for it to be able to be used in a 32-bit environment.
It is a bit annoying, but it appears that it will require the maintaining of two separate Omnified framework versions: one for 64-bit, and one for 32-bit. Further research will be done in this area in the future to see how we can marry the two of these version as tightly together as possible.
Other than that though, we’re done for now! Enemies in Dragon’s Dogma now do (potentially) insane amounts of damage, among other things! Here is the complete code required for the implementation of the Apocalypse system into Dragon’s Dogma:
Apocalypse Initiation Hook – Complete
// Initiates the Apocalypse system. // edi: Health structure of damage target. // esp+1A: Damage amount. // xmm0: Working health value. // esp+166: Location structure of damage source. define(omnifyApocalypseHook, "DDDA.exe" + 376F82) assert(omnifyApocalypseHook, F3 0F 5C 44 24 0C) alloc(initiateApocalypse,$1000, omnifyApocalypseHook) registersymbol(omnifyApocalypseHook) initiateApocalypse: pushf // Backing up the eax and ebx registers are they will be used // to hold return values from the Apocalypse functions. push eax push ebx // Backing up a working register that will be used to hold // temporary values. push ecx // The damage amount is found at [esp+1A], its address having // shfited due to stack preservation efforts. mov eax,[esp+1A] // If the player is the target of the damage, we'll want to execute // the Player Apocalypse. Otherwise, if an NPC is receiving damage, // we'll want to ensure the player is the source of that damage. cmp [playerHealth],edi je initiatePlayerApocalypse // The location structure for the entity dealing the damage // can be found at [esp+166], its address having shifted due to // stack preservation efforts. mov ecx,[esp+166] cmp [playerLocation],ecx je initiateEnemyApocalypse jmp initiateApocalypseExit initiatePlayerApocalypse: // We realign the player's location structure so it begins at the // X coordinate. mov ebx,[playerLocation] lea ecx,[ebx+40] // Push the damage amount parameter. push eax // Push the working health value parameter. sub esp,4 movd [esp],xmm0 // Push the maximum health value parameter. push [edi+C] // Push the aligned coordinates struct parameter. push ecx call executePlayerApocalypse jmp initiateApocalypseUpdateDamage initiateEnemyApocalypse: // Push the damage amount parameter. push eax // push the working health value parameter. sub rsp,4 movd [esp],xmm0 call executeEnemyApocalypse initiateApocalypseUpdateDamage: // Take the updated damage amount and store it in its proper place. mov [esp+1A],eax // Take the updated working health value and store it in its proper // place. movd xmm0,ebx initiateApocalypseExit: pop ecx pop ebx pop eax initiateApocalypseOriginalCode: popf subss xmm0,[esp+0C] jmp initiateApocalypseReturn omnifyApocalypseHook: jmp initiateApocalypse nop initiateApocalypseReturn: teleportitisDisplacementX: dd (float)100.0
And that’s a wrap for the Apocalypse system. The remaining Omnified systems to be implemented are the Predator and Abomnification systems. These will be covered in subsequent articles, so stay tuned.
Thanks once again for reading. And don’t forget! If you need to catch this Omnified action live, there’s only one place for that: on my official Twitch stream located at https://twitch.tv/omni! Once we have this game Omnified, you’ll eventually be seeing live gameplay of it on my stream.
I hope to see you there. Until then, take care.