| ESTUDIO COLECTIVO DE DESPROTECCIONES Última Actualizacion: 25/10/2001 |
Programa | NI2CRACKME | W95 / W98 / NT
Descripción | Un Crackme con muy mala leche
Tipo | Ya sabéis cómo se las gasta NI2
Tipo de Tutorial | [X]Original, []Adaptación, []Aplicación, []Traducción
Url | Ni2Crackme2.zip
Protección | Encriptación del código, sólo el serial bueno lo desencripta
Dificultad | 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas | Masm, Sice y PUPE (opcional)
Objetivo | Conseguir el serial válido.
Cracker | Crack el Destripador & Marmota
Grupo | http://welcome.to/craaaack
Fecha | 4 de enero de 2001
INTRODUCCION
El Incrackeable... Así se llamo durante mucho tiempo este Crackme... Personalmente opino que no hay nada Incrackeable, todo es cuestión de tiempo y constancia. Pero este Crackme ha estado mas cerca de esta calificación que cualquier otro programa que (por lo menos yo) conozca. Es tal su singularidad, que NO tiene ningún tipo de protecciones añadidas, como pudieran ser los típicos anti-debug, anti-dump, anti-desensamblador, etc. (y ni puñetera falta que le hace). Tan solo, puro y “simple” asm. La clave a buscar debe de reunir unas determinadas características. Según el autor: La clave contiene 8 letras (caracteres entre A-z), es decir, caracteres del alfabeto español, tanto mayúsculas como minúsculas) Tampoco existen espacios La palabra no aparece en el diccionario (p.e.: HolaPEPE, no esta en el diccionario :-)) Con estos datos es fácil calcular el numero de combinaciones posibles. 26 mayúsculas + 26 minúsculas = 52 caracteres posibles. Como son 8 letras serian: 52^8 = 5,345972853146e+13 o sea 53.459.728.531.460 que traducido al cristiano serian 53 BILLONES 459 MIL MILLONES Y PICO. Vamos... para desanimar a cualquiera. XDDDDDDDDDDDD. Mas adelante, y gracias a nuevas pistas proporcionadas por el autor, esta cantidad se redujo considerablemente. La primera letra empezaba por “H”. Esto nos dejaba el numero de combinaciones posibles en tan solo 1.028.071.702.528 , poca cosa... 1 BILLONCETE 28 MIL MILLONES de nada. Esta clave es imprescindible para desencriptar un pedacito de código, donde se ocultan las instrucciones y los datos necesarios para la presentación de la MessageBox que nos anunciara nuestro éxito.
AL ATAQUE
Vamos a omitir la presentación del listado asm completo, solo pondremos los bloques que nos interesan para comprender la forma con que trabaja este endiablado engendro (a falta de otros calificativos ;-)). Esta es la rutina principal de desencriptación. Se utiliza 3 veces en el programa, (luego veremos para que). Este es el listado original de la zona del código por la que pasa la cadena introducida. Cuando se llega a ella la 1ª vez, esi apunta al buffer donde se ha guardado la clave introducida en el control de edición y eax vale 0. 0187:004012FE 33C0 XOR EAX,EAX 0187:00401300 51 PUSH ECX 0187:00401301 56 PUSH ESI 0187:00401302 52 PUSH EDX 0187:00401303 33D2 XOR EDX,EDX 0187:00401305 4E DEC ESI 0187:00401306 46 INC ESI 0187:00401307 3226 XOR AH,[ESI] 0187:00401309 32C2 XOR AL,DL 0187:0040130B 052032494E ADD EAX,4E493220 0187:00401310 8AC8 MOV CL,AL 0187:00401312 D3C8 ROR EAX,CL 0187:00401314 355A5AAA55 XOR EAX,55AA5A5A 0187:00401319 664A DEC DX 0187:0040131B 75EC JNZ 00401309 0187:0040131D 803E00 CMP BYTE PTR [ESI],00 0187:00401320 75E4 JNZ 00401306 0187:00401322 5A POP EDX 0187:00401323 5E POP ESI 0187:00401324 59 POP ECX 0187:00401325 C3 RET Resumiendo... estas pocas instrucciones provocan 9 bucles de FFFF veces cada uno (uno para cada letra + el del final) La comprobación de que se ha llegado al final de la cadena se produce después de que la instrucción 401306 inc esi apunte a un byte de valor 0, y realice toooodo el bucle FFFFh veces otra vez. l resultado de todo este barullo es devuelto en eax, y para que la cadena sea considerada como valida tiene que cumplir la condición eax = 84AEB79E. 0040121E cmp eax, 84AEB79Eh 00401223 jz 40123D Llegados a este punto y en vista de la descomunal cantidad de instrucciones que debe procesar la máquina para pasar todas las combinaciones posibles 9 veces por este fragmento de código, es fácil pensar, o al menos así lo creyó Ni2, que es imposible dar con la combinación buena, por ese motivo queremos hacer especial hincapié en la necesidad de aligerar lo máximo posible el código que busque el serial adecuado, de lo contrario probablemente moriríamos de viejos antes de dar con él. También comentaros que TAN SOLO UNA COMBINACIÓN hace que se desencripte el código a ejecutar. No penséis que invirtiendo el salto o forzándolo vais a conseguir algo... XDDDD. Bueno, sí conseguiríais un cuelgue, y con un poco de suerte tener que reiniciar el trasto. Jeje. He intentado revertir la función, es decir, hacer que empiece por el final, y repita todo el ciclo al revés. Pero al llegar a la instrucción xor ah, [esi] estaba igual que al principio, tendría que poner todos los valores posibles donde apunta esi en ese momento y el resultado seria peor. Si la cadena introducida no pasa este test, nos tira de un patadon a la calle, y si pasa el test nos lleva a un lugar de lo más interesante. ¿Quién tiene la Clave? Como en casi todas las películas, la clave la tiene eax. Pero... 0040123D mov eax, 40124E ;puntero al código encriptado 00401242 mov ecx, 68h ;nº de bytes a cambiar 00401247 mov edx, esi ;puntero al buffer donde esta la clave 00401249 call 401326 Empieza la movida... esta vez eax vale 48415348 antes de entrar en la rutina principal. Siiii esa de los bucles sin fin. 00401326 pusha 00401327 mov edi, eax 00401329 mov esi, edx 0040132B mov eax, 48415348h 00401330 xor al, [esi] 00401332 call 401300 Resultado... al entrar en la rutina con un valor eax distinto de 0, el resultado ya no es nuestro querido 84AEB79E, si no otro distinto, a saber... 00401337 mov ebx, eax Y ese resultado se carga en ebx, que luego servirá para la desencriptacion. 00401339 xor ah, [esi] 0040133B call 401300 Otro xor y otra llamada a la super rutina. Que nos devuelve un nuevo valor para eax, y ya esta... ya se puede desencriptar el churro. Al llegar aquí tenemos que edi apunta a la zona encriptada, en ebx tenemos el valor devuelto por la 2ª llamada a la super rutina, en eax, el valor de la 3ª llamada y en ecx el nº de bytes a desencriptar. 00401340 shr ecx, 2 00401343 mov edx, ecx 00401345 xor [edi], eax 00401347 mov cl, al 00401349 add edi, 4 0040134C rol ebx, cl 0040134E xor eax, ebx 00401350 mov cl, bh 00401352 ror eax, cl 00401354 add ebx, eax 00401356 dec edx 00401357 jnz 401345 00401359 popa 0040135A retn Atacando por los flancos Se podría intentar un ataque en esta zona, dando por supuestas las primeras instrucciones que se deben ejecutar para la presentación de una MessageBox (De eso se trata ¿no?). Tengo referencias de que Metamorfe consiguió desencriptar esta zona de código (aun no se como, ya que no conozco ningún escrito ni tute explicando como y donde). La teoría seria la siguiente: Instrucciones necesarias para una MessageBox 6A00 push 0 ;botón aceptar MB_OK 68xxxxxxxx push xxxxxxxx ;puntero al Caption 68xxxxxxxx push xxxxxxxx ;puntero al texto 6A00 push 0 ;handle del dialogo E8xxxxxxxx call xxxxxxxx ;llamada a la función Las x sustituyen las direcciones donde se deberían encontrar los datos correspondientes. Sabemos que deberían estar entre la dirección 0040124E y 0040124E + 68h (¿recordáis el nº de bytes a desencriptar?)= 004012B5. Si afinamos mas, podíamos descontar los bytes necesarios para completar la función de MessageBox. Vamos... que las direcciones donde se podría esconder el Caption y el mensaje estarían entre 00401261 y 004012B5. Nos hacen falta solo los 8 primeros bytes en 2 dwords, una para la primera eax y otra para la segunda eax (tranki... ya me explico). 6A0068XX en este caso las xx pueden tomar un valor entre 61h y B5h. y XXXXXX68 Aquí no hay problema, las x solo pueden valer 124000. Y ahora los valores que tenemos en la zona protegida son: 0975EF94 y 2B3C11C7 xoreando 0975EF94 XOR 6A0068XX = 637587XX Para obtener todos los valores posibles (xx estará entre 61h y B5h) solo hay que hacer operaciones. XDDDDDD xoreando 2B3C11C7 XOR 12400068 = 397C11AF Ok, volvemos a la rutina anterior. En el primer ciclo, eax debe valer XX877563 y al completar el primer ciclo eax valdrá AF117C39 00401340 shr ecx, 2 00401343 mov edx, ecx 00401345 xor [edi], eax 00401347 mov cl, al 00401349 add edi, 4 0040134C rol ebx, cl 0040134E xor eax, ebx 00401350 mov cl, bh 00401352 ror eax, cl 00401354 add ebx, eax 00401356 dec edx 00401357 jnz 401345 00401359 popa 0040135A retn Podemos escribir una pequeña rutina que nos calculara el valor de ebx, para los 2 valores estimados de eax. xor ebx, ebx a0: mov eax, XX877563 mov cl, al rol ebx, cl xor eax, ebx mov cl, bh ror eax, cl cmp eax, AF117C39 je lotengo inc ebx jmp a0 Esto en teoría debería funcionar, tendríamos los valores iniciales de eax y ebx. Pero en la practica no funciono... Ni2 se supero otra vez. Para montar la MessageBox utilizo lo siguiente: 6A00 push 00 ;botón MB_OK B858124000 mov eax, 00401258 ;carga en eax la dirección del Caption 50 push eax ;y luego la pushea De forma que desmontaba todas nuestras suposiciones. Tal vez algún día Metamorfe nos diga como adivino estos cambios. Fuerza Bruta ¿qué remedio? Pues si...solo quedaba el recurso de la Fuerza Bruta, pero con la cantidad de combinaciones existentes y la enormidad de los bucles que hay que pasar (mas de medio millón de instrucciones para comprobar cada cadena), había que pensar en la forma de acelerar el proceso siguiendo unas pautas lógicas. Este es el listado asm que desarrollamos Crack el Destripador y un servidor para la ocasión (Ni2, vaya que nos diste trabajo). El programa se llamo en un principio Destripador, pero después de... 9, 10 versiones (no ma cuerdo), lo llamamos marm6.exe. Tiene la particularidad de utilizar un sistema de filtros para evitar que cadenas sin sentido entren en los bucles quasi-infinitos de los test. Las cadenas que cumplen las condiciones son almacenadas en un archivo de texto llamado marmota4.txt, y en ese archivo se introduce la cadena desde donde empezara la búsqueda, seguida de un salto de línea y retorno de carro para equilibrar los caracteres de control. Es posible que algún Cracker más avispado sea capaz de dar con la solución de otro modo, nosotros no hemos visto otra salida y quizás nadie la haya visto, ya que éste monstruo ha estado sin solución hasta la fecha. El Arma para Matar Dinosaurios .486 .model flat,stdcall option casemap:none include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib .data buffer db 256 dup (0) sacabo db "Se ha terminado el tiempo",0 marmota db "Marmota & Crack el Destripador 2000",0 archivo db "marmota4.txt",0 mayusculas db 0 vocales db 0 efes db 0 cues db 0 ges db 0 cetas db 0 haches db 0 uves_dobles db 0 jotas db 0 kas db 0 emes db 0 enes db 0 pes db 0 erres db 0 eses db 0 tes db 0 uves db 0 exis db 0 bes db 0 ces db 0 des db 0 consonantes db 0 .data? hInstance dd ? hfichero dd ? fsize dd ? memptr dd ? bread dd ? bwrite dd ? .code start: invoke GetModuleHandle,NULL mov hInstance,eax call leer jmp altajo mas1: mov esi, offset buffer inc esi mov ecx, 7 a1: inc byte ptr [esi] .if byte ptr [esi]==05bh add byte ptr [esi], 6 .endif cmp byte ptr [esi], 7Bh jnz sigue mov byte ptr [esi], 41h inc esi loop a1 sigue: mov esi, offset buffer .if dword ptr [esi]==07a7a7a48h .if dword ptr [esi+4]==07a7a7a7ah jmp fin .endif .endif ; este codigo es mucho mas rápido ;* Sustituido filtro: Ahora elimina las que tienen menos de 2 mayusculas ;********************************************************* ; Eliminar: filtro que elimina las cadenas que no contienen ; 2 mayusculas ;********************************************************* ; mov esi, offset buffer mov mayusculas, 1 ; esto por la H ; no es necesario comprobar que sea mayor que 40, todas son mayor que 40 MOV AX, WORD PTR [ESI+1] .if al < 05bh inc mayusculas .endif .if ah < 05bh inc mayusculas .endif MOV AX, WORD PTR [ESI+3] .if al < 05bh inc mayusculas .endif .if ah < 05bh inc mayusculas .endif MOV AX, WORD PTR [ESI+5] .if al < 05bh inc mayusculas .endif .if ah < 05bh inc mayusculas .endif .if byte ptr [esi+7]< 05bh inc mayusculas .endif .if mayusculas != 2 jmp mas1 .endif ;********************************************************************* ; eliminar2: filtro que elimina las cadenas que contienen dos ; mayusculas ; consecutivas ;********************************************************************* ; el segundo caracter ya está sujeto a a,e,i,o,u,y MOV AX, WORD PTR [ESI+2] .if al < 05bh .if ah < 05bh jmp mas1 .endif .endif MOV AX, WORD PTR [ESI+3] .if al < 05bh .if ah < 05bh jmp mas1 .endif .endif MOV AX, WORD PTR [ESI+4] .if al < 05bh .if ah < 05bh jmp mas1 .endif .endif MOV AX, WORD PTR [ESI+5] .if al < 05bh .if ah < 05bh jmp mas1 .endif .endif MOV AX, WORD PTR [ESI+6] .if al < 05bh .if ah < 05bh jmp mas1 .endif .endif ;************************************************************ ;* ;* He comprobado en un diccionario ingles que solo empiezan ;* las palabras por ha, he, hi, ho, hu y hy. en español ;* es lo mismo menos la y ;* ;************************************************************ ;********** dice Jotake que tiene claro que el segundo caracter es minuscula mov esi, offset buffer mov al, [esi+1] .if al != "a" && al != "e" && al != "i" && al != "o" && al != "u" && al != "y" JMP mas1 .ENDIF ;************************************************************ ;* ;* comprobado que tanto en ingles como en español despues ;* despues de Q siempre va U ;* ;************************************************************ ;***************** comprobamos la Q y q .if byte ptr [esi+2]== "Q" || byte ptr [esi+2]=="q" ;***** comprobamos la U y u .if byte ptr [esi+3]!= "U" && byte ptr [esi+3] != "u" JMP mas1 .ENDIF ;************ ademas, el tercer caracter ha de ser otra vocal mov al, byte ptr [esi+4] .if al != "a" && al != "A" && al != "e" && al != "E" && al != "i" && \ al != "I" && al != "o" && al != "O" && al != "u" && al != "U" JMP mas1 .ENDIF .endif .if byte ptr [esi+3]== "Q" || byte ptr [esi+3]=="q" .if byte ptr [esi+4]!= "U" && byte ptr [esi+4] != "u" JMP mas1 .ENDIF ;************ ademas, el tercer caracter ha de ser otra vocal mov al, byte ptr [esi+5] .if al != "a" && al != "A" && al != "e" && al != "E" && al != "i" && \ al != "I" && al != "o" && al != "O" && al != "u" && al != "U" JMP mas1 .ENDIF .endif .if byte ptr [esi+4]== "Q" || byte ptr [esi+4]=="q" .if byte ptr [esi+5]!= "U" && byte ptr [esi+5] != "u" JMP mas1 ;************ ademas, el tercer caracter ha de ser otra vocal mov al, byte ptr [esi+6] .if al != "a" && al != "A" && al != "e" && al != "E" && al != "i" && \ al != "I" && al != "o" && al != "O" && al != "u" && al != "U" JMP mas1 .ENDIF .ENDIF .endif .if byte ptr [esi+5]== "Q" || byte ptr [esi+5]=="q" .if byte ptr [esi+6]!= "U" && byte ptr [esi+6] != "u" JMP mas1 .ENDIF ;************ ademas, el tercer caracter ha de ser otra vocal mov al, byte ptr [esi+7] .if al != "a" && al != "A" && al != "e" && al != "E" && al != "i" && \ al != "I" && al != "o" && al != "O" && al != "u" && al != "U" JMP mas1 .ENDIF .endif ;************************************************************ ;* ;* si tenemos una hache las palabras deben estar compuestas ;* como minimo de dos vocales y como maximo de cuatro ;* de otro modo nos saldrían palabras sin sentido ;* ;************************************************************ ;********* vocales valía 1 cuando estaba el filtro de que la segunda letra debía ser vocal ;* ahora debe valer 0 mov vocales, 0 mov esi, offset buffer mov ax, word ptr [esi] .if al == "a" || al =="e" || al == "i" || al=="o" || al=="u" || al=="y" \ || al=="A" || al =="E" || al == "I" || al=="O" || al=="U" || al=="Y" inc vocales .endif ;******esto eliminado porque en ah esta la "H" ;.if ah == "a" || ah =="e" || ah == "i" || ah=="o" || al=="u" \ ; || ah=="A" || ah =="E" || ah == "I" || ah=="O" || ah=="U" || ah=="y" || ah=="Y" ; inc vocales ;.endif mov ax, word ptr [esi+2] .if al == "a" || al =="e" || al == "i" || al=="o" || al=="u" \ || al=="A" || al =="E" || al == "I" || al=="O" || al=="U"|| al=="y" || al=="Y" inc vocales .endif .if ah == "a" || ah =="e" || ah == "i" || ah=="o" || al=="u" \ || ah=="A" || ah =="E" || ah == "I" || ah=="O" || ah=="U"|| ah=="y" || ah=="Y" inc vocales .endif mov ax, word ptr [esi+4] .if al == "a" || al =="e" || al == "i" || al=="o" || al=="u"\ || al=="A" || al =="E" || al == "I" || al=="O" || al=="U"|| al=="y" || al=="Y" inc vocales .endif .if ah == "a" || ah =="e" || ah == "i" || ah=="o" || al=="u" \ || ah=="A" || ah =="E" || ah == "I" || ah=="O" || ah=="U"|| ah=="y" || ah=="Y" inc vocales .endif mov ax, word ptr [esi+6] .if al == "a" || al =="e" || al == "i" || al=="o" || al=="u"\ || al=="A" || al =="E" || al == "I" || al=="O" || al=="U"|| al=="y" || al=="Y" inc vocales .endif .if ah == "a" || ah =="e" || ah == "i" || ah=="o" || al=="u" \ || ah=="A" || ah =="E" || ah == "I" || ah=="O" || ah=="U"|| ah=="y" || ah=="Y" inc vocales .endif .if vocales < 2 || vocales > 4 JMP mas1 .ENDIF ;************************************************************ ; tres consonantes seguidas es tonto ¿no? ;************************************************************ ; no es necesario cargar en esi el offset de buffer ya lo hemos cargado ; mov esi, offset buffer ;****** la primera es una H, no? entonces consonantes ya vale 1 pero no lo necesitamos ; comenzamos la comparación por esi+1 mov ax, word ptr [esi+1] ;****** si comparamos vocales solo comparamos 10 veces .if ah!= "a" && ah!= "e" && ah!= "i" && ah!= "o" && ah!= "u" && ah!= "A" \ && ah!= "E" && ah!= "I" && ah!= "O" && ah!= "U" .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" ; si llegamos aquí es que hay tres consonantes seguidas ; la H primera lo que contiene ah y lo que contiene al JMP mas1 .endif .endif mov ax, word ptr [esi+1] ; estamos en el segunod caracter y vamos a ver 2º 3º y 4º si son consonantes .if ah!= "a" && ah!= "e" && ah!= "i" && ah!= "o" && ah!= "u" && ah!= "A" \ && ah!= "E" && ah!= "I" && ah!= "O" && ah!= "U" .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" ; si llegamos aqui esque esi+1 y esi+2 son consonantes mov al, byte ptr [esi+3] .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" JMP mas1 .endif .endif .endif mov ax, word ptr [esi+2] ; estamos en el tercer caracter y vamos a ver 3º 4º y 5º si son consonantes .if ah!= "a" && ah!= "e" && ah!= "i" && ah!= "o" && ah!= "u" && ah!= "A" \ && ah!= "E" && ah!= "I" && ah!= "O" && ah!= "U" .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" ; si llegamos aqui esque esi+2 y esi+3 son consonantes mov al, byte ptr [esi+4] .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" JMP mas1 .endif .endif .endif mov ax, word ptr [esi+3] ; estamos en el cuarto caracter y vamos a ver 4º 5º y 6º si son consonantes .if ah!= "a" && ah!= "e" && ah!= "i" && ah!= "o" && ah!= "u" && ah!= "A" \ && ah!= "E" && ah!= "I" && ah!= "O" && ah!= "U" .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" mov al, byte ptr [esi+5] .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" JMP mas1 .endif .endif .endif mov ax, word ptr [esi+4] ; estamos en el quinto caracter y vamos a ver 5º 6º y 7º si son consonantes .if ah!= "a" && ah!= "e" && ah!= "i" && ah!= "o" && ah!= "u" && ah!= "A" \ && ah!= "E" && ah!= "I" && ah!= "O" && ah!= "U" .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" mov al, byte ptr [esi+6] .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" JMP mas1 .endif .endif .endif mov ax, word ptr [esi+5] ; estamos en el sexto caracter y vamos a ver 6º 7º y 8º si son consonantes .if ah!= "a" && ah!= "e" && ah!= "i" && ah!= "o" && ah!= "u" && ah!= "A" \ && ah!= "E" && ah!= "I" && ah!= "O" && ah!= "U" .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" mov al, byte ptr [esi+7] .if al!= "a" && al!= "e" && al!= "i" && al!= "o" && al!= "u" && al!= "A" \ && al!= "E" && al!= "I" && al!= "O" && al!= "U" JMP mas1 .endif .endif .endif ;*****************fin consonantes******************************* ;************************************************************ ; tres vocales seguidas tambien es tonto ¿no? ;************************************************************ ; ya lo tenemos en esi ; mov esi, offset buffer mov ax, word ptr [esi+1] ; estamos en el segunod caracter y vamos a ver 2º 3º y 4º si son vocales .if ah== "a" || ah== "e" || ah== "i" || ah== "o" || ah== "u" || ah== "A" \ || ah== "E" || ah== "I" || ah== "O" || ah== "U" .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" ; si llegamos aqui esque esi+1 y esi+2 son vocales mov al, byte ptr [esi+3] .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" JMP mas1 .endif .endif .endif mov ax, word ptr [esi+2] ; estamos en el tercer caracter y vamos a ver 3º 4º y 5º si son vocales .if ah== "a" || ah== "e" || ah== "i" || ah== "o" || ah== "u" || ah== "A" \ || ah== "E" || ah== "I" || ah== "O" || ah== "U" .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" ; si llegamos aqui esque esi+2 y esi+3 son vocales mov al, byte ptr [esi+4] .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" JMP mas1 .endif .endif .endif mov ax, word ptr [esi+3] ; estamos en el cuarto caracter y vamos a ver 4º 5º y 6º si son vocales .if ah== "a" || ah== "e" || ah== "i" || ah== "o" || ah== "u" || ah== "A" \ || ah== "E" || ah== "I" || ah== "O" || ah== "U" .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" mov al, byte ptr [esi+5] .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" JMP mas1 .endif .endif .endif mov ax, word ptr [esi+4] ; estamos en el quinto caracter y vamos a ver 5º 6º y 7º si son vocales .if ah== "a" || ah== "e" || ah== "i" || ah== "o" || ah== "u" || ah== "A" \ || ah== "E" || ah== "I" || ah== "O" || ah== "U" .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" mov al, byte ptr [esi+6] .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" JMP mas1 .endif .endif .endif mov ax, word ptr [esi+5] ; estamos en el sexto caracter y vamos a ver 6º 7º y 8º si son vocales .if ah== "a" || ah== "e" || ah== "i" || ah== "o" || ah== "u" || ah== "A" \ || ah== "E" || ah== "I" || ah== "O" || ah== "U" .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" mov al, byte ptr [esi+7] .if al== "a" || al== "e" || al== "i" || al== "o" || al== "u" || al== "A" \ || al== "E" || al== "I" || al== "O" || al== "U" JMP mas1 .endif .endif .endif ;***************************************************************** ; ; No consentimos 3 B en las combinaciones ; ;***************************************************************** mov bes, 0 mov ax, word ptr [esi+1] .if ah=="B" || ah=="b" inc bes .endif .if al=="B" || al=="b" inc bes .endif mov ax, word ptr [esi+3] .if ah=="B" || ah=="b" inc bes .endif .if al=="B" || al=="b" inc bes .endif mov ax, word ptr [esi+5] .if ah=="B" || ah=="b" inc bes .endif .if al=="B" || al=="b" inc bes .endif mov ah, byte ptr [esi+7] .if ah=="B" || ah=="b" inc bes .endif .if bes >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 C en las combinaciones ; ;***************************************************************** mov ces, 0 mov ax, word ptr [esi+1] .if ah=="C" || ah=="c" inc ces .endif .if al=="C" || al=="c" inc ces .endif mov ax, word ptr [esi+3] .if ah=="C" || ah=="c" inc ces .endif .if al=="C" || al=="c" inc ces .endif mov ax, word ptr [esi+5] .if ah=="C" || ah=="c" inc ces .endif .if al=="C" || al=="c" inc ces .endif mov ah, byte ptr [esi+7] .if ah=="C" || ah=="c" inc ces .endif .if ces >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 D en las combinaciones ; ;***************************************************************** mov des, 0 mov ax, word ptr [esi+1] .if ah=="D" || ah=="d" inc des .endif .if al=="D" || al=="d" inc des .endif mov ax, word ptr [esi+3] .if ah=="D" || ah=="d" inc des .endif .if al=="D" || al=="d" inc des .endif mov ax, word ptr [esi+5] .if ah=="D" || ah=="d" inc des .endif .if al=="D" || al=="d" inc des .endif mov ah, byte ptr [esi+7] .if ah=="D" || ah=="d" inc des .endif .if des >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 G en las combinaciones ; ;***************************************************************** mov ges, 0 mov ax, word ptr [esi+1] .if ah=="G" || ah=="g" inc ges .endif .if al=="G" || al=="g" inc ges .endif mov ax, word ptr [esi+3] .if ah=="G" || ah=="g" inc ges .endif .if al=="G" || al=="g" inc ges .endif mov ax, word ptr [esi+5] .if ah=="G" || ah=="g" inc ges .endif .if al=="G" || al=="g" inc ges .endif mov ah, byte ptr [esi+7] .if ah=="G" || ah=="g" inc ges .endif .if ges >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 H en las combinaciones ; ;***************************************************************** ; la primera es una hache asi que haches ya vale 1 mov haches, 1 mov ax, word ptr [esi+1] .if ah=="H" || ah=="h" inc haches .endif .if al=="H" || al=="h" inc haches .endif mov ax, word ptr [esi+3] .if ah=="H" || ah=="h" inc haches .endif .if al=="H" || al=="h" inc haches .endif mov ax, word ptr [esi+5] .if ah=="H" || ah=="h" inc haches .endif .if al=="H" || al=="h" inc haches .endif mov ah, byte ptr [esi+7] .if ah=="H" || ah=="h" inc haches .endif .if haches>2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 Z en las combinaciones ; ;***************************************************************** mov cetas, 0 mov ax, word ptr [esi+1] .if ah=="Z" || ah=="z" inc cetas .endif .if al=="Z" || al=="z" inc cetas .endif mov ax, word ptr [esi+3] .if ah=="Z" || ah=="z" inc cetas .endif .if al=="Z" || al=="z" inc cetas .endif mov ax, word ptr [esi+5] .if ah=="Z" || ah=="z" inc cetas .endif .if al=="Z" || al=="z" inc cetas .endif mov ah, byte ptr [esi+7] .if ah=="Z" || ah=="z" inc cetas .endif .if cetas >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 2 J en las combinaciones ; ;***************************************************************** mov jotas, 0 mov ax, word ptr [esi+1] .if ah=="J" || ah=="j" inc jotas .endif .if al=="J" || al=="j" inc jotas .endif mov ax, word ptr [esi+3] .if ah=="J" || ah=="j" inc jotas .endif .if al=="J" || al=="j" inc jotas .endif mov ax, word ptr [esi+5] .if ah=="J" || ah=="j" inc jotas .endif .if al=="J" || al=="j" inc jotas .endif mov ah, byte ptr [esi+7] .if ah=="J" || ah=="j" inc jotas .endif .if jotas >1 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 K en las combinaciones ; ;***************************************************************** mov kas, 0 mov ax, word ptr [esi+1] .if ah=="K" || ah=="k" inc kas .endif .if al=="K" || al=="k" inc kas .endif mov ax, word ptr [esi+3] .if ah=="K" || ah=="k" inc kas .endif .if al=="K" || al=="k" inc kas .endif mov ax, word ptr [esi+5] .if ah=="K" || ah=="k" inc kas .endif .if al=="K" || al=="k" inc kas .endif mov ah, byte ptr [esi+7] .if ah=="K" || ah=="k" inc kas .endif .if kas >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 M en las combinaciones ; ;***************************************************************** mov emes, 0 mov ax, word ptr [esi+1] .if ah=="M" || ah=="m" inc emes .endif .if al=="M" || al=="m" inc emes .endif mov ax, word ptr [esi+3] .if ah=="M" || ah=="m" inc emes .endif .if al=="M" || al=="m" inc emes .endif mov ax, word ptr [esi+5] .if ah=="M" || ah=="m" inc emes .endif .if al=="M" || al=="m" inc emes .endif mov ah, byte ptr [esi+7] .if ah=="M" || ah=="m" inc emes .endif .if emes >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 N en las combinaciones ; ;***************************************************************** mov enes, 0 mov ax, word ptr [esi+1] .if ah=="N" || ah=="n" inc enes .endif .if al=="N" || al=="n" inc enes .endif mov ax, word ptr [esi+3] .if ah=="N" || ah=="n" inc enes .endif .if al=="N" || al=="n" inc enes .endif mov ax, word ptr [esi+5] .if ah=="N" || ah=="n" inc enes .endif .if al=="N" || al=="n" inc enes .endif mov ah, byte ptr [esi+7] .if ah=="N" || ah=="n" inc enes .endif .if enes >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 P en las combinaciones ; ;***************************************************************** mov pes, 0 mov ax, word ptr [esi+1] .if ah=="P" || ah=="p" inc pes .endif .if al=="P" || al=="p" inc pes .endif mov ax, word ptr [esi+3] .if ah=="P" || ah=="p" inc pes .endif .if al=="P" || al=="p" inc pes .endif mov ax, word ptr [esi+5] .if ah=="P" || ah=="p" inc pes .endif .if al=="P" || al=="p" inc pes .endif mov ah, byte ptr [esi+7] .if ah=="P" || ah=="p" inc pes .endif .if pes >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 R en las combinaciones ; ;***************************************************************** mov erres, 0 mov ax, word ptr [esi+1] .if ah=="R" || ah=="r" inc erres .endif .if al=="R" || al=="r" inc erres .endif mov ax, word ptr [esi+3] .if ah=="R" || ah=="r" inc erres .endif .if al=="R" || al=="r" inc erres .endif mov ax, word ptr [esi+5] .if ah=="R" || ah=="r" inc erres .endif .if al=="R" || al=="r" inc erres .endif mov ah, byte ptr [esi+7] .if ah=="R" || ah=="r" inc erres .endif .if erres >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 S en las combinaciones ; ;***************************************************************** mov eses, 0 mov ax, word ptr [esi+1] .if ah=="S" || ah=="s" inc eses .endif .if al=="S" || al=="s" inc eses .endif mov ax, word ptr [esi+3] .if ah=="S" || ah=="s" inc eses .endif .if al=="S" || al=="s" inc eses .endif mov ax, word ptr [esi+5] .if ah=="S" || ah=="s" inc eses .endif .if al=="S" || al=="s" inc eses .endif mov ah, byte ptr [esi+7] .if ah=="S" || ah=="s" inc eses .endif .if eses >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 T en las combinaciones ; ;***************************************************************** mov tes, 0 mov ax, word ptr [esi+1] .if ah=="T" || ah=="t" inc tes .endif .if al=="T" || al=="t" inc tes .endif mov ax, word ptr [esi+3] .if ah=="T" || ah=="t" inc tes .endif .if al=="T" || al=="t" inc tes .endif mov ax, word ptr [esi+5] .if ah=="T" || ah=="t" inc tes .endif .if al=="T" || al=="t" inc tes .endif mov ah, byte ptr [esi+7] .if ah=="T" || ah=="t" inc tes .endif .if tes >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 3 V en las combinaciones ; ;***************************************************************** mov uves, 0 mov ax, word ptr [esi+1] .if ah=="V" || ah=="v" inc uves .endif .if al=="V" || al=="v" inc uves .endif mov ax, word ptr [esi+3] .if ah=="V" || ah=="v" inc uves .endif .if al=="V" || al=="v" inc uves .endif mov ax, word ptr [esi+5] .if ah=="V" || ah=="v" inc uves .endif .if al=="V" || al=="v" inc uves .endif mov ah, byte ptr [esi+7] .if ah=="V" || ah=="v" inc uves .endif .if uves >2 JMP mas1 .endif ;***************************************************************** ; ; No consentimos 2 X en las combinaciones ; ;***************************************************************** mov exis, 0 mov ax, word ptr [esi+1] .if ah=="X" || ah=="x" inc exis .endif .if al=="X" || al=="x" inc exis .endif mov ax, word ptr [esi+3] .if ah=="X" || ah=="x" inc exis .endif .if al=="X" || al=="x" inc exis .endif mov ax, word ptr [esi+5] .if ah=="X" || ah=="x" inc exis .endif .if al=="X" || al=="x" inc exis .endif mov ah, byte ptr [esi+7] .if ah=="X" || ah=="x" inc exis .endif .if exis >1 JMP mas1 .endif altajo: mov esi, offset buffer pusha inc esi ;inc esi para saltarse la H xor eax, eax xor edx, edx dec esi mov eax, 0f1c05789h ;este nº raro es para saltarse la comprovacion a3: ;de la 1ª H (nos evitamos un bucle de FFFF ciclos) inc esi xor ah,[esi] a0: xor al, dl add eax, 04E493220h mov cl, al ror eax, cl xor eax, 055AA5A5Ah dec dx jnz a0 cmp byte ptr [esi+1], 0 ;en el ultimo bucle esi apunta a 0 jnz a3 ;por lo tanto siempre tendremos el mismo resultado xor eax, 0d202eef9h ;de esta forma nos evitamos otro bucle de FFFFh ciclos popa jz escribir jmp mas1 leer: pusha invoke CreateFile,ADDR archivo, \ GENERIC_READ+GENERIC_WRITE, \ NULL, \ NULL, \ OPEN_EXISTING, \ FILE_ATTRIBUTE_NORMAL, \ NULL mov hfichero, eax invoke GetFileSize,hfichero,NULL mov fsize, eax mov eax, 0ffffh invoke GlobalAlloc, NULL,eax mov memptr,eax invoke ReadFile,hfichero,memptr,fsize,ADDR bread,NULL mov eax, memptr add eax, fsize sub eax, 0Ah invoke lstrcpyn,ADDR buffer , ; copio la cadena eax, 9 ;invoke GlobalFree, memptr invoke CloseHandle, hfichero popa ret escribir: pusha invoke CreateFile,ADDR archivo, \ GENERIC_READ+GENERIC_WRITE, \ NULL, \ NULL, \ OPEN_EXISTING, \ FILE_ATTRIBUTE_NORMAL, \ NULL mov hfichero, eax invoke GetFileSize,hfichero,NULL mov fsize,eax ; add eax, 0ah ; invoke GlobalReAlloc,memptr ,eax,NULL ;mov memptr,eax invoke ReadFile,hfichero,memptr,fsize,ADDR bread,NULL mov ebx, memptr add ebx, fsize invoke lstrcpyn,ebx , ; copio la cadena ADDR buffer, 9 mov dword ptr [ebx+8], 0a0dh mov ebx, fsize add ebx, 0AH push ebx pop fsize invoke SetFilePointer,hfichero,NULL,NULL,FILE_BEGIN invoke WriteFile,hfichero,memptr,fsize,ADDR bwrite,NULL ; invoke GlobalFree, memptr invoke CloseHandle, hfichero popa jmp mas1 fin: invoke MessageBox,NULL,ADDR sacabo,ADDR marmota,MB_OK invoke ExitProcess,hInstance end start Conclusiones Creo necesario puntualizar algunos detalles que considero importantes. - Casi al final del codigo, están las rutinas que comprueban si la cadena pasa los test. Observad como se ha optimizado el codigo para aumentar la velocidad de proceso. Podréis ver anotaciones que hemos ido haciéndonos sobre la marcha para sacarle el mayor partido posible. - Hay muchas combinaciones que pasan los test y producen en el Crackme el desencriptado del codigo. Opino que es un fallo grave en la estructura del programa, ya que genera codigo impredecible y despues lo ejecuta. No se han dado casos de situaciones graves, pero estas cosas son peligrosas ;-). - Crack el Destripador opina que tal y como esta planteada la protección, no tiene viabilidad comercial, ya que depende de un único serial, y ya sabemos todos como circulan los seriales XDDDD. Sería interensate este tipo de protección si el serial se montara en base a alguna peculiaridad del equipo donde rueda como por ejemplo el número de serie de windows. - Como el marm6.exe funciona en segundo plano y el sistema estaba bajo minimos para aumentar el tiempo de procesador dedicado al FB, utilizamos PUPE para visualizar la evolucion de las combinaciones y matar el proceso cuando teniamos que ocuparnos de otras cosas. - Agradecer a todos los que por unos días nos prestaron algo de su tiempo de procesador y corrieron en sus maquinas el marm6.exe Crick Avalanche JJKay coheta Un saludo muy especial a JoTaKe y a Crick que también invirtieron su tiempo en desentrañar el misterio de la Clave del Crackme #2 de Ni2. Antes de que se me olvide... la clave es HeyBoyos Marmota & Crack el Destripador a 31 de Diciembre de 2000.
[ Entrada | Documentos Genéricos | WkT! Web Site ]
[ Todo el ECD | x Tipo de Protección | x Fecha de Publicación | x Orden Alfabético ]
(c) Whiskey Kon Tekila [WkT!] - The Original Spanish Reversers. Si necesitas contactar con nosotros , lee esto antes e infórmate de cómo puedes ayudarnos