Background
In the previous two lessons, we made changes to the game’s code to alter its functionality. For both of these changes, we replaced the original instruction with a new instruction. But what if we want to keep the original instruction or replace it with multiple instructions? In these cases, we will need to use a code cave.
A code cave is a section of the game’s memory that we fill with instructions. We then change the game’s original code to call these instructions. The name comes from the fact that we are creating a hidden “cave” of instructions. Most games will have large sections of unused memory between functions or at the end of the executable. These locations are perfect for creating a code cave in.
Redirection
In our last lesson, we changed the function for displaying a terrain description to instead call a debug menu. By using a code cave, we can still invoke the debug menu, but also call the terrain description function after. By doing this, we won’t lose any functionality in the game.
The original instruction for the terrain description call looked like:
0x00CCAF90 call dword ptr ds:[eax+28]
For this example, assume that there is an empty section of memory at 0x00D00000
. Our first goal is to recreate the original call at 0x00D00000
and then redirect the original code to this new code. First, we will copy the original instruction to 0x00D00000
:
0x00D00000 call dword ptr ds:[eax+28]
Next, we will redirect the original code to this call:
0x00CCAF90 jmp 0x00D00000
Finally, in our code cave, we need to go back to the original code. This can be accomplished by jumping to the instruction that comes after the one we replaced. In this case, the next instruction in the game is at 0x00CCAF93
. Our completed code cave would then look like:
0x00D00000 call dword ptr ds:[eax+28]
jmp 0x00CCAF93
Restoring Instructions
As of right now, this code cave only recreates the original instruction. This is an important first step to ensure that our redirection isn’t breaking anything. This is not always the case, especially when dealing with game functions that modify the stack. We will discuss how to deal with these in future lessons. For now, just be aware that redirecting the game’s code will not always be a smooth process.
When writing a code cave, it’s critical to only modify what you require and nothing else. Accidentally changing other registers, sections of memory, or the stack can cause the game to crash. To illustrate this principle, imagine we had the following code that we intended to redirect:
mov eax, 999
call 0xDEADBEEF
Let’s say we redirected the call to a code cave that looked like:
mov eax, 123
call some_other_function
call 0xDEADBEEF
jmp back
If the function at 0xDEADBEEF
required eax to be 999, the game would throw an exception and crash. While this is a trivial example, calling game functions will often have many side-effects that you won’t be aware of.
To save and restore the game’s register values (eax, ebx, ecx, and so forth), we will use two instructions: pushad and popad. pushad pushes (or saves) all register values on the stack. popad pops (or restores) all register values from the stack. In future lessons, we will cover the stack itself and how to restore the game’s stack. However, restoring the register values will prevent most crashes.
Cave Skeleton
With these instructions, we now have a basic skeleton for a code cave. It looks like:
pushad
execute new functionality
popad
invoke original instruction
jmp back to game's code
Let’s return to our Wesnoth example. In our Redirection example above, we had the following code cave:
0x00D00000 call dword ptr ds:[eax+28]
jmp 0x00CCAF93
With pushad/popad, we can now safely introduce new instructions. The code to invoke the debug menu looked like:
call dword ptr ds:[eax+68]
Let’s assume that this call doesn’t modify the stack in any way. We can safely call this function in our code cave by saving the registers, calling the function, and then restoring the registers. We will then execute the original instruction and jump back to the game’s code after the original instruction. Our final code cave would look like:
0x00D00000 pushad
call dword ptr ds:[eax+68]
popad
call dword ptr ds:[eax+28]
jmp 0x00CCAF93
By doing this, we have created a cave in the game’s code that replaced one instruction with multiple instructions. As long as everything is restored, there is no limit to the amount of new code that can be called.