What's new
RevTeam.Re - Reverse Engineering Team

Welcome Guest! Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox! Register and wait for our approve!

How Do I Mod This?” to “How Does This Program Think?

h1r0t0

New member
Joined
Jul 18, 2026
Messages
3
Reaction score
6
I started modding games when I was around 14 years old. At that time, I mostly asked “How do I mod this?” Over time, that question slowly changed into “How does this program think?” I cannot say this method will work for everyone, but it changed the way I learn reverse engineering. I want to share the approach that worked for me.

When you begin asking that, the target changes. You stop seeing only:
  • a license check
  • an integrity check
  • an anti-debug function
  • a branch that needs patching
And you begin seeing:
  • trust boundaries
  • state transitions
  • design decisions
  • failure conditions
  • the relationship between input, logic, and result
Then reverse engineering stops being only about defeating software. It becomes a way to understand how software thinks. You may still begin with a crack request. That is how many of us started. But do not stop at “Which bytes should I change?” Ask “Why does changing these bytes alter the result?” That one question can take you from copying someone else’s solution to building your own. Im not writing this to teach anyone or to judge people who are just starting. Im sharing it because I also started by searching for working scripts and patches.

The moment my progress changed was when I stopped collecting answers and started collecting questions. So, when you face something difficult, try asking “If an expert encountered this target, what would they examine first?” Then inspect the evidence:
  • inputs
  • comparisons
  • branches
  • return values
  • callers and callees
  • what happens before and after the check
You do not need to understand everything immediately. You only need to understand one decision better than you did yesterday. Do not reverse engineer only to make the program obey you. Reverse engineer until you understand why it behaved that way in the first place.

It’s okay if your question is not clear today. Sometimes, looking at the problem from another perspective is enough to reveal the next step. I would like to share one of my classic approaches for dealing with problems in Android reverse engineering. Instead of asking only “How can I hook or patch this function?” Try asking “Why does this function exist, how is it used, and what assumption does the program make about its result?” Let’s begin with a simple example function hooking.

How Hooking Works​

Hooking is a method where you intercept a function call and change what happens. Your code gets control, so you can modify the arguments, replace the result, call the original function, or block it entirely. And Hooking intercepts a function call and gives our code control before, after, or instead of the original function.

Frida Custom Script Example:
JavaScript:
Please, Log in to view codes content!

Hooking does not always mean running code before the original function. Depending on the technique, a hook can:
  • inspect or modify arguments before the function runs,
  • inspect or modify the return value after it finishes,
  • call the original function with additional logic,
  • or replace the original function entirely.

In simple terms, hooking gives our code control before, after, or instead of the original function.

Game Class Structure​

Suppose there is an open-world game with players, enemies, vehicles, guns, and bullets. The developer starts by creating a PlayerController class with variables and functions.

Example variables inside PlayerController:
C++:
Please, Log in to view codes content!

Memory Layout & Padding​

In memory, class fields are usually stored in order, with each field located at an offset from the beginning of the object.
C++:
Please, Log in to view codes content!

Why Is Padding Added?
The compiler may insert unused bytes between fields so that each value begins at an address suitable for its alignment requirements.
For example, a 4-byte float is commonly aligned to a 4-byte boundary:
C++:
Please, Log in to view codes content!
A pointer in a 64-bit process is commonly 8 bytes and is usually aligned to an 8-byte boundary:
C++:
Please, Log in to view codes content!
In this example, isAlive and id use offsets 0x4 and 0x5. The compiler adds two padding bytes at 0x6–0x7, allowing health to begin at the next 4-byte boundary, 0x8.

However, the exact layout can depend on the compiler, platform, ABI, packing settings, inheritance, and runtime. In reverse engineering, we should confirm the real offsets from the binary or runtime instead of assuming them.

Do not assume the layout. Verify how the program actually stores it. These offsets are a simplified example. The real layout may differ depending on the compiler, architecture, ABI, packing, inheritance, and runtime. In reverse engineering, always verify the actual offsets.
They can change from:
  • compiler and ABI
  • 32-bit vs 64-bit
  • #pragma pack or packed struct
  • inheritance / virtual table
  • field reordering on some runtime
  • IL2CPP, Unity, Java/Kotlin object layout
  • optimization and metadata by engine
For Example:
C++:
Please, Log in to view codes content!

Normalize:
C++:
Please, Log in to view codes content!

If packed:
C++:
Please, Log in to view codes content!

Maybe they gonna change to:
C++:
Please, Log in to view codes content!

Original Game Functions​

The game uses functions to control logic. For example:
C++:
Please, Log in to view codes content!

These are the game's original functions running every frame. When we want to modify behaviour, we have two options:
  1. Hook the function - intercept it and return our own value
  2. Edit the field directly - change the variable in memory

Method 1: Function Hooking

Case 1: Fixed Speed (no toggle)
We want the player speed to always be 10.0f, no enable/disable needed:
C++:
Please, Log in to view codes content!

Why does the hook have void *instance but the original function does not? When the game calls a function inside a class, it already knows which object it is working with. However, when we hook that function from outside, we need to receive the address of that object, which is called the [I]instance[/I]. Every class function internally receives a hidden this pointer as the first parameter, which points to the current object.

Case 2: Enable/ Disable Toggle
We want the user to be able to turn the speed hack on or off:
C++:
Please, Log in to view codes content!
The orig_getSpeed pointer stores the original function address, allowing us to call the game's original code whenever the hack is disabled. This keeps the game working normally when we do not want to modify the function behavior.

Case 3: Speed Multiplier
Instead of a fixed value, make all movement 3x faster while preserving the walking/running/crouching difference:
C++:
Please, Log in to view codes content!
Now walking, running, and crouching speeds are all increased by 3 times. Each speed value keeps its original behavior and is multiplied, instead of replacing everything with one fixed speed value.

Method 2: Field Editing​

Instead of hooking, we write directly to the memory offset:
C++:
Please, Log in to view codes content!
Problem: The getSpeed() function still applies the stamina formula: speed * 0.1f * stamina. If stamina is 5, final speed = 10 * 0.1 * 5 = 5, not 10. Field editing here alone doesn't work as expected.
To fix it, you'd also need to change stamina:
C++:
Please, Log in to view codes content!
And for a toggle, values written to memory remain even after you disable the hack, until the game overwrites them naturally. The hack cannot be cleanly disabled unless the original values are restored.

When Is Field Editing Better?​

Field editing is the right choice when there is no function available to hook. For example, stamina may drain gradually over time, and there is no getStamina() function. In this case, we have no choice except to directly modify the stamina field in memory:
C++:
Please, Log in to view codes content!
Field editing is also useful for:
  • Reading values (e.g. checking health or position)
  • Getting class pointer chains
  • Changing variables that have no associated getter function

Final Thought​

When I was younger, I mostly asked “How do I mod this?” Today, I usually ask something different “How does this program think?”
A hook, an offset, or a patch is only one answer to one problem. What matters more is understanding why it works.

If field editing does not give the result you expected, follow the logic.
If a hook behaves differently than expected, look at the caller, the arguments, the object, and what happens afterward.

You do not need to understand everything in one day. Just understand one more decision than you understood yesterday.

This is simply the approach that worked for me, and I hope it gives someone here another way to look at the problem.

Don’t just collect offsets. Collect questions.

See you around. 🤝
 
Top