|
|
 |
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 |
|
|
|
|
|