"Binary Diffing" visual en Linux con Radare2

Binary Diffing o simplemente Bindiffing es una técnica utilizada para la visualización de código (mayormente en ensamblador) y así detectar patrones de conducta, así como también diferentes rutas de ejecución después de una modificación a un programa original, como una actualización o un parche de seguridad. Esta técnica es actualmente utilizada por muchos reverse engineers para identificar muchísimas cosas, malware, parches de seguridad mal implementados, backdoors, cambios en actualizaciones en software, cracking, y muchas cosas más.

"UNA IMAGEN DICEN MÁS QUE MIL PALABRAS"

Y bien, este blog post es básicamente una demostración básica del análisis visual para identificar diferencias entre binarios ELF en Linux utilizando Radare2. Esta es una gran herramienta para ingeniería inversa, e incluye un gran set de otras herramientas como un debugger, editor hexadecimal, etc.

Para las pruebas hice el siguiente código en C, el cual consta de simples comparaciones (if-else), un ciclo for, incremento de variables y un par de funciones de libc:
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
    if(strtol(argv[1], NULL, 16) == 0x41414141)
        printf("FOO\n");
    else if(strtol(argv[1], NULL, 16) == 0x42424242)
        printf("BAR\n");
    else {
        int i = 0xdead;

        for(int k = 0; k < i; k++)
            k++;

        printf("0x%x\n", i);
    }

    exit(0);
}
Compilamos con $gcc if.c -o if


Una vez descargado Radare2, es posible ver el listado convencional de código ensamblador de nuestro binario recién creado con $radare2 ./if y pdf @ sys.main dentro de Radare2:


Ahora, crearemos una copia de nuestro ejecutable con $cp if if2. Posteriormente editaremos una simple instrucción en esamblador para de allí detectar el cambio de forma visual  viendo el flujo del programa. Para este paso utilizaremos el editor HTE, en el cual una vez cargado ($ht ./if2), le damos View - ELF image, buscamos el código referente a la primer comparación contra el valor 0x41414141, y editamos cualquier jmp hacia cualquier otra etiqueta. Este es solo un ejemplo demostrativo e hice que saltara al printf("0x%x\n", i); del final, en la línea 16 del código en C, guardé el archivo y salí a la shell:


En la ejecución, lo único que cambia es lo que está en rojo, cuando se le pasa el parámtro 0x41414141. A diferencia del if normal, este imprime 0x00 ya que saltamos directo al printf() y el for anterior no se ejecutó, por lo tanto la variable que se imprime sigue siendo cero.


Ahora la parte interesante. El bindiffing más básico es ejecutar $radiff2 if if2, dando el offset y la diferencia de bytes, en este caso fue el offset de un simple jmp, así que solo cambió un byte:

Con el parámetro -C se analizan las funciones y se listan para ver si son iguales o no (notar el UNMATCH de la primer línea, correspondiente a la función main() ):


Finalmente, la opción más interesante, -g <función o dirección de memoria> para enviar la salida a formato xdot y poder visualizar las diferencias a través de un gráfico:


Con los comandos xdot ejecutados anteriormente se abrirán dos ventanas mostrando las diferencias:


A partir de aquí, lo interesante es la interpretación gráfica de los bloques de ensamblador. Este es un ejemplo bastante sencillo con pocos bloques de código y es muy fácil de identificar ciertos patrones visuales, por ejemplo:

  • Los bloques marcados en amarillo son los que cambiaron, es la diferencia binaria como tal (bindiff) ya que cambió la dirección a la que el jmp saltará.
  • A lo lejos, en la imagen de arriba, es posible ver que el path de ejecución de enmedio (el de la flecha azul vertical) en la imagen de la izquierda, simplemente se pasó a ser el path de ejecución de la derecha en la imagen de la derecha. Ese es el trozo de código que imprime "BAR". No se modificó en nada.
  •  Lo siguientes es un ciclo for. En el primer bloque de hasta arriba, el de la etiqueta loc.00400605 corresponde a la inicialización de una variable con el valor 0xdead y una variable con 0 (cero, que es el contador del for). De allí entra al bloque del for como tal, en donde se hace la comparación (instrucción cmp) para ver si continuar dentro del for o salir saltando al bloque de código que imprime el valor de la variable incrementada (flecha roja). En caso de seguir con el ciclo for, salta al pequeño bloque con dos instrucciones iguales las cuales suman 1 a la misma variable en la pila. Esta variable en nuestro código en C es k y se incrementa dos veces, en for(..;..; k++) k++;

  • La siguiente captura es la diferencia binaria, el cambio de ejecución. Nótese detenidamente  que en el primer código, del bloque amarillo se salta directamente al código de salida, con la función exit(), mientras que en el código modificado, después del código en amarillo se salta al código que imprime el valor de la variable incrementada después del ciclo for (como no se ejecutó el for, la variable tendrá un valor de cero y por eso nuestro programa en la shell imprime FOO 0x00) y posteriormente al bloque de salida (exit())
  • Por último, en la primer ejecución hay tres formas de llegar al bloque de exit() (el bloque donde dice call dword imp.exit):

mientras que en la segunda solo hay dos paths de ejecución para llegar allí:


Como es posible ver, el bindiffing es una técnica bastante útil para de forma visual identificar ciertos patrones de cambio entre dos binarios.

Espero les haya gustado este pequeño post y ojalá le encuentren provecho.

Happy Bindiffing B-) !

Alejandro.
@nitr0usmx

Comentarios

  1. hola nitrousmx bro una pregunta ...quiero byppasear el x-trap se me esta haciendo algo dificil usas facebook para conversar y explicartelo bien gracias

    ResponderEliminar

Publicar un comentario

Entradas populares