ESTUDIO COLECTIVO DE DESPROTECCIONES
Última Actualizacion: 25/10/2001

Programa Cualquiera con protección de password Package For The Web v2.1 W95 / W98 / NT
Descripción Breve Introducción a la Criptografía
Tipo Package For The Web v2.1
Tipo de Tutorial [X]Original, []Adaptación, []Aplicación, []Traducción
Url http://www.installshield.com
Protección Criptografía de Clave Secreta
Dificultad 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas Uedit + Softice
Objetivo Obtención de la clave secreta que permite la instalación
Cracker Mr.Crimson
Grupo Whiskey Kon Tekila
Fecha 20 de Febrero de 2000

INTRODUCCION

Este tutorial explica detalladamente cómo extraer la clave secreta que protege la instalación de algunos programas distribuidos con PackageForTheWeb (en adelante, PFTW).

He comprobado que hay cierta confusión entre los newbies acerca de PFTW e InstallShield.
Ambos programas pertenecen a InstallShield Software Corporation y tienen funciones diferentes:

  • InstallShield: Ejecuta la  instalación de software. Copia ficheros, actualiza el registro, crea iconos en el escritorio, etc.
  • PFTW: Un set de instalación InstallShield es una colección de ficheros (scripts, CABs, setup.exe, etc). Para empaquetarlos y distribuirlos en un fichero único puede usarse PFTW. 
Así, PFTW, es un paso previo a la instalación, momento que aprovechan algunos programadores para pedir una clave secreta. El motivo fundamental debe ser el poder trazar los downloads de sus programas (la clave se envía por e-mail después de haber cumplimentado un formulario) o simplemente desautorizar al gran público a usar la demo.

Para seguir este tutorial sería interesante disponer de una app protegida de esta manera. Debería haber una, creada para la ocasión, aquí:  test.zip (237 Kb)

He implementado el método explicado en este tute en una pequeña utilidad. Úsala a tu discreción: PFTW Password Recovery v1.0  (Bugs report appreciated).

Ponte tus gafas de culo de botella, alísate el flequillo con saliva y pon carita de alienado porque entramos a saco en el retorcido mundo del criptoanálisis.

LA REFLEXION CASPOSA

El cracker nace, crece, se reproduce y se convierte en programador a sueldo.
Obviando la reproducción, la etapa más interesante es la del crecimiento porque en ella tiene lugar un cambio de actitud. 

Los primeros cracks suelen ser parches, 74h/EBh, EBh/90h. Cambiar instrucciones de bifurcación , NOPear bytes, borrar claves del registro. La característica común está en que se intenta ELIMINAR  aquello que protege el programa.

Conforme vamos evolucionando, empezamos a tratar con programas que piden la APORTACIÓN de algo, ya se trate de números de serie, discos/ficheros llave, passwords, dongles... Pero esto lleva su tiempo y muchas veces no disponemos del necesario.

Con esta casposa reflexión quiero decir que una protección incrementa sus posibilidades de éxito cuando se basa en un elemento externo que NO PUEDE DEDUCIRSE de la propia aplicación. Más aún, cuando ese elemento externo forma parte activa de la aplicación en sí.

Un esquema clásico de número de serie, por ejemplo, hace aguas en dos puntos:

  • El algoritmo puede ser muy complejo, pero está en el código. En caso de ser invertible siempre podemos averiguar la clave correcta puesto que sabemos como debe ser el resultado final (validación)
  • El resultado mismo de la validación puede ser falseado.
Un método más potente encriptaría la propia aplicación con una clave secreta:
  • Podemos acceder al algoritmo de cifrado en el código pero no sirve de nada puesto que no conocemos ni la clave ni el resultado de la transformación
  • La validación no puede falsearse porque es en sí mismo el funcionamiento de la aplicación.
 Estas disquisiciones quedarán claras al lector avejentado, que no aventajado, conforme avance en la lectura de este ameno tute.
TRAZANDO APLICACIONES 'Packaged For The Web' v2.1

Escribí este tute pensando en la versión 2.1.13.000 de PFTW. Puede que siga siendo válido en versiones futuras y puede que no. No me escribais para decirme que los offsets no coinciden o que el método no funciona con versiones posteriores... simplemente resolvedlo.

Empezamos, como es usual por arrancar SoftICE con la aplicacion empaquetada con PFTW. (En este tute usaré la demo que os he preparado: test.zip (237 Kb)
Avanzando sobre los sucesivos cuadros de diálogo llegamos a donde se pide el password:

CTRL-D para regresar a SoftICE y establecer un breakpoint en la función 'GetWindowText'. Regresando a la ventana tecleamos cualquier cosa y tenemos el debugger activado.
Cada vez que tecleamos un carácter, el total es leído y sometido a la validación como posible password. El botón finalizar se habilita cuando esta validación se ha completado con éxito.

La cosa tiene lugar así:

:00404A80 call GetWindowTextA           ;Lee passwd tecleado
:00404A86 mov eax, dword ptr [0041A8F4]
:00404A8B push eax                      ;pasa passwd a función
:00404A8C call 00406E83                 ;funcion encriptado passwd 
:00404A91 add esp, 00000004
:00404A94 mov dword ptr [ebp-04], eax   ;Resultado en eax
:00404A97 mov ecx, dword ptr [ebp-04]   ;Copiado a ecx
:00404A9A cmp ecx, dword ptr [0041933C] ;Comparado con el correcto
:00404AA0 jne 00404AA9                  ;No coincide---> Fuckoff! 
:00404AA2 mov eax, 00000001 
:00404AA7 jmp 00404AAF                  ;Ok, prueba superada...!

Ooops... esto parece tan sencillo....
Vamos a falsear la comparación en :404AA0 y para ello tecleamos "R FL=Z" en SoftICE (activa la bandera Z, comparación exitosa). 
F5 para proseguir la ejecución y volvemos a la ventana original, esta vez con el botón "Finalizar" habilitado. 
Pulsando el botón aterrizamos en una segunda comprobación en el mismo sitio anterior. Resolvemos de la misma manera y llegamos hasta el siguiente cuadro de diálogo:

 Esto tiene muy buen color... pulsamos "Continue".
Crash...! Obtenemos un mensaje de error que puede variar pero será algo del estilo de:

  • "invalid command line"
  • "Not enough free space in disk"
  • "Error running xxxx.exe"
con el cual se abortará la instalación.
Pero, cómo es posible...? Si habíamos conseguido "engañar" al código responsable de validar el password. Quizá tiene lugar alguna otra comparación que hemos pasado por alto...?
Veamoslo.
CIENTOS DE CLAVES

El lector interesado puede detenerse a analizar la 'validación' que tiene lugar en la llamada a la función:

:00404A8B push eax                      ;pasa passwd a función
:00404A8C call 00406E83                 ;funcion encriptado passwd 

La cadena que introducimos como presunto password es transformada después de algunas operaciones en una doble palabra que será comparada con el valor correcto.

Si atacamos este esquema por fuerza bruta quizá podamos averiguar algo. Teniendo presente las limitaciones de esta 'tecnica' (ver tute "AZPR Cracking Multitarea!") podemos escribir un programa 'copiando' la rutina :406E83 que permita comprobar la validez de un elevado número de presuntos passwords. 

El resultado es relativamente sorprendente: Con cualquier longitud, hay CIENTOS de passwords que habilitan el botón "Finalizar". Sin embargo después de probar unos cuantos observé que todos reportaban el mismo error al desempaquetar el programa. La fuerza bruta no resuelve el problema en este caso. 

Lo que sucede está claro ahora. Existe una comprobación preliminar de validez del password que simplemente nos permite seguir adelante con el proceso. Sin embargo hay un momento en que sólo la AUTENTICA clave será capaz de desencriptar el paquete.

EL METODO DE Vigenère

El encriptado del paquete se basa en una variante del método de Vigenère (1.586). Durante mucho tiempo se consideró la forma más segura de guardar datos aunque ha llovido bastante desde eso.
El método consiste en lo siguiente:
  • Se elige un password.
  • Se encripta cada carácter del mensaje con cada uno del password.
  • Cuando se acaban los caracteres del password se vuelve al primero.
Veamos un ejemplo sencillo:
 
Mensaje original: Mr Crimson 4D 72 20 43 72 69 6D 73 6F 6E
Clave: wkt  wk twktwkt 77 6B 74 77 6B 74 77 6B 74 77
Encriptado (XOR): :......... 3A 19 54 34 19 1D 1A 18 1B 19

Que tal...? Cómo extraeríais el mensaje original de:

3A 19 54 34 19 1D 1A 18 1B 19

si no sabeis siquiera la longitud del password...?

La potencia de esta sencilla técnica aumenta considerablemente si los datos a encriptar han sido a su vez previamente encriptados o comprimidos.
Hay mucho que contar sobre esto, por eso lo mejor será dejarlo y volver al tema que nos ocupa, PFTW

EL ALGORITMO EN PFTW

Vamos a ver como se implementa esta variante psicodélica del método de Vigenère en PFTW.
Primero hay que decir que el bloque encriptado que contiene la instalación protegida se vuelca en tiempo de ejecución a un fichero externo alojado en:

\windows\temp\pftXXXX~TMP\pft1.pkg

En estado puro tiene este aspecto:

Una vez creado se mapea en memoria y se procede a deshacer el cifrado.
Cuando la desencriptación se completa con éxito, proporciona a la instalación información necesaria acerca de los ficheros de programa, las ubicaciones y sus contenidos.
Un password falso (o un falseo de las validaciones comentadas anteriormente) da lugar a un pft1.pkg sin sentido y es por esto que se obtenien errores del mismo tipo

El comienzo del proceso tiene lugar en :40744A donde se procede al encriptado del propio password para reforzar un poco el esquema. Este cifrado se realiza por el mismo método comentado antes y utiliza una llave  fija (usada incluso en otras versiones):

13h-35h-86h-07h

Entonces un password introducido como 'MrCrimson' se encriptaría así:
 
Password original: Mr Crimson  4D 72 20 43 72 69 6D 73 6F 6E
Clave: 13358607h  13 35 86 07 13 35 86 07 13 35
Encriptado (XOR): 5E 47 A6 44 61 5C EB 74 7C 5B

Puedes ver que esto es así analizando este bucle de código:

:00407448 33C9                    xor ecx, ecx
:0040744A 8A08                    mov cl, byte ptr [eax]
:0040744C 8B45F8                  mov eax, dword ptr [ebp-08]
:0040744F 99                      cdq
:00407450 F77D14                  idiv [ebp+14]
:00407453 8B4510                  mov eax, dword ptr [ebp+10]
:00407456 33DB                    xor ebx, ebx
:00407458 8A1C10                  mov bl, byte ptr [eax+edx]
:0040745B 33CB                   xor ecx, ebx Encriptado
:0040745D 8B55FC                  mov edx, dword ptr [ebp-04]
:00407460 0355F8                  add edx, dword ptr [ebp-08]
:00407463 880A                    mov byte ptr [edx], cl
:00407465 EBCA                    jmp 00407431
 

Ahora, con este password cifrado se procede al desencriptado del fichero mencionado.
El algoritmo es sólo un poquito más complejo aunque mantiene la analogía:

  • Se toma el 1er byte del fichero y se rota (i.e. ECh cambia a CEh)
  • Se hace XOR con el 1er byte del password cifrado.
  • Se niega con NOT
  • Siguiente byte del fichero / Siguiente byte del passwd
  • Cuando se acaban los bytes del passwd se vuelve al primero
Esto sucede en un bucle al total de bytes del fichero:

:004072FA mov al, byte ptr [edx+eax]  ;i-esimo byte del password
:004072FD push eax
:004072FE mov ecx, dword ptr [ebp-38]
:00407301 mov edx, dword ptr [ebp-3C]
:00407304 mov al, byte ptr [edx+ecx]  ;i-ésimo byte del fichero :00407307 push eax
:00407308 call 004073CF               ;Rotacion/Xor/Not
:0040730D add esp, 00000008
:00407310 mov ecx, dword ptr [ebp-38]
:00407313 mov edx, dword ptr [ebp-3C]
:00407316 mov byte ptr [edx+ecx], al  ;Nuevo valor al fichero.

Hasta aquí hemos llegado. Y ahora..?
Sabemos cómo se desencriptaría el bloque de datos si tuviéramos el password correcto.
Sin embargo no sabemos nada acerca del propio password.

Puede intentarse la fuerza bruta en este caso...? dificilmente dado que tampoco disponemos de un método de validación viable.
En definitiva,  NO SABRÍAMOS distinguir un fichero bien descifrado de otro erroneo....!!! Ambos son enormes streams de bytes  aunque sólo uno de ellos permite completar la instalación con éxito...

COMETIERON 2 ERRORES

Cual podría ser el punto débil de este esquema....? Hay alguna manera de averiguar el password correcto...?
Si que la hay y es absolutamente sorprendente pues puede ser considerado como un error en el diseño de la protección entera.
Echemos un vistazo a uno de estos ficheros 'pftw1.pkg' una vez desencriptado:

Las preguntas que debemos hacernos son:
 

  • ¿Que es común a todos los ficheros ? 
  • ¿ Hay algo que tenga que estar presente en TODOS los casos y que nos pueda servir de referencia para analizar el origen del cifrado?
La respuesta es..... La cabecera!.

En efecto examinando otros PFTW descubrí que en todos los casos se encontraba esta cabecera. Investigando un poco encontré que la estructura de los primeros 28 bytes parecía obedecer lo siguiente:
 
Tipo de dato Significado Valor
DWORD ID de tipo de fichero "MSCF"
DWORD Desconocido 00000000h
DWORD Tamaño de fichero 0002CDC1h
DWORD Desconocido 00000000h
DWORD Desconocido 0000002Ch
DWORD Desconocido 00000000h
DWORD Desconocido 03010100h

Lo que queda no precisa de mucha explicación. Determinando el tamaño del fichero podemos "reconstruir" sus primeros 28 bytes dado que aparentemente la estrucutura se mantiene en todos los casos. Siguiendo el ejemplo propuesto los primeros 28 bytes para nuestra 'victima' deberían ser:

4D 53 43 46 00 00 00 00 C1 CD 02 00 00 00 00 00 
2C 00 00 00 00 00 00 00 03 01 01 00

El tamaño de fichero lo podemos obtener en la llamada a GetFileSize que tiene lugar en :4071f4 (este dato se necesita para habilitar memoria para el mapeo)
Tambien puede mirarse directamente el tamaño de "pftw1.pkg".

Puesto que conocemos el proceso que se lleva a cabo en el cifrado (*) procedemos al revés:

a) NOT de cada byte del fichero sin encriptar

B2 AC BC B9 FF FF FF FF 3E 32 FD FF FF FF FF FF
D3 FF FF FF FF FF FF FF FC FE FE FF

b) Rotación de cada byte del fichero encriptado

ED F8 49 9E 96 AB 17 99 45 68 09 91 8D B9 59 9D
B3 BE 18 96 CC A9 16 9B 86 AF 19 8B

c) XOR entre bytes de a) y b)

5f 54 f5 27 69 54 E8 66 7B 5A F4 6E 72 46 A6 62 
60 41 E7 69 33 56 E9 64 7A 51 E7 74

d) XOR con los bytes de la llave fija 13358607h

4C 61 73 20 7A 61 6E 61 68 6F 72 69 61 73 20 65
73 74 61 6E 20 63 6F 63 69 64 61 73

Esta última colección de bytes es precisamente la clave secreta: "Las zanahorias estan cocidas".
Como sabemos cual es la longitud real del password ? Muy fácil, tecleamos los caracteres obtenidos hasta que se habilite el botón "finalizar".Una opción mas fina sería usar directamente aquella primera rutina de validación. Cuando el número de caracteres sea el correcto devolverá el valor apropiado.

Con esto se resuelve el problema en la infinita mayoría de los casos (son inusuales los passwords tan largos, lo más común es que no excedan de 10-12 caracteres.)

Como decía al comienzo, cometieron dos errores:

  • Basar un sistema de cifrado en un método tan añejo
  • Aplicar el cifrado a un fichero altamente formateado, con cabeceras y estructuras fijas
CONCLUSION

Lo que hemos visto hoy es un caso muy sencillo de criptografía aplicada a la protección de software. 

La tendencia de los ultimos tiempos apunta a un incremento del uso de esquemas potentes basados en clave publica. En estos casos los métodos se vuelven difíciles de seguir sin una base matemática sólida. No obstante les pondremos un ojo encima en cuanto sea posible.

Eso es todo por esta vez, espero que hayais disfrutado leyendo lo mismo que yo escribiendo.
 

Up The Hammer!
Mr.Crimson
 

[ 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