Ofuscación de Código Técnicas para Ocultarlo
Ofuscación de Código Técnicas para Ocultarlo

Ofuscación de Código: Técnicas de Ofuscación para Ocultarlo

Las técnicas de ofuscación sirven para ocultar el código de aplicación de los programas informáticos con el fin de hacer inútiles los intentos de ingeniería inversa. Pero al mismo tiempo resultan muy útiles a los hackers criminales para ocultar el código del malware.

Qué es la Ofuscación de Código

Concepto de Ofuscación de Código
Concepto de Ofuscación de Código

En el campo de la programación de software, las técnicas de ofuscación sirven para ocultar el código real de la aplicación (la llamada carga útil) con el fin de hacer inútiles, o al menos complejos, los intentos de ingeniería inversa. Las razones pueden ser muchas y no necesariamente maliciosas:

  • una de ellas puede ser el deseo de evitar la duplicación del código y, por tanto, en cierta medida, la piratería y el robo de la propiedad intelectual;
  • otro motivo puede ser la protección, es decir, impedir que un hacker comprenda cómo funciona el código y explote la presencia de posibles errores, de acuerdo con el paradigma de la “seguridad por oscuridad”;
  • por último, y este es el motivo que más nos interesa en este caso, un atacante malintencionado que quiere complicar la vida al analista impidiéndole analizar el malware para comprender su funcionamiento y extraer indicadores importantes (como URL, nombres de archivos o claves de registro), o hacer inútil el uso de antivirus.

Curiosamente, en los dos primeros escenarios, el “bueno” es el programador y el “malo” es el ingeniero inverso. El bueno intenta encontrar los mejores mecanismos posibles de ofuscación del código. En el tercer escenario, es al revés. El bueno intenta encontrar los mejores mecanismos de ofuscación posibles. Lo malo es que los investigadores interesados en los dos primeros escenarios (protección de la propiedad intelectual y seguridad por ocultación) complican involuntariamente la vida a su colega del tercer escenario (el que intenta estudiar el malware).

FUD

Para un escritor de código malicioso, el santo grial es FUD (Fully Undetectable), es decir, la capacidad de ser completamente indetectable por cualquier herramienta o analista. De este modo, el malware puede propagarse sin ser molestado.

Existen métodos para analizar el funcionamiento del código malicioso, la dificultad reside, por supuesto, en descubrir y tener la previsión de comprobar los programas que instalas en tu dispositivo.

Técnicas de Ofuscación: Reglas Básicas y Programas de Aplicación

En general, la ofuscación puede realizarse a varios niveles:

  • nivel de red, por ejemplo fragmentando los paquetes para que, mientras están en tránsito, no tengan sentido. Sólo lo adquirirán una vez reensamblados en el destino. Para evitar su detección, el malware puede dividirse en paquetes de diferentes tamaños, desfasados, parcialmente solapados, mezclados con otros paquetes que serán descartados, o con intervalos muy largos entre un paquete y el siguiente;
  • nivel de contenido: en este caso, el malware, transmitido principalmente a través de HTTP, explota las posibilidades de codificación del contenido (content-transfer-encoding) que ofrece el protocolo: cifrado a través de HTTPS, compresión a través de gzip, codificación con múltiples conjuntos de caracteres (por ejemplo, ASCII, UTF-8, UTF-7, UTF16LE, UTF16BE, UTF-32LE, UTF-32BE, etc.), Transfer Encoding, como chunked o token-extension, etc;
  • nivel de aplicación: explotar las características de la aplicación (por ejemplo, el navegador web) que habla “HTML” y, en particular, la forma en que éste se renderiza, compila o ejecuta. Una forma puede ser dividir el malware en varios archivos fuente, por ejemplo .css y .js, que luego se combinan e interpretan sólo en el destino. Otra forma es explotar las capacidades de ofuscación de lenguajes como javascript. Otra es explotar lenguajes de scripting propietarios como los de Adobe Acrobat o Flash. Este tipo de ofuscación es especialmente insidiosa porque requiere que la herramienta de seguridad que supervisa el tráfico y que, en teoría, debería detectar el malware, esté dotada a su vez de lógica de aplicación, similar a la de un navegador web real;
  • nivel ejecutable: en este caso, la ofuscación es del código ejecutable del malware real, y esto es lo que veremos mejor dentro de un momento.

Los tipos de software utilizados para la ofuscación se dividen en tres categorías principales:

  • Packer, la forma más sencilla de ofuscación es la compresión. En este caso, el archivo se descomprime en memoria en tiempo de ejecución (runtime packer). Exeinfo PE era una herramienta capaz de detectar este tipo de software. Ahora existe ASL;
https://github.com/ExeinfoASL/ASL
  • crypter: utiliza el cifrado, ya sea de clave única o doble, para hacer ininteligible todo el archivo o partes de él;
  • protector: es un término “paraguas” que suele incluir varios niveles de protección contra la ingeniería inversa, como aplicar tanto compresión como cifrado o virtualizar el código (un ejemplo es WProtect).
https://github.com/xiaoweime/WProtect
Interfaz del programa   Exeinfo PE
Interfaz del programa Exeinfo PE

Mecanismos Más Utilizados para Ofuscar Código

Entre los mecanismos más utilizados para la ofuscación se encuentran:

Compuerta XOR u OR exclusiva

El OR exclusivo (XOR) es quizás el método de ofuscación más común debido a su simplicidad de implementación. Es simétrico y reversible, por lo que sólo se necesita una función para cifrar y descifrar, lo que se traduce en un archivo de menor tamaño.

En su forma básica, se realiza la operación lógica XOR entre el texto fuente y un valor dado de longitud 1 byte. Veamos un ejemplo, basado en la tabla de verdad XOR. Hacemos el XOR entre los caracteres “L”= 0x4C (codificación ASCII en hexadecimal)= 01001100 y “‘m”= 0x6D= 01101101. El resultado es 00100001 = 0x21 = “!”.

En versiones más avanzadas, esta operación se puede realizar varias veces con valores diferentes en cada paso, o se pueden “XORear” distintas secciones del texto con valores diferentes entre sí, o incluso cada carácter con un valor diferente, partiendo de un valor base y autoincrementándolo en 1 (por ejemplo, se puede XORear la letra “h” con 0x55, luego la letra “t” con 0x56 y así sucesivamente). Existe software como XORSearch que puede buscar cadenas en archivos binarios codificados con XOR, ROL, ROT o SHIFT.

Codificación Base64

La codificación Base64 es un sistema de codificación ampliamente utilizado para convertir datos binarios en formato de texto utilizando 64 caracteres ASCII (A-Z, a-z, 0-9, + y /, con el signo = utilizado como carácter de relleno). Normalmente, uno de los elementos reconocibles es el carácter de relleno. Aquí tienes una herramienta base64 encode que puedes utilizar.

ROT13

ROT13 es un sistema de cifrado muy sencillo, basado en el cifrado de César, y su nombre significa ROTate 13, ya que sustituye cada letra por su equivalente que se encuentra 13 lugares más arriba en el alfabeto, por ejemplo, “a” por “n”, “b” por “o” y así sucesivamente. Obviamente, no es necesario utilizar sólo 13 como valor clave. El valor 13 es especial sólo porque, en un alfabeto de 26 letras, se puede utilizar la misma operación para codificar y descodificar, por ejemplo, ‘a’ <-> ‘n’.

Técnicas de Ofuscación: Distintas Formas de Aplicarlas

Abordando el problema de una manera muy general, podríamos preguntarnos: ¿cuántas maneras diferentes hay de hacer ofuscación? Y dados dos programas de ofuscación P y P’, ¿cómo se determina cuál es el mejor de ellos?

Empecemos primero por la segunda pregunta. Hay tres cantidades que se pueden calcular y que pueden ayudar a medir la calidad de un programa de ofuscación:

Potencia

Mide cuánta “ocultación” (a nivel de un analista humano) se añade al programa de partida mediante la operación de ofuscación. Para ello, obviamente es necesario tener primero una medida del nivel de ofuscación de un programa.

La idea es que una pieza de software es tanto más oculta cuanto más compleja es, y la complejidad puede medirse por varias cantidades, incluyendo la longitud del programa, la complejidad ciclotómica de McCabe, hasta qué punto las condiciones dentro del programa están anidadas unas dentro de otras (por ejemplo, una serie de if’s contenidas unas dentro de otras), lo complejas que son las estructuras de datos utilizadas en el software, cuántos parámetros hay en las funciones de entrada contenidas, etc.

Si un programa ofuscador quiere ser potente, debe actuar sobre estas variables, por ejemplo, añadiendo condiciones anidadas, aumentando el número de parámetros de entrada en las funciones, alargando el programa, perturbando la estructura del programa al situar lo más alejados posible los puntos en los que se declaran las variables y aquellos en los que se utilizan, etc. En resumen, el objetivo es hacer ilegible el software.

Resiliencia

mide la dificultad necesaria para desofuscar el programa automáticamente. Puede variar de trivial a unidireccional. En este último caso, significa que ya no se puede desofuscar el programa. Depende de dos factores:

  • esfuerzo del programador: cuánto tarda un programador en construir un programa desofuscador automático que pueda reducir la potencia de un programa ofuscado;
  • esfuerzo del desofuscador: cuánto tarda el programa desofuscador construido en el paso anterior en reducir la potencia del software ofuscado.

Coste

es la capacidad de cálculo, o el tiempo, necesario para desofuscar un programa ofuscado. Dicho de otro modo, es la sobrecarga computacional que añade la ofuscación en comparación con el programa original.

La calidad global de un programa ofuscado estará en función de las tres cantidades que acabamos de ver.

Llegamos ahora a la primera pregunta que nos hacíamos, es decir, de cuántas formas distintas se puede ofuscar un programa. La respuesta depende de:

  • el tipo de información que afecta la ofuscación;
  • el tipo de transformación que se haga sobre los datos del punto anterior.

Nos remitiremos a la figura. En concreto, podríamos tener:

Diferentes formas de ofuscar un programa
Diferentes formas de ofuscar un programa (Fuente: unive.it)

Ofuscación de la estructura

Actúa sobre la estructura del código fuente y, en particular, sobre el formato (por ejemplo, eliminando todos los espacios), los nombres de las variables (por ejemplo, transformándolos de nombres “hablados” a nombres compuestos de caracteres aleatorios), etc:

  • agregación: los cálculos que lógicamente encajan se colocan en posiciones diferentes y los cálculos que no tienen relación entre sí se colocan juntos;
  • ordenación: se aleatoriza el orden en que se realizan los cálculos;
  • cálculos: se inserta nuevo código (redundante o inútil) o se introducen cambios algorítmicos en el código fuente.

Ofuscación de datos

El objetivo es enmascarar los datos (índices, matrices, estructuras, parámetros, etc.) dondequiera que se encuentren en el programa:

  • almacenamiento: las transformaciones de este tipo intentan utilizar tipos de datos extraños para almacenar datos. Por ejemplo, normalmente si uno necesita desplazarse por los elementos de un array utilizaría una variable local de tipo entero no negativo del tamaño adecuado, pero nada impide utilizar tipos diferentes como un objeto entero. Otro ejemplo consiste en sustituir una cadena estática por una función que devuelva el valor de la cadena;
  • codificación: se utilizan codificaciones no naturales para tipos de datos comunes. Por ejemplo, al tener que recorrer una matriz de 1000 elementos, la opción más natural sería una variable i que oscilara entre 1 y 1000, pero nada impide utilizar i’=8i + 3 con i’ oscilando entre 11 y 8003;
  • agregación: en los lenguajes orientados a objetos hay dos formas de agregar datos: en matrices y en objetos. Dos variables enteras de 32 bits, por ejemplo, podrían ser la primera y la segunda parte de una única variable de 64 bits Z(X,Y) = 2^32 Y + X. En el caso de las matrices, se pueden realizar varias operaciones para transformarlas: dividir (división de una matriz en varias submatrices), fusionar (unión de dos o más matrices en una sola), plegar (aumentar el número de dimensiones de una matriz) o aplanar (disminuir el número de dimensiones de una matriz);
  • ordenación: en este caso, las transformaciones consisten en modificar la ordenación de las declaraciones de variables o de los elementos de una matriz.

Ofuscación del flujo o ofuscación de control

Actúa sobre el flujo del programa alterándolo, por ejemplo insertando código redundante o no esencial, modificando los algoritmos utilizados, aleatorizando la ejecución del código, etc. El precio a pagar es casi con toda seguridad un deterioro del rendimiento. La idea básica es hacer complejo el flujo con una serie de punteros aquí y allá a los distintos bloques de código.

Un concepto que aparece a menudo en la documentación, en referencia a la ofuscación del flujo de un programa, es el de predicado opaco (predicado falso o instrucción opaca). Se trata de una variable booleana (Verdadero/Falso) en función de cuyo valor se sigue una rama del flujo en lugar de otra. Se llama “opaco” porque la existencia de esta variable es conocida tanto por el programador como por el usuario final, mientras que el valor que toma (y, por tanto, la rama que se sigue) es bien conocido por el programador pero no por el usuario final, y el esfuerzo que éste tiene que hacer para conocerlo, en términos de capacidad de cálculo, es considerable.

Además, con esta técnica, el análisis estático queda fuera de uso porque la condición se evalúa en tiempo de ejecución y, por tanto, la rama que se recorrerá sólo puede conocerse cuando se ejecuta el programa.

Transformaciones preventivas

Este tipo de transformación difiere de las transformaciones de datos o de flujo en que su objetivo principal no es ofuscar el programa a un lector humano, sino hacer más complejo el desofuscamiento automático (transformaciones preventivas inherentes) o explotar problemas conocidos en los desofuscadores y descompiladores existentes (transformaciones preventivas dirigidas):

  • Inherentes: se trata de transformaciones de bajo consumo y alta resiliencia, como la realización de bucles en sentido inverso, relativo al índice, y la inserción en el bucle de variables inútiles que cambian de valor a medida que avanza el bucle. El objetivo es aumentar la cantidad de cálculos a realizar;
  • Objetivo (Targeted): un ejemplo era HoseMocha.java, que era capaz de colapsar el descompilador Java Mocha añadiendo una instrucción apropiada después de cada retorno.

Formas de Protegerte de la Ofuscación de Malware

Protección contra ofuscación y malware

Protegerte contra la ofuscación de malware es un desafío constante en el campo de la ciberseguridad. Aquí hay algunas medidas que puedes tomar para fortalecer tu defensa contra este tipo de amenazas:

  1. Mantén tus sistemas actualizados: Asegúrate de tener instaladas las últimas actualizaciones de software, parches de seguridad y versiones de antivirus.
  2. Utiliza soluciones de seguridad confiables: Implementa soluciones de seguridad robustas, como programas antivirus, firewalls y herramientas antimalware, que estén actualizadas y sean capaces de detectar y combatir amenazas avanzadas de malware, incluso si están ofuscadas.
  3. Emplea herramientas de análisis de código y sandboxing: Utiliza herramientas que puedan analizar y examinar el código sospechoso en un entorno aislado, como un sandbox. Esto permitirá identificar comportamientos maliciosos y técnicas de ofuscación utilizadas por el malware sin afectar directamente tu sistema principal.
  4. Fomenta la conciencia de seguridad: Capacita a tus usuarios y empleados en prácticas de seguridad cibernética.
  5. Monitoriza y analiza el tráfico de red: Implementa soluciones de monitoreo de red que sean capaces de detectar patrones y comportamientos anómalos en el tráfico de red.
  6. Realiza análisis de comportamiento: Observa el comportamiento de las aplicaciones y procesos en tu sistema en busca de actividades sospechosas, como intentos de modificaciones no autorizadas, comunicaciones inusuales o acceso a recursos críticos del sistema. Los cambios inesperados pueden indicar la presencia de malware ofuscado.

Palabras Finales

En conclusión, la ofuscación de código es una práctica que se utiliza para ocultar el código de aplicación de los programas informáticos, con el objetivo de dificultar los intentos de ingeniería inversa. A través de diversas técnicas, como la introducción de código redundante, la reorganización de instrucciones y la utilización de saltos condicionales complejos, se busca aumentar la complejidad y opacidad del código.

Si bien la ofuscación de código puede ser útil para proteger la propiedad intelectual y salvaguardar la seguridad de las aplicaciones, también puede ser empleada por hackers criminales para ocultar el código malicioso, como el malware. Por tanto, es importante tener en cuenta los aspectos éticos y legales asociados con estas técnicas.

En última instancia, la ofuscación de código es una herramienta que requiere un uso responsable y equilibrado. Los desarrolladores deben evaluar cuidadosamente los beneficios y riesgos asociados, teniendo en cuenta la protección de la propiedad intelectual, la seguridad y la transparencia del software que desarrollan.

Mi Carro Close (×)

Tu carrito está vacío
Ver tienda