Proteja sus archivos flash de los descompiladores mediante el uso de cifrado

Dos veces al mes, volvemos a visitar algunas de las publicaciones favoritas de nuestros lectores de toda la historia de Activetuts +. Este tutorial se publicó por primera vez en febrero de 2010..

En este tutorial demostraré una técnica que utilizo para proteger el código y los activos contra el robo.

Los descompiladores son una verdadera preocupación para las personas que crean contenido Flash. Puedes poner mucho esfuerzo en crear el mejor juego que existe, y luego alguien puede robarlo, reemplazar el logotipo y ponerlo en su sitio sin preguntarte. ¿Cómo? Utilizando un Flash Decompiler. A menos que ponga algo de protección sobre su SWF, se puede descompilar con solo presionar un botón y el decompilador generará un código fuente legible.


Antes de que comencemos

Utilicé un pequeño proyecto mío para demostrar cuán vulnerables son los SWF a la descompilación. Puede descargarlo y probarse a sí mismo a través del enlace de fuente anterior. Usé Sothink SWF Decompiler 5 para descompilar el SWF y mirar debajo de su capucha. El código es bastante legible y se puede entender y reutilizar con bastante facilidad..


Qué podemos hacer al respecto?

Se me ocurrió una técnica para proteger los SWF de los descompiladores y lo demostraré en este tutorial. Deberíamos poder producir esto:

El código que se descompila es en realidad el código para descifrar el contenido y no tiene nada que ver con su código principal. Además, los nombres son ilegales, por lo que no se volverán a compilar. Intenta descompilarlo tú mismo..

Antes de comenzar, quiero señalar que este tutorial no es adecuado para principiantes y usted debe tener un conocimiento sólido de AS3 si desea seguirlo. Este tutorial también trata sobre la programación de bajo nivel que involucra bytes, ByteArrays y la manipulación de archivos SWF con un editor hexadecimal.

Esto es lo que necesitamos:

  • Un SWF para proteger. Siéntete libre de descargar el SWF en el que estaré trabajando..
  • Flex SDK. Lo utilizaremos para incrustar contenido utilizando la etiqueta Incrustar. Puedes descargarlo desde opensource.adobe.com.
  • Un editor hexadecimal. Estaré usando un editor gratuito llamado Hex-Ed. Puede descargarlo de nielshorn.net o puede usar un editor de su elección.
  • Un descompilador. Si bien no es necesario, sería bueno verificar si nuestra protección realmente funciona. Puede obtener una versión de prueba de Sothink SWF Decompiler en sothink.com

Paso 1: Cargar SWF en tiempo de ejecución

Abra un nuevo proyecto ActionScript 3.0 y configúrelo para compilarlo con Flex SDK (yo uso FlashDevelop para escribir código). Elija el SWF que desea proteger e incrústelo como datos binarios utilizando la etiqueta Incrustar:

 [Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream")] // source = ruta al swf que desea proteger contenido var privado: Clase;

Ahora el SWF está incrustado como un ByteArray en el cargador SWF y se puede cargar a través de Loader.loadBytes ().

 var loader: Loader = new Loader (); addChild (cargador); loader.loadBytes (new content (), new LoaderContext (false, new ApplicationDomain ()));

Al final deberíamos tener este código:

 paquete import flash.display.Loader; importar flash.display.Sprite; import flash.system.ApplicationDomain; importar flash.system.LoaderContext; [SWF (width = 640, height = 423)] // las dimensiones deben ser las mismas que las de la clase pública cargada de swf Main Sprite [Embed (source = "VerletCloth.swf", mimeType = "application / octet-stream") ] // fuente = ruta al swf que desea proteger contenido var privado: Clase; función pública Main (): void var loader: Loader = new Loader (); addChild (cargador); loader.loadBytes (new content (), new LoaderContext (false, new ApplicationDomain ())); 

Compilar y ver si funciona (debería). De ahora en adelante llamaré al SWF incorporado "SWF protegido", y al SWF que acabamos de compilar "SWF de carga".


Paso 2: Analizar el resultado

Intentemos descompilar y ver si funciona..

Yey Los activos y el código original se han ido! Lo que se muestra ahora es el código que carga el archivo SWF protegido y no su contenido. Esto probablemente detendría a la mayoría de los atacantes por primera vez que no están muy familiarizados con Flash, pero aún así no es lo suficientemente bueno para proteger su trabajo de los atacantes expertos porque el SWF protegido los está esperando intactos dentro del SWF de carga..


Paso 3: Descomprimir el SWF

Vamos a abrir el SWF de carga con un editor hexadecimal:

Debería parecer datos binarios aleatorios porque está comprimido y debe comenzar con ASCII "CWS". ¡Necesitamos descomprimirlo! (Si su SWF comienza con "FWS" y ve cadenas significativas en el SWF, es probable que no se haya comprimido. Tiene que habilitar la compresión para seguir).

Al principio puede sonar difícil pero no lo es. El formato SWF es un formato abierto y hay un documento que lo describe. Descárguelo de adobe.com y desplácese hasta la página 25 en el documento. Hay una descripción del encabezado y cómo se comprime el SWF, por lo que podemos descomprimirlo fácilmente.

Lo que se escribe allí es que los primeros 3 bytes son una firma (CWS o FWS), el siguiente byte es la versión de Flash, los siguientes 4 bytes son del tamaño del SWF. El resto se comprime si la firma es CWS o no está comprimida si la firma es FWS. Vamos a escribir una función simple para descomprimir un SWF:

 función privada descomprimir (datos: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var comprimido: ByteArray = new ByteArray (); var descomprimido: ByteArray = new ByteArray (); header.writeBytes (datos, 3, 5); // leer el encabezado sin comprimir, excluyendo la firma comprimido.writeBytes (datos, 8); // leer el resto, comprimido comprimido.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // marca como descomprimido descomprimido.writeBytes (encabezado); // escribe el encabezado de nuevo decompressed.writeBytes (comprimido); // escribe el contenido descomprimido ahora devuelto descomprimido; 

La función hace algunas cosas:

  1. Lee el encabezado sin comprimir (los primeros 8 bytes) sin la firma y lo recuerda.
  2. Lee el resto de los datos y los descomprime..
  3. Registra el encabezado (con la firma "FWS") y los datos sin comprimir, creando un nuevo archivo SWF sin comprimir..

Paso 4: Crear una utilidad

A continuación, crearemos una útil utilidad en Flash para comprimir y descomprimir archivos SWF. En un nuevo proyecto de AS3, compile la siguiente clase como una clase de documento:

 paquete import flash.display.Sprite; import flash.events.Event; importar flash.net.FileFilter; importar flash.net.FileReference; import flash.utils.ByteArray; la clase pública Compressor extiende Sprite private var ref: FileReference; función pública Compressor () ref = new FileReference (); ref.addEventListener (Event.SELECT, cargar); ref.browse ([nuevo FileFilter ("Archivos SWF", "* .swf")]);  carga de función privada (e: Evento): void ref.addEventListener (Event.COMPLETE, processSWF); ref.load ();  función privada processSWF (e: Event): void var swf: ByteArray; switch (ref.data.readMultiByte (3, "us-ascii")) caso "CWS": swf = descomprimir (ref.data); descanso; caso "FWS": swf = compress (ref.data); descanso; predeterminado: lanzar Error ("¿No SWF?"); descanso;  nueva FileReference (). save (swf);  compresa de función privada (datos: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var descomprimido: ByteArray = new ByteArray (); var comprimido: ByteArray = new ByteArray (); header.writeBytes (datos, 3, 5); // leer el encabezado, excluyendo la firma decompressed.writeBytes (data, 8); // leer el resto decompressed.compress (); comprimido.writeMultiByte ("CWS", "us-ascii"); // marca como comprimido comprimido.writeBytes (encabezado); comprimido.writeBytes (descomprimido); retorno comprimido;  función privada descomprimir (datos: ByteArray): ByteArray var header: ByteArray = new ByteArray (); var comprimido: ByteArray = new ByteArray (); var descomprimido: ByteArray = new ByteArray (); header.writeBytes (datos, 3, 5); // leer el encabezado sin comprimir, excluyendo la firma comprimido.writeBytes (datos, 8); // leer el resto, comprimido comprimido.uncompress (); decompressed.writeMultiByte ("FWS", "us-ascii"); // marca como descomprimido descomprimido.writeBytes (encabezado); // escribe el encabezado de nuevo decompressed.writeBytes (comprimido); // escribe el contenido descomprimido ahora devuelto descomprimido; 

Como probablemente habrá notado, he agregado 2 cosas: la carga de archivos y la función de compresión..

La función de compresión es idéntica a la función de descompresión, pero a la inversa. La carga del archivo se realiza mediante FileReference (se requiere FP10) y el archivo cargado está comprimido o sin comprimir. Tenga en cuenta que debe ejecutar el SWF localmente desde un reproductor independiente, ya que FileReference.browse () debe ser invocado por la interacción del usuario (pero el jugador local independiente permite ejecutarlo sin).


Paso 5: Descomprimir el SWF de carga

Para probar la herramienta, enciéndala, seleccione el SWF de carga y elija dónde guardarla. Luego ábrelo con un editor hexadecimal y frota. Deberías ver cuerdas ascii dentro de esta manera:


Paso 6: Analizar de nuevo

Regresemos al paso 2. Aunque el descompilador no mostró ninguna información útil sobre el SWF protegido, es bastante fácil obtener el SWF del cargador ahora sin comprimir; simplemente busque la firma "CWS" (si el archivo SWF protegido no está comprimido, busque "FWS") y vea los resultados:

Lo que encontramos es una etiqueta DefineBinaryData que contiene el archivo SWF protegido, y extraerlo de allí es un pedazo de pastel. Estamos a punto de agregar otra capa de protección sobre el SWF de carga: Encriptación.


Paso 7: Encriptación

Para que el SWF protegido sea menos "accesible" agregaremos algún tipo de cifrado. Elegí usar as3crypto y puedes descargarlo desde code.google.com. Puede usar cualquier biblioteca que desee en su lugar (o su propia implementación, incluso mejor), el único requisito es que debe poder cifrar y descifrar datos binarios utilizando una clave.


Paso 8: Encriptar datos

Lo primero que queremos hacer es escribir una utilidad para cifrar el SWF protegido antes de incrustarlo. Requiere un conocimiento muy básico de la biblioteca as3crypto y es bastante sencillo. Agregue la biblioteca a la ruta de su biblioteca y comencemos escribiendo lo siguiente:

 var aes: AESKey = new AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // asegúrese de que se puede dividir en 16, cero los últimos 4 bytes para (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i);

¿Que está pasando aqui? Utilizamos una clase de as3crypto llamada AESKey para cifrar el contenido. La clase encripta 16 bytes a la vez (128 bits), y tenemos que hacer un bucle sobre los datos para encriptarlos todos. Tenga en cuenta la segunda línea: data.length & ~ 15. Se asegura de que el número de bytes encriptados se pueda dividir por 16 y no se nos agoten los datos al llamar aes.encrypt ().

Nota: Es importante entender el punto de cifrado en este caso. No se trata realmente de cifrado, sino de ofuscación, ya que incluimos la clave dentro del SWF. El propósito es convertir los datos en basura binaria, y el código anterior hace su trabajo, aunque puede dejar hasta 15 bytes sin cifrar (lo que no importa en nuestro caso). No soy criptógrafo, y estoy bastante seguro de que el código anterior puede parecer poco convincente y débil desde la perspectiva de un criptógrafo, pero como dije, es bastante irrelevante ya que incluimos la clave dentro del SWF..


Paso 9: Utilidad de cifrado

Es hora de crear otra utilidad que nos ayude a cifrar los archivos SWF. Es casi lo mismo que el compresor que creamos anteriormente, así que no hablaré mucho sobre él. Compílalo en un nuevo proyecto como una clase de documento:

 package import com.hurlant.crypto.symmetric.AESKey; importar flash.display.Sprite; import flash.events.Event; importar flash.net.FileReference; import flash.utils.ByteArray; la clase pública Encryptor extiende Sprite private var key: String = "activetuts"; // He codificado la clave privada var ref: FileReference; función pública Encryptor () ref = new FileReference (); ref.addEventListener (Event.SELECT, cargar); ref.browse ();  carga de función privada (e: Evento): void ref.addEventListener (Event.COMPLETE, encrypt); ref.load ();  cifrado de funciones privadas (e: Evento): void var data: ByteArray = ref.data; var binKey: ByteArray = new ByteArray (); binKey.writeUTF (clave); // AESKey requiere varias claves binarias: AESKey = new AESKey (binKey); var bytesToEncrypt: int = (data.length & ~ 15); // asegúrese de que puede dividirse por 16, cero los últimos 4 bytes para (var i: int = 0; i < bytesToEncrypt; i += 16) aes.encrypt(data, i); new FileReference().save(data);   

Ahora ejecútelo y haga una copia encriptada del SWF protegido seleccionándolo primero y luego guardándolo con un nombre diferente.


Paso 10: Modificando el cargador

Vuelva al proyecto SWF de carga. Debido a que el contenido ahora está encriptado, necesitamos modificar el SWF de carga y agregarle un código de descifrado. No olvide cambiar el src en la etiqueta Incrustar para que apunte al SWF cifrado.

 package import com.hurlant.crypto.symmetric.AESKey; importar flash.display.Loader; importar flash.display.Sprite; import flash.system.ApplicationDomain; importar flash.system.LoaderContext; import flash.utils.ByteArray; [SWF (ancho = 640, altura = 423)] // las dimensiones deben ser las mismas que las de la clase pública del swf cargado Extiende el Sprite [Insertar (fuente = "VerletClothEn.swf", mimeType = "application / octet-stream") ] // fuente = ruta al swf que desea proteger contenido var privado: Clase; clave privada var: String = "activetuts"; función pública Main (): void var data: ByteArray = new content (); var binKey: ByteArray = new ByteArray (); binKey.writeUTF (clave); // AESKey requiere varias claves binarias: AESKey = new AESKey (binKey); var bytesToDecrypt: int = (data.length & ~ 15); // asegúrese de que puede dividirse por 16, cero los últimos 4 bytes para (var i: int = 0; i < bytesToDecrypt; i += 16) aes.decrypt(data, i); var loader:Loader = new Loader(); addChild(loader); loader.loadBytes(data, new LoaderContext(false, new ApplicationDomain()));   

Esto es lo mismo que antes, excepto con el código de descifrado pegado en el medio. Ahora compile el SWF de carga y compruebe si funciona. Si ha seguido cuidadosamente hasta ahora, el SWF protegido debería cargarse y mostrarse sin errores..


Paso 11: mirar dentro usando un descompilador

Abra el nuevo SWF de carga con un descompilador y eche un vistazo.

Contiene más de mil líneas de código de encriptación de aspecto difícil, y probablemente sea más difícil obtener el SWF protegido. Hemos agregado algunos pasos más que el atacante debe emprender:

  1. Él (o ella) tiene que encontrar el DefineBinaryData que contiene el contenido cifrado y extraerlo.
  2. Debe crear una utilidad para descifrarla..

El problema es que crear una utilidad es tan simple como copiar y pegar desde el descompilador al editor de código y ajustar el código un poco. Intenté romper mi protección, y fue bastante fácil: logré hacerlo en unos 5 minutos. Así que vamos a tener que tomar algunas medidas contra ello..


Paso 12: ofuscación de cuerdas

Primero colocamos el SWF protegido en el SWF de carga, luego lo ciframos, y ahora le damos los últimos toques al SWF de carga. Cambiaremos el nombre de clases, funciones y variables a nombres ilegales.

Diciendo nombres ilegales Me refiero a nombres como,;! @@, ^ # ^ y (^ _ ^). Lo bueno es que esto es importante para el compilador pero no para Flash Player. Cuando el compilador encuentra caracteres ilegales dentro de los identificadores, no puede analizarlos y, por lo tanto, el proyecto no se compila. Por otro lado, el Jugador no tiene ningún problema con esos nombres ilegales. Podemos compilar el SWF con identificadores legales, descomprimirlo y cambiar su nombre a un grupo de símbolos ilegales sin sentido. El descompilador generará un código ilegal y el atacante tendrá que revisar los cientos de líneas de código manualmente, eliminando los identificadores ilegales antes de que pueda compilarlo. Él se lo merece!

Así es como se ve antes de cualquier ofuscación de cuerdas:

¡Empecemos! Descomprima el SWF de carga con la utilidad que creamos anteriormente y active un editor hexadecimal.


Paso 13: Tu primera ofuscación

Vamos a tratar de cambiar el nombre de la clase de documento. Suponiendo que haya dejado el nombre original (Principal), búsquelo en el SWF del cargador sin comprimir con un editor hexadecimal:

RenombrarPrincipal" a ;;;;. Ahora busca otras "Main" y renómbrelas a ;;;; también.

Al cambiar el nombre, asegúrese de no cambiar el nombre de cadenas innecesarias o el SWF no se ejecutará.

Guarde y ejecute el SWF. ¡Funciona! Y mira lo que dice el descompilador:

¡¡Victoria!! :)


Paso 14: Renombrando el resto de las clases

Sigue cambiando el nombre del resto de tus clases. Elija un nombre de clase y búsquelo, reemplazándolo con símbolos ilegales hasta que llegue al final del archivo. Como dije, lo más importante aquí es usar su sentido común, asegúrese de no arruinar su SWF. Después de cambiar el nombre de las clases, puede comenzar a cambiar el nombre de los paquetes. Tenga en cuenta que al cambiar el nombre de un paquete, también puede borrar los puntos y convertirlo en un nombre de paquete ilegal largo. Mira lo que hice:

Una vez que termine de renombrar las clases y los paquetes, puede comenzar a renombrar funciones y variables. Son incluso más fáciles de cambiar de nombre, ya que generalmente aparecen solo una vez, en una gran nube. De nuevo, asegúrese de cambiar solo el nombre de "sus" métodos y no los métodos Flash incorporados. Asegúrate de no borrar la clave ("activetuts" en nuestro caso).


Paso 15: Comprimir el SWF

Después de que termine de cambiar el nombre probablemente querrá comprimir el SWF para que tenga un tamaño más pequeño. No hay problema, podemos usar la utilidad de compresión que creamos antes y hará el trabajo. Ejecute la utilidad, seleccione el SWF y guárdelo con otro nombre.


Conclusión: tener una mirada final

Ábrelo una última vez y échale un vistazo. Las clases, las variables y los nombres de los métodos están ofuscados y el SWF protegido está en algún lugar dentro, encriptado. Esta técnica podría tardar en aplicarse al principio, pero después de algunas veces solo toma unos minutos.

Hace un tiempo creé una utilidad automática para inyectar el SWF protegido en el SWF de carga y funcionó bien. El único problema es que si se puede inyectar con una utilidad automática, se puede descifrar con otra utilidad, por lo que si el atacante hace una utilidad, obtendrá todo su SWF fácilmente. Debido a esto, prefiero proteger los SWF manualmente cada vez, agregando una ligera modificación para que sea más difícil automatizar.

Otra buena aplicación de la técnica es Bloqueo de dominio. En lugar de descifrar el SWF con una cadena constante, puede descifrarlo con el dominio en el que se está ejecutando actualmente el SWF. Entonces, en lugar de tener una declaración if para verificar el dominio, puede presentar una forma más poderosa de proteger el SWF de la ubicación en otros sitios..

Lo último, es posible que desee reemplazar el código de encriptación con su propia implementación. ¿Por qué? Invertimos esfuerzos para hacer que el código criptográfico sea ilegal, pero el código que utilizamos proviene de una popular biblioteca de código abierto y el atacante podría reconocerlo como tal. Él descargará una copia limpia, y todo el trabajo de ofuscación se vuelve innecesario. Por otro lado, usar su propia implementación requerirá que él arregle todos los nombres ilegales antes de que pueda continuar.


Otros métodos de protección

Debido a que el robo de SWF es un gran problema en el mundo de Flash, existen otras opciones para proteger los SWF. Existen numerosos programas para ofuscar a AS en el nivel de bytecode (como secureSWF de Kindisoft). Ellos arruinan el código de bytes compilado y cuando el descompilador intenta generar código, este falla e incluso falla a veces. Por supuesto, esta protección es mejor en términos de seguridad, pero cuesta $$$, así que antes de elegir cómo proteger su SWF considere la cantidad de seguridad necesaria. Si se trata de proteger un algoritmo propietario que su estudio de Flash de 50 empleados ha estado desarrollando durante los últimos dos años, puede considerar algo mejor y luego cambiar el nombre de las variables. Por otro lado, si desea evitar que los niños envíen puntajes altos falsos, puede considerar usar esta técnica..

Lo que me gusta de esta técnica es el hecho de que su SWF protegido queda intacto cuando se ejecuta. Como la ofuscación altera el código de byte y podría dañar el SWF y causar errores (aunque no he encontrado ninguno).

Eso es todo por hoy, ¡espero que hayas disfrutado el tutorial y hayas aprendido algo nuevo! Si tienes alguna pregunta no dudes en dejar un comentario..