Estrategias para derrotar a los editores de memoria para hacer trampa - Juegos de escritorio [cerrado]

35

Supongo que estamos hablando de juegos de escritorio, algo que el jugador descarga y ejecuta en su computadora local. Muchos son los editores de memoria que le permiten detectar y congelar valores, como la salud de su jugador.

¿Cómo se evita hacer trampa mediante la modificación de la memoria? ¿Qué estrategias son efectivas para combatir este tipo de trampas?

Como referencia, sé que los jugadores pueden: - Buscar algo por valor o rango - Buscar algo que haya cambiado de valor - Establecer valores de memoria - Congelar valores de memoria

Estoy buscando algunas buenas. Dos que uso que son mediocres son:

  • Mostrar valores como un porcentaje en lugar del número (p. Ej. 46/50 = 92% de salud)
  • Una clase de bajo nivel que contiene valores en una matriz y los mueve con cada cambio. (Por ejemplo, en lugar de un int, tengo una clase que es una matriz de entradas, y cada vez que cambia el valor, utilizo un elemento de matriz diferente, elegido al azar para mantener el valor)

El juego es fuera de línea, gratis y para un jugador.

Edit: for posterity, I solved this (in C#) and posted my solution here, on my blog.

ashes999
fuente

Respuestas:

21

Another possibility (for integer values) is to have a second member-variable which holds a bitwise complement of an existing variable.

So imagine you got health, you would also have healthComplement. Your implementation could look like this:

// setter sets value and complement
void setHealth(int value){
    health = value;
    healthComplement = ~value;
}

// getter checks if value was changed outside of the setter
int getHealth(){
    if(value != ~healthComplement){
        // launch some cheat-counter-measure, like crashing the game?
        exit;
    }
    return health;
}

Somebody who wants to cheat would have to set health and healthComplement correctly, otherwise the game would crash.

Creo que no tiene sentido tratar de evitar cosas como esta. La gente ha creado con éxito hacks / trucos para cosas mucho más complejas y simplemente ofusca innecesariamente tu código o empeora tu juego.

bummzack
fuente
12

Aquí hay un esquema que se me ocurrió cuando alguien lo solicitó en algún foro hace mucho tiempo. El uso de porcentajes o variables duplicadas realmente no funciona, ya que puede buscar "cualquier" valor y congelarlo.

En cambio, crea un monstruo de un tipo de datos.

Básicamente, almacene sus valores importantes como una estructura con:

  • 32 punteros a bits
  • Para una lectura rápida, haga que la estructura incluya el valor actual como un int simple también

Para la manipulación, recopile el valor actual de los bits asignados por separado (que pueden ser entradas verdaderas / falsas), cambie el valor y luego guárdelos.

Aquí está el bit desagradable: cada vez que manipulas el valor, libera y asigna los bits nuevamente. La asignación debe ocurrir en orden aleatorio , por lo que los datos saltan en la memoria y no se pueden congelar fácilmente.

Las salvaguardas adicionales serían almacenar sumas de verificación de los valores (así como el int "simple") y compararlos durante las llamadas de manipulación también. Si se encuentra una falta de coincidencia, haga algo sutil, como eliminar una clave aleatoria necesaria para desbloquear puertas para progresar en el juego.

Edit: For further memory movement, make sure you allocate the new "bits" before freeing the old ones.

Edit: For multiplayer games, it's also possible to serialize the data structure, compress it, and send it over the network. Not the most efficient use of the network, but definitely harder to figure out through packet sniffing.

Jari Komppa
fuente
9

How about you store the health complemented with a random value generated each time you store it. This would at least defeat the simple mechanism of searching for increasing/decreasing values in memory to find the health.

class Player
{
  int m_Health;
  int m_RandomHealth;
  Random m_Rnd = new Random();

  public int Health {
    get
    {
      return m_Health ^ m_RandomHealth;
    }

    set
    {
      m_RandomHealth = m_Rnd.Next();
      m_Health = value ^ m_RandomHealth;
    }
  }
}

In addition, you might want to reset the value each frame, even if the health doesn't change. This will prevent people from searching for changed addresses when their health changes and that stay the same when their health stays the same.

Bonus: Create a new object each time it changes so the memory addresess change, and set the health to change the random number, xored value, and address in memory every time you read it:

class Player
{
  int [] m_HealthArray; // [0] is random int, [1] is xored health
  Random m_Rnd = new Random();

  public Player() {
    this.Health = 100;
  }

  public int Health {
    get
    {
      int health = m_HealthArray[0] ^ m_HealthArray[1];
      Health = health; // reset to move in memory and change xor value
      return health;
    }

    set
    {
      m_HealthArray = new int[2];
      m_HealthArray[0] = m_Rnd.Next();
      m_HealthArray[1] = m_HealthArray[0] ^ value;
    }
  }
}
Jason Goemaat
fuente
8

Although I made the first comment questioning the point of degrading the experience of a portion of your audience for no apparent gain, I still find it an interesting question from a technical point of view.

I just had this idea: What cheaters do is find values that change and freeze them. The search would then happen only between deaths or events that changed the player's health. Moreover, the cheater could be refining the search by filtering out what changed when he was "not dying".

What if the "health" counter is changing the whole time? Make it a pointer and reallocate it every frame or every N frames if the performance hit is too big. Or XOR it with a random value that changes every frame (XORing again against the same value for decrypting before encrypting with a new random value).

If you have other in-game data also changing the whole time (including x and y positions of the player character, or the time counter), that might make it harder to find out which of all the changing data is the health. And freezing the whole game state is a no-go for the cheater.

Para engañar aún más, en realidad puede almacenar la salud en una variable de solo escritura que se entiende como un tarro de miel.

Editar :

Aún así, el tramposo podría tratar de encontrar cuál de las variables que está cambiando todo el tiempo es la que se congela a través de prueba y error. Una posible solución sería acoplar las variables juntas.

Un ejemplo:

En lugar de almacenar la salud (h) y la posición (x), las almacena en dos variables a y b, desde donde puede recuperar los valores más adelante:

a = x+h; b = x-h
x = (a+b)/2; h = (a-b)/2

De esta manera, si el tramposo congela solo uno de ellos y luego mueve al personaje, la posición se ve afectada y, dependiendo de cuál se haya congelado, h se vuelve negativo (muerte instantánea). Puede cambiar entre las fórmulas anteriores y:

a = x-h; b = x+h
x = (a+b)/2; h = (b-a)/2

In consecutive frames, and you guarantee that in at most 2 frames after either one of the variables have been frozen health will turn 0 the moment x changes. Remember that you are storing only a and b. Combine this with the continuous XOR as mentioned above. The result is a collection of variables that are changing every frame to seemingly random values, and freezing any single one or a subset of them only produces undesired side effects in the game, instant death being one of them.

CeeJay
fuente
7

One possible option I think might work (I'm not too familiar with these cheating tools): don't hide it. Perform your operations and expect certain results. After the operation (possibly next frame), check to ensure that the results are as you expect them to be. If you subtract health and then verify it, only to find health never changed, you can be pretty sure (provided you don't have some obscure bug) that it was locked.

If you detect cheating, you are now free to change the rules of the game as you see fit for maximum griefing, if that is your intent. If your intent is to ignore the cheating and have the game continue to work, reallocate the health elsewhere in memory and change your pointers so that the new value gets used and the old is ignored. Repeat ad infinitum.

James
fuente
6

Any attempts at mere obfuscation are bound to fail in theory, but in practice you just need to make it hard enough to break that the challenge becomes less fun than succeeding at the game. I don’t know what your game is, so I won’t risk suggesting data obfuscation methods, even less as I believe they’re pointless.

That said, and you will probably not be fond of it, I believe you are looking for memory curtaining, which is a Trusted Computing concept.

sam hocevar
fuente
3

One very effective technique used by some roguelikes (DoomRL, Brogue) is to mask the actual variables. What this means is that instead of showing health as "2/10," show it as "20%."

Remember, most memory editors work well with specific value searching/filtering. If you don't know the specific value, you can still track it down (eg. search for changed/decreased values after the player health drops), albeit it takes much longer to do it that way.

I also suggest an anti-cheat backlash. For example, if you have a game where the player takes non-positive damage and their health didn't drop, you can easily and accurately check this and know that the player cheats. Do something interesting, like spawning some uber enemies around him :)

ashes999
fuente
2

Write a ring 0 driver that hooks SSDT and logs / blocks when ReadProcessMemory / WriteProcessMemory is called on your application. It'd be better to just log so you can slowly change their game behavior over time rather than just crash.

Doug-W
fuente
It sounds like you're talking about low-level implementation details (Windows API?) I'm asking about general, high-level strategies.
ashes999
2

Why would you prevent players from cheating themselves (which cheating in a single player game amounts to)? In a multiplayer environment, it's the server's task to detect unnatural changes and counter them (typically by either ignoring the input or blocking the culprit out from the server completely), but in a single player environment there's nothing happening except the cheater is doing himself a disservice.

IOW, unless you're creating a multiplayer game it's a waste of money, and if you are you're thinking of hardening the wrong place.

jwenting
fuente
2

Research the known cheating tools - and frequently check whether any of the most common ones are detected running (check process names?)

If any are detected, let the player cheat (if offline, it's harmless), but make sure no scores/acheivements will be posted to any online leaderboard/acheivement system - just make it silently fail?

Won't stop more determined hackers, but will reduce the chance of more casual cheats messing up your leaderboards.

(Might annoy coders that have dev tools open and minimized for legit purposes and are taking a gaming break, though...)

bluescrn
fuente
7
I was a big fan of Steam as a distribution platform, and played a lot of Team Fortress II a couple of years ago. One evening, I took a break from a long coding session and left open Visual Studio and a debugger while I played. I found myself banned from TF2 for "cheating." No appeal, no framework in place to have a ban lifted. One "offense" and it is permanent for the account. I'd still have no idea why -- and believe me, I was fuming -- except that I saw buried deep in a FAQ that having a "memory editor" open while playing the game constituted cheating and was ban-able. Wow.
1

I think the simplest solution to this is to implement a cheat option. Disable scoring while in cheat option.

Another good idea, that I have no idea how you would implement. Is to shut off the game when there is a frozen memory variable or something like that :)

Good luck (Y)

HgMerk
fuente
3
Implementing cheats is a good option. Shutting off the game, not so much... (imagine if you get this wrong, and shut down a legitimate player.)
ashes999