¿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?

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:

  1. conocimientos en el campo en el que quieres aplicar la ingeniería inversa
  2. 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:

int count = 0;for (int i = 0; i < 10; ++i){count++;}std::cout << count;

Si compilamos este código a un archivo ejecutable, veremos esto en el desensamblador:

004113DE loc_4113DE:004113DE mov eax, 004113E1 add eax, 1004113E4 mov , eax004113E7 loc_4113E7:004113E7 cmp , 0Ah004113EB jge short loc_4113F8004113ED mov eax, 004113F0 add eax, 1004113F3 mov , eax004113F6 jmp short loc_4113DE004113F8 loc_4113F8:004113F8 mov ecx, ds:004113FE push eax00411400 call ds:<<(int)00411404 xor eax, eax00411406 retn 

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:

00401000 main proc near00401000 mov ecx, ds:00401006 push 0Ah00401008 call ds:<<(int)0040100E xor eax, eax00401010 retn00401010 main endp

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.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.