¿Alguna vez has sentido el deseo de desmontar algún mecanismo para saber cómo funciona? Pues quién no lo ha hecho. Ese deseo es el protagonista de la ingeniería inversa. Esta habilidad es útil para analizar la seguridad de un producto, averiguar el propósito de un archivo .exe sospechoso sin ejecutarlo, recuperar documentación perdida, desarrollar una nueva solución basada en software heredado, etc.
En este artículo, discutimos la base de conocimientos necesaria para realizar ingeniería inversa, los principios básicos de la ingeniería inversa de una pieza de software de Windows, desensambladores y herramientas. También proporcionamos un ejemplo paso a paso de la ingeniería inversa de una aplicación.
Escrito por
Sergii Bratus,
Coordinador de Desarrollo,
Equipo de Seguridad de Redes
y
Anton Kukoba,
Líder de Investigación en Seguridad
Contenidos
¿Qué es la inversión de software?
¿Qué necesitamos para la ingeniería inversa?
Conocimientos teóricos. Proceso de ingeniería inversa del software
Herramientas útiles para la ingeniería inversa del software de Windows
Desmontadores
Sistemas internos de Windows
Herramientas de monitorización de red
Debuggers
Real-vida real de ingeniería inversa de software
Cómo hacer ingeniería inversa de un driver
Conclusión
- ¿Qué es la inversión de software?
- ¿Qué necesitamos para hacer ingeniería inversa?
- Conocimientos teóricos. Proceso de ingeniería inversa de software
- Herramientas útiles para la ingeniería inversa de software de Windows
- Desensambladores
- Windows Sysinternals
- Herramientas de monitorización de red
- Depuradores
- Ejemplo de ingeniería inversa de software en la vida real
- Cómo hacer ingeniería inversa de un driver
- Conclusión
¿Qué es la inversión de software?
La ingeniería inversa es el proceso de descubrir los principios detrás de una pieza de hardware o software, como su arquitectura y estructura interna. La pregunta que impulsa la ingeniería inversa es ¿Cómo funciona?
Obviamente, si se tiene documentación, todo el proceso se vuelve mucho más sencillo. Pero a menudo ocurre que no hay documentación y hay que encontrar otra forma de aprender cómo funciona un software.
¿Cuándo podría necesitar hacer ingeniería inversa de un software y cómo podría ayudarle hacerlo?
Hay muchos usos de la ingeniería inversa en el campo de la informática, entre ellos:
- Investigar protocolos de comunicación de red
- Encontrar algoritmos utilizados en el malware, como virus informáticos, troyanos, ransomware, etc.
- Investigar el formato de archivo utilizado para almacenar cualquier tipo de información, por ejemplo, bases de datos de correos electrónicos e imágenes de disco
- Comprobar la capacidad de su propio software para resistir la ingeniería inversa
- Mejorar la compatibilidad del software con plataformas y software de terceros
- Utilizar características no documentadas de la plataforma
La legalidad de la ingeniería inversa depende de su propósito y de cómo se utilizará el software. Todos los propósitos mencionados anteriormente son completamente legítimos, suponiendo que se haya obtenido una copia del software legalmente. Pero si pretende, por ejemplo, realizar ingeniería inversa de una determinada característica de una aplicación cerrada y luego implementarla en otra aplicación, probablemente tendrá problemas.
En cuanto a la documentación legal, la ingeniería inversa suele estar prohibida por los acuerdos de licencia de usuario final (EULA). Pero la Ley de Derechos de Autor del Milenio Digital de EE.UU. especifica que revertir una pieza de software es legal si se hace para mejorar la compatibilidad con otros productos.
Los requisitos legales varían de un país a otro, así que tómese su tiempo para investigarlos antes de empezar.
Ahora vamos a ver cómo hacer ingeniería inversa de software.
¿Qué necesitamos para hacer ingeniería inversa?
Para empezar a hacer ingeniería inversa de software, necesitas:
- conocimientos en el campo en el que quieres aplicar la ingeniería inversa
- herramientas que te permitan aplicar tus conocimientos mientras intentas desensamblar el software.
Consideremos un ejemplo genérico que no está relacionado con el software. Digamos que tienes un reloj y quieres averiguar si es mecánico, de cuarzo o automático.
Tener conocimientos en la materia significa que deberías saber que hay tres tipos de relojes. Además, debes saber que si hay una pila, ésta se encuentra dentro del reloj, y puedes verla si lo abres. También debe tener conocimientos básicos sobre la estructura interna de un reloj, el aspecto de la pila y las herramientas necesarias para abrir la caja de un reloj. Tener las herramientas para aplicar sus conocimientos significa que necesita tener un destornillador u otra herramienta dedicada que le dará la oportunidad de abrir el reloj.
Al igual que la ingeniería inversa de un reloj requiere un conjunto de habilidades y herramientas específicas, la ingeniería inversa de software requiere su propio conocimiento y herramientas específicas del campo.
Conocimientos teóricos. Proceso de ingeniería inversa de software
Para diferentes tareas de ingeniería inversa de software, se necesitan diferentes tipos de conocimiento. Por supuesto, hay conocimientos comunes que le ayudarán en la mayoría de las tareas de ingeniería inversa: conocimiento de estructuras de aplicaciones comunes, lenguajes de programación, compiladores, etc. Sin embargo, sin conocimientos teóricos especiales, no podrás resolver tareas específicas de ingeniería inversa.
Si… |
Necesitas conocimientos de… |
Ingeniería inversa de cualquier aplicación de red |
principios de las comunicaciones entre procesos, la estructura de las redes, las conexiones, los paquetes de red, etc. |
rever los algoritmos criptográficos |
la criptografía y los algoritmos más populares utilizados en este campo |
investigar las estructuras |
conceptos básicos de los archivos y cómo los diferentes sistemas o componentes trabajan con ellos |
Técnicas especiales pueden ahorrar mucho tiempo al revertir tipos especiales de software. En el caso de las interacciones de archivos, hacer una prueba que escriba valores de tipo único en un archivo mientras registra los desplazamientos y el tamaño de los datos en el archivo de almacenamiento real puede ayudarle a encontrar patrones comunes en los desplazamientos. Esto le dará una pista sobre las estructuras internas de estos archivos.
Cuando se inicia un proceso de ingeniería inversa, los desarrolladores de software generalmente utilizan un desensamblador con el fin de encontrar los algoritmos y la lógica del programa en su lugar. Existen muchos formatos diferentes de archivos ejecutables, compiladores (que dan diferentes resultados) y sistemas operativos. Esta diversidad de tecnologías impide el uso de una única tecnología para revertir todos los tipos de software.
Para entender el código descompilado, es necesario conocer el lenguaje de ensamblador, las convenciones de llamada a funciones, la estructura de la pila, el concepto de marcos de pila, etc.
Conocer la salida del ensamblador para diferentes muestras de código puede ayudar a descubrir la funcionalidad original. Consideremos algunos ejemplos para la plataforma Windows x86.
Digamos que tenemos el siguiente código:
Si compilamos este código a un archivo ejecutable, veremos esto en el desensamblador:
Como podemos ver, el ciclo regular se convirtió en código ensamblador con comparaciones y saltos. Observe que el código ensamblador no utiliza el bucle ensamblador regular con el contador en el registro ecx. Además, las variables locales aquí son referidas como y en consecuencia.
Veamos qué ocurrirá si compilamos este código usando la compilación de liberación:
Este trozo de código no se parece en nada al anterior. Esto se debe a cómo se optimizó el código. Técnicamente, el bucle fue eliminado, ya que no está haciendo nada valioso aparte de incrementar la variable count a 10. Así que el optimizador decidió simplemente mantener el valor final de la variable count y colocar el valor directamente como argumento para el operador de salida count.
Los compiladores que utilizamos hoy en día son muy buenos en la optimización de código. Por eso, a la hora de hacer ingeniería inversa, es mejor entender la idea que hay detrás del código (los principios del código) que intentar conseguir el código original en sí. Si entiendes la idea detrás del código, puedes simplemente escribir tu propio prototipo que se ajuste a la tarea original.
Será muy útil saber qué código ensamblador obtendrás si compilas diferentes operadores, estructuras y otras construcciones del lenguaje. Entender el código ensamblador resultante es una buena manera de empezar el proceso de ingeniería inversa de C++, pero no entraremos en detalles técnicos de ello aquí.
Herramientas útiles para la ingeniería inversa de software de Windows
Ya hemos descrito varias herramientas de ingeniería inversa, incluyendo ProcessMonitor y ProcessExplorer, en nuestra investigación sobre la arquitectura de aplicaciones. Estas herramientas son absolutamente indispensables para la ingeniería inversa.
En esta sección, revisaremos los desensambladores más populares y algunas herramientas más que utilizamos para nuestros proyectos de ingeniería inversa.
Puedes obtener más detalles y ejemplos de uso en nuestro artículo sobre las mejores herramientas de ingeniería inversa de software.
Desensambladores
Un desensamblador es un programa que traduce un archivo ejecutable a lenguaje ensamblador. El más popular es IDA Pro
IDA Pro
IDA Pro
IDA Pro es una cómoda y potente herramienta de desensamblaje. Tiene un gran número de instrumentos que le permiten desensamblar rápidamente una pieza de software. Puede mostrar el árbol de llamadas de función, analizar la importación y exportación del ejecutable, y mostrar información sobre ellos. Incluso puede mostrar el código en C. Además, soporta múltiples arquitecturas de CPU, por lo que es posible utilizar IDA Pro para realizar ingeniería inversa de código para ARM, AVR, M68k, y muchas otras arquitecturas.
Radare
Radare
El desensamblador Radare es una alternativa a IDA. Básicamente tiene todas las características de IDA sin ser tan robusto y estable. Pero es gratuito y de código abierto. Radare en sí es una herramienta de consola, pero tiene un frontend de Cutter, lo que lo convierte en una verdadera alternativa a IDA.
Windows Sysinternals
Las utilidades de Windows Sysinternals se utilizan generalmente para la gestión, el diagnóstico, la solución de problemas y la supervisión del entorno de Microsoft Windows. Pero también son adecuadas para la ingeniería inversa del software de Windows.
TCPView es un sniffer de red que muestra toda la información sobre los paquetes TCP/UDP de todos los procesos. Esta herramienta es útil para revertir protocolos de red.
PortMon es un monitor de puertos del sistema físico. Monitoriza los puertos serie y paralelo y todo el tráfico que pasa por ellos.
WinObj muestra todos los objetos globales del sistema en una estructura jerárquica. Esta herramienta puede ser útil cuando se revierte una aplicación que trabaja con primitivas de sincronización como mutexes y semáforos y también cuando se hace ingeniería inversa de controladores en modo kernel.
Herramientas de monitorización de red
Wireshark
Wireshark
Wireshark es uno de los sniffers de red más potentes. No sólo permite capturar el tráfico de red, sino que también contiene analizadores para varios protocolos de red, empezando por los de muy bajo nivel, como Ethernet, TCP e IP, hasta los protocolos específicos de las aplicaciones, como WebSockets y XMPP.
Fiddler
Fiddler
Fiddler es un proxy web que registra el tráfico de los navegadores y permite analizar las peticiones HTTP/HTTPS. A diferencia de Wireshark, muestra sesiones HTTP en lugar de paquetes de red separados. Fiddler también permite analizar datos comprimidos enviados a través de HTTP y analizar datos JSON y XML cuando se monitorizan peticiones SOAP, REST y AJAX.
API Monitor
API Monitor
API Monitor es una herramienta útil para descubrir qué APIs son llamadas por una aplicación y qué comportamiento espera la aplicación de esas APIs. Esta herramienta cuenta con una potente base de datos y permite ver las llamadas a un gran número de funciones API no sólo de kernel32 y ntdll, sino también de COM, entorno gestionado y otros. Además, API Monitor proporciona convenientes mecanismos de filtrado.
Depuradores
Un depurador es inestimable para que cualquier desarrollador pueda ver lo que un programa está haciendo en este momento. Se obtiene el mismo beneficio de la depuración al revertir aplicaciones que al depurar aplicaciones en vivo.
Los depuradores más populares son OllyDbg, WinDbg y Windbg Preview.
OllyDbg
OllyDBG
OllyDbg (y su sucesor x64dbg) es probablemente el mejor depurador cuando se trata de ingeniería inversa de software. Fue desarrollado específicamente para las necesidades de reversión, y tiene todas las herramientas necesarias para ese propósito:
- un desensamblador incorporado con la capacidad de analizar e identificar las estructuras de datos clave
- una función de análisis de importación y exportación
- un motor de ensamblaje y parcheo incorporado
La capacidad de analizar las funciones de la API y sus parámetros facilita las interacciones inversas con un sistema. La vista de la pila proporciona mucha información sobre la pila de llamadas. Una ventaja más importante es que puede utilizar OllyDbg con aplicaciones protegidas contra la depuración, cuando los depuradores habituales no pueden hacer nada.
WinDbg
Windbg
A pesar de su sencilla interfaz, WinDbg tiene potentes herramientas para la depuración. Tiene un desensamblador incorporado, varios comandos que le permiten saber casi todo sobre el proceso/sistema que está depurando, y la capacidad de hacer depuración en modo kernel, que es probablemente la característica más valiosa. Es una gran ventaja para revertir controladores, controladores en modo kernel en particular.
Windbg Preview
Windbg Preview
Windbg Preview es una nueva versión de Windbg desarrollada por Microsoft. Se distribuye únicamente a través de la Windows Store. Tiene todas las características del Windbg clásico junto con una nueva interfaz de usuario y varias características nuevas. Una de estas nuevas características es el Time Travel Debugging, que permite grabar algún periodo de la ejecución del programa y luego reproducirlo tantas veces como sea necesario. De esta manera, puedes ejecutar las partes interesantes del código por pasos, sin tener miedo de ejecutar algún código accidentalmente y perder el contexto o todos los datos.
Lee también:
9 mejores herramientas de ingeniería inversa para 2018
Ejemplo de ingeniería inversa de software en la vida real
Ahora veremos un ejemplo de cómo hacer ingeniería inversa a una pieza de software. Imaginemos que tienes un archivo ejecutable sospechoso. Necesitas averiguar qué hace este programa y si es seguro para los usuarios.
Considerando el escenario, es una buena idea no ejecutar este ejecutable en tu ordenador de trabajo, sino utilizar una máquina virtual. Vamos a iniciar la aplicación en nuestra máquina virtual.
El proceso crea un servicio
Como podemos ver, este archivo crea un servicio de Windows llamado TestDriver. Tiene el tipo kernel, por lo que sabemos que es un driver. Pero, ¿de dónde toma el archivo del driver para ejecutarse? Podemos utilizar ProcessMonitor de Sysinternals Suite para averiguarlo. Cuando abrimos ProcessMonitor, podemos configurar filtros para que nos muestre sólo la actividad de los archivos del proceso que nos interesa. Su registro de actividad tiene el siguiente aspecto:
Información de FileMon
El archivo del controlador es creado por el proceso que estamos invirtiendo, y este proceso pone este archivo en el directorio temporal del usuario. No hay necesidad de buscar el archivo en la carpeta temporal ya que vemos que el proceso lo borra justo después de su uso. Entonces, ¿qué hace el proceso con este archivo? Si desempaqueta el archivo, podemos intentar encontrarlo en la sección de recursos del proceso, ya que este es un lugar común para almacenar tales datos. Busquemos allí. Usaremos otra herramienta – Resource Hacker – para examinar los recursos. Ejecutémosla:
Examine los recursos con Resource Hacker
¡Bingo! Como podemos ver en el contenido de los recursos encontrados, este es probablemente el archivo ejecutable de Windows, ya que comienza con una firma MZ y tiene la cadena «Este programa no se puede ejecutar en modo DOS.» Comprobemos si se trata de nuestro archivo de controladores. Para ello, extraemos el recurso utilizando Resource Hacker y lo abrimos en el desensamblador.
Pantalla del desensamblador
Como sabemos, DriverEntry es el punto de entrada de los drivers en modo kernel en los sistemas Windows. Podemos seguir investigando, ya que parece que hemos encontrado el driver adecuado.
Cómo hacer ingeniería inversa de un driver
Para empezar a hacer ingeniería inversa del driver, examinamos las funciones que se llaman desde DriverEntry una por una. Si vamos al sub_14005, no encontramos nada interesante, así que continuamos con el sub_110F0 y encontramos este código:
Pieza de código 1
Pieza de código 2
Pieza de código 3
Pieza de código 4
Aquí se omiten algunas líneas para simplificar.
En el primer listado, se crea una cadena unicode, y esta cadena apunta a la ruta C:\hello.txt. Después, la estructura OBJECT_ATTRIBUTES se rellena con valores normales; sabemos que esta estructura suele ser necesaria cuando se llaman funciones como ZwCreateFile.
En el segundo listado, vemos que efectivamente se llama a ZwCreateFile, lo que nos hace estar bastante seguros de que el controlador crea el fichero – y sabemos dónde se encuentra este fichero después de ser creado.
Desde el tercer y cuarto listado, podemos ver que el driver toma la cadena unicode y la escribe en el buffer (esto ocurre en la función sub_11150), y el buffer se escribirá en el fichero usando la función ZwWriteFile. Al final, el controlador cierra el archivo usando la API ZwClose.
Resumamos. Descubrimos que el programa original extrae el archivo del controlador de sus recursos, lo pone en la carpeta temporal del usuario actual, crea el servicio de Windows para este controlador y lo ejecuta. Después, el programa se detiene y elimina el servicio y el archivo del controlador original del directorio temporal. De este comportamiento y del análisis del desensamblaje, parece que el driver no hace nada excepto crear un archivo en la unidad C llamado hello.txt y escribir la cadena «Hello from driver».
Ahora tenemos que comprobar si estamos en lo cierto. Ejecutemos el programa y comprobemos la unidad C:
Pantalla de aplicación
¡Maravilloso! Hemos hecho ingeniería inversa de este sencillo programa y ahora sabemos que es seguro utilizarlo.
Podríamos haber conseguido este resultado de muchas maneras diferentes: utilizando la depuración o la API Mon, escribiendo pruebas, etc. Puedes encontrar tus propias formas de realizar ingeniería inversa de software que te funcionen.
Conclusión
La ingeniería inversa de software de Windows requiere una sólida formación educativa y experiencia en programación. Para realizar ingeniería inversa, hay que combinar habilidades de desensamblaje, monitorización de redes, depuración, integración de API, varios lenguajes de programa, compiladores, etc. También hay que tener mucho cuidado al revertir el software para no romper las leyes de derechos de autor o dañar el sistema.
En Apriorit, tenemos un equipo experimentado de ingenieros inversos. Si desea aplicar las habilidades de ingeniería inversa a su proyecto, no dude en ponerse en contacto con nosotros.