Objetivo: REVIVAL 2.1
Nombre: revive21.zip
Tamaño: 874.644 bytes
Versión: 2.1
Site: http://uc2.unicall.be/revival/
Herramientas: SoftIce,W32dasm o IDA 3.75 y un compilador
de C.
Dificultad: No mu difícil.
Tiempo: 5 horas.
Este es un interesante programa que te permite recuperar
ficheros borrados de discos duros y disquetes que soporta
FAT32 y NTFS. Tiene una típica ventana de registro a partir
de la cual podemos acceder directamente a la rutina de verificación
de la pass. Esta rutina es extremadamente sencilla e independiente
de los datos del usuario, por eso ha sido la elegida como
demostración.
Aconsejo desensamblar con el IDA PRO 3.75(una pequeña maravilla
de desensamblador). Se puede hacer con el W32dasm pero el
IDA nos da más información y nos ahorra trabajo. Por ejemplo
descubre de forma automática rutinas (_touper, isdigit...)
que no son reconocidas como tales por el W32dasm.
Bien, manos a la obra, desensamblemos con el IDA.
¿Ya está?
Bien, ahora sólo hay que localizar la rutina de verificación.
Empleemos un viejo truco: el 80% de las rutinas de verificación
intentan localizar el carácter '-' (2D en hexa) o el carácter
'+' (2B en hexa).
No me preguntéis por qué, pero lo hacen. Sólo hace falta buscar
un 2Dh o un 2Bh y con un poco de suerte aterrizaremos en plena
rutina de verificación. Debemos buscar una comprobación con
2Dh o bien 2Bh, pero como las comprobaciones pueden ser de
muchos tipos , sólo buscaremos la parte final de la comprobación.
Resumiendo, buscaremos ", 2Dh" y ", 2Bh". En caso de existir
demasiadas ocurrencias, mejor decantarse por otro método de
ataque. Al final es el olfato de cracker el que te indica
si estás en la ocurrencia correcta o no.
En este caso hay 20 ocurrencias de "2D" y 18 de "2B". UFF,
quizás demasiadas (de hecho es la primera ocurrencia de "2D"
la correcta), así que probemos un método más directo con el
Softice.
Metemos como nombre ESTADO, como campañía PORCINO y como número
de serie estúpido por ejemplo 1212121212. CTRL+D y le damos
al botón de OK y aparece una ventana de error.
Esta ventana se parece a un messageboxexa (por su simplicidad
y por el icono en forma de exclamación y por el único botón
que aparece). Si repetimos el mismo proceso pero poniendo
en el softice bpx messsageboxexa y pulsamos el botón de OK
...
Bingo, aparecemos en la rutina de messageboxexa. Sólo hay
que seguir la secuencia de llamadas hacia atrás buscando un
salto que evite llamar a la ventana de mensaje de error. La
secuencia de pasos es:
* Paramos en bpx messageboxexa
* Pulsamos F12 para llegar a la rutina padre que llamó a messageboxexa.
* Aparecemos en :4313CA pero por se ve ningún salto que evite
la llamada a messageboxexa.
* Pulsamos F12 para seguir subiendo hasta la rutina padre.
* Aparecemos en :43145D. Pero de nuevo nada interesante.
* De nuevo F12 y ....
* Aparecemos en :40CAAC8, esta si tiene lo que buscamos, exactamente.
0040AA6C call sub_40CD10 ; RUTINA DE VERIFICACIÓN
0040AA71 add esp, 4
0040AA74 test eax, eax
0040AA76 jz short loc_40AABA; SALTA SI ERES UN MAL CRACKER.
0040AA78 mov dword ptr [esi+5Ch], 1
0040AA7F mov eax, [esi+64h]
0040AA82 push eax
0040AA83 push offset aName;NOMBRE.
0040AA88 call sub_40AD70
0040AA8D add esp, 8
0040AA90 mov eax, [esi+60h]
0040AA93 push eax
0040AA94 push offset aCompany;COMPAÑÍA.
0040AA99 call sub_40AD70
0040AA9E add esp, 8
0040AAA1 mov eax, [edi]
0040AAA3 push eax
0040AAA4 push offset aSerial;NÚMERO DE SERIE.
0040AAA9 call sub_40AD70
0040AAAE add esp, 8
0040AAB1 mov ecx, esi
0040AAB3 call sub_42550E
0040AAB8 jmp short loc_40AACF
0040AABA ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
0040AABA
0040AABA loc_40AABA: ; CODE XREF: sub_40AA20+56_j
0040AABA push 0FFFFFFFFh
0040AABC push 30h
0040AABE push 0EF1Fh ; DIRECCION DEL MENSAJE DE ERROR
0040AAC3 call sub_431413 ; VENTANA DE MESAJE DE ERROR
0040AAC8 mov ecx, esi
0040AACA call sub_425527
0040AACF
0040AACF loc_40AACF: ; CODE XREF: sub_40AA20+98_j
0040AACF push 0FFFFFFFFh
0040AAD1 mov ecx, edi
0040AAD3 call sub_429A33
0040AAD8 pop edi
0040AAD9 pop esi
0040AADA retn
0040AADB ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
0040AADB
0040AADB loc_40AADB: ; CODE XREF: sub_40AA20+25_j
0040AADB ; sub_40AA20+40_j
0040AADB push 0FFFFFFFFh
0040AADD push 30h
0040AADF push 0EF1Eh
0040AAE4 call sub_431413
0040AAE9 pop edi
0040AAEA pop esi
0040AAEB retn
0040AAEB sub_40AA20 endp
Fijaos en el salto en :40AA76. Si saltamos caemos en la ventana
de mensaje y evitamos acceder a NOMBRE,COMPAÑÍA y NÚMERO DE
SERIE. El salto está controlado por :40AA6C call sub_40CD10
.Que interesante, una rutina que controla la ventana de mensaje
de error, ¿a qué nos suena ésto?. BINGO, estamos ante la rutina
de verificación.
Échemósle un vistazo y comentémosla.
;Desensamblado con el IDA ;p(0) indica el carácter 0 de la
password. Recordad, empiezo a contar los caracteres desde
0.
0040CD10 sub_40CD10 proc near ; CODE XREF: sub_404600+98_p
0040CD10 ; sub_40AA20+4C_p
0040CD10
0040CD10 var_24 = byte ptr -24h ; 1 variable local.
0040CD10 var_20 = word ptr -20h ; 2 variable local.
0040CD10 var_1E = byte ptr -1Eh ; 3 variable local.
0040CD10 var_1B = byte ptr -1Bh ; 4 variable local.
0040CD10 arg_0 = dword ptr 4 ; Argumento de la función que no es más que la dirección de nuestra password.
0040CD10
0040CD10 sub esp, 24h; Ajusta la pila para reservar espacio para las varibles locales.
0040CD13 push ebx; Guarda algunos registros.
0040CD14 push esi
0040CD15 mov esi, [esp+2Ch+arg_0] ; esi= dirección de nuestra password.
0040CD19 push edi
0040CD1A movsx eax, byte ptr [esi] ; eax=p(0)
0040CD1D push eax
0040CD1E call _toupper ; Pasamos a mayúsculas p(0).
0040CD23 add esp, 4
0040CD26 cmp eax, 52h ; ¿ES P(0) = R?
0040CD29 jnz loc_40CE7F ; Salta a flag de error si p(0) no es R.
0040CD2F movsx eax, byte ptr [esi+1] ; eax=p(1)
0040CD33 push eax
0040CD34 call _toupper ; Pasamos a mayúsculas p(1).
0040CD39 add esp, 4
0040CD3C cmp eax, 56h ; ¿ES P(1) = V?
0040CD3F jnz loc_40CE7F ; Salta a flag de error si p(1) no es V.
0040CD45 cmp byte ptr [esi+7], 2Dh ; ¿ES P(7) = '-'?
0040CD49 jnz loc_40CE7F ; Salta a flag de error si p(7) no es '-'.
0040CD4F push esi
0040CD50 call ds:lstrlenA ; Calcula el tamaño de la password.
0040CD56 cmp eax, 0Fh ; ¿Es el tamaño 15?
0040CD59 jnz loc_40CE7F ; Salta a flag de error si el tamaño no es 15
0040CD5F mov edi, 2 ; Segundo carácter.
0040CD64
0040CD64 loc_40CD64: ; CODE XREF: sub_40CD10+6D_j
;Bucle para comprobar que son números p(2)...p(6)
0040CD64 movsx eax, byte ptr [edi+esi]; eax=p(2)
0040CD68 push eax
0040CD69 call _isdigit ; ¿es un número p(2)?
0040CD6E add esp, 4
0040CD71 test eax, eax
0040CD73 jz loc_40CE64 ; Salta con flag de error si p(2) no es número.
0040CD79 inc edi ; Apuntamos al siguiente carácter.
0040CD7A cmp edi, 7 ; ¿Hemos llegado a p(7)?
0040CD7D jl short loc_40CD64 ; Salta si no hemos llegado a p(7).
0040CD7F mov edi, 8 ; Octavo carácter.
;Bucle para comprobar que son números p(8)...p(14)
0040CD84
0040CD84 loc_40CD84: ; CODE XREF: sub_40CD10+8D_j
0040CD84 movsx eax, byte ptr [edi+esi];; eax=p(8)
0040CD88 push eax
0040CD89 call _isdigit ;¿es un número p(8)?
0040CD8E add esp, 4
0040CD91 test eax, eax
0040CD93 jz loc_40CE6D ; Salta con flag de error si p(8) no es número.
0040CD99 inc edi ; Apuntamos al siguiente carácter.
0040CD9A cmp edi, 0Fh ; ¿Hemos llegado a p(15)?
0040CD9D jl short loc_40CD84; Salta si no hemos llegado a p(15).
0040CD9F mov ax, [esi+2] ; ax=p(2)p(3)
0040CDA3 mov [esp+30h+var_20], ax
0040CDA8 lea eax, [esp+30h+var_20]
0040CDAC mov [esp+30h+var_1E], 0
0040CDB1 push eax
0040CDB2 call _atoi ;Pasa p(2)p(3) a número.
0040CDB7 mov cx, [esi+5] ;cx=p(5)p(6)
0040CDBB add esp, 4
0040CDBE mov [esp+30h+var_20], cx
0040CDC3 sub al, 13h ; al=p(2)p(3)-19
0040CDC5 lea ecx, [esp+30h+var_20]
0040CDC9 mov [esp+30h+var_24], al
0040CDCD mov [esp+30h+var_1E], 0
0040CDD2 push ecx
0040CDD3 call _atoi ;Pasa p(5)p(6) a número.
0040CDD8 lea edx, [esp+34h+var_20];
0040CDDC add esp, 4
0040CDDF lea ebx, [eax-25h] ;ebx=p(5)p(6)-37
0040CDE2 lea ecx, [esi+0Ah] ;ecx=dirección de p(10)
0040CDE5 push edx
0040CDE6 mov eax, [ecx] ;ebx=p(10)p(11)p(12)p(13)
0040CDE8 mov [edx], eax
0040CDEA mov cl, [ecx+4] ;cl=p(14)
0040CDED mov [edx+4], cl
0040CDF0 mov [esp+34h+var_1B], 0
0040CDF5 call _atoi ;Pasa p(10)p(11)p(12)p(13)p(14) a número.
0040CDFA add esp, 4
0040CDFD mov edi, eax
0040CDFF xor di, 5468h ;di=p(10)p(11)p(12)p(13)p(14) XOR 21508
0040CE04 mov ax, [esi+8] ;ax=p(8)p(9)
0040CE08 mov [esp+30h+var_20], ax
0040CE0D lea eax, [esp+30h+var_20]
0040CE11 mov [esp+30h+var_1E], 0
0040CE16 movzx edi, di
0040CE19 push eax
0040CE1A call _atoi ;Pasa p(8)p(9) a número.
0040CE1F mov byte ptr [esp+34h+var_20], al
0040CE23 add esp, 4
0040CE26 xor eax, eax
0040CE28 mov ecx, 64h
0040CE2D mov al, bl ;al=p(5)p(6)-37
0040CE2F mov ebx, 0Ah
0040CE34 lea eax, [eax+edi+3]; eax'=(p(5)p(6)-37)+(p(10)p(11)p(12)p(13)p(14) XOR 21508) + 3
0040CE38 cdq
0040CE39 idiv ecx ;Divide eax'/100
0040CE3B mov cl, dl ;cl=resto(eax'/100)
0040CE3D xor eax, eax
0040CE3F mov al, [esp+30h+var_24]
0040CE43 lea eax, [eax+edi+3]; eax=(p(2)p(3)-19)+(p(10)p(11)p(12)p(13)p(14) XOR 21508) + 3
0040CE47 cdq
0040CE48 idiv ebx ;Divide eax/10
0040CE4A sub dl, [esi+4] ;dl=resto(eax/10)-p(4)
0040CE4D cmp dl, 0D0h ;¿Es resto(eax/10) = p(4)?
0040CE50 jnz short loc_40CE76;Salta a flag de error si resto(eax/10) no es p(4)
0040CE52 cmp byte ptr [esp+30h+var_20], cl;¿Es resto(eax'/100) = p(8)p(9)?
0040CE56 jnz short loc_40CE76;Salta a flag de error si resto(eax'/100) no es p(4)
0040CE58 mov eax, 1 ; Ok todo correcto. Flag de éxito activado.
0040CE5D pop edi
0040CE5E pop esi
0040CE5F pop ebx
0040CE60 add esp, 24h
0040CE63 retn
0040CE64 ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
0040CE64
0040CE64 loc_40CE64: ; CODE XREF: sub_40CD10+63_j
0040CE64 xor eax, eax ; Eres un mal chico.Flag de error activado.
0040CE66 pop edi
0040CE67 pop esi
0040CE68 pop ebx
0040CE69 add esp, 24h
0040CE6C retn
0040CE6D ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
0040CE6D
0040CE6D loc_40CE6D: ; CODE XREF: sub_40CD10+83_j
0040CE6D xor eax, eax ; Eres un mal chico.Flag de error activado.
0040CE6F pop edi
0040CE70 pop esi
0040CE71 pop ebx
0040CE72 add esp, 24h
0040CE75 retn
0040CE76 ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
Antes de seguir adelante, centremonos en un par de puntos:
- ¿Habéis descubierto los números mágicos?, sip los hay son
5468h, 0Ah y 64h.
- Como es tradición la rutina checkea en este caso la presencia
del carácter '-'.
Luego con un poco de paciencia, nuestra búsqueda inicial hubiera
tenido sus frutos.
- Habéis notado la pésima calidad del código. Uso innecesario
de variables, instrucciones inútiles, tamaño del código exagerado.
Todo esto es debido a que se programó en alto nivel, seguramente
en C.
¿Cómo quieren los programadores proteger su software si es
de pésima calidad?. Están directamente vendidos (salvo honrosas
excepciones, por supuesto.)
- Si andais un poco pegaos de operacones aritméticas y de
ensamblador, buscad alguno
de los fabulosos cursos de ensamblador que hay en la Web.
Resumamos los momentos más interesantes de la rutina de verificación:
A)0040CD26 cmp eax, 52h ; ¿ES P(0) = R?
B)0040CD3C cmp eax, 56h ; ¿ES P(1) = V?
C)0040CD45 cmp byte ptr [esi+7], 2Dh ; ¿ES P(7) = '-'?
D)0040CD56 cmp eax, 0Fh ; ¿Es el tamaño 15?
E)0040CD64 ;Bucle para comprobar que son números p(2)...p(6)
F)0040CD84 ;Bucle para comprobar que son números p(8)...p(14)
G)0040CDC3 sub al, 13h ; al=p(2)p(3)-19
H)0040CDDF lea ebx, [eax-25h] ; ebx=p(5)p(6)-37
I)0040CE34 lea eax, [eax+edi+3] ; eax'=(p(5)p(6)-37)+(p(10)p(11)p(12)p(13)p(14)
XOR 21508) + 3
J)0040CE3B mov cl, dl ; cl=resto(eax'/100)
K)0040CE43 lea eax, [eax+edi+3] ; eax=(p(2)p(3)-19)+(p(10)p(11)p(12)p(13)p(14)
XOR 21508) + 3
L)0040CE4D cmp dl, 0D0h ;¿Es resto(eax/10) = p(4)?
M)0040CE52 cmp byte ptr [esp+30h+var_20], cl;¿Es resto(eax'/100) = p(8)p(9)?.
Por A),B),C),D),E) y F) sabemos que la password debe de tener este aspecto:
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
R V x x x x x - x x x x x x x x
Donde x es un número del 0 al 9.
Despues hay dos bonitas ecuaciones:
Por H),I),J) y M)
I) p(8)p(9)=resto( (p(5)p(6)-0x25)+(p(10)p(11)p(12)p(13)p(14) XOR 21508) + 3) / 0x64)
Por G),K),L)
II) p(4)=resto((p(2)p(3)-0x13)+(p(10)p(11)p(12)p(13)p(14) XOR 21508) + 3) / 0x0A)
Pos ya está. Estas son las ecuaciones de la rutina de
verificación, ya se puede implementar nuestro propio Generador
de Llaves, que no será más que implementar estas dos ecuaciones.
Estas dos ecuaciones comprueban que la parte derecha sea
igual a la parte izquierda (p(8)p(9) y p(4)). Nuestro Generador
calculara la parte derecha y construirá la parte izquierda
de forma adecuada. Se podrían simplificar un poco, pero
no lo haré pa no complicar el asunto.
Un posible Generador en C sería algo así como:
VER
CODIGO FUENTE DEL GENERADOR
Utilizo números aleatorios (random) para generar un número
de serie diferente cada vez que se ejecute e programa. Una
última curiosidad, donde creereis que guarda nuestra pass
el programa. Si lanzais el La utilidad regmon (analiza todos
los accesos al Registro dels Sistema) con el programa, podréis
apreciar que se accede a "HKEY_LOCAL_MACHINE\SOFTWARE\Revival\Revival\2.0\Serial"
Poco imaginativo, ¿verdad?. Podéis modificar este número para
evitar registraos y probad Con nuevas pass.
|