Turning a Temporary DLC Check Fix into a Runtime Patch(reverse-engineering)
I wanted to understand how a game was checking for DLC availability and what would happen if the result of that check was changed at runtime.
This was done as a reverse-engineering experiment on a copy of the game I was analyzing. The goal was mainly to understand how managed code, native functions, and return values interact.
## 0x01 - Finding the DLC check
While inspecting the game’s code, I found a call to:
Steamworks.SteamApps.BIsDlcInstalled
The function name was a pretty good indication of what it was responsible for: asking Steam whether a DLC was installed.
The interesting part was that the game appeared to use the returned boolean as part of its local DLC logic.
Conceptually, the flow looked like:
Game
↓
BIsDlcInstalled()
↓
true / false
↓
DLC state
That gave me a useful function to investigate.
## 0x02 - The temporary modification
I first tested the idea by modifying the function at runtime.
The important part was:
mov rax, 1
ret
On x86-64, RAX is used for the return value of a function.
Setting it to 1 and immediately returning therefore makes the function return true.
Conceptually, I changed:
BIsDlcInstalled() → Steam's result
into:
BIsDlcInstalled() → true
This was enough to confirm that the function’s return value was being trusted by the game’s local logic.
## 0x03 - From a temporary fix to a patch
Once I confirmed the behavior, I turned the runtime modification into an actual patch.
The script first locates the function:
Steamworks.SteamApps.BIsDlcInstalled
and compiles it so that it can be modified.
I also kept the original bytes:
40 53 48 83 EC 20 8B D9 33 C9 E8
This allowed the patch to restore the original function when disabled.
The relevant patch was:
DLC_CHECK:
mov rax, 1
ret
So the resulting flow became:
BIsDlcInstalled()
↓
mov rax, 1
↓
ret
↓
true
## 0x04 - Why the byte check matters
The patch uses:
assert(DLC_CHECK, bytes)
before modifying the function.
This checks that the bytes currently present at the target address match the expected original bytes.
That’s useful because blindly overwriting an address can cause problems if the game has changed between versions.
Instead of assuming:
"This address is still the function I expect."
the patch verifies:
"The bytes here still match what I analyzed."
If they don’t match, the patch should fail rather than blindly writing over potentially different code.
## 0x05 - Restoring the original function
The patch also has a disable section:
[DISABLE]
DLC_CHECK:
db bytes
This writes the original bytes back.
The overall process therefore becomes:
Enable
↓
Verify original bytes
↓
Replace function
↓
Function returns true
Disable
↓
Restore original bytes
↓
Original function behavior
This was the part that turned the experiment from a temporary runtime modification into a reusable patch.
## 0x06 - What I learned
The interesting part of this experiment wasn’t the DLC itself. It was seeing how a relatively small native function could influence higher-level game logic.
The game can have complicated systems surrounding DLC, but if a particular decision ultimately depends on a boolean returned from a function, changing that return value can change everything downstream that relies on it.
It also demonstrated the difference between modifying managed code and modifying the native code underneath it.
A managed patch can change the game’s logic directly, while this experiment modified the native function responsible for returning the value.
## 0x07 - Result
The final patch changed the behavior of the targeted function so that calls to it returned true immediately, while retaining the ability to restore the original function.
The experiment gave me a practical example of:
Finding a function
↓
Understanding its return value
↓
Testing a runtime modification
↓
Verifying the result
↓
Turning the modification into a reversible patch
It was a small experiment, but it helped me understand how reverse engineering can go from simply finding something interesting in a decompiler to actually tracing and modifying the behavior of the compiled program.
## 0x08 - Download
I also packaged the patch used for the experiment so I could keep the tested version separate from the rest of my notes.
Patch:
BIsDlcInstalledruntime patch Purpose: Reverse-engineering experiment Type: Cheat Engine table/script