Buenas tardes.
Sigo haciendo progresos con este tema.
La función de cifrado de archivos lo que hace es leer el fichero, desordena los bytes del buffer y luego los cifra con CryptEncrypt.
int32_t __convention("regparm") encriptarBuffer(int32_t arg1, uint32_t arg2, uint32_t arg3, void* arg4, void* arg5)
{
int32_t __saved_ebp;
int32_t eax_1 = (__security_cookie ^ &__saved_ebp);
if (data_479b08_registrar_mensajes_depuracion != 0)
{
escribirLog(u"GenKey");
}
uint8_t* pbBuffer = (arg4 + 0x68);
int32_t pdwDataLen = 0x40;
if ((CryptGenRandom(arg3, 0x20, pbBuffer) != 0 && CryptGenRandom(arg3, 0x20, (arg4 + 0x48)) != 0))
{
int32_t i_4 = 0x40;
char* eax_6 = (arg4 + 8);
int32_t i;
do
{
*eax_6 = 0;
eax_6 = &eax_6[1];
i = i_4;
i_4 = (i_4 - 1);
} while (i != 1);
*(arg4 + 0x18) = *pbBuffer;
*(arg4 + 0x1c) = *(pbBuffer + 4);
*(arg4 + 0x20) = *(pbBuffer + 8);
*(arg4 + 0x24) = *(pbBuffer + 0xc);
*(arg4 + 0x28) = *(pbBuffer + 0x10);
*(arg4 + 0x2c) = *(pbBuffer + 0x14);
*(arg4 + 0x30) = *(pbBuffer + 0x18);
*(arg4 + 0x34) = *(pbBuffer + 0x1c);
__builtin_strncpy((arg4 + 8), "expand 32-byte k", 0x10);
*(arg4 + 0x38) = 0;
*(arg4 + 0x3c) = 0;
*(arg4 + 0x40) = *(arg4 + 0x48);
*(arg4 + 0x44) = *(arg4 + 0x4c);
*(arg5 + 0xc) = *pbBuffer;
*(arg5 + 0x1c) = *(pbBuffer + 0x10);
*(arg5 + 0x2c) = *(arg4 + 0x48);
*(arg5 + 0x3c) = *(arg4 + 0x58);
CryptEncrypt(arg2, 0, 1, 0, (arg5 + 0xc), &pdwDataLen, 0x100);
}
int32_t i_3 = 0x20;
int32_t i_1;
do
{
*pbBuffer = 0;
pbBuffer = &pbBuffer[1];
i_1 = i_3;
i_3 = (i_3 - 1);
} while (i_1 != 1);
int32_t i_5 = 0x20;
void* eax_18 = (arg4 + 0x48);
int32_t i_2;
do
{
*eax_18 = 0;
eax_18 = (eax_18 + 1);
i_2 = i_5;
i_5 = (i_5 - 1);
} while (i_2 != 1);
if (data_479b08_registrar_mensajes_depuracion != i_5)
{
escribirLog(u"return GenKey");
}
@__security_check_cookie@4((eax_1 ^ &__saved_ebp));
return 1;
}
Arg2 es el handler de la clave publica RSA de 2048 bit que ha generado.
Se da la circunstancia de que la clave privada RSA de 2048 bit también se desordena y se cifra.
Este fragmento a continuación está dentro de un bucle que hace algo parecido, revuelve y cifra.
data_47a328: Este es el handle a la clave de cifrado que se usará para cifrar los datos.
0: Este es el handle al hash, que en este caso es 0, indicando que no se está usando ningún hash.
1: Este es el valor booleano que indica si este es el último bloque de datos a cifrar. 1 (TRUE) significa que sí, es el último bloque.
0: Este es el valor para las opciones adicionales. 0 significa que no se están utilizando opciones adicionales.
i: Este es el puntero a los datos que se van a cifrar. La clave privada RSA.
&var_c: Este es el puntero a la longitud de los datos que se están cifrando y se actualizará con la longitud de los datos cifrados.
0x100: Este es el tamaño del búfer que contiene los datos. En este caso, es de 256 bytes (0x100 en hexadecimal).
*/
eax_18 = CryptEncrypt(data_47a328, 0, 1, 0, i, &var_c, 0x100);
Ahora viene lo interesante data_47a328 en el código ensamblador se corresponde con un registro dword_47A328 que inicialmente aparece como ? en el visor IDA Pro. Es decir, que según parece no está inicializado.
El ransom, tiene detector de depuradores mediante IsDebuggerPresent, lo que hacía que cuando empezara a depurar, terminara con exit code 1. Tras consultar documentación le he implementado un bypass para continuar con el flujo de ejecución y he podido acceder al registro o dirección de memoria.
Obtengo esto en IDA Pro. ¿Me podéis ayudar a interpretarlo?
Por otra parte, consultando la documentación de Microsoft se dice:
Identificador de la clave de cifrado. Una aplicación obtiene este identificador mediante la función CryptGenKey o CryptImportKey .
Esta variable efectivamente aparece en el siguiente bloque de código con CryptImportKey.
/*
Hex:
ddee7277d2930aed39d89da731c8105bd8ae0e8e5589b8a1639a2a431f258728721ad409fa3d4d04fb9559edbfeba4f43c10b55f0464
b3b7c12e4b49fa90bccf3491d4000ef8ab20b1983234aa43da5384338fb3010b357aa41a2f0d64da0e9eb3cdf0a19f834e8a67ef1fdc
fd59fc7fb505434b6b2401201c3b80b8
124 bytes / 992 bits
No hay mas referencias a var_8c
*/
__builtin_memcpy(&var_8c, "\xde\xde\x72\x77\xd2\x93\x0a\xed\x39\xd8\x9d\xa7\x31\xc8\x10\x5b\xd8\xae\x0e\x8e\x55\x89\xb8\xa1\x63\x9a\x2a\x43\x1f\x25\x87\x28\x72\x1a\xd4\x09\xfa\x3d\x4d\x04\xfb\x95\x59\xed\xbf\xeb\xa4\xf4\x3c\x10\xb5\x5f\x04\x64\xb3\xb7\xc1\x2e\x4b\x49\xfa\x90\xbc\xcf\x34\x91\xd4\x00\x0e\xf8\xab\x20\xb1\x98\x32\x34\xaa\x43\xda\x53\x84\x33\x8f\xb3\x01\x0b\x35\x7a\xa4\x1a\x2f\x0d\x64\xda\x0e\x9e\xb3\xcd\xf0\xa1\x9f\x83\x4e\x8a\x67\xef\x1f\xdc\xfd\x59\xfc\x7f\xb5\x05\x43\x4b\x6b\x24\x01\x20\x1c\x3b\x80\xb8", 0x7c);
// pbData = 0x206
if (calcularCRC32(&pbData, 0x114) != 0x4401dddf)
{
escribirLog(u"KEY ERROR #100");
Sleep(0x4b0);
ExitProcess(1);
/* no return */
}
/*
data_478108_manejador_servicios_criptograficos: Es probable que sea un manejador (handle) para el contexto del proveedor de servicios criptográficos (CSP).
&pbData: Apunta a la clave a ser importada. pbData contiene la clave en formato binario.
0x114: Es el tamaño de la clave en bytes (276 bytes en decimal).
0: Este parámetro podría ser el identificador de una clave pública o privada que se va a usar para la importación. En este caso, se pasa 0, indicando que no se usa ninguna clave adicional para el proceso.
0: Bandera de opciones adicionales, en este caso se pasa 0, lo que indica que no hay opciones adicionales.
&data_47a328: Apunta a una variable que recibirá el manejador de la clave importada.
*/
if (CryptImportKey(data_478108_manejador_servicios_criptograficos, &pbData, 0x114, 0, 0, &data_47a328) == 0)
{
escribirLog(u"CryptImportKey failed!!");
@__security_check_cookie@4((eax_1 ^ &__saved_ebp));
return 0;
}
Lo que viene a decir que data_47a328 se genera a través de CryptImportKey y a su vez se carga de pbData.
pbData es manipulado por esta función:
int32_t __fastcall calcularCRC32(int32_t arg1, int32_t arg2)
{
// Inicializar eax a -1 (0xFFFFFFFF)
int32_t eax = 0xffffffff;
// Puntero para iterar sobre los datos
void* esi = nullptr;
// Si la longitud de los datos (arg2) no es cero, proceder con el cálculo de CRC
if (arg2 != 0)
{
do
{
// Cargar un byte de los datos
uint32_t edx = *(esi + arg1); // Leer un byte de los datos
esi = (esi + 1); // Avanzar el puntero
// Actualizar el valor de CRC
int32_t eax_1 = (eax ^ edx);
int32_t ecx_4 = ((-((eax_1 & 1)) & 0xedb88320) ^ (eax_1 >> 1));
int32_t eax_7 = ((-((ecx_4 & 1)) & 0xedb88320) ^ (ecx_4 >> 1));
int32_t ecx_10 = ((-((eax_7 & 1)) & 0xedb88320) ^ (eax_7 >> 1));
int32_t eax_13 = ((-((ecx_10 & 1)) & 0xedb88320) ^ (ecx_10 >> 1));
int32_t ecx_16 = ((-((eax_13 & 1)) & 0xedb88320) ^ (eax_13 >> 1));
int32_t eax_19 = ((-((ecx_16 & 1)) & 0xedb88320) ^ (ecx_16 >> 1));
int32_t ecx_22 = ((-((eax_19 & 1)) & 0xedb88320) ^ (eax_19 >> 1));
eax = ((-((ecx_22 & 1)) & 0xedb88320) ^ (ecx_22 >> 1));
} while (esi < arg2);
}
// Retornar la negación del valor final de CRC
return !(eax);
}
En definitiva. ¿Me ayudáis a juntar las piezas? Creo que con esto se puede obtener la clave que cifra la clave RSA privada.
Gracias.