Introducción al Reversing – 0xD Análisis de código con Ghidra

Buenas a todos! En esta sección analizaremos el código de un crackme muy sencillo usando el decompile de Ghidra, sin necesidad de tener que mirar código ensamblador. La opción de poder analizar el código nos ofrece la oportunidad de obtener una versión «casi» idéntica por no decir igual, del código fuente en C. Del mismo modo resolveremos un crackme sin necesidad de realizar un análisis dinámico del mismo.

Análisis estático

Comenzamos desde el principio. Por salida estándar nos imprime por pantalla una string y seguidamente usando la función fgets nos pedirá un input de entrada. Se almacenara en este puntero &DAT_00104040. Usando la función strnlen obtiene el size del input. Si es distinto al size 0x2b el programa se saldrá e imprimirá por pantalla «Wrong. You need to work it».

La primera función toma el complemento de cada carácter:

Posteriormente hay 2 funciones más. Itera sobre el input y hace algunas operaciones de bits en cada char. Luego invierte la cadena al final de la función. Este sería el formato para un total de 43 caracteres:

DawgCTF{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}

Una vez se realiza la transformación usando el algoritmo anterior, se procede a la comparación.

El puntero &DAT_00104040 es nuestro input más la transformación que se realiza en la función donde se localiza el algoritmo. Eso tiene que coincidir con PTR_DAT_00104010, que es sólo un puntero a DAT_00102008.

El data cifrado es lo siguiente:

"\xdd\x79\x11\x19\x3d\xd5\x9d\x21\x8b\xd1\x6d\x59\x31\xb1\x59\x91\x59\xb5\xd9\x89\x7d\xd1\x6d\xf1\x69\xc9\x9d\xcb\x89\x11\x09\xdd\x19\x89\x69\xe9\xd5\x61\x4d\xd1\x51\xf5\x41"

Creación de un solver en C

Finalmente ya entendido todo el código del decompile, podemos replicarlo perfectamente en un código escrito en C.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned char byte;

//data encrypted
char *DAT_00102008="\xDD\x79\x11\x19\x3D\xD5\x9D\x21\x8B\xD1\x6D\x59\x31\xB1\x59\x91\x59\xB5\xD9\x89\x7D\xD1\x6D\xF1\x69\xC9\x9D\xCB\x89\x11\x09\xDD\x19\x89\x69\xE9\xD5\x61\x4D\xD1\x51\xF5\x41";

//our input
char input[44] = "DawgCTF{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}";

//array to store our input+encrypted
char DAT_00104040[44] = {0};

//all printable chars
char *chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";


void encrypt_char(int local_18, const char foo)
{
        byte local_1b;
        uint local_14;

	//takes the complement of printable char and store in array index
        DAT_00104040[local_18] = ~foo;
        local_1b = 0;
        local_14 = 0;
	//take exactly algoritm code from Ghidra decompile
        while (local_14 < 8) {
                if (((byte)(1 << ((byte)local_14 & 0x1f)) & DAT_00104040[local_18]) != 0) {
                        local_1b = local_1b | (byte)(1 << (7 - (byte)local_14 & 0x1f));
                }
                local_14 = local_14 + 1;
        }
        DAT_00104040[local_18] = local_1b;
        return;
}

int main() {
	//size of printable chars
	int size = strlen(chars);
	//copy input to array
	strcpy(DAT_00104040,input);
	//loop iteration in all input chars
	for (int i = 0; i <= 42; i++) {
		//loop interation in all printable input
                for (int x = 0; x < size; x++) {
			//get printable char using index
                        char brute = chars[x];
                        encrypt_char(i, brute);
			//check if input encrypted is the same that data encrypted
                        if (DAT_00104040[i] == DAT_00102008[i]) {
				//store printable char to our input array
                                input[i] = brute;
                                printf("%s\n",input);
                                break;
                        }
                }
        }
	return 0;

}

El resultado es la obtención de la password del crackme. Con esta metodología podemos siempre en este tipo de crackmes, definidos como, una password como input que se cifra o realiza una serie de transformaciones en función de un algoritmo y luego es comparado con un data que esta cifrado y hardcodeado en la sección .data). Al usar el decompile de Ghidra y replicarlo fácilmente en C, podemos resolverlo de manera muy intuitiva y rápida. Si tienen alguna duda o quieren el crackme, podéis contactar conmigo via telegram, un saludo @naivenom.