Autor: Andrewl
información del crackme:http://crackmes.de
Autor del tutorial:UND3R
Objetivos: Realizar Keygen
Abrimos el crackme directamente, observaremos que nos informa el número de dígitos del serial:
Si introducimos un serial falso nos mostrará el siguiente mensaje:
Abrimos el crackme desde OllyDbg y veremos el siguiente Entry Point:
Buscamos referencias de texto dentro del crackme:
Notaremos la cadena de texto "Congratulations!", hacemos doble clic para dirigirnos hacia la dirección en donde es utilizada:
Vemos una instrucción PUSH, encargada de meter el puntero de la cadena de texto en el stack y luego una instrucción JMP SHORT que permite la utilización de la misma call hacia la API MessageBoxA permitiendo el ahorro de instrucciones:
Si subimos un poco veremos que para que el crackme nos muestre el mensaje de "Congratulations!" se deben cumplir ocho comparaciones. Si alguna de las ocho nos da como resultado cero (Activación de la bandera ZF) nos mostrará el mensaje equivalente al introducir un serial falso. Para obtener el mensaje de un serial correcto se debería cumplir las siguientes afirmaciones:
Citar
00402D78 . 807C24 11 00 CMP BYTE PTR SS:[ESP+11],0
Stack SS:[0012FAC5]=01
00402D7F . 807C24 14 00 CMP BYTE PTR SS:[ESP+14],0
Stack SS:[0012FAC8]=01
00402D86 . 807C24 13 00 CMP BYTE PTR SS:[ESP+13],0
Stack SS:[0012FAC7]=01
00402D8D . 807C24 17 00 CMP BYTE PTR SS:[ESP+17],0
Stack SS:[0012FACB]=01
00402D94 . 807C24 16 00 CMP BYTE PTR SS:[ESP+16],0
Stack SS:[0012FACA]=01
00402D9B . 807C24 15 00 CMP BYTE PTR SS:[ESP+15],0
Stack SS:[0012FAC9]=01
00402DA2 . 807C24 10 00 CMP BYTE PTR SS:[ESP+10],0
Stack SS:[0012FAC4]=01
00402DA9 . 807C24 12 00 CMP BYTE PTR SS:[ESP+12],0
Stack SS:[0012FAC6]=01
Stack SS:[0012FAC5]=01
00402D7F . 807C24 14 00 CMP BYTE PTR SS:[ESP+14],0
Stack SS:[0012FAC8]=01
00402D86 . 807C24 13 00 CMP BYTE PTR SS:[ESP+13],0
Stack SS:[0012FAC7]=01
00402D8D . 807C24 17 00 CMP BYTE PTR SS:[ESP+17],0
Stack SS:[0012FACB]=01
00402D94 . 807C24 16 00 CMP BYTE PTR SS:[ESP+16],0
Stack SS:[0012FACA]=01
00402D9B . 807C24 15 00 CMP BYTE PTR SS:[ESP+15],0
Stack SS:[0012FAC9]=01
00402DA2 . 807C24 10 00 CMP BYTE PTR SS:[ESP+10],0
Stack SS:[0012FAC4]=01
00402DA9 . 807C24 12 00 CMP BYTE PTR SS:[ESP+12],0
Stack SS:[0012FAC6]=01
* (Otra manera sería invertir los nemónicos de instrucciones JE SHORT por JNE SHORT)
También vemos un CALL ESI llamativo ya que este podría ser el encargado de comprobar si el serial introducido corresponde a un serial válido, coloquemos un BP en él:
Iniciamos el crackme (F9) e introducimos un serial falso:
Notaremos que se ha detenido en nuestro BP:
Entramos al CALL ESI y veremos lo siguiente:
Traceamos algunas instrucciones hasta llegar a la instrucción XOR:
Si pasamos la instrucción XOR (F7), notaremos que han cambiado algunas instrucciones posteriores a JNZ SHORT. Esto se debe a una función de descifrado en donde el contenido de ESI es alterado producto de la operación booleana XOR entre [ESI] y 76, guardando el resultado en el operando de destino (dentro de ESI). Luego de eso es incrementado ESI para apuntar a las siguientes instrucciones y disminuido ECX, este segundo registro es utilizado como contador de bucle ya que luego de SUB ECX,1 hay un nemónico de instrucción de tipo salto condicional en donde salta a la instrucción XOR si el resultado de la instrucción SUB ECX,1 es distinto de cero:
Colocamos un BP a continuación de JNZ SHORT e iniciamos el crackme (F9):
Veremos una serie de instrucciones descifradas:
Quitamos el BP y traceamos (F7), notaremos las siguientes instrucciones encargadas de la validación del serial:
Código:
0FB648 03 MOVZX ECX,BYTE PTR DS:[EAX+3] ; mueve el cuarto valor del serial a ECX
83F9 6D CMP ECX,6D ; lo compara con 6D (m)
En caso de que la comparación de como resultado la activación de la bandera Cero (ZF), se moverá dentro de EDX el byte 1:
Código:
MOV BYTE PTR DS:[EDX],1
Si miramos el valor de EDX en los registros de propósito general de 32 bits veremos que EDX apunta a una de las ocho direcciones que debe contener 1 byte para que el serial sea considerado como válido:
Si seguimos traceando (F7) notaremos una función encargada de cifrar las instrucciones previamente descifradas:
Seguimos traceando (F7), veremos nuevamente el procedimiento de descifrado:
Colocamos un BP a continuación de la instrucción JNZ SHORT:
Iniciamos nuevamente el crackme (F9) y notaremos que se repiten nuevamente los procedimientos:
podríamos resumirlo de la siguiente manera:
-descifra las instrucciones a ejecutar
-ejecuta las instrucciones descifradas encargadas de verificar el serial
-cifra las instrucciones previamente ejecutadas
(Lo más probable es que el programa cifre las instrucciones ejecutadas para evitar que al retornar del CALL ESI queden descubiertas todas las instrucciones de comprobación del serial, al igual que luego de introducir un serial si es dumpeado el ejecutable, quedarían expuestas las comprobaciones.)
Si seguimos traceando notaremos que siempre se repiten las mismas funciones:
Código:
0015E028 0FB648 03 MOVZX ECX,BYTE PTR DS:[EAX+3] ; mueve el cuarto valor del serial a ECX
0015E02F 83F9 6D CMP ECX,6D ; lo compara con 6D (m)
0015E069 0FB648 02 MOVZX ECX,BYTE PTR DS:[EAX+2] ; mueve el tercer valor del serial a ECX
0015E070 83F9 37 CMP ECX,37 ; lo compara con 37 (7)
0015E0AA 0FB648 03 MOVZX ECX,BYTE PTR DS:[EAX+3] ; mueve el cuarto valor del serial a ECX
0015E0B1 83F9 65 CMP ECX,65 ; lo compara con 65 (e)
0015E0EB 0FB648 07 MOVZX ECX,BYTE PTR DS:[EAX+7] ; mueve el octavo valor del serial a ECX
0015E0F2 83F9 48 CMP ECX,48 ; lo compara con 48 (H)
0015E12C 0FB648 03 MOVZX ECX,BYTE PTR DS:[EAX+3] ; mueve el cuarto valor del serial a ECX
0015E133 83F9 37 CMP ECX,37 ; lo compara con 37 (7)
0015E16D 0FB648 00 MOVZX ECX,BYTE PTR DS:[EAX] ; mueve el primer valor del serial a ECX
0015E174 83F9 37 CMP ECX,37 ; lo compara con 37 (7)
0015E1AE 0FB648 01 MOVZX ECX,BYTE PTR DS:[EAX+1] ; mueve el segundo valor del serial a ECX
0015E1B5 83F9 55 CMP ECX,55 ; lo compara con 55 (U)
0015E1EF 0FB648 01 MOVZX ECX,BYTE PTR DS:[EAX+1] ; mueve el segundo valor del serial a ECX
0015E1F6 83F9 64 CMP ECX,64 ; lo compara con 64 (d)
0015E230 0FB648 04 MOVZX ECX,BYTE PTR DS:[EAX+4] ; mueve el quinto valor del serial a ECX
0015E237 83F9 47 CMP ECX,47 ; lo compara con 47 (G)
0015E271 0FB648 02 MOVZX ECX,BYTE PTR DS:[EAX+2] ; mueve el tercer valor del serial a ECX
0015E278 83F9 37 CMP ECX,37 ; lo compara con 37 (7)
0015E2B2 0FB648 01 MOVZX ECX,BYTE PTR DS:[EAX+1] ; mueve el segundo valor del serial a ECX
0015E2B9 83F9 55 CMP ECX,55 ; lo compara con 55 (U)
0015E2F3 0FB648 07 MOVZX ECX,BYTE PTR DS:[EAX+7] ; mueve el octavo valor del serial a ECX
0015E2FA 83F9 6F CMP ECX,6F ; lo compara con 6F (o)
0015E334 0FB648 05 MOVZX ECX,BYTE PTR DS:[EAX+5] ; mueve el sexto valor del serial a ECX
0015E33B 83F9 37 CMP ECX,37 ; lo compara con 37 (7)
0015E375 0FB648 03 MOVZX ECX,BYTE PTR DS:[EAX+3] ; mueve el cuarto valor del serial a ECX
0015E37C 83F9 36 CMP ECX,36 ; lo compara con 36 (6)
0015E3B6 0FB648 01 MOVZX ECX,BYTE PTR DS:[EAX+1] ; mueve el segundo valor del serial a ECX
0015E3BD 83F9 33 CMP ECX,33 ; lo compara con 33 (3)
0015E3F7 0FB648 04 MOVZX ECX,BYTE PTR DS:[EAX+4] ; mueve el quinto valor del serial a ECX
0015E3FE 83F9 45 CMP ECX,45 ; lo compara con 45 (E)
0015E438 0FB648 02 MOVZX ECX,BYTE PTR DS:[EAX+2] ; mueve el tercer valor del serial a ECX
0015E43F 83F9 50 CMP ECX,50 ; lo compara con 50 (P)
0015E479 0FB648 02 MOVZX ECX,BYTE PTR DS:[EAX+2] ; mueve el tercer valor del serial a ECX
0015E480 83F9 36 CMP ECX,36 ; lo compara con 36 (6)
0015E4BA 0FB648 05 MOVZX ECX,BYTE PTR DS:[EAX+5] ; mueve el sexto valor del serial a ECX
0015E4C1 83F9 39 CMP ECX,39 ; lo compara con 39 (9)
0015E4FB 0FB648 05 MOVZX ECX,BYTE PTR DS:[EAX+5] ; mueve el sexto valor del serial a ECX
0015E502 83F9 49 CMP ECX,49 ; lo compara con 49 (I)
0015E53C 0FB648 00 MOVZX ECX,BYTE PTR DS:[EAX] ; mueve el primer valor del serial a ECX
0015E543 83F9 35 CMP ECX,35 ; lo compara con 35 (5)
0015E57D 0FB648 02 MOVZX ECX,BYTE PTR DS:[EAX+2] ; mueve el tercer valor del serial a ECX
0015E584 83F9 7A CMP ECX,7A ; lo compara con 7A (z)
0015E5BE 0FB648 01 MOVZX ECX,BYTE PTR DS:[EAX+1] ; mueve el segundo valor del serial a ECX
0015E5C5 83F9 6E CMP ECX,6E ; lo compara con 6E (n)
0015E5FF 0FB648 03 MOVZX ECX,BYTE PTR DS:[EAX+3] ; mueve el cuarto valor del serial a ECX
0015E606 83F9 6A CMP ECX,6A ; lo compara con 6A (j)
0015E640 0FB648 01 MOVZX ECX,BYTE PTR DS:[EAX+1] ; mueve el segundo valor del serial a ECX
0015E647 83F9 7A CMP ECX,7A ; lo compara con 7A (z)
0015E681 0FB648 06 MOVZX ECX,BYTE PTR DS:[EAX+6] ; mueve el séptimo valor del serial a ECX
0015E688 83F9 57 CMP ECX,57 ; lo compara con 57 (W)
0015E6C2 0FB648 05 MOVZX ECX,BYTE PTR DS:[EAX+5] ; mueve el sexto valor del serial a ECX
0015E6C9 83F9 4F CMP ECX,4F ; lo compara con 47 (O)
Por lo que podríamos crear un script de ODbg encargado de almacenar todas las comparaciones que realiza. Al realizar comprobaciones de los datos obtenidos por el script, los serial ingresados eran considerados como incorrectos por lo que por lógica deberían haber más comparaciones que si se cumplen se introduce un byte 0, por lo que encontré funciones muy similares pero con la gran diferencia que si estas se cumplían en vez de poseer la instrucción MOV BYTE PTR DS:[EDX],1 a continuación de JNZ SHORT, contenían la instrucción MOV BYTE PTR DS:[EDX],0, a consecuencia de esto cree un script que en un documento de texto me mostrara la comparación junto con un SI o un NO en donde SI es introducido cuando la instrucción siguiente a JNZ SHORT es MOV BYTE PTR DS:[EDX],1 y un NO cuando la instrucción siguiente a JNZ SHORT es MOV BYTE PTR DS:[EDX],0
Script:
Código
VAR AUX VAR FSERIAL VAR SERIAL WRT "log.txt","Log:" INICIO: TICND "byte [eip]==75 && byte [eip+1]==0F7" BP eip+2 RUN BC eip TICND "byte [eip]==8B && byte [eip+1]==55" STI CMP edx,0012FAC5 JE ENCONTRADO CMP edx,0012FAC8 JE ENCONTRADO CMP edx,0012FAC7 JE ENCONTRADO CMP edx,0012FACB JE ENCONTRADO CMP edx,0012FACA JE ENCONTRADO CMP edx,0012FAC9 JE ENCONTRADO CMP edx,0012FAC4 JE ENCONTRADO CMP edx,0012FAC6 JE ENCONTRADO JMP INICIO ENCONTRADO: MOV FSERIAL,ecx BUF FSERIAL STR FSERIAL MOV SERIAL,[eip+2],1 BUF SERIAL STR SERIAL STI ADD eip,2 CMP [eip],C6,1 JNE INICIO CMP [eip+1],02,1 JNE INICIO CMP [eip+2],01,1 JE SI NO: MOV AUX,"NO" JMP CONTINUAR SI: MOV AUX,"SI" CONTINUAR: WRTA "log.txt",FSERIAL WRTA "log.txt","=","" WRTA "log.txt",SERIAL,"" WRTA "log.txt"," ","" WRTA "log.txt",AUX,"" jmp INICIO
El script debe ser ejecutado de la siguiente forma:
1)colocar BP en CALL ESI
2)iniciar el crackme (F9)
3)introducir el serial: 12345678 (ya que nos dará la referencia de la posición del dígito que se está comparando)
4)Se detendrá en el BP se debe quitar y entrar dentro del CALL (001CFFE8 55 PUSH EBP)
El script debe ser detenido una vez que hemos presionado aceptar a la alerta generada por la API MessageBoxA del serial incorrecto.
Una vez terminado el script si abrimos log.txt (documento de texto creado por el script) veremos lo siguiente:
En la primera linea:
el cuarto carácter es comparado con m y si este se cumple mueve dentro de EDX el byte 1.
Ya que el script logea las comparaciones de manera secuencial, la lógica sería buscar desde abajo hacia arriba una comparación con SI y luego buscar hacia abajo la misma comparación con un NO, si se encuentra la comparación el dígito es inválido en caso contrario el dígito es válido, para optimizar la búsqueda utilizaremos Excel, seleccionamos todo el contenido de log.txt y lo pegamos dentro de una nueva hoja de Excel:
Nos dirigimos a Datos->Filtro:
En la ubicación A1 ("Log:") contendrá un botón:
Seleccionamos el botón y nos vamos a Filtros de texto-> Comienza por...:
He introducimos 1:
De esta forma Excel filtrará de manera secuencial todos los datos que comienza con 1, en otras palabras nos mostrará todas las comparaciones realizadas con el primer dígito:
Copiamos el contenido filtrado en un nuevo documento de texto, nos dirigimos hasta el final y comenzamos a buscar "SI" de la siguiente manera:
De manera lógica estamos buscando la última comparación válida del dígito 1
Ya encontrado el "SI" buscamos hacia abajo la igualdad válida para saber si es anulada por un "NO" (En este caso está a la vista, pero en los demás dígitos no son tan visibles las igualdades válidas que más abajo se ven afectadas por un "NO"):
Si no se encuentra la comparación hemos encontrado un dígito válido:
en caso contrario debemos volver al "SI" encontrado y buscar nuevamente "SI" hacia arriba ya encontrado el primer dígito se debe filtrar nuevamente en Excel pero esta vez por los que comienzan en 2 y repetir el procedimiento de búsqueda hasta encontrar los ocho dígitos.
Una vez encontrado los ocho dígitos al introducir el serial, el crackme nos mostrará el siguiente mensaje:
Ya con un serial válido podemos obtener un Keygen:
1)debemos introducir el serial válido.
2)EJ: CAaC5EEs.
3)Si queremos saber todas las posibilidades del primer dígito vamos introduciendo números desde el "0" al "9", luego letras mayúsculas desde la "A" a la "Z" y letras minúsculas desde la "a" a la "z".
Pero el mensaje del serial incorrecto nos es un poco molesto, por lo que podríamos borrarlo, nos dirigimos a la string: "sorry, try harder!":
y llenamos con instrucciones NOP (Not operand) y agregamos un JMP tal como lo muestra la imagen:
Guardamos los cambios y ya podremos obtener los dígitos válidos para cada posición del serial con mayor comodidad.
La lista completa de posibles combinaciones es la siguiente:
Código:
1ºD 2ºD 3ºD 4ºD 5Dº 6Dº 7ºD 8ºD
VA 1 4 2 8 4 7 1 4
C A a C 5 E E s
B D F
C J H
E Ñ K
J P L
K U M
M ñ N
Ñ Ñ
O Q
Q R
R S
V U
Z V
a W
g X
i Y
k g
m h
q i
s j
y ñ
o
q
t
z
Keygen:
Para el Keygen he tenido que suprimir las letras Ñ y ñ en cuanto a la generación del serial debido a que estoy iniciándome en el lenguaje ASM y estoy utilizando procedimientos pre-diseñados por lo que desconozco como solucionarlo y como trabajar directamente con APIS por lo que el Keygen es muy simple, pero cumple con su finalidad, aquí el código de fuente:
Código
TITLE Keygen Russian Dolls (Keygen-Russian-Dolls.asm) ; Genera combinaciones de dígitos válidos para el crackme Russian Dolls ; Autor: UND3R ; Fecha: Miércoles 4 de enero de 2012 INCLUDE c:\masm32\include\Irvine32.inc INCLUDELIB c:\masm32\lib\kernel32.lib INCLUDELIB c:\masm32\lib\Irvine32.lib INCLUDELIB c:\masm32\lib\User32.lib .data ; en el arreglo SegundoDigito se ha omitido la letra Ñ ; en el arreglo QuintoDigito se ha omitido la letra Ñ y ñ ; en el arreglo SextoDigito se ha omitido la letra Ñ y ñ PrimerDigito BYTE 31h,"C" SegundoDigito BYTE 34h,"A","B","C","E","J","K","M","O","Q","R","V","Z","a","g","i","k","m","q","s","y" TercerDigito BYTE 32h,"a" CuartoDigito BYTE 38h,"C" QuintoDigito BYTE 34h,35h,"D","J","P","U" SextoDigito BYTE 37h,"E","F","H","K","L","M","N","Q","R","S","U","V","W","X","Y","g","h","i","j","o","q","t","z" SeptimoDigito BYTE 31h,"E" OctavoDigito BYTE 34h,"s" Serial BYTE 8 DUP(?) Color BYTE ? .code main PROC call GetTextColor ; obtiene el color de texto y fondo de la consola mov Color,al ; mue AL dentro del arreglo Color call Randomize ; inicializa el valor de la semilla para el procedimiento RandomRange call GenerarSerial call MostrarSerial jmp main main ENDP GenerarSerial PROC ; Genera seriales válidos de manera aleatoria ; RECIBE: NADA ; DEVUELVE: Serial válido en el arreglo sin inicializar "Serial" ;-------------------------------------------------------------------- mov eax,sizeof PrimerDigito ; mueve a EAX el número de valores del arreglo call RandomRange ; genera números aleatorios desde 0 hasta EAX - 1 mov dl,[PrimerDigito+eax] ; mueve a DL un valor aleatorio del arreglo "PrimerDigito" mov [Serial],dl ; mueve DL dentro del arreglo "Serial" mov eax,sizeof SegundoDigito ; mueve a EAX el número de valores del arreglo call RandomRange ; genera números aleatorios desde 0 hasta EAX - 1 mov dl,[SegundoDigito+eax] ; mueve a DL un valor aleatorio del arreglo "SegundoDigito" mov [Serial+1],dl ; mueve DL dentro del arreglo "Serial" mov eax,sizeof TercerDigito ; mueve a EAX el número de valores del arreglo call RandomRange ; genera números aleatorios desde 0 hasta EAX - 1 mov dl,[TercerDigito+eax] ; mueve a DL un valor aleatorio del arreglo "TercerDigito" mov [Serial+2],dl ; mueve DL dentro del arreglo "Serial" mov eax,sizeof CuartoDigito ; mueve a EAX el número de valores del arreglo call RandomRange ; genera números aleatorios desde 0 hasta EAX - 1 mov dl,[CuartoDigito+eax] ; mueve a DL un valor aleatorio del arreglo "CuartoDigito" mov [Serial+3],dl ; mueve DL dentro del arreglo "Serial" mov eax,sizeof QuintoDigito ; mueve a EAX el número de valores del arreglo call RandomRange ; genera números aleatorios desde 0 hasta EAX - 1 mov dl,[QuintoDigito+eax] ; mueve a DL un valor aleatorio del arreglo "QuintoDigito" mov [Serial+4],dl ; mueve DL dentro del arreglo "Serial" mov eax,sizeof SextoDigito ; mueve a EAX el número de valores del arreglo call RandomRange ; genera números aleatorios desde 0 hasta EAX - 1 mov dl,[SextoDigito+eax] ; mueve a DL un valor aleatorio del arreglo "SextoDigito" mov [Serial+5],dl ; mueve DL dentro del arreglo "Serial" mov eax,sizeof SeptimoDigito ; mueve a EAX el número de valores del arreglo call RandomRange ; genera números aleatorios desde 0 hasta EAX - 1 mov dl,[SeptimoDigito+eax] ; mueve a DL un valor aleatorio del arreglo "SeptimoDigito" mov [Serial+6],dl ; mueve DL dentro del arreglo "Serial" mov eax,sizeof OctavoDigito ; mueve a EAX el número de valores del arreglo call RandomRange ; genera números aleatorios desde 0 hasta EAX - 1 mov dl,[OctavoDigito+eax] ; mueve a DL un valor aleatorio del arreglo "OctavoDigito" mov [Serial+7],dl ; mueve DL dentro del arreglo "Serial" ret GenerarSerial ENDP MostrarSerial PROC mov ecx,8 ; establece ECX = 8 mov esi,0 ; establece ESI = 0 L2: mov eax,16 ; establece EAX = 16 call RandomRange ; Genera un número aleatorio entre 0 y EAX - 15 cmp eax,0 ; compara EAX con 0 je L2 ; si EAX = 0 se dirige a la etiqueta L2 (Evita un texto negro junto con un fondo de igual color) call SetTextColor ; Cambia el color de texto y de fondo de la consola, de acuerdo al valor de AL L1: mov al,[Serial+esi] ; mueve los dígitos del arreglo "Serial" a AL call WriteChar ; escribe un dígito del arreglo "Serial" en la consola inc esi ; aumenta en uno esi loopd L1 ; decrementa ECX y si ECX no es cero se dirige a la etiqueta L1 call Crlf ; desplaza el cursor abajo, hacia la derecha mov al,[Color] ; mueve el contenido del arreglo Color a AL call SetTextColor ; Cambia el color de texto y de fondo de la consola, de acuerdo al valor de AL call WaitMsg ; se espera que el usuario presiona alguna tecla para continuar call Clrscr ; limpia el contenido de la consola ret MostrarSerial ENDP END main
En el cada vez que presionemos una tecla nos mostrará seriales distintos:
IMPORTANTE:
Si el serial es copiado directamente y no es introducido de manera manual o ni si quiera un dígito, el crackme lo considera como inválido pudiendo ser este verdadero.
UND3R
Pack de descarga:
-Crackme modificado sin el mensaje de serial incorrecto
-Código de fuente Keygen .asm
-Seriales correctos.txt
-Crackme
-Keygen
-Script
-log.txt
LINK: http://www.mediafire.com/?nxlnml8nx4i8dvb