Comment puis-je complètement cacher et protéger les chaînes du lecteur dans Unity?

47

J'utilisais Unity pour créer un jeu 2D complètement hors ligne (ce qui est le problème), le jeu nécessite que vous saisissiez certaines chaînes à certains niveaux et Unity compile en DLL, ce qui peut être facilement inversé. existe-t-il un moyen de protéger ces chaînes (le jeu est hors ligne, je ne peux donc pas le récupérer depuis une autre source)?

Le jeu repose énormément sur ces chaînes, et oui je suis conscient de l’obfuscation mais je veux quelque chose de plus robuste. Et je sais que la solution de facilité serait de tout faire en ligne à partir d'une source de données, mais je me demandais si c'était possible.

Il peut être décompilé comme ceci: entrez la description de l'image ici

TheBinaryGuy
la source
10
Bien que je convienne que c'est une bataille que vous ne pouvez jamais vraiment gagner, vous pouvez certainement la rendre plus difficile avec peu d'effort. obfuscar.codeplex.com
Jacob Persi
46
@ Gabriel Hein? La décompilation de code C # non obfusqué est à peu près aussi simple que possible. Vous obtenez ainsi un code parfaitement lisible, comparez-le à ce que IDA génère pour du code C ou C ++ optimisé. Cela dit, avec suffisamment d’effort, le code natif peut tout aussi bien être compris, mais il est plus difficile de tout ordre de grandeur. Aucune idée de l'efficacité de l'obscurcissement - si cela rend les décompilateurs habituels (DotPeek, ILSpy, quelque chose d'autre?) Sur le code de la VA qui devrait introduire un obstacle pour tenir la personne occasionnelle à l'écart.
Voo
15
@ GabrieleVierti, extraire du texte d'une DLL est trivial: sous Linux ou Cygwin, vous pouvez le faire simplement en pointant le stringsprogramme dessus.
Mark
31
Qu'est-ce que vous essayez exactement de faire ici? Cela ressemble à un cas assez grave du problème XY. meta.stackexchange.com/questions/66377/what-is-the-xy-problem Tout ce que vous essayez d'accomplir (du point de vue du gameplay plutôt que du point de vue technique) n'est certainement pas mieux servi en essayant de cacher et de chiffrer ces chaînes. .
GrandOpener
36
Je me demande si cacher les ficelles sert réellement à quelque chose: une fois que certains joueurs les auront résolues, elles seront très probablement réparties dans des wikis ou similaires, de sorte que toute personne désirant les connaître pourra facilement les rechercher. Oui, en les obscurcissant, le joueur ne peut pas ouvrir la dll dans un éditeur de texte et chercher des chaînes de caractères, mais la plupart consulteront d'abord Google (ou leur moteur de recherche de choix) ...
hoffmale

Réponses:

159

Ne stockez pas ces chaînes, stockez leur hachage (cryptographique).

Une fonction de hachage (cryptographique), comme le cryptage, est un moyen de transformer une chaîne en "charabia" (appelé hachage), mais contrairement au cryptage, vous ne pouvez pas obtenir la chaîne d'origine à partir de ce hachage (sauf si vous pouvez le forcer brutalement ou le hachage la fonction est cassée). La plupart (sinon toutes) des fonctions de hachage prennent une chaîne de longueur arbitraire et retournent une chaîne de longueur constante (dépend de la fonction).

Comment vérifier que la chaîne entrée par l'utilisateur est la bonne? Étant donné que vous ne pouvez pas obtenir la chaîne valide du hachage, vous ne pouvez que hacher l'estimation de l'utilisateur et la comparer au hachage correct.

Avertissement (par Eric Lippert): N'UTILISEZ PAS la fonction intégrée en GetHashCodetant que telle. Son résultat peut différer selon les versions .NET et les plates-formes. Votre code ne fonctionne que sur des versions et une plate-forme spécifiques du framework .NET.

utilisateur49822
la source
81
C'est en effet la meilleure réponse. Mais rappelez-vous que vous ne devez absolument pas utiliser l'algorithme de hachage intégré sur les chaînes . il est conçu pour faire une chose et une seule chose et c'est équilibrer une table de hachage. Vous ne pouvez pas stocker des hachages de chaînes et les utiliser comme authentificateurs d'un secret partagé car les auteurs d'exécution .NET se réservent le droit de modifier l'algorithme de hachage de chaîne à tout moment pour quelque raison que ce soit et ce, en fait, ils l'ont déjà fait . Utilisez un hachage standard crypto-force ou implémentez votre propre hachage simple.
Eric Lippert
4
Cette. Exactement. Si vous utilisez un algorithme tel que sha256 (de nombreuses bibliothèques l'implémentent donc vous n'avez pas à écrire le vôtre), vous aurez quelque chose qui ne peut pas être cassé facilement et qui est très fiable.
Micheal Johnson
12
@Michael Johnson en utilisant un sel rendra beaucoup plus difficile l'utilisation de tables arc-en-ciel, et l'utilisation d'un mot de passe haché, comme bcrypt, le rendra beaucoup plus lent et donc plus difficile à casser. Mais en fin de compte, cela semble être un effort beaucoup trop lourd; l'utilisateur a acheté le jeu s'il veut tricher, c'est son choix
Melkor
2
@ MichealJohnson: Un étirement des clés pourrait rendre ces attaques par force brute un peu plus difficiles (disons d'un facteur d'un milliard environ). Mais le vrai problème avec cette réponse est probablement que, à un moment donné, le jeu doit être capable de dire au joueur quelle chaîne il doit entrer. J'imagine que, à moins que ces chaînes ne soient réellement des solutions à un puzzle que le joueur doit résoudre. Ou à moins que les chaînes ne soient fournies en ligne, même si le jeu est hors ligne, un peu comme les clés de licence à l'ancienne.
Ilmari Karonen
5
@Sentinel Cela signifie que le gameplay peut être différent pour différents joueurs. Nous avons vraiment besoin de savoir de quel type de chaînes nous parlons, si elles sont contenues dans des livres / des symboles / du dialogue dans le jeu, s'il s'agit de solutions à des énigmes où la réponse réelle n'est pas donnée directement au joueur, ou s'ils sont générés aléatoirement. Et si ce sont des mots, des phrases ou des caractères aléatoires.
Micheal Johnson
106

Ce que vous essayez de faire est à la fois inutile et inutile.

C'est inutile car il n'y a aucun moyen de cacher correctement les informations qui se trouvent sur la machine de l'utilisateur. Toute personne assez dévouée le trouvera. Vous pouvez le rendre plus difficile, mais vous ne pouvez jamais l’empêcher. Si vous le chiffrez, vous devez alors stocker la clé de chiffrement et l'algorithme quelque part. Peu importe le nombre de couches de cryptage que vous ajoutez, la couche la plus éloignée devra toujours être non cryptée pour que votre jeu fonctionne.

C'est également inutile, car nous vivons à l'ère d'Internet. Lorsque votre jeu devient très populaire à distance, ces codes d'authentification seront publiés sur tout le Web.

Tout ce que vous pouvez faire est de faire confiance au joueur pour ne pas gâcher sa propre expérience de jeu en recherchant des informations que le jeu n'est pas censé leur dire encore. De toute façon, la grande majorité des joueurs ne se lancera pas dans l’ingénierie inverse. Et si les quelques personnes qui possèdent les compétences nécessaires le font, alors c'est de leur faute.

Philipp
la source
38
L'ingénierie inverse est un jeu à part! C’est la seule réponse correcte - il ne faut pas cacher les informations dans les parties en mode solo, les joueurs y auront accès s’ils le souhaitent.
Mephy
27
@TheBinaryGuy La sécurité à partir de quoi? Vos utilisateurs jouent à un jeu hors ligne . Quelle menace expose le code? Le joueur est finalement censé le découvrir malgré tout pour pouvoir jouer au jeu! Une règle de sécurité importante est que vous devez identifier le traitement que vous protégez; c'est ce qu'on appelle un "modèle de menace".
jpmc26
7
@TheBinaryGuy En plus des autres points, je m'interroge sur la possibilité d'utiliser des codes d'accès "fixes" - même sans consulter les codes en ligne / par ingénierie inverse, le fait de jouer au jeu une seconde fois permettra déjà aux joueurs de passer directement à travers sections du jeu (car ils connaissent déjà les codes). Si vous voulez vraiment "forcer" les joueurs à obtenir ces codes, vous devez les faire changer à chaque tour (par exemple: avoir une forme quelconque de randomisation)
UnholySheep
6
Cette réponse a quelques bons points, mais le deuxième paragraphe est incorrect. Vous n'êtes pas obligé de chiffrer la chaîne. Vous pouvez le hacher. Si un joueur peut rompre le hachage, alors il va voler des comptes bancaires, sera embauché par le FBI ou quelque chose comme ça. Les autres points sont valables cependant.
Pedro Un
5
@Hamsterrific Je suppose que la réponse suppose que vous avez besoin de stocker les chaînes réelles, par exemple pour les montrer au joueur à un moment donné, ce qui signifie que le hachage ne fera pas l'affaire.
Frank Hopkins
4

Si une telle chose est vraiment souhaitée, au lieu du hachage, vous pouvez envisager de construire les chaînes à partir d'une valeur d'entrée numérique au moment de l'exécution.

L'avantage est que, comme l'a souligné @Philipp, il est un peu inutile d'essayer de cacher des codes dans l'exécutable si vous pouvez vous attendre à ce qu'ils soient postés sur Internet de toute façon. Haché ou non, le même mot trouvé sur Internet et entré dans le jeu donnera le même hachage et fonctionnera dans les deux sens.

Sauf que ... sauf si le code de quelqu'un d'autre ne fonctionne pas pour vous. Ce que vous pouvez trivialement faire - pas à 100% inviolable mais raisonnablement difficile à contourner pour l'utilisateur moyen. N'importe quel élément aussi simple que le "Générateur de noms elfes en ligne" fera l'affaire (peut être arbitrairement simple, n'a pas vraiment besoin d'un moteur de Markov text gen, extraire 4 à 5 syllabes d'une liste aléatoire est suffisant).

Générez simplement un numéro quelque peu spécifique à l'utilisateur ou à la machine, il n'est même pas nécessaire qu'il soit parfaitement unique ou très inviolable. Quelque chose qui est probablement différent pour la plupart des gens et qui ne changera pas régulièrement, par exemple le nom de réseau de l'ordinateur, l'adresse MAC ou le GUID du lecteur de disque système, peu importe (le numéro de série du GPU peut être un très mauvaisidée puisque les utilisateurs sont susceptibles de mettre à niveau les GPU). Ajoutez à cela le code numérique auquel le code de déverrouillage fait référence et introduisez-le dans votre générateur de mots. Mais soyez prêt à répondre aux demandes d'assistance lorsque les joueurs utilisent deux ordinateurs ou changent de carte réseau (ce qui est inhabituel, mais pas impossible). Ce pourrait être un bon plan de ne générer qu'une seule fois l'identifiant aléatoire et de le stocker avec les paramètres du jeu. De cette façon, au moins, cela ne casse pas les installations existantes sur la même machine si quelque chose change.

Ou vous pouvez simplement utiliser le numéro de série du jeu qui est unique et fonctionnera si l'utilisateur change de matériel (paradoxalement, cela pourrait favoriser le piratage, car les codes de déverrouillage partagés fonctionnent pour les publications en série piratées mais pas pour les clients légitimes!).

Notez qu'empêcher les utilisateurs de tricher n'est pas nécessairement une bonne chose. Dans un jeu hors ligne (c'est-à-dire un jeu non compétitif), il n'y a généralement pas de problème si l'utilisateur triche et obtient les codes quelque part plutôt qu'en jouant. Il se trompe seulement. On s'en fout.
D'autre part, se mettre trop dans le droit chemin s'ils veulent vraiment tricher est une excellente occasion de faire chier complètement les clients qui paient.

Alors ... avant de faire quelque chose de cette façon, réfléchissez bien si vous le voulez vraiment et ce que vous voulez. Il est fort possible que disposer de chaînes lisibles par l'homme (ou rendu trivialement "illisible" avec xor) soit suffisant et même préférable.

Damon
la source
Quel processus envisagez-vous? Si le programme génère la valeur de hachage après le téléchargement, il doit alors avoir la chaîne unhashed lors du téléchargement. Le serveur va-t-il interroger le client pour identifier les informations puis générer le hash côté serveur?
Accumulation du
@ Accumulation: quel serveur? Q dit "complètement déconnecté". Ce qui signifie probablement un programme sur un DVD (ou peut-être un téléchargement) plus un numéro de série. Vous avez donc des événements 1, 2, 3, 4 pour lesquels un code d'authentification doit exister. Vous calculez par exemple hash(serial + 1)pour obtenir un nombre correspondant au premier code. Introduisez ensuite ces informations dans votre générateur de mots, qui extrait, par exemple, une syllabe d’une liste de 16 pour 4 bits d’entrée. Voilà, des "mots" individuels pour chaque utilisateur.
Damon
S'il s'agit d'un téléchargement, il est téléchargé depuis un serveur. Si le programme calcule hash(serial+1) après le téléchargement, qu'est-ce qui empêche l'utilisateur de calculer hash(serial+1)? Une fois le programme téléchargé, l'utilisateur a accès à tout ce qu'il fait.
Accumulation du
@ Accumulation: Rien n'empêche l'utilisateur de décompiler d'abord le programme , puis de le calculer hash(serial + 1). Et alors? Ce n'est pas un problème. Regardez, si quelqu'un investit une à deux heures (probablement entre 6 et 8 heures pour un "utilisateur" typique sans antécédents de développeur de logiciel), il suffit de se tromper, eh bien ... laissez-le. Le problème, c’est que cela fonctionne pour une personne, mais pas pour tout le monde, et que ce n’est pas concurrentiel… alors, pas de problème.
Damon
3

Si vous n'avez pas besoin de montrer les chaînes, alors l'idée de hachage est probablement la voie à suivre. Par contre, si vous avez besoin de les montrer à l'utilisateur, vous pouvez les empêcher autrement de les afficher directement dans votre DLL.

En plus de chiffrer ou d’obscurcir les chaînes, une solution consiste à les décomposer. Peut-être avez-vous simplement un dictionnaire alphabétiquement trié de tous les mots possibles de toutes les chaînes du jeu. Ensuite, installez un tableau quelque part qui vous permet de rassembler les mots dans la chaîne dont vous avez besoin en les indexant dans le tableau de mots. De cette façon, vous ne possédez pas les chaînes complètes dans le jeu. Aucune clé principale n'est nécessaire pour déchiffrer les chaînes. Et les données qui indiquent leur ordre peuvent se trouver partout dans votre source, si, par exemple, chaque fonction qui utilisait une chaîne ne contenait qu'un tableau d'index local à la fonction. Je ne sais pas si c'est pratique dans votre cas, mais c'est une façon de le faire.

Vous pourriez même avoir une chaîne composée de quelques mots, mais ils finissent par être placés dans des ordres différents. Par exemple, vous pourriez avoir besoin des 2 chaînes suivantes:

  1. Un ours a traversé ma maison
  2. Ma maison m'a protégé d'un ours

Votre liste de phrases contiendrait à la fois "un ours" et "ma maison", mais vous auriez une longue liste d'autres phrases pouvant être insérées entre elles, afin de déterminer laquelle serait aussi difficile que de déterminer réellement le puzzle dans le jeu (ou autre). Par exemple, les phrases d'action pourraient être "traverser", "brûler", "pousser sur", "me protéger de", "me séparer de", "produire de façon magique", etc.

Vous pouvez intégrer cela dans votre jeu en faisant en sorte que les index soient basés sur quelque chose que le joueur a collecté ou fait. Donc, il n'y aurait pas de liste maîtresse d'index n'importe où dans le jeu. Ils seraient générés par le joueur jouant le jeu.

utilisateur1118321
la source
4
Ainsi, au lieu de rechercher la fonction qui référence une chaîne spécifique, vous recherchez la fonction qui référence un tableau avec des index, puis vous la recréez. Cela ne semble pas plus qu'une protection supplémentaire de 30 secondes. Ce que cela protège, c’est que quelqu'un utilise simplement stringscontre l’exécutable.
Voo
Comme je l'ai dit, si les tableaux qui référencent les chaînes sont distribués à toutes les fonctions qui en ont besoin, il est alors un peu plus difficile que de rechercher un tableau unique dans une seule fonction. Pas impossible, mais couvre une plus grande surface sans beaucoup de travail supplémentaire.
user1118321
N’est-ce pas une forme de cryptage moindre?
Commentaires
2
Attendez, pas même le cryptage. Juste l'encodage.
Commentaires
2
J'ai mis à jour la réponse pour être plus clair. Fondamentalement, si les index sont basés sur quelque chose que le joueur a fait, ils ne sont pas réellement stockés dans le jeu. Encore une fois, cela n’est peut-être pas infaillible, ni parfait, mais je le présente ici comme une option que les gens aimeraient peut-être explorer.
user1118321