/* S21sec Labs - Reto 4 ===================== El pseudocódigo del crackme sería el siguiente : Por un lado se comprueba si el programa está siendo depurado y en función de esto se almacena en una variable un valor u otro : if(!isBeingDebugged()) gDbgStatus = 5 else gDbgStatus = 6 La función isBeingDebugged() está implementada en una entrada TLS, por lo que este valor está inicializado antes de que se comience a ejecutar el Entry Point del programa. El truco antidebug está basado en un recorrido por todos los procesos en ejecución sobre los que se chequea si en la posición de memoria virtual (0x407622) se encuentra la siguiente dword : .data:00403247 8B 47 1C mov eax, [edi+1Ch] .data:0040324A 89 db 89h No lo he comprobado ya que he analizado todo el programa en estático, pero supongo que estos bytes se encontrarán presentes en algún debugger conocido (Ollydbg maybe?) Por otro lado desde la GUI se obtienen dos valores enteros, de manera que la clave va a ser una combinación del tipo ( int_1 - int_2 ). int_1 = GetDlgItemInt(a1, 1001, 0, 1); int_2 = GetDlgItemInt(a1, 1003, 0, 1); Más tarde se generan otros dos valores enteros int_a e int_b, int_a se genera en base a el primer numero introducido en la GUI, int_b depende de el segundo número y del valor obtenido por isBeingDebugged() : int_a = get_n(int_1); int_b = gDbgStatus + 3 * int_2; Para que el crackme nos diga que hemos acertado estos dos números deben ser el mismo : if ( int_a == int_b ) Messagebox("Acertaste"); else MessageBox("Fallaste"); Para realizar el keygen puede haber diferentes estrategias : a) Generamos un int_2 aleatorio, calculamos int_b directamente y tenemos que encontrar un número int_1 que cumpla la siguiente condición : get_n(int_1) == int_b b) Generamos un int_1 aleatorio y calculamos directamente int_a usando get_n(), luego tendríamos la siguiente expresión : int_a = gDbgStatus + 3 * int_2 de la que podemos inferir : int_2 = ( int_a - gDbgStatus ) / 3 Una cosa a tener en cuenta es que (int_a - gDbgStatus) debe ser múltiplo de tres, sino fuera así podríamos generar un número que podria no tener solución, esto se puede "apañar" llamando varias veces a get_n() hasta que se obtenga un número que cumpla esta condición. La opción b, es la más rapida, ya que no es necesario revertir get_n(), la podemos copiar y pegar directamente desde el IDA ;-) ( necesitais el Visual Studio para compilarlo ya que el asm inline está en el formato que soporta este ), todo buen "crax0r" debe ser un poco vago o intentarse ahorrar el mayor trabajo posible xD, así que es la que he utilizado y la que os dejo a continuación ... Un saludo!, Mario Ballano (mballano(at)gmail.com) http://www.48bits.com/ */ #include #include #include #define NOT_BEING_DEBUGGED 5 #define BEING_DEBUGGED 6 // Si queréis que el keygen funcione con el debugger, cambiad el valor a BEING_DEBUGGED #define DEBUG_STATUS NOT_BEING_DEBUGGED __declspec (naked) int get_n ( unsigned int n ) { __asm { push ebp mov ebp, esp mov edx, [ebp+8] xor eax, eax mov ebx, 40000000h mov ecx, 10h loc_401040: add eax, ebx cmp eax, edx ja short loc_40104C sub edx, eax add eax, ebx jnz short loc_40104E loc_40104C: sub eax, ebx loc_40104E: shr eax, 1 shr ebx, 2 dec ecx jnz short loc_401040 cmp eax, edx jnb short locret_40105B inc eax locret_40105B: leave retn } } int main (int argc, char **argv) { int int_1, int_2; srand((int)time(NULL)); do { int_1 = rand() % 100000; int_2 = ( get_n(int_1) - DEBUG_STATUS ); }while(int_2 % 3); int_2 /=3; printf("%d - %d\n", int_1, int_2); }