Ofuscación de código: te reto a que entiendas este programa

26 octubre, 2015

Uno de los aspectos más valorados en el código fuente de un proyecto software es su facilidad de comprensión. Si es factible entender cómo funciona el código, entonces es más sencillo corregir errores y añadir nuevas funcionalidades. Además, es más fácil que otras personas más allá del autor puedan participar y contribuir al proyecto. Todos estos son, precisamente, aspectos clave en el desarrollo de proyectos de código abierto.

Sin embargo, existen algunas situaciones en los que la facilidad de comprensión no es un valor añadido sino todo lo contrario. Es decir, ¿qué pasa si no queremos que se sepa cómo funciona un programa? Si el lenguaje de programación es compilado, aquí ya hay una primera barrera: el binario no es tan sencillo de entender como el código fuente, aunque no es una barrera insalvable. Pero si es un lenguaje interpretado, el código fuente está a nuestra disposición. Entonces, ¿cómo lo hacemos para ocultar el comportamiento de nuestro código? Aquí es donde entran en acción las herramientas de ofuscación de código.

Programa ofuscado en C que implementa una calculadora científica. Fuente: hou @ IOCCC 2011 - Licencia: CC BY-SA 3.0
Programa ofuscado en C que implementa una calculadora científica. Fuente: hou @ IOCCC 2011 – Licencia: CC BY-SA 3.0

Según el Diccionario de la Real Academia, ofuscar significa «oscurecer y hacer sombra» o «trastornar, conturbar o confundir las ideas, alucinar». Precisamente, la ofuscación de código es el proceso que intenta modificar un programa para hacer más compleja su comprensión. La ofuscación perfecta es imposible, porque tarde o temprano hay que decirle al ordenador lo que tiene que hacer, pero pueden darse suficientes «rodeos» para confundir a quién esté analizando nuestro programa (ya sea un humano o una herramienta automática). Las técnicas que pueden usarse para ofuscar un programa son muy variadas:

– Eliminar comentarios, espacios en blanco y saltos de línea (o a la inversa, introducirlos aleatoriamente para dificultar la lectura).

– Modificar los nombres de variables y funciones para que no proporcionen información sobre su objetivo.

– Complicar artificialmente el control de flujo del programa (p.ej. usar gotos en lugar de bucles o condicionales).

– Alterar la distribución del código dentro del proyecto (partir funciones, trasladar código de un fichero a otro, copiarlo, etc.).

– Utilizar macros para que el precompilador deba expandir parte del código del programa.

– Convertir determinadas constantes del código en valores que deben calcularse durante la ejecución (p.ej. convertir un «5» en un bucle que suma 1 cinco veces).

– Añadir código innecesario (p.ej. un condicional que sabemos que siempre evaluará a cierto, un parámetro de la función que sólo se usa en instrucciones innecesarias).

– Hacer que parte de los datos del programa o el código fuente estén cifrados y deban descifrarse durante la ejecución.

– Hacer que el código del programa se modifique a sí mismo antes de ejecutarse.

Precisamente, la ofuscación es el proceso contrario al conocido como ingeniería inversa, que analiza un cierto sistema para comprender su funcionamiento. La ingeniería inversa de software es un proceso complejo de por sí: en los sistemas legacy puede faltar documentación, partes del código fuente o incluso conocimiento de qué hace el sistema o por qué se hace de una manera determinada. ¿Por qué entonces la ofuscación intenta poner todavía más palos en las ruedas a este proceso?

– El primer motivo para usar ofuscación de código es motivos de seguridad: no queremos que un atacante comprenda el funcionamiento del código para que no lo altere con malos propósitos. Por ejemplo, si un programa contiene un control de licencias, nos puede interesar que nadie lo modifique para saltarse dicho control. O bien nos puede interesar que un software cliente no envíe peticiones malintencionadas al servidor. Hay que tener en cuenta que esto sólo consigue «seguridad por oscuridad«: el sistema es seguro simplemente porque nadie sabe cómo funciona. Pero si alguien consigue entender su funcionamiento mediante el análisis, toda la seguridad se va al traste. Por tanto, usar ofuscación con este propósito sólo tiene sentido como medida adicional para poner las cosas más difíciles a un atacante, porque no es infalible en absoluto.

– Un segundo motivo para usar ofuscación está muy relacionado con el primero (pero al revés): ocultar código malicioso (un virus, un troyano, un rootkit, una bomba lógica, una puerta trasera, …). Obviamente, los autores de este tipo de malware están interesados en impedir su detección y pueden usar estas técnicas para dificultar la tarea de antivirus o revisores humanos.

– Otra razón para usar ofuscación es la protección de la propiedad intelectual. Quizás no quieres que tu competencia no sepa qué método usas para resolver tan rápido un cierto problema o que nadie se copie esa animación tan chula que has programado para tu web (o que si la usa, al menos no sea sencillo quitarle tu nombre y la licencia que le hayas asignado).

– Existen determinados procesos que producen ofuscación como efecto colateral. Un ejemplo es la minificación: la transformación del código fuente de un programa con el propósito de reducir el número de bytes que ocupa. En lenguajes interpretados como JavaScript esto reduce el espacio de almacenamiento y el tiempo necesario para descargar un script por Internet. También hay quién dice que la optimización de código de un compilador consigue, en cierto modo, un cierto nivel de ofuscación al modificar el programa para mejorar su rendimiento.

– Un último motivo para utilizar la ofuscación de código es, simplemente, pura diversión. Entender cómo funciona un programa ofuscado es como un rompecabezas, y un programa difícil de entender puede llegar a ser una obra de arte. Por ejemplo, el lenguaje de programación esotérico Brainfuck fue diseñado específicamente para hacer difícil la comprensión de sus programas. Y hay un concurso muy popular, el IOCCC (International Obfuscated C Code Contest), cuyo propósito es concebir un programa «útil» de la forma más incomprensible posible.

(Visited 735 times, 1 visits today)
Autor / Autora
Robert Clarisó Viladrosa
Comentarios
Miguel Ángel Bueno27 octubre, 2015 a las 7:45 pm

Hola.

Código ofuscado (a posta, se entiende) que tuviera que analizar no me he encontrado, pero sí mucho código muy, muy difícil de entender.

En esos casos, la experiencia muchas veces te dicta que más vale no perder el tiempo y neuronas intentando averiguar qué puñetas quería hacer ahí el/la que hizo aquello, y que acabarás antes borrándolo todo y haciéndolo de nuevo…

Saludos.

Responder
    robert27 octubre, 2015 a las 11:27 pm

    Hola Miguel Ángel.

    Creo que esta experiencia la compartes con muchos compañeros y compañeras de profesión. Llegar a un proyecto y horrorizarte con lo que encuentras… en fin, gajes del oficio.

    La pena es que a veces no siempre puedes empezar de cero 🙁

    Responder
Arturo6 octubre, 2017 a las 12:13 am

Hola Robert, gracias por tu colaboración. Quisiera preguntarte si me puedes recomendar algún programa (aunque tenga que comprarlo) que cumpla con los siguientes requisitos:

– Tecnología a ofuscar: Binarios de Aplicaciones Java – JAR’s y WAR’s – como mínimo
– Encriptación de cadenas y recursos.
– Con capacidad de ocultar llamadas a métodos, tipos de datos, acceso a recursos.
– Con opción a optimizar el tamaño del aplicativo del código ofuscado.
– Con opción a notificar en caso hubiera realizado algún tipo de modificación.

Muchas gracias.-

Responder
hola17 octubre, 2017 a las 7:42 pm

muy util muchas gracias-.

Responder
H Briseno5 agosto, 2019 a las 6:15 pm

Hola, y respecto a proteger el contenido en especifico imagenes dentro del JAR. La idea es que al descomprimirlo estas imagenes no esten disponibles.

Saludos

Responder
Deja un comentario