Ponto V!

Home Unity Protegendo Dados
Bruno Xavier
Protegendo DadosImprimir
Escrito por Bruno Xavier

Praticamente desde o surgimento dos jogos já haviam pessoas recorrendo de trapaças para ganhar algum tipo de vantagem.

Seja por aprendizado, diversão ou más intenções, hackers adoram atacar jogos; se você faz jogos você precisa lidar com hackers de uma forma ou de outra.

Se você faz jogos para PC, esse cenário se torna um pesadelo se seu jogo tem como foco uma jogabilidade justa para todos os envolvidos.

Antes de tudo, se seu jogo é on-line, precisa ser com servidor autoritativo. Sem discussão aqui.

Se você pretende fazer jogos com multiplayer P2P (sem uso de custosos servidores), vale muito seguir esta dica; Até mesmo para jogos baseados em servidores esta dica é válida, mas não se deve trocar uma arquitetura autoritativa por um pequeno "truque com variáveis"!

Tudo esclarecido, ok vamos lá.

Garimpando dados

Com o passar do tempo, venho vez ou outra escutando que para parar os hackers, toda a estrutura do computador precisaria ser modificada pois tudo que um programa tem acesso em um computador, o hacker também tem.

Isso é verdade, basta observar as medidas de segurança em todos esses diversos hardwares que operam de forma diferente de um PC comum.

A regra básica é simples: se seu jogo pode usar uma variável, um hacker também pode.

O hacker pode manipula-la da maneira que bem entender se não houver ninguém para consertar o estrago que ele fez logo em seguida (leia-se servidor autoritativo).

Mas, com o surgimento de tantos jogos independentes graças a Unity e outras ferramentas maravilhosas, existe um cardápio bem grande de jogos por ai onde todos os níveis de hackers fazem uma festa.

Isso se deve ao fato de que pequenos jogos produzidos por pessoas com poucos recursos não tem condições de manter um servidor autoritativo e simplesmente não podem arcar com os custos de ter que constantemente limpar um banco de dados de ranking de pontuações.

Por falar em ranking, até pouco tempo atrás surgiram alguns casos de promoções publicitárias com jogos feitos em Unity que viraram alvo de fraude por parte de hackers tentando se dar bem na pontuação. Isso poderia ser facilmente evitado…

Mesmo que não possa parar os experientes, você não deve deixar a porta escancarada pra qualquer um hackear seus jogos…

Contra um verdadeiro hacker, não importa o que você faça para esconder, mascarar, re-alocar as variáveis no seu programa; Se existe um endereço em algum lugar, um hacker determinado irá encontrar e fazer o que quiser no jogo.

Ainda assim, existe um número considerável de jogos que evitam ser invadidos por qualquer um que está começando a invadir programas como hobby.

Não é possível se proteger de "verdadeiros hackers" a não ser que invista($) em patches freqüentes que sempre mudam a estrutura de dados no jogo. De qualquer forma, este grupo de hackers é muito muito pequeno e geralmente não se darão ao trabalho de atacar seu jogo a não ser que seja um mega hit.

Geralmente eles se concentram em jogos de grandes nomes e marcas.

Fora deste pequeno grupo, a maioria esmagadora serão usuários de programas como CheatEngine, exemplo de uso em um jogo:

Exemplo de uso em um jogoGRANDE:

Os demais serão usuários de "cracks" duvidosos que mais ferram com a máquina deles do que com o jogo.

Se deu uma lida na wiki da Cheat Engine, deve ter notado que esse tipo de ferramenta permite que o usuário revire a memória do PC de cima a baixo. Ainda assim não é muito complexo proteger o jogo, basta pensar um pouco.

Claro que usuários bem avançados pode acabar destruindo todas as formas de segurança que você encontrar, mas novamente, esse tipo de hacker é um número muito, muito pequeno.

A maioria dos usuários de programas como a CheatEngine são jovens rapazes atrás de uma chance de "ferrar com o sistema" e fazem isso apenas por diversão ou auto-satisfação. Contra esse tipo de hacker é relativamente fácil proteger os dados do seu jogo;

São necessários apenas dois passos:

Ao salvar os dados em arquivos persistentes, utilize codificadores, como por exemplo a biblioteca JSON que já foi re-escrita de diversas formas para Unity. Isso praticamente elimina as chances de alguém editar os arquivos salvos pelo jogo.

O problema aqui é que isso não elima a possibilidade do jogador editar os dados enquanto o jogo esteja sendo executado. É ai onde muitos se perdem e deixam o jogo ao acaso.

Existem incontáveis truques que podem ser usados para evitar o acesso indesejado dos dados na memória, o mais comum é mudar constantemente o endereço onde esses dados são armazenados.

Infelizmente hoje em dia isso já não é problema para os hackers medianos que entendem como uma CheatEngine funciona.

Se o jogo pode encontrar o endereço de uma variável, o hacker também pode.

Pensando nisso me veio uma idéia que até então não tenho visto ser usada em projeto algum. Muito principalmente em projetos em Unity.

A alguns anos atrás me peguei sorrindo e imaginando como deve ter sido divertido programar a linguagem "Al Bhed" no jogo Final Fantasy X. Ao ver como a linguagem funcionava logo notei que o sistema se tratava de um Caesar Cipher.

Pela internet vejo pequenos desenvolveres em Unity constantemente dizerem que é impossível lidar com hackers e simplesmente ignoram o caso.

Eu particularmente nunca trabalhei em nenhum projeto on-line ou com modo multiplayer, mas como essa possível ferramenta para combater invasões sempre me veio á tona, resolvi trabalhar o conceito;

O que me veio a cabeça é, ao invés de acessar os dados como eles são, podemos guardar todos os dados criptografados também na memória ao invés de criptografar somente os arquivos persistentes e fazendo uma dança randomica de endereços na memória que não surte efeito…

Conceito

Não podemos parar todos os hackers, mas aquele garoto que gosta de "se sentir" um hacker, este você para com um par de funções;

Mudando a lógica de como seu programa enxerga as variáveis, cerca de 99% de usuários da CheatEngine se tornam inofensivos.

Já que para usuários de CheatEngine e afins a memória da máquina é um livro aberto, deixe-os abrir o livro… Mude o idioma.

Eles podem literalmente pausar qualquer aplicativo e procurar qualquer valor na memória, de qualquer tipo; int, float, string, etc…

Tentar esconder variáveis em endereços randomicos não vai funcionar com os menos novatos. Então vamos a uma possível solução:

Execução

O que vamos fazer é "parar de acessar os dados como eles são" e passar a acessar uma cópia codificada dos dados.

Nós costumamos armazenar todas, todas mesmo… Todas nossas variáveis nos formatos que elas devem ter em tempo de execução do programa;

Bools, ints, floats, chars, strings, etc… Acostume-se a nunca mais fazer isso em um projeto que pretende ter um mínimo de segurança.

De agora em diante, considere que tudo é uma string. Strings podem ser codificadas de forma que não siga nenhuma lógica computacional comum; Qualquer outro formato de dados, se o hacker entender a lógica ele quebra sua segurança pois ele conhece a fórmula.

Com strings, a brincadeira fica mais interessante, e Caesar Cipher é tão simples para os hackers acostumados com criptografia que provavelmente seria a última lógica na terra que um avançado pensaria a respeito; Vejamos:

Vamos criar uma classe estática que contenha todas as referências e funções necessárias:

public class GCCPencryption {

    static readonly char[] References = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789@~`!#$%^&*()_-+=+/|[]{}';:,<>?.".ToCharArray();

    static readonly char[] Dictionary = "@8NaMzObLyPcKxQdJwReIvSfHuT6GtUhFsViErWjDqXkCpYlBoZmA453g271n9#?<%^&+/)_-+]{=|[*(>$}';:,~`!.0".ToCharArray();

    //
    public static string EncodeString(string In) {

    char[] Chars = In.ToCharArray();
    string Out = null;

    for (int I = 0; I < Chars.Length; I++) {
        int i=-1; foreach (char CH in References) {
            i++; if (Chars[I] == CH) {Chars[I] = Dictionary[i]; i=-1; break;}
        }

        Out += Chars[I].ToString();
    }

    return Out;

    }

    //

    public static string DecodeString(string In) {

        char[] Chars = In.ToCharArray();

        string Out = null;

        for (int I = 0; I < Chars.Length; I++) {
            int i=-1; 

            foreach (char CH in Dictionary) {
                i++; 

                if (Chars[I] == CH) 
                {
                    Chars[I] = References[i]; 
                    i=-1; 
                    break;
                }

            }

            Out += Chars[I].ToString();
        }

        return Out;
    }
}

GCCencryption é nossa versão do Caesar Cipher. A única forma de decifrar a escrita é conhecendo ou tendo acesso a nossa "chave", que no caso é o lista Dictionary.

A lista References, no caso nosso "idioma" comum, é usada para reverter o processo de embaralhamento das letras.

EncodeString() cria o valor codificado e DecodeString() reverte o processo. Para que esta classe permaneça oculta ao olhos de um usuário da CheatEngine é muito muito importante:

Execute as funções desta classe somente como parâmetros de C# Properties! Isso é importante pois caço a classe seja executada por qualquer função comum, a CheatEngine pode encontrar o processo e qualquer usuário pode remover as funções ou substitui-las por funções fantasmas.

Vamos a um exemplo do voodoo para manter a classe oculta e evitar que os hackers mediano e novato encontrem suas variáveis na CheatEngine.

Neste exemplo há dois medidores de "vida". A variável do canto esquerdo (GCCtest) é o valor protegido e a do canto direito (Unprotected) é uma variável comum que estamos acostumados a armazenar na memória, representada com o texto "Vida Hackeavel", vejamos:

Img1

using UnityEngine;
using System.Collections;


public class GCCReference : MonoBehaviour {

    protected string _GCCtest; // Possível, mas difícil encontrar, retorna valor irreconhecível pela CheatEngine.

    public int GCCtest { // Impossível encontrar! Este objeto parece na verdade não existir na memória.

        get {
            int value; 
            if (System.Int32.TryParse(GCCPencryption.DecodeString(_GCCtest),out value)) {
                return value;
            } else {
                return 0;
            }
        }

        set {
            _GCCtest = GCCPencryption.EncodeString(value.ToString());
        }
    }

    //
    private int Unprotected = 5000; // Hackeado.

    //
    void Awake() {

        GCCtest = 5000; // Impossível encontrar!

    }

    void OnGUI() {

        if (GUI.Button(new Rect(5,5,300,30),"Click Me!:: "+GCCtest)) {
            GCCtest-=Random.Range(1,5); 
            Unprotected = GCCtest;
        } // Encontrado o botão, nunca GCCtest!

        GUI.Box(new Rect(5,Screen.height-45,300,40),"Vida:: "+GCCtest); // Hackeado somente o texto do botão.

        GUI.Box(new Rect(Screen.width-305,Screen.height-45,300,40),"Vida Hackavel:: "+Unprotected); // Hackeados texto, funções, classes, valor real… Tudo!
    }

}

Vamos examinar de perto o que acontece com a variável GCCtest:

Usando a propriedade get, a variável entrega um valor que é decodificado no momento de chamada. Isso quer dizer que o valor não existe antes de alguma função fazer uma consulta com a variável.

Como a criação do valor decodificado ocorre muito muito rápido, a CheatEngine pode até enxergar o valor, mas assim que o usuário clicar em "Next Scan" para refinar sua lista, este mesmo valor já não existe mais. Em tempo de CPU, o valor deixou de existir eras e eras atrás…

Usando a propriedade set, a variável GCCtest simplesmente "entrega" o valor para _GCCtest, novamente não armazenando nada de forma permanente. No momento em que o usuário clica em "Next Scan", GCCtest já é nula novamente.

E como _GCCtest armazena strings codificadas aos invés do valor exato, muito raramente ela aparecerá na lista da CheatEngine e quando aparece, contém um valor completamente negligenciável!

E é simples assim. Utilizando este pequeno conceito você acabou de eliminar 99% dos hackers no seu jogo em Unity, sem nem ao menos suar. Com um mero script em C#…

Poucas coisas são realmente impossíveis, para todo o resto existe criatividade :)

Faça uma cópia desses scripts, crie um executável na Unity, instale a CheatEngine… E tente hackear os pontos de vida na variável GCCtest.

Se você conseguir… Meus parabéns!! Você é um hacker muito muito melhor do que eu, hahahah!

Estou a várias horas testando com a CheatEngine, tentando encontrar uma forma de quebrar essa macumba… A "Vida Hackeavel" foi quebrada em alguns segundos… Já o valor que realmente importa...

O mais próximo que cheguei foi encontrar o endereço da _GCCtest que retorna um valor completamente sem sentido e sem efeito ao ser editado:

Img2

Resumindo, vasculhei tudo na CheatEngine. Até agora nem sinal de conseguir editar a GCCtest. Pelo fato de que não dá para saber ao certo em que formato os dados foram gravados.

Sem saber o que realmente procurar (sem o dicionário), a maioria dos usuários desses leitores de memória muito provavelmente vai simplesmente desistir…

E é isso, boa sorte com seus projetos e boa sorte com a Unity! Eu poderia cobrar créditos por isto aqui, já que isso você não verá em nenhum outro lugar da web, mas eu não sou um cara egoísta, então… Boa sorte fazendo os "cheaters" passarem raiva! heheheh


Comentários (19)
  • Neto  - Neto
    avatar

    Não programo em C# e nunca usei o CheatEngine. Mas se quizesse hackear seu jogo eu procuraria as constantes. Como vai haver duas cosntantes com o mesmo tamanho fica, de certa forma, facil de associar isso a um dicionário. Ficaria interessante se:

    1 - O dicionario fosse gerado em tempo de execução
    2 - Fosse de tamanho variavel

    Bom, por não entender muito da arquitetura por traz do C# encerro meus comentarios por aqui. Abraço :D

  • Bruno Xavier
    avatar

    Blz Neto.

    É, se você for um cracker e reverter o código compilado para código fonte fica uma tremenda moleza entender como a coisa funciona.
    Mas a CheatEngine apenas procura por valores na memória e não escaneia o código fonte do programa então o dicionário jamais aparecerá nas buscas.

    Contra crackers não há o que fazer realmente.
    Claro, você pode obfuscar a classe como eu fiz a alguns dias atrás e inclusive alguns estúdios já estão utilizando uma DLL que criei baseada no mesmo conceito deste tópico, está a venda na Asset Store da Unity.
    E a coisa realmente está funcionando apesar da desconfiança desses primeiros usuários; mas realmente, se alguém crackear seu executável e recompilar uma versão modificada somente um DRM resolve o problema.

    Abraço!

  • Marcelo
    avatar

    Será que essa abordagem não gera um overhead alto? Já que além de ter que codificar e decodificar as strings, é preciso fazer um parse das variáveis numéricas.

  • Bruno Xavier
    avatar

    Olá Marcelo.
    O processo de codificar/decodificar ocorre somente durante o "get/set" da variável, é tão suave que no exemplo postado acima as funções nem se quer aparecem no Profiler da Unity Pro. Abraço.

  • Neto  - Entendo...
    avatar

    Sendo assim, viva as linguagens gerenciadas, elas facilitam o trabalho dos crackers, já que, vamos combinar, decompilar bytecode é muito mais simples que binário puro.

  • Anônimo
    avatar

    :side: Isso só é problema para jogos online ou com pontuação online não vejo porque colocar defesas em um game offline. No video do Starcraft ele esta "hackiando" o game em offline para jogar contra o PC não há problema no game Age of Empires 2 você podia hackiar em off line a vontade, mas se tentasse mudar o valor de alguma variável durante um game online ele parava a partida e falava : O fulano de tal tentou hackiar o game...

    Uma coisinha simples que evita esses "hacker" que usam cheat enginner é buscar o nome do cheat enginner na lista de processos do computador se ele estiver lá simplesmente fecha o game. Lógico que um hacker intermediário pode evitar isso, mas já diminui os l4mm3r pé de chinelo.

    Seu artigo esta muito legal, agora se todo o calculo for feito no servidor acho que o único jeito de hackear seria invadindo o próprio servidor.

  • Vinícius Godoy de Mendonça
    avatar

    Por outro lado, todo cálculo no servidor aumenta a carga do servidor. E é bom lembrar que essa carga é multiplicada pelo número de jogadores. Além disso, torna mais aparente o efeito do lag.

    O que eu faria é deixar boa parte dos calculos no cliente, mas fazer verificações periódicas mais lentas no servidor (como a cada 5 segundos de jogo). Se algum valor ali ficar muito estranho, aí sim, bloquear o game.

    Os cálculos no servidor só ficariam mais frequentes em situações específicas, como PvP.

  • Eliakim
    avatar

    Concordo com os comentários de que é inútil proteger um jogo offline. Embora (pelo que eu sei) um jogo seja uma prestação de serviço, quem comprou tem direito de jogar e brincar com ele. Eu mesmo já me diverti muito tentando alterar os bytes de jogos. Admito que sem muito sucesso. Uma proteção básica é sempre bem vinda, mas não é tão preocupante.
    Já se for jogo online, o caso é grave. Gostei do comentário do Vinícius Godoy, e acredito que seja uma prática aplicável em grande parte dos jogos.

    Claro que eu sempre acreditei na boa e velha "gambiarra organizada" (claro que planejada, e não gambiarra pura), já que mexer com bagunça é terrível! Imagina só: um byte aqui, um trecho ali, isso aí nem parece uma classe, nem uma struct...

    Aproveitando o post, eu gostaria também de ver um artigo sobre proteger contra pirataria (Cracks, se alguém souber como são feitos, enfim)... (embora a melhor proteção contra a pirataria seja a legislação, já que é impossível prevenir 100% por meio de código).
    PS.: escrevi com pressa.

  • Bruno Xavier
    avatar

    Claro que não é necessario proteger jogo offline.
    Mas e se o jogo tem leaderboards? E se o jogo for um P2P? Em mobile a grande maioria dos multiplayers são P2P e eles sofrem constantemente com cheaters, inclusive os que roubam moeda virtual vendida via InApp.
    Mencionaram Age of Empires e, desculpe mas, eu sempre usei injeção de variáveis neste jogo em modo online e nunca fui pego...

    Quanto a crackers, o JIT para mobiles melhora muito o nivel de proteção e esconder as variaveis dos produtos que pretende vender nas InApp purchases é importante para quem pretende fazer dinheiro de alguma forma com o jogo. Já em PCs, a única coisa que vi funcionar foi o DRM da Ubisoft; que é o motivo por tantos players hoje em dia odiarem ela. T+

  • Marco  - Cheat Engine
    avatar

    Como vai Bruno ? Bom é o seguinte , eu uso um servidor de xadrez para jogar , acontece que quando vou jogar o torneio de 10 minutos o meu oponente sempre acaba com meu tempo , dando a mensagem time Out , tenho certeza que ele esta trapaceando e usando o cheat engine , você sabe como faz isso e se existe uma maneira de eu me proteger desta fraude , tem muitas pessoas sendo prejudicadas com isto , desde já agradeço sua colaboração . Abraço !!

  • Bruno Xavier
    avatar

    Olá Marco, desculpe a demora na resposta.
    Se o seu oponente está manipulando o timer do jogo, significa que o servidor do jogo não faz nenhuma checagem e não é autoritativo.
    Me parece que esse servidor apenas faz o matchmaking e não interfere em nada durante o jogo, assim fica bem fácil para o adversário trapacear por que na verdade a partida é uma conexão P2P sem nenhum sistema de segurança.
    O outro jogador deve editar o timer e enviar pacotes para o teu cliente dizendo que o tempo acabou e como o jogo não tem nenhuma forma de defesa contra isso, não há nada que você possa fazer a não ser que o desenvolvedor implemente uma proteção de dados como citada no texto.
    Abraço.

  • Bernardo
    avatar

    teria algum exemplo disso fazendo a persistencia em banco de dados?

  • Bruno Xavier
    avatar

    Olá Bernardo, desculpe a demora na resposta.
    Eu não trabalho com nada relacionado a bancos de dados online, mas o conceito é o mesmo; a única coisa que muda é que ao invés de armazenar a variável apenas localmente, uma cópia da mesma é enviada para o servidor que faz checagem dos valores.
    Geralmente você já envia uma string em JSON para o server quando trabalhando com a Unity, mas quem usa JSON consegue descriptografa-la com facilidade, é desta forma que alguns enviam scores falsos para o GameCenter da Apple, um dos motivos que me forçaram a trabalhar uma forma de criptografia mais personalizada.
    Eu não tenho nenhum exemplo aqui, mas você apenas enviaria para o servidor o valor criptografado "_GCCtest" ao invés do ponteiro "GCCtest".
    Se os dados precisam ser exibidos em uma pagina web, como highscores por exemplo, um script PHP pode descriptografar os dados gravados antes de exibi-los, da mesma forma que o cliente o faz.
    Claro que a chave da sua criptografia ("static readonly char[] Dictionary" ) precisaria ser mascarada nesse script PHP e você precisaria editar no seu host a segurança do mesmo para evitar que terceiros acessem esse script, mas é bem funcional, nada de arrancar-se os cabelos.
    Abraço.

  • Yuri dAvila  - Script de Criptografia
    avatar

    Olá Bruno, nao conhecia sua coluna, nem o site, nem sei como cheguei aqui, mas add ao favoritos pois de cara já vi que aqui tem gente que entende do assunto e com material interessante.

    Já faço games a algum tempo e estou pegando o Unity 3D e estudando massivamente, fiz alguns projetos que podem se tornar jogos. Li seu texto e me fez pensar sobre, pois não havia deparado com nada parecido e nem pensado no assunto "proteger meu jogo de hackers".

    Queria so saber certo como faço para inplementar seu codigo ao meu projeto, eu tenho que usar sempre a classe "GCCReference" para adicionar a "GCCPencryption" ou foi so o exemplo usado, minha var pode ter qualquer nome e nao, ter que ser exatamente "protected string _GCCtest". E eu uso uma var para cada variavel do jogo ou esta criptografa todas as outras?

    Agradeço e congratulo pela coluna.

  • Bruno Xavier
    avatar

    Olá Yuri, desculpe a demora na resposta! :pinch:

    Então, as suas vars podem ter qualquer nome válido para variáveis, o mesmo vale para o nome da sua classe que implementa a criptografia.
    Esses nomes que usei são meros exemplos. Abraço.

  • Anônimo  - re:
    avatar

    Olá Bruno,

    Estou com um projeto de criar um site com disponibilidade de jogos web na própria página, porém será um jogo de ranking. Qual linguagem devo fazer para conseguir o máximo de segurança?
    Flash, Java, Unity, HTML5 ?

    Obrigado!

  • fagner nobre  - ddtank
    avatar

    ei man eu sei fazer um hacker com o cheat engine mais os cupons apareçe mais n possso gasta tipo uma ilusão só tem como vc corrigi isso?

  • fagner nobre
    avatar

    qualquer coisa min adcionar no face fagner nobre ou imail fagnernobrebf@hotmail.com

  • matheus ferreira
    avatar

    Como seria possível quebrar isso bruno?

Escrever um comentário
Your Contact Details:
Gravatar enabled
Comentário:
[b] [i] [u] [url] [quote] [code] [img]   
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch::(:shock:
:X:side::):P:unsure::woohoo::huh::whistle:;):S:!::?::idea::arrow:
Security
Por favor coloque o código anti-spam que você lê na imagem.
LAST_UPDATED2  

Busca

Linguagens

Twitter