How Memory Scanners Harm Your Games and How to Prevent Them?
- Tolga Karanlikoglu
- Sep 14, 2023
- 3 min read
Programs called "Memory scanner" can scan for certain values in memory and replace them with some other values while your game is open. For example, let's say you have $12,345 in your account as in the picture of the sample game below.

A memory scanner program (e.g. Cheat Engine) can accesses your game's process and searches for the number 12345 to determine which memory locations it resides in. Then, when you replace the number 12345 with, say, 999999, the result will look like this:

Suddenly, your balance in the game has gone up :) But as you would approve, this was not a good thing at all. Once the player sees that you can easily generate the game money for free, which he was supposed to buy from you, he will no longer buy anything from you. But that's not the worst, what's worse is that by selling the game money he unfairly obtained to other players below your price, he not only hindered your earnings but also made an unfair profit off your back.
Don't worry, there is more than one solution to this issue. If your game is a server-based one and the game logic is run entirely server-side (e.g. a poker game) then you don't need to worry. Because if the increase or decrease of the player money is done on the server side, then the attackers only change a sign on their own computers, the amount of game money does not actually change.
But if it is not like that, if the game logic is run on the user's device, at that point you have to solve it from the code. The way to solve it from the code is easy. You need to replace the data types you want to store in memory with obscured types. In summary, obscured types work as follows:
* Numerical value increased by a certain difference within the type
* Actual value of the variable is stored in a special variable,
* Difference between the actual value and the incremented version of it is also stored
The C# code for this obscured type can be viewed as follows:
public class ObscuredInt
{
int realValue;
int obscuredValue;
int obscuredDiff;
}
Let's explain this a little bit. Why did we do this? Because we want to understand whether the value of this type has been changed or not. Once its value is determined, it will be written to the realValue variable and a random difference will be determined and written to the obscuredDiff field. Then the value of the obscuredValue variable will be determined as realValue+obscuredDiff. So why do we do this calculation?
Our goal here is to understand whether the value in the realValue field has changed. Before using it, if you add obscuredDiff to realValue, you get obscuredValue. If the number held in that variable is not the same, then the realValue value has been changed. So why can't attackers change the obscuredDiff and obscuredValue fields? Because attackers can only find variables by their values, they cannot access them because they do not know the value of neither fields.
Now let's view the final version of our code as follows:
using UnityEngine;
public class ObscuredInt
{
int realValue;
int obscuredValue;
int obscuredDiff;
public ObscuredInt(int value)
{
realValue = value;
obscuredDiff = UnityEngine.Random(13, 9013);
obscuredValue = realValue + obscuredDiff;
}
}
Now we will define a get function for this and create an event called OnChangeDetected. You do not have to create this event, I thought it was appropriate to include it since I wrote general-purpose code, but if you are not going to perform an activity when the value changes, you do not need to use this part.
Final version of the code:
using UnityEngine;
public class ObscuredInt
{
int realValue;
int obscuredValue;
int obscuredDiff;
public delegate void ChangeDetectedEvent();
public ChangeDetectedEvent ChangeDetected = null;
public ObscuredInt(int value)
{
realValue = value;
obscuredDiff = UnityEngine.Random(13, 9013);
obscuredValue = realValue + obscuredDiff;
}
public int Get()
{
if(obscuredValue - obscuredDiff != realValue)
{
ChangeDetected?.Invoke();
realValue = obscuredValue - obscuredDiff
}
return obscuredValue - obscuredDiff;
}
}
In addition to this code, you can overload +, -, = and other mathematical operators and use your own class instead of int.
Caution! There is a risk in specifying random numbers between 13 and 9013 if the value of obscuredValue is greater than int.MaxValue - obscuredDiff, you will probably get an error. Therefore, when copying this code into your game, code a precaution against this issue, or at least try to run this code in try..catch. Then why didn't you fix this error? I can hear you say. Because this is a tutorial article and the more I put in such articles that are not necessary at the core of the subject, the harder it becomes for the reader to understand. It is certain that people who have enough programming knowledge to understand the method explained here will have the knowledge to quickly fix this error. |
With the same method, float, double, byte, etc. You can obscure all numeric types and protect them from attackers. So how can you do this forstring? I will touch on that subject in another article.
Comments