Binary Diffing visual en Linux con Radare2

Anuncio
"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 (ifelse), 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
Descargar