Ingeniería inversa – Parte 1 – Conceptos básicos

Nuestros analistas examinan una gran cantidad de programas todos los días y deciden si son software malintencionado. Para hacer esto, operan lo que se conoce como ingeniería inversa. Pero, ¿qué significa eso y cómo funciona? Damos una idea.

La ingeniería inversa es el intento de reconstruir el plan de construcción subyacente a partir de un objeto terminado. En el caso de la ingeniería inversa de software, normalmente se trata de restaurar el código fuente original a partir de un programa ejecutable. Esto permite z. Por ejemplo, es mejor analizar si un archivo es malware. En una serie de varias partes, proporcionamos una visión fundamental de la ingeniería inversa. Cubrimos tanto los conceptos básicos teóricos como la aplicación práctica de las herramientas correspondientes.

En la primera parte presentamos lo que tiene que suceder en la ingeniería inversa. La atención se centra en los  archivos binarios nativos , es decir, los archivos que se generan especialmente para un sistema operativo y una arquitectura de procesador. Primero, una descripción general de cómo se crea dicho archivo binario.

Lenguaje de alto nivel para código máquina

Toda persona que haya programado algo ha visto el código fuente en uno de los muchos lenguajes de programación. Aquí hay un ejemplo de código simple en el lenguaje de programación C:

El código en un lenguaje de tan alto nivel es fácil de leer y entender para los humanos con un poco de práctica. Pero una computadora realmente no puede hacer nada con ella. Se debe compilar el código del programa. Este propósito lo cumplen los compiladores que convierten el código en lenguaje de máquina, es decir, unos y ceros, y por lo tanto en archivos binarios nativos.  

Si bien el código de lenguaje de alto nivel casi siempre es independiente del hardware en el que se va a ejecutar, ocurre lo contrario con el código de máquina. El código de máquina depende de la arquitectura del procesador. Por ejemplo, un programa que se compiló para un sistema con un procesador ARM (por ejemplo, la mayoría de los teléfonos inteligentes) no se ejecuta en una computadora doméstica “estándar” con un procesador x86. 

También hay compiladores que traducen el código fuente a lenguajes intermedios como el código de bytes de Java. Los archivos compilados de esta manera se pueden ejecutar en diferentes arquitecturas de sistema, siempre que un entorno de ejecución adecuado (por ejemplo, Java Runtime Environment con una máquina virtual Java asociada) esté disponible en el sistema correspondiente.
Esto hace posible ejecutar la misma aplicación en cualquier sistema operativo para el que haya un entorno de ejecución. Esto convierte a Java en un lenguaje de programación multiplataforma que no requiere una aplicación especialmente compilada para cada arquitectura.

Lenguajes de ensamblaje: la capa intermedia a menudo olvidada

Los lenguajes de alto nivel están fuertemente abstraídos del código de máquina y orientados hacia la comprensión humana. Como capa intermedia entre estos dos niveles bastante distantes, están los lenguajes ensambladores relacionados con el hardware. Hay uno separado para cada arquitectura de procesador. La programación de una aplicación directamente en lenguaje ensamblador permite una implementación muy rápida y de alto rendimiento de los comandos respectivos, porque el paso intermedio a través de un compilador, que puede no traducir el lenguaje de alto nivel de manera óptima, no es necesario. Los comandos del programa están en un formato mucho más cercano al hardware. Sin embargo, esto es a expensas de la compatibilidad con otras plataformas. Además, el ensamblador no es fácil de aprender en comparación con uno de los lenguajes de alto nivel como C o Java.

Los lenguajes ensambladores se desarrollaron para no tener que programar más en los códigos numéricos del lenguaje máquina, sino para utilizar un lenguaje más fácil de entender para los humanos. En los lenguajes ensambladores, por ejemplo, existe una breve representación textual de las operaciones a realizar, como “mov” para mover un valor. Esto es mucho más fácil de entender que la representación binaria en lenguaje de máquina (por ejemplo, 10110000) o la representación hexadecimal (por ejemplo, 0xb0).

En la segunda parte de la serie veremos más de cerca los conceptos básicos de los lenguajes ensambladores, por lo que no daremos una explicación más detallada aquí.

Algunos compiladores no traducen el código del lenguaje de alto nivel directamente al código máquina, sino al lenguaje ensamblador. A continuación, un ensamblador lleva a cabo el último paso de traducción. 

El resultado es la siguiente secuencia:

    

Ingeniería inversa

Idealmente, el código fuente para analizar un programa está en un lenguaje de alto nivel. Esto facilita la comprensión de las funcionalidades que contiene el programa y si se trata de malware. Si bien este es particularmente el caso del malware en lenguajes de scripting como PowerShell, nuestros analistas a menudo también tienen que evaluar los archivos compilados. 

Pero, ¿cómo funciona cuando el código de máquina, es decir, los unos y los ceros, es ilegible o incomprensible para los humanos? Afortunadamente, existen herramientas que los analistas pueden utilizar para realizar ingeniería inversa. Por ejemplo, el código de máquina se desmonta, es decir, se vuelve a convertir en lenguaje ensamblador. Algunos programas de análisis utilizan este resultado para descompilar e intentar restaurar el código fuente original en un lenguaje de alto nivel. El resultado final no es el código original, pero da una idea aproximada de cómo podría haber sido, siempre que los autores no usaran la ofuscación para dificultar la ingeniería inversa. Las herramientas intentan revertir el proceso descrito anteriormente y, por lo tanto, simplifican y aceleran el análisis. 

En el siguiente artículo, echamos un vistazo a los conceptos básicos de los lenguajes ensambladores, que forman una base importante para la ingeniería inversa.

Enlace: https://www.gdata.de/blog/reverse-engineering-basics Blog de G DATA