¿Cómo puedo ocultar y proteger completamente las cadenas del jugador en Unity?

47

He estado usando Unity para crear un juego en 2D que estará completamente fuera de línea (que es el problema), el juego necesita que ingreses ciertas cadenas en ciertos niveles y Unity compila en DLL, que pueden ser fácilmente revertidas, así que ¿Hay alguna manera de proteger esas cadenas (el juego está fuera de línea, así que no puedo recuperarlo de otra fuente)?

El juego depende en gran medida de esas cadenas, y sí, estoy al tanto de la ofuscación, pero quiero algo más robusto. Y sé que la salida fácil sería hacer todo en línea desde una fuente de datos, pero me preguntaba si es posible.

Se puede descompilar así: ingrese la descripción de la imagen aquí

TheBinaryGuy
fuente
10
Si bien estoy de acuerdo en que es una batalla que nunca se puede ganar realmente, definitivamente se puede hacer más difícil con poco esfuerzo. obfuscar.codeplex.com
Jacob Persi
46
@Gabriele ¿Eh? Descompilar el código C # no ofuscado es tan fácil como parece. Obtendrá un código bastante legible de esa manera, compárelo con lo que IDA genera para el código C o C ++ optimizado. Dicho esto, con suficiente esfuerzo, el código nativo puede entenderse de la misma manera, pero sus órdenes de magnitud son más difíciles. No tengo idea de qué tan bien funciona la ofuscación, si hace que los descompiladores habituales (¿DotPeek, ILSpy, algo más?) No toquen el código IL que debería introducir una barrera para mantener a la persona casual lejos de él.
Voo
15
@GabrieleVierti, extraer texto de un archivo DLL es trivial: en Linux o Cygwin, puede hacerlo simplemente señalando el stringsprograma.
Mark
31
¿Qué estás tratando de hacer exactamente aquí? Esto suena como un caso bastante severo del problema XY. meta.stackexchange.com/questions/66377/what-is-the-xy-problem Lo que sea que esté tratando de lograr (desde una perspectiva de juego, no desde una perspectiva técnica) es casi seguro que no sea mejor tratar de ocultar y cifrar estas cadenas .
GrandOpener
36
Me pregunto si ocultar las cadenas realmente tiene un propósito: una vez que algunos jugadores las hayan resuelto, es muy probable que se distribuyan en wikis o similares, por lo que cualquiera que quiera conocerlas podrá buscarlas fácilmente. Sí, al ofuscarlos, el jugador no puede simplemente abrir el
archivo DLL

Respuestas:

159

No almacene esas cadenas, almacene el hash (criptográfico) de ellas.

Una función de hash (criptográfica), como el cifrado, es una forma de convertir una cadena en "galimatías" (llamada hash), pero a diferencia del cifrado, no puede obtener la cadena original de este hash (a menos que pueda forzarla por fuerza bruta o el hash la función está rota). La mayoría de las funciones hash (si no todas) toman una cadena de longitud arbitraria y devuelve una cadena de longitud constante (depende de la función).

¿Cómo verifica que una cadena que el usuario ha ingresado es la correcta? Como no puede obtener la cadena válida del hash, lo único que puede hacer es descifrar las conjeturas del usuario y compararlas con el hash correcto.

Advertencia (por Eric Lippert): NO UTILICE la función integrada GetHashCodecomo tal función; su resultado puede diferir entre diferentes versiones y plataformas .NET, haciendo que su código funcione solo en versiones y plataformas específicas de .NET framework.

usuario49822
fuente
81
Esta es de hecho la mejor respuesta. Pero recuerde que absolutamente no debe usar el algoritmo hash incorporado en las cadenas . está diseñado para hacer una cosa y solo una cosa y es equilibrar una tabla hash. No puede almacenar hashes de cadenas y usarlos como autenticadores de un secreto compartido porque los autores de tiempo de ejecución .NET se reservan el derecho de cambiar el algoritmo de hash de cadenas en cualquier momento por cualquier motivo y de hecho lo han hecho en el pasado . Use un hash estándar de fuerza de cifrado o implemente su propio hash simple.
Eric Lippert
44
Esta. Exactamente. Si usa un algoritmo como sha256 (hay muchas bibliotecas que implementan esto para que no tenga que escribir el suyo), entonces tendrá algo que no se puede romper fácilmente y es muy confiable.
Micheal Johnson el
12
@Michael Johnson usar una sal hará que sea mucho más difícil usar tablas de arcoiris, y usar un hash de contraseña como bcrypt lo hará mucho más lento y, por lo tanto, más difícil de romper. Pero en última instancia, al final del día, esto parece demasiado esfuerzo; el usuario ha comprado el juego si quiere hacer trampa que es su elección
Melkor
2
@MichealJohnson: El estiramiento clave podría hacer que tales ataques de fuerza bruta sean un poco más difíciles (por ejemplo, por un factor de aproximadamente mil millones). Pero el verdadero problema con esta respuesta es que, presumiblemente, en algún momento el juego necesita poder decirle al jugador en qué cadena debe ingresar. Supongo que, a menos que esas cadenas sean en realidad soluciones a algún tipo de rompecabezas que el jugador necesita resolver. O a menos que las cadenas se proporcionen en línea, aunque el juego esté fuera de línea, algo así como las claves de licencia de estilo antiguo.
Ilmari Karonen
55
@Sentinel Eso significa que la jugabilidad puede ser diferente para diferentes jugadores. Realmente necesitamos saber de qué tipo de cadenas estamos hablando, si están contenidas en libros / signos / diálogos dentro del juego, si son soluciones a rompecabezas donde la respuesta real no se le da directamente al jugador, o si se generan aleatoriamente. Y si son palabras, oraciones o caracteres aleatorios.
Micheal Johnson el
106

Lo que estás tratando de hacer es inútil y sin sentido.

Es inútil porque no hay forma de ocultar adecuadamente la información que está en la máquina del usuario. Cualquiera lo suficientemente dedicado lo encontrará. Puedes hacerlo más difícil, pero nunca puedes evitarlo. Si lo encripta, debe almacenar la clave de encriptación y el algoritmo en algún lugar. No importa cuántas capas de cifrado agregue, la capa más externa siempre tendrá que estar sin cifrar para que su juego se ejecute.

Tampoco tiene sentido, porque estamos viviendo en la era de Internet. Cuando tu juego se vuelve remotamente popular, esas contraseñas se publicarán en toda la web.

Todo lo que puede hacer es confiar en que el jugador no arruinará su propia experiencia de juego buscando información que el juego aún no debe decirles. La gran mayoría de los jugadores no comenzarán a realizar ingeniería inversa en tu juego de todos modos. Y si los pocos que tienen las habilidades necesarias hacen esto, entonces es su culpa.

Philipp
fuente
38
¡La ingeniería inversa es un juego en sí mismo! Esta es la única respuesta correcta: hoy en día no se debe ocultar información en juegos de un solo jugador, los jugadores accederán a ella si lo desean.
Mephy
27
@TheBinaryGuy ¿Seguridad de qué? Tus usuarios están jugando un juego fuera de línea . ¿Qué amenaza presenta la exposición del código? ¡Se supone que el jugador lo descubrirá de todos modos para poder jugar! Una regla importante de seguridad es que debe identificar el tratamiento del que se está protegiendo; Esto se conoce como un "modelo de amenaza".
jpmc26
77
@TheBinaryGuy Además de los otros puntos, cuestionaría la sensibilidad de usar códigos de acceso "fijos", incluso sin buscar los códigos en línea / mediante ingeniería inversa, solo jugar el juego por segunda vez ya permitirá a los jugadores saltar directamente. secciones del juego (como ya conocen los códigos). Si realmente quieres "forzar" a los jugadores a ganar estos códigos, debes hacer que cambien en cada partida (por ejemplo: tener alguna forma de aleatorización)
UnholySheep
66
Esta respuesta tiene algunos puntos buenos, pero el segundo párrafo es incorrecto. No tienes que encriptar la cadena. Puedes hacer picadillo. Si un jugador puede romper el hash, entonces robará cuentas bancarias, será contratado por el FBI o algo así. Sin embargo, los otros puntos son válidos.
Pedro A
55
@Hamsterrific Supongo que la respuesta supone que necesita almacenar las cadenas reales, por ejemplo, para mostrarlas al jugador en algún momento, lo que significa que el hash no funcionará.
Frank Hopkins el
4

Si realmente se desea tal cosa, entonces, en lugar de hash, podría considerar construir las cadenas a partir de un valor de entrada numérico en tiempo de ejecución.

La ventaja es que, como señaló @Philipp, no tiene sentido intentar ocultar los códigos en el ejecutable si puede esperar que se publiquen en Internet de todos modos. Hashed o no, la misma palabra encontrada en Internet e ingresada en el juego dará el mismo hash y funcionará de cualquier manera.

Excepto ... excepto si el código de otra persona no funciona para ti. Lo que puede hacer trivialmente: no es 100% a prueba de manipulaciones pero es razonablemente difícil de solucionar para el usuario promedio. Algo tan simple como lo hará el "generador de nombres en línea Elven" (puede ser arbitrariamente simple, realmente no necesita mucho de un motor de generación de texto markov, extraer 4-5 sílabas de una lista aleatoria es lo suficientemente bueno).

Simplemente genere un número específico del usuario o específico de la máquina, ni siquiera tiene que ser perfectamente único o muy resistente a la manipulación. Algo que probablemente sea diferente para la mayoría de las personas y que sea poco probable que cambie regularmente, por ejemplo, el nombre de red de la computadora, la dirección MAC o el GUID de la unidad de disco del sistema, lo que sea (el número de serie de la GPU puede ser un muy malo)idea ya que es probable que los usuarios actualicen las GPU). Agregue a eso el código numérico al que se refiere el código de desbloqueo e ingrese eso en su generador de palabras. Pero prepárese para responder consultas de soporte cuando los jugadores usen dos computadoras o cambien su tarjeta de red (lo cual es inusual, pero no imposible). Puede ser un buen plan generar solo la ID aleatoria una vez y almacenarla con la configuración del juego. De esa manera, al menos no interrumpe las instalaciones existentes en la misma máquina si algo cambia.

O bien, puede usar el número de serie del juego, que es único y funcionará si el usuario cambia el hardware (irónicamente, sin embargo, esto podría promover la piratería ya que los códigos de desbloqueo compartidos funcionan para series pirateadas pero no para clientes legítimos).

Tenga en cuenta que evitar que los usuarios hagan trampa no es necesariamente algo bueno. En un juego fuera de línea (es decir, no competitivo), generalmente no hay problema si el usuario hace trampa y obtiene los códigos de algún lugar en lugar de jugar. Él solo se está engañando a sí mismo. A quien le importa.
Por otro lado, ponerse demasiado en su camino si realmente desean hacer trampa es una gran oportunidad para cabrear por completo a los clientes que pagan.

Entonces ... antes de hacer algo de esa manera, piense muy bien si realmente quiere eso y qué quiere. Muy posiblemente, tener cadenas legibles por humanos (o trivialmente hechas "ilegibles" con xor) es lo suficientemente bueno y de hecho preferible.

Damon
fuente
¿Qué proceso imaginas? Si el programa genera el valor hash después de ser descargado, entonces debe tener la cadena sin hash cuando se descarga. Entonces, ¿el servidor consultará al cliente para identificar información y luego generará el hash del lado del servidor?
Acumulación
@ Acumulación: ¿Qué servidor? Q dice "completamente fuera de línea". Lo que probablemente significa un programa en un DVD (o tal vez una descarga) más un número de serie. Entonces tiene eventos 1,2,3,4 para los cuales debe existir un código de acceso. Calcula, por ejemplo, hash(serial + 1)para obtener un número correspondiente al primer código. Luego alimente eso en su generador de palabras, que extrae, por ejemplo, una sílaba de una lista de 16 por cada 4 bits de entrada. Ahí tienes, "palabras" individuales para cada usuario.
Damon
Si se trata de una descarga, se está descargando desde un servidor. Si el programa calcula hash(serial+1) después de descargarlo, ¿qué impide que el usuario realice el cálculo hash(serial+1)? Una vez que se descarga el programa, el usuario tiene acceso a todo lo que hace el programa.
Acumulación
@ Acumulación: nada impide que el usuario descompile primero el programa y luego lo calcule hash(serial + 1). ¿Y qué? Esto no es un problema. Mire, si alguien invierte una o dos horas (probablemente 6-8 horas para un "usuario" típico sin experiencia en desarrollo de software) solo para engañarse a sí mismo, bueno ... déjelo. La cuestión es que esto funciona para una persona, pero no para todos, y no es competitivo ... así que no hay problema.
Damon
3

Si no necesita mostrar las cadenas, entonces la idea de hash es probablemente el camino a seguir. Si, por otro lado, necesita mostrárselos al usuario, hay otras maneras de evitar que aparezcan directamente en su DLL.

Una forma de lidiar con esto además de cifrar u ofuscar las cadenas es dividirlas. Tal vez solo tenga un diccionario ordenado alfabéticamente de todas las palabras posibles de todas las cadenas del juego. Luego, tenga una matriz en algún lugar que le permita juntar las palabras en la cadena que necesita indexando en la matriz de palabras. De esta manera, no tienes las cadenas completas en ninguna parte del juego. No se necesita una clave maestra para descifrar las cadenas. Y los datos que indican su orden podrían estar en toda su fuente, si, por ejemplo, cada función que utiliza una cadena solo tiene una matriz de índices locales para la función. No estoy seguro de lo práctico que es en su caso específico, pero es una forma de hacerlo.

Incluso puede tener una cadena compuesta de algunas palabras, pero terminan en diferentes órdenes. Por ejemplo, puede necesitar las siguientes 2 cadenas:

  1. Un oso atravesó mi casa
  2. Mi casa me protegió de un oso

Su lista de frases contendría tanto "un oso" como "mi casa", pero tendría una gran lista de otras frases que podrían colocarse entre ellas, por lo que descubrir cuál sería tan difícil como descubrirlo realmente El rompecabezas en el juego (o lo que sea). Por ejemplo, las frases de acción podrían ser "caminadas", "quemadas", "empujadas", "protegidas de", "separadas de mí", "producidas mágicamente", etc.

Podrías incorporar esto a tu juego haciendo que los índices se basen en algo que el jugador ha recopilado o hecho. Por lo tanto, no habría una lista maestra de índices en ninguna parte del juego. Serían generados por el jugador que juega el juego.

usuario1118321
fuente
44
Entonces, en lugar de encontrar la función que hace referencia a una cadena específica, encuentra la función que hace referencia a una matriz con índices y luego vuelve a crear la cadena. Eso no parece más que una protección adicional de 30 segundos. Sin embargo, lo que sí protege es a alguien que solo usa stringscontra el ejecutable.
Voo
Como dije, si las matrices que hacen referencia a las cadenas se distribuyen a todas las funciones que las necesitan, entonces es un poco más difícil que solo encontrar una matriz en una sola función. No es imposible, pero cubre un área más grande sin mucho trabajo adicional.
user1118321
¿No es esta una forma menor de encriptación?
Arturo Torres Sánchez
2
Espera, ni siquiera el cifrado. Solo codificando.
Arturo Torres Sánchez
2
He actualizado la respuesta para que sea más clara. Básicamente, si los índices se basan en algo que el jugador ha hecho, entonces no están realmente almacenados en el juego. De nuevo, puede que no sea infalible o perfecto, pero lo pongo aquí como una opción que a la gente le gustaría explorar.
user1118321