Estratégias para derrotar editores de memória por trapaça - Desktop Games [closed]

35

Suponho que estamos falando de jogos para desktop - algo que o jogador baixa e executa no computador local. Muitos são os editores de memória que permitem detectar e congelar valores, como a saúde do seu aparelho.

Como você evita trapacear através da modificação de memória? Quais estratégias são eficazes para combater esse tipo de trapaça?

Para referência, eu sei que os jogadores podem: - Procurar algo por valor ou intervalo - Procurar algo que alterou valor - Definir valores de memória - Congelar valores de memória

Eu estou procurando por alguns bons. Dois que eu uso que são medíocres são:

  • Exibindo valores como uma porcentagem em vez do número (por exemplo, 46/50 = 92% de integridade)
  • Uma classe de baixo nível que mantém valores em uma matriz e os move a cada alteração. (Por exemplo, em vez de um int, eu tenho uma classe que é uma matriz de entradas e, sempre que o valor muda, eu uso um item de matriz diferente escolhido aleatoriamente para manter o valor)

O jogo é offline, gratuito e para um jogador.

Edit: para a posteridade, eu resolvi isso (em C #) e postei minha solução aqui , no meu blog.

ashes999
fonte

Respostas:

21

Outra possibilidade (para valores inteiros) é ter uma segunda variável de membro que contém um complemento bit a bit de uma variável existente.

Então imagine que você conseguiu health, você também teria healthComplement. Sua implementação pode ficar assim:

// 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;
}

Alguém que queira trapacear teria que definir saúde e complemento de saúde corretamente, caso contrário, o jogo travaria.

Eu acho que é meio inútil tentar impedir coisas assim. As pessoas criaram com sucesso hacks / cheats para coisas muito mais complexas e isso ofusca desnecessariamente o seu código ou piora o seu jogo.

bummzack
fonte
12

Aqui está um esquema que eu inventei quando alguém estava pedindo em algum quadro há muito tempo. Usar porcentagens ou variáveis ​​dobradas não funciona realmente, pois você pode procurar por "qualquer" valor e congelá-lo.

Em vez disso, crie um monstro de um tipo de dados.

Basicamente, armazene seus valores importantes como uma estrutura com:

  • 32 ponteiros para bits
  • Para uma leitura rápida, faça com que o struct inclua o valor atual como um int simples

Para manipulação, colete o valor atual dos bits alocados separadamente (que podem ser entradas verdadeiras / falsas), altere o valor e guarde-os novamente.

Aqui está a parte desagradável: toda vez que você manipular o valor, libere e aloque os bits novamente. A alocação deve ocorrer em ordem aleatória , para que os dados pulem na memória e não possam ser facilmente congelados.

Salvaguardas adicionais seriam armazenar somas de verificação dos valores (assim como o int "simples") e compará-los também durante as chamadas de manipulação. Se uma incompatibilidade for encontrada, faça algo sutil, como remover uma chave aleatória necessária para destrancar as portas e progredir no jogo.

Editar: Para mais movimentos de memória, aloque os novos "bits" antes de liberar os antigos.

Editar: Para jogos multiplayer, também é possível serializar a estrutura de dados, compactá-la e enviá-la pela rede. Não é o uso mais eficiente da rede, mas definitivamente mais difícil de descobrir através da detecção de pacotes.

Jari Komppa
fonte
9

Que tal você armazenar a saúde complementada com um valor aleatório gerado toda vez que você a armazena. Isso pelo menos anularia o mecanismo simples de procurar valores crescentes / decrescentes na memória para encontrar a saúde.

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;
    }
  }
}

Além disso, convém redefinir o valor de cada quadro, mesmo que a saúde não seja alterada. Isso impedirá as pessoas de procurarem endereços alterados quando a saúde deles mudar e permanecerem iguais quando a saúde deles permanecer a mesma.

Bônus: crie um novo objeto toda vez que ele for alterado, para que o endereço da memória seja alterado e defina a saúde para alterar o número aleatório, o valor armazenado e o endereço na memória toda vez que você o :

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
fonte
8

Embora eu tenha feito o primeiro comentário questionando o ponto de degradar a experiência de uma parte do seu público sem nenhum ganho aparente, ainda acho uma questão interessante do ponto de vista técnico.

Acabei de ter uma ideia: o que os trapaceiros fazem é encontrar valores que os mudam e os congelam. A busca aconteceria apenas entre mortes ou eventos que mudassem a saúde do jogador. Além disso, o trapaceiro poderia refinar a pesquisa filtrando o que mudou quando ele "não estava morrendo".

E se o contador de "saúde" estiver mudando o tempo todo? Torne-o um ponteiro e realoque-o a cada quadro ou a cada N quadro, se o desempenho for muito grande. Ou faça um XOR com um valor aleatório que altere todos os quadros (XOR novamente contra o mesmo valor para descriptografar antes de criptografar com um novo valor aleatório).

Se você tiver outros dados no jogo também alterando o tempo todo (incluindo as posições xey do personagem do jogador ou o contador de tempo), isso poderá dificultar a descoberta de qual dos dados alterados é a saúde. E congelar todo o estado do jogo é proibido para o trapaceiro.

Para enganar ainda mais, você pode realmente armazenar a saúde em uma variável simples de gravação, criada como um pote de mel.

Editar :

Ainda assim, o trapaceiro pode tentar descobrir qual das variáveis ​​que estão mudando o tempo todo é a que deve congelar por tentativa e erro. Uma solução possível seria acoplar as variáveis.

Um exemplo:

Em vez de armazenar a saúde (h) e a posição (x), você as armazena em duas variáveis ​​aeb, das quais é possível recuperar os valores posteriormente:

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

Dessa forma, se o trapaceiro congela apenas um deles e depois move o personagem, a posição é afetada e, dependendo de qual deles foi congelado, h fica negativo (morte instantânea). Você pode alternar entre as fórmulas acima e:

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

Em quadros consecutivos, e você garante que, no máximo, 2 quadros depois que uma das variáveis ​​tenha sido congelada, a saúde tornará 0 no momento em que x mudar. Lembre-se de que você está armazenando apenas aeb. Combine isso com o XOR contínuo, como mencionado acima. O resultado é uma coleção de variáveis ​​que estão mudando cada quadro para valores aparentemente aleatórios, e congelar qualquer um ou um subconjunto deles produz apenas efeitos colaterais indesejados no jogo, sendo a morte instantânea um deles.

CeeJay
fonte
7

Uma opção possível que acho que pode funcionar (não estou muito familiarizada com essas ferramentas de trapaça): não a oculte. Execute suas operações e espere determinados resultados. Após a operação (possivelmente no próximo quadro), verifique se os resultados são os esperados. Se você subtrair a saúde e a verificar, apenas para descobrir que a saúde nunca foi alterada, você pode ter certeza (desde que não tenha nenhum bug obscuro) de que ela estava bloqueada.

Se você detectar trapaças, agora está livre para alterar as regras do jogo, como achar melhor, para o máximo de luto, se essa for sua intenção. Se sua intenção é ignorar a trapaça e o jogo continuar funcionando, realoque a saúde em outro lugar na memória e altere seus indicadores para que o novo valor seja usado e o antigo seja ignorado. Repita ad infinitum.

James
fonte
6

Qualquer tentativa de mero ofuscamento está fadada ao fracasso na teoria, mas na prática você só precisa dificultar o suficiente para quebrar o desafio que se torna menos divertido do que ter sucesso no jogo. Não sei qual é o seu jogo, então não correrei o risco de sugerir métodos de ofuscação de dados, muito menos porque acredito que eles não fazem sentido.

Dito isto, e você provavelmente não gostará disso, acredito que esteja procurando por uma cortina de memória , que é um conceito de Computação Confiável .

sam hocevar
fonte
3

Uma técnica muito eficaz usada por alguns roguelikes (DoomRL, Brogue) é mascarar as variáveis ​​reais. O que isso significa é que, em vez de mostrar a saúde como "2/10", mostre como "20%".

Lembre-se, a maioria dos editores de memória funciona bem com busca / filtragem de valores específicos. Se você não souber o valor específico, ainda poderá localizá-lo (por exemplo, pesquise valores alterados / diminuídos após a saúde do jogador cair), embora demore muito mais tempo para fazê-lo dessa maneira.

Eu também sugiro uma reação anti-fraude. Por exemplo, se você tem um jogo em que o jogador sofre dano não positivo e sua saúde não diminuiu, você pode verificar isso com facilidade e precisão e saber que o jogador trapaceia. Faça algo interessante, como gerar alguns inimigos uber ao seu redor :)

ashes999
fonte
2

Escreva um driver de anel 0 que conecte SSDT e registre / bloqueie quando ReadProcessMemory / WriteProcessMemory é chamado em seu aplicativo. Seria melhor registrar-se para poder alterar lentamente o comportamento do jogo ao longo do tempo, em vez de apenas travar.

Doug-W
fonte
Parece que você está falando de detalhes de implementação de baixo nível (API do Windows?). Estou perguntando sobre estratégias gerais de alto nível.
Ashes999 # 22/15
2

Por que você impediria que os jogadores se enganassem (que trapaça em um jogo para um jogador equivale a)? Em um ambiente multiplayer, a tarefa do servidor é detectar alterações não naturais e combatê-las (geralmente ignorando a entrada ou bloqueando o culpado do servidor completamente), mas em um ambiente para um único jogador não há nada acontecendo, exceto que o trapaceiro desserviço.

A menos que você esteja criando um jogo multiplayer, é um desperdício de dinheiro e, se você estiver, está pensando em fortalecer o lugar errado.

jwenting
fonte
2

Pesquise as ferramentas de trapaça conhecidas - e verifique frequentemente se alguma das mais comuns foi detectada em execução (verifique os nomes dos processos?)

Se alguma for detectada, deixe o jogador trapacear (se estiver offline, é inofensivo), mas verifique se nenhuma pontuação / conquista será publicada em qualquer sistema de classificação / conquista online - apenas faça com que falhe silenciosamente?

Não impedirá hackers mais determinados, mas reduzirá a chance de truques mais casuais atrapalharem suas tabelas de classificação.

(Pode irritar codificadores que possuem ferramentas de desenvolvimento abertas e minimizadas para fins legítimos e que estão dando um tempo nos jogos ...)

bluescrn
fonte
7
Eu era um grande fã do Steam como plataforma de distribuição e joguei muito no Team Fortress II há alguns anos. Uma noite, fiz uma pausa em uma longa sessão de codificação e deixei o Visual Studio aberto e um depurador enquanto eu tocava. Eu me vi banido do TF2 por "trapaça". Nenhum apelo, nenhuma estrutura para proibir a suspensão. Uma "ofensa" e é permanente para a conta. Eu ainda não fazia ideia do porquê - e acredite, estava furioso - exceto que vi profundamente enterrado em um FAQ que ter um "editor de memória" aberto durante o jogo constituía trapaça e era banível. Uau.
1

Eu acho que a solução mais simples para isso é implementar uma opção de trapaça. Desative a pontuação enquanto estiver na opção de fraude.

Outra boa idéia, que eu não tenho idéia de como você implementaria. É desligar o jogo quando há uma variável de memória congelada ou algo parecido :)

Boa sorte)

HgMerk
fonte
3
Implementar truques é uma boa opção. Desligando o jogo, não tanto ... (imagine se você receber esta errado, e encerrar um jogador legítimo.)
ashes999