¿Cómo se hace ingeniería inversa a un cliente?

Sí, la ingeniería inversa de software funciona exactamente así: comienza con el binario y termina con una comprensión de cómo funciona (una imagen en su cerebro, una lista de programas en pseudocódigo o en ensamblaje, etc.).

  1. Primero debe comprender el formato de archivo ejecutable para identificar el código que se ejecutará cuando inicie ese binario en su sistema operativo. Afortunadamente, la mayoría absoluta de las herramientas de inversión puede hacerlo automáticamente.
  2. Luego, desmonta el código con un desensamblador adecuado, comenzando en los puntos de entrada identificados dentro de la (s) sección (es) de código del binario ejecutable analizado, y navega por el código de ensamblaje producido tratando de entenderlo.
  3. Después del n. ° 2, está viendo un montón de ensamblaje que tiene poco sentido a menos que encuentre cómo interactúa con el entorno: importaciones y exportaciones como se usan con bibliotecas dinámicas, cadenas de texto que se imprimen o hacen referencia, llamadas a funciones específicas y llamadas al sistema operativo, etc.
  4. Identifica los patrones que se utilizan en estos lugares y crea gráficos de flujo de código (utilizando la funcionalidad del desensamblador o más herramientas externas).
  5. Si es posible, puede usar un descompilador que intente reconstruir el código C / C ++ original de la lista de ensamblados. Sabe cómo funcionan los compiladores modernos y puede adivinar cuál fue el código C que produjo cada secuencia particular de instrucciones. Por supuesto, se pierde mucha información para siempre al compilar C / C ++ para el ensamblaje, y los descompiladores tampoco son perfectos, por lo que la salida del descompilador se verá bastante horrible y es posible que deba mirar el ensamblaje una y otra vez.
  6. En este punto, puede explicar cómo interactúa el programa con el entorno y qué ruta de código se está tomando cuando sucede algo.

Si tiene un binario que no está en el código nativo, sino en el código administrado, los pasos son casi los mismos, pero las herramientas le brindan los listados en lenguaje administrado en lugar de ensamblado (por supuesto, puede echar un vistazo al ensamblaje tal como lo utiliza el máquina virtual de dicho tiempo de ejecución de lenguaje administrado, por ejemplo, código de bytes Java VM o código de bytes .NET IL o código de bytes Python o lo que sea).

A veces, esa traducción también falla, especialmente cuando se utilizan algunas técnicas anti-depuración / anti-inversión para alterar el código de bytes para hacer que la inversión sea más difícil (agregar código basura, torcer el gráfico de flujo de control, transformar las operaciones aritméticas en bloques de equivalentes y / o redundantes, etc.). Por supuesto, estas técnicas se introdujeron inicialmente para el código nativo y son mucho más maduras allí, y se utilizan para proteger tanto el software legítimo como el malicioso, lo que complica mucho el proceso de ingeniería inversa.

Y de la peor manera posible, cuando solo tiene un manual de referencia de conjunto de instrucciones binario y de CPU, comienza leyendo el código binario, decodificando cada instrucción e interpretándola manualmente en su cerebro, a una velocidad de una instrucción por minuto o incluso menos, luego haciendo sus propias herramientas, compartiéndolas con otros, etc. hasta que todos tengan los medios para seguir el enfoque normal.

Normalmente tendría un desensamblador para la mayoría de las arquitecturas conocidas públicamente. Objdump es una parte de la cadena de herramientas GCC que puede ejecutarse en decenas o incluso cientos de arquitecturas, y si le arrojas un binario, te mostrará un código de ensamblaje. La mejor manera es tener un desensamblador interactivo que le permita elegir cómo interpretar cada instrucción ambigua o bloque de instrucciones (concepto promovido por el software comercial IDA hace unos 20 años y seguido recientemente por soluciones gratuitas de código abierto como radare2). Debido al famoso problema de detención de una máquina Turing, no podemos saber de antemano si un programa alguna vez se detendrá y, como resultado, tampoco podemos saber de antemano si algún código de nuestro programa se ejecutará o no. Por lo tanto, se necesita una decisión humana para ayudar al desensamblador en lugares tan difíciles.

Depende del entorno del cliente. Suponiendo que está hablando del entorno de PC moderno (que generalmente son arquitecturas basadas en Intel), el ingeniero inverso generalmente comienza desde los binarios ejecutables (y no ejecutables) (código compilado). Es un código de máquina / ensamblaje al final del día, y herramientas como IDA Pro, etc. pueden ayudar a desmontarlo. Desmontar el entorno de la máquina virtual, como Java y .NET, en realidad es mucho más fácil. ¿De dónde crees que provienen todos estos hacks de licencias / generador de números de serie?