Introducción al Reversing – 0x11 Algoritmo descifrado básico de una función

En este apartado del curso de Reversing tenemos un algoritmo básico de descifrado de una función en C. El binario es un ejecutable ELF. Es interesante siempre una vez aprendido a interpretar código ensamblador de la sintaxis de Intel, ver el pseudocódigo en C y realizar análisis estático del mismo para poder escribir nuestro script y ahorrar tiempo. Siempre en caso de duda usar el debugger GDB o r2.

Es de vital importancia el input, donde nosotros tenemos el control de la aplicación, ya que el descifrado se realiza según el input que nosotros introduzcamos.

IDA – entropy.i64 (entropy) D:\re\Entropy\entropy.i64 __int64 __fastcall main(int a1, char **input, char **a3) { char crypted_byte; // bl int i; // [rsp+14h] [rbp-2Ch] __int64 input_transf[5]; // [rsp+18h] [rbp-28h] BYREF input_transf[2] = __readfsqword(0x28u); puts(input[1]); input_transf[0] = *input[1] & 0xFFFFFFFFFFFFLL; printf("%lu\n", input_transf[0]); for ( i = 0; i <= 99; ++i ) { crypted_byte = crypted_func[i]; crypted_func[i] = crypted_byte ^ func_input_transf(input_transf); } if ( input_transf[0] == 0xFD94E6E84A0ALL ) puts("looking good"); input_transf[1] = crypted_func; (*crypted_func)(); return 0LL; }

Se descifra 100 bytes correspondiente a la función que finalmente se llamará. Pero la comprobación con el valor 0xFD94E6E84A0A es con nuestro input, transformado 100 veces por la función func_input_transf que realiza la siguiente operación aritmética:

IDA – entropy.i64 (entropy) D:\re\Entropy\entropy.i64 __int64 __fastcall func_input_transf(__int64 input_transf) { *input_transf = (0x5DEECE66DLL * *input_transf + 11) & 0xFFFFFFFFFFFFLL; return *input_transf; }

Si después de 100 veces de transformaciones la comprobación es correcta con el valor hardcodeado antes mencionado, entonces se realizará de manera correcta el descifrado de la función final crypted_func. Podemos usar Z3 para resolver este problema, si obtenemos el valor en decimal input_transf cuando sea igual a 0xFD94E6E84A0A, entonces tendremos el input esperado. El input_transf sera un valor en decimal, y en little endian, del valor en hexadecimal que vemos en el debugger.

z3 solver

[x = 79427706059334] es lo mismo que 0x483d34347a46, correspondiente a H=44zF. Como es little endian el input es: Fz44=H. Entonces sacamos de conclusión que la ‘password’ para poder descifrar la función que llamará al final es la antes mencionada. Es muy típico en malware, tener cosas parecidas a esta, descifrar una función en memoria y llamarse una vez descifrada.

Finalmente usando el debugger, tenemos la función descifrada:

Inside crypted_func