Como ocultar e proteger completamente as cordas do player no Unity?

47

Eu tenho usado o Unity para criar um jogo 2D que será completamente offline (que é o problema), o jogo precisa que você insira certas strings em certos níveis e o Unity compila para DLLs, que podem ser facilmente modificadas com engenharia reversa, por isso é existe uma maneira de proteger essas cordas (o jogo está offline, então não consigo recuperar de outra fonte)?

O jogo depende muito dessas seqüências, e sim, eu estou ciente de Ofuscação, mas quero algo mais robusto. E eu sei que a saída mais fácil seria fazer tudo on-line a partir de uma fonte de dados, mas eu queria saber se é possível.

Pode ser descompilado assim: insira a descrição da imagem aqui

TheBinaryGuy
fonte
10
Embora eu concorde que é uma batalha que você nunca pode vencer de verdade, você pode definitivamente torná-la mais difícil com pouco esforço. obfuscar.codeplex.com
Jacob Persi
46
@Gabriele Huh? Descompilar código C # não ofuscado é o mais fácil possível. Dessa forma, você obtém um código perfeitamente legível e compara com o que o IDA gera para um código C ou C ++ otimizado. Dito isso, com esforço suficiente, o código nativo pode ser entendido da mesma maneira, mas são ordens de magnitudes mais difíceis. Não faço ideia de como a ofuscação funciona - se ela faz com que os descompiladores habituais (DotPeek, ILSpy, outra coisa?) Vomitem o código da IL que deve introduzir uma barreira para manter a pessoa casual longe dela.
Voo
15
@GabrieleVierti, retirar texto de uma DLL é trivial: no Linux ou Cygwin, você pode fazer isso apenas apontando o stringsprograma para ele.
Mark
31
O que exatamente você está tentando fazer aqui? Parece um caso bastante grave do problema XY. meta.stackexchange.com/questions/66377/what-is-the-xy-problem O que você está tentando realizar (de uma perspectiva de jogo, não de uma perspectiva técnica) quase certamente não é melhor servido tentando ocultar e criptografar essas seqüências .
GrandOpener
36
Eu me pergunto se esconder as cordas realmente serve a um propósito: uma vez que alguns jogadores as resolvam, elas provavelmente serão espalhadas em wikis ou similares, portanto, quem quiser conhecê-las poderá facilmente procurá-las. Sim, por ofuscar-los, o jogador não pode simplesmente abrir a dll em um editor de texto e olhar para cordas, mas a maioria vai consultar o google (ou seu motor de busca de escolha) em primeiro lugar ...
hoffmale

Respostas:

159

Não armazene essas strings, armazene o hash (criptográfico) delas.

Uma função de hash (criptográfica), como criptografia, é uma maneira de transformar uma string em "sem sentido" (chamado de hash), mas, diferentemente da criptografia, você não pode obter a string original desse hash (a menos que possa fazer força bruta ou o hash função está quebrada). A maioria das funções hash (se não todas) utiliza uma sequência de comprimento arbitrário e retorna uma sequência de comprimento constante (depende da função).

Como você verifica se uma sequência inserida pelo usuário é a correta? Como você não pode obter a sequência válida do hash, a única coisa que você pode fazer é fazer o hash do palpite do usuário e compará-lo com o hash correto.

Aviso (de Eric Lippert): NÃO USE a função incorporada GetHashCodecomo tal - seu resultado pode diferir entre diferentes versões e plataformas .NET, fazendo com que seu código funcione apenas em versões e plataformas específicas do .NET Framework.

user49822
fonte
81
Esta é realmente a melhor resposta. Mas lembre-se de que você absolutamente positivamente não deve usar o algoritmo de hash incorporado em strings . Ele foi projetado para fazer uma coisa e apenas uma coisa e equilibrar uma tabela de hash. Você não pode armazenar hashes de strings e usá-los como autenticadores de um segredo compartilhado, porque os autores do runtime do .NET reservam-se o direito de alterar o algoritmo de hash de strings a qualquer momento, por qualquer motivo e, de fato, eles o fizeram no passado . Use um hash padrão com força de criptografia ou implemente seu próprio hash simples.
precisa
4
Este. Exatamente. Se você usar um algoritmo como sha256 (existem muitas bibliotecas que implementam isso para que você não precise escrever suas próprias), você terá algo que não pode ser quebrado facilmente e é muito confiável.
Micheal Johnson
12
@ Michael Johnson usando um salt tornará muito mais difícil o uso de tabelas arco-íris, e o uso de um hash de senha como o bcrypt tornará muito mais lento e, portanto, mais difícil de quebrar. Mas, no final das contas, no final do dia, isso parece um esforço demais; o usuário comprou o jogo se ele quiser trapacear, é sua própria escolha.
Melkor
2
@MichealJohnson: O alongamento de teclas pode dificultar um pouco esses ataques de força bruta (digamos, por um fator de um bilhão ou mais). Mas o verdadeiro problema com essa resposta é que, presumivelmente, em algum momento o jogo precisa ser capaz de dizer ao jogador em qual sequência ele deve entrar. A menos que essas cordas sejam realmente soluções para algum tipo de quebra-cabeça que o jogador precisa resolver, eu acho. Ou, a menos que as strings sejam fornecidas on-line, mesmo que o jogo esteja offline, como chaves de licença antigas.
Ilmari Karonen
5
@ Sentinel Isso significa que a jogabilidade pode ser diferente para diferentes jogadores. Nós realmente precisamos saber de que tipo de strings estamos falando, se eles estão contidos em livros / placas / diálogos dentro do jogo, se são soluções para quebra-cabeças em que a resposta real não é dada diretamente ao jogador, ou se forem gerados aleatoriamente. E se forem palavras, frases ou caracteres aleatórios.
precisa
106

O que você está tentando fazer é inútil e inútil.

É inútil porque não há como ocultar adequadamente as informações que estão na máquina do usuário. Qualquer pessoa dedicada o encontrará. Você pode dificultar, mas nunca pode impedir. Se você criptografá-lo, precisará armazenar a chave e o algoritmo de criptografia em algum lugar. Não importa quantas camadas de criptografia você adicione, a camada mais externa sempre precisará ser criptografada para que o jogo seja executado.

Também não faz sentido, porque estamos vivendo na era da internet. Quando o seu jogo se tornar popular remotamente, esses códigos serão publicados em toda a web.

Tudo o que você pode fazer é confiar no jogador para não estragar sua própria experiência de jogo, procurando informações que o jogo ainda não deve lhes contar. A grande maioria dos jogadores não começará a engenharia reversa do seu jogo de qualquer maneira. E se os poucos que têm as habilidades necessárias fazem isso, a culpa é deles.

Philipp
fonte
38
A engenharia reversa é um jogo próprio! Esta é a única resposta correta - hoje em dia não se deve ocultar informações nos jogos para um jogador, pois os jogadores irão acessá-las.
Mephy
27
@TheBinaryGuy Segurança de quê? Seus usuários estão jogando um jogo offline . Que ameaça está expondo o código? O jogador deve finalmente descobrir de qualquer maneira para poder jogar o jogo! Uma regra importante de segurança é que você deve identificar o tratamento do qual está protegendo; isso é chamado de "modelo de ameaça".
jpmc26
7
@TheBinaryGuy Além dos outros pontos, eu questionaria a sensibilidade de usar códigos de acesso "fixos" - mesmo sem consultar os códigos on-line / por engenharia reversa, apenas jogar o jogo pela segunda vez já permitirá que os jogadores passem direto pelos seções do jogo (como eles já conhecem os códigos). Se você realmente quer para os jogadores "força" para ganhar esses códigos que você precisa para fazê-los mudar em cada jogada (por exemplo: ter alguma forma de randomização)
UnholySheep
6
Esta resposta tem alguns pontos positivos, mas o segundo parágrafo está incorreto. Você não precisa criptografar a string. Você pode fazer hash. Se um jogador pode quebrar o hash, ele roubará contas bancárias, será contratado pelo FBI, ou algo assim. Os outros pontos são válidos embora.
Pedro A
5
@ Hamsterrific Acho que a resposta assume que você precisa que as strings reais sejam armazenadas, por exemplo, para mostrá-las ao jogador em algum momento, o que significa que o hash não funciona.
Frank Hopkins
4

Se uma coisa dessas for realmente desejada, em vez de usar hash, considere construir as strings a partir de um valor de entrada numérico no tempo de execução.

A vantagem é que, como apontado pelo @Philipp, é um pouco inútil tentar ocultar códigos no executável se você puder esperar que eles sejam publicados na Internet de qualquer maneira. Com hash ou não, a mesma palavra encontrada na internet e inserida no jogo fornecerá o mesmo hash e funcionará de qualquer maneira.

Exceto ... exceto se o código de outra pessoa não funcionar para você. O que você pode fazer de maneira trivial - não 100% à prova de violações, mas é razoavelmente difícil de solucionar para o usuário comum. Qualquer coisa tão simples quanto o "gerador de nomes Elven Online" fará (pode ser arbitrariamente simples, realmente não precisa de muito mecanismo de geração de texto markov, puxar 4-5 sílabas de uma lista aleatória é bom o suficiente).

Basta gerar um número um tanto específico do usuário ou específico da máquina, ele nem precisa ser perfeitamente exclusivo ou muito resistente a violações. Algo que provavelmente é diferente para a maioria das pessoas e provavelmente não muda regularmente, por exemplo, o nome da rede do computador, o endereço MAC ou o GUID da unidade de disco do sistema, seja qual for (o número de série da GPU pode ser muito ruimidéia, pois os usuários provavelmente atualizarão as GPUs). Acrescente a isso o código numérico ao qual o código de desbloqueio se refere e alimente-o no seu gerador de palavras. Mas esteja preparado para responder a perguntas de suporte quando os jogadores usam dois computadores ou alteram sua placa de rede (o que é incomum, mas não impossível). Pode ser um bom plano gerar o ID aleatório apenas uma vez e armazená-lo com as configurações do jogo. Dessa forma, pelo menos não interrompe as instalações existentes na mesma máquina se algo mudar.

Ou então, você pode apenas usar o número de série do jogo, que é único e funcionará se o usuário alterar o hardware (ironicamente, no entanto, isso pode promover a pirataria, pois os códigos de desbloqueio compartilhados funcionam para seriais pirateados, mas não para clientes legítimos!).

Observe que impedir os usuários de trapacear não é necessariamente uma coisa boa. Em um jogo offline (ou seja, não competitivo), normalmente não há problema se o usuário trapaceia e obtém os códigos de algum lugar, em vez de jogar. Ele está apenas se enganando. Quem se importa.
Por outro lado, atrapalhar demais se realmente deseja trapacear é uma ótima oportunidade para irritar completamente os clientes pagantes.

Então ... antes de fazer algo dessa maneira, pense muito bem se você realmente quer isso e o que deseja. Possivelmente, ter cordas legíveis por humanos (ou trivialmente tornadas "ilegíveis" com xor) é bom o suficiente e, de fato, preferível.

Damon
fonte
Que processo você imagina? Se o programa gerar o valor do hash após o download, ele deverá ter a cadeia unificada quando baixado. Então, o servidor consultará o cliente para identificar informações e, em seguida, gerará o hash do lado do servidor?
Acumulação
@ Accumulation: Qual servidor? Q diz "completamente offline". O que provavelmente significa um programa em um DVD (ou talvez um download) mais um número de série. Então você tem eventos 1,2,3,4 para os quais uma senha deve existir. Você calcula, por exemplo, hash(serial + 1)para obter um número correspondente ao primeiro código. Em seguida, alimente isso no seu gerador de palavras, que extrai, digamos, uma sílaba de uma lista de 16 para cada 4 bits de entrada. Lá vai você, "palavras" individuais para cada usuário.
Damon
Se for um download, está sendo baixado de um servidor. Se o programa calcula hash(serial+1) após o download, o que impede o usuário de calcular hash(serial+1)? Após o download do programa, o usuário tem acesso a tudo o que o programa faz.
Acccumulation
@ Acumulação: Nada impede o usuário de descompilar o programa e depois calcular hash(serial + 1). E daí? Isso não é um problema. Olha, se alguém investe de uma a duas horas (provavelmente de 6 a 8 horas para um "usuário" típico sem experiência com desenvolvedor de software) apenas para se enganar, bem ... deixe-o. O problema é que isso funciona para uma pessoa, mas não para todos, e não é competitivo ... então, não há problema.
Damon
3

Se você não precisa mostrar as strings, a ideia do hash provavelmente é o caminho a percorrer. Se, por outro lado, você precisar mostrá-los ao usuário, existem outras maneiras de evitar que eles apareçam na sua DLL diretamente.

Uma maneira de lidar com isso além de criptografar ou ofuscar as strings é quebrá-las. Talvez apenas tenha um dicionário ordenado alfabeticamente de todas as palavras possíveis de todas as strings do jogo. Em seguida, tenha um array em algum lugar que permita reunir as palavras na string de que você precisa, indexando o array de palavras. Dessa forma, você não tem as cordas completas em nenhum lugar do jogo. Não há nenhuma chave mestra necessária para descriptografar as strings. E os dados que informam a ordem deles podem estar em toda a sua fonte, se, por exemplo, cada função que usou uma string tiver apenas uma matriz de índices locais para a função. Não tenho certeza do quanto isso é prático no seu caso específico, mas é uma maneira de fazê-lo.

Você pode até ter uma sequência composta por algumas palavras, mas elas acabam sendo colocadas em diferentes ordens. Por exemplo, você pode precisar das 2 seqüências a seguir:

  1. Um urso passou pela minha casa
  2. Minha casa me protegeu de um urso

Sua lista de frases conteria "um urso" e "minha casa", mas você teria uma lista grande de outras frases que poderiam ser colocadas entre elas, para descobrir qual delas seria tão difícil quanto realmente descobrir o quebra-cabeça do jogo (ou o que seja). Por exemplo, as frases de ação podem ser "percorridas", "queimadas", "empurradas", "protegidas de", "me separadas de", "produzidas magicamente" etc.

Você pode trabalhar isso no seu jogo fazendo com que os índices se baseiem em algo que o jogador coletou ou fez. Portanto, não haveria uma lista principal de índices em nenhum lugar do jogo. Eles seriam gerados pelo jogador que jogava o jogo.

user1118321
fonte
4
Portanto, em vez de encontrar a função que referencia uma sequência específica, você encontra a função que referencia uma matriz com índices e, em seguida, recria a sequência. Isso não parece mais do que uma proteção extra de 30 segundos. No entanto, o que ele protege é alguém usando apenas stringso executável.
Voo
Como eu disse, se as matrizes que fazem referência às seqüências de caracteres são distribuídas para todas as funções que precisam delas, é um pouco mais difícil do que encontrar uma única matriz em uma única função. Não é impossível, mas cobre uma área maior sem muito trabalho extra.
user1118321
Não é apenas uma forma menor de criptografia?
Arturo Torres Sánchez
2
Espere, nem mesmo criptografia. Apenas codificando.
Arturo Torres Sánchez
2
Atualizei a resposta para ficar mais claro. Basicamente, se os índices são baseados em algo que o jogador fez, eles não são realmente armazenados no jogo. Novamente, pode não ser à prova de idiotas ou perfeito, mas estou colocando aqui como uma opção que as pessoas gostariam de explorar.
user1118321