Prep: Speed Hack
Last updated
Last updated
We really need speed hack to save time, this should the very first step in our journey. If there is no anti-cheat system on the server-side, we should be able to change character's speed by patching game client binary. For our case the game logic is implemented in GameLogic.dll
.
In game client's folder, we can find two interesting files GameLogic.dll
and GameLogic.pdb
at C:\PwnAdventure3\PwnAdventure3\Binaries\Win32
.
Dropping GameLogic.dll
into IDA, we figure out that this file is indeed what it is called: all the C++ game logic implementations are stored here. What is GameLogic.pdb
?
Program database (PDB) is a file format (developed by Microsoft) for storing debugging information about a program (or, commonly, program modules such as a DLL or EXE). PDB files commonly have a .pdb extension. A PDB file is typically created from source files during compilation. It stores a list of all symbols in a module with their addresses and possibly the name of the file and the line on which the symbol was declared. This symbol information is not stored in the module itself, because it takes up a lot of space.
Loading GameLogic.pdb
in IDA could give us the actual function names, which helps a lot in reverse engineering. If DLL file and PDB file are in the same folder, IDA is going to detect PDB file automatically:
"Speed" should be a property of the main character object. Usually the main character is called something like "pawn", "player" or "actor". Here I used a trick: searching for Player::
in the symbols. It returns some interesting properties. Among them we find a Player::GetSprintMultiplier
function:
Double click on it to view its content:
Press F5 to decompile it into pseudocode:
Currently the multiplier is 3.0. If we modify it to 30.0, our speed should be 10 times faster. Back to the flow chart and double click on __real@40400000
:
Why 0x40400000
stands for float 3.0 in C++? Here is a nice video explaining how floating point numbers work:
Here is an online converter:
I am tempted to change this multiplier to max float. Write a simple C++ program to print out max float:
Use an online compiler such as https://www.onlinegdb.com/online_c++_compiler to run it. The output is:
3.402823e+38 converts to 0x7f7ffffd
in hex, so we want to patch the binary and change the following bytes:
To change bytes in IDA, select .rdata:10078B34
with mouse and go to "Edit -> Patch code -> Change Byte":
After patching it, we have to "apply" the patch so that it overwrites the old binary. Go to "Edit -> Patch code -> Apply patches to input file":
In 2025 I revisited this hack and chose to use Ghidra this time. Go to Symbol Tree
-> Player
:
Scroll down and you will see GetSprintMultiplier
:
A few things to discuss here.
Ghidra lists several cross references to this function:
XREF[3]:
?GetSprintMultiplier@Player@@UAEMXZ
?GetSpreadAngle@Pistol@@UAEMXZ
?GetCooldownTime@RubicksCube@@UAEMXZ
These references indicate that other functions (or methods) in the project call this function, providing insight into how it is used in the larger codebase.
The functions Player::GetSprintMultiplier
, Pistol::GetSpreadAngle
, and RubicksCube::GetCooldownTime
all return the same constant value (3.0 in this case). Because they are defined (probably inline) to do nothing more than return a constant, their compiled machine code is identical.
FLD instruction stands for "floating-point load". The __real
stands for real number (I think).
IEEE-754 Representation:
IEEE-754 is the standard for representing floating-point numbers. In single precision (32 bits), a floating-point number is divided into:
1 bit for the sign
8 bits for the exponent (with a bias of 127)
23 bits for the fraction (mantissa)
The hexadecimal number 40400000h
(01000000010000000000000000000000
in binary format) represents the value 3.0
in this format. Here's how:
Sign Bit (1 bit): 0
(indicating a positive number)
Exponent (8 bits): 10000000
(or 128
in decimal). After subtracting the bias (127), the actual exponent is 1
.
Fraction (23 bits): The bits represent 0.5
(because the fraction starts with 1
in the binary significand for normalized numbers, making it 1 + 0.5 = 1.5
).
The formula can be found here: https://en.wikipedia.org/wiki/Single-precision_floating-point_format. An example:
Double click on __real@40400000:
Location 10078b34 is what we are looking for. Here you just need to find a suitable multiplier, turn it into floating number in hex and patch the binary. Go to Window -> Bytes: GameLogic.dll and look for location 10078b34:
Click the pencil icon on the top right to enter edit mode. Here I change the multiplier to 100 (0x42c80000):
Press ctrl+s to save. Finally, export the patched binary in File -> Export Program. The "format" should be "Original File":
Before overwriting GameLogic.dll, don't forget to save a copy of the original file as backup. Speed hack done.
Don't forget to select the "Create backup" option. Click "OK" and log into the game. Enjoy the speed of light
Thus, the number is calculated as: