La pista de aterrizaje.
En el anterior tutorial se localizaron sin demasiado trabajo los valores
del registro en donde ambas aplicaciones almacenan las fechas de instalación
y última ejecución.
La tarea que nos queda por hacer es más rutinaria que otra cosa.
Las opciones son variadas. Inicialmente lo más sencillo puede ser
localizar el punto del programa donde se verifica si se ha encontrado el
valor en el registro o no. Si conseguimos hacer creer al programa de que
no ha encontrado el valor, simularemos que el registro se encuentra limpio,
por lo que como se vio anteriormente el programa se limitará a crear
el valor en el registro sin hacer ninguna verificación de la caducidad
de la licencia. Otra opción es colocar un punto de ruptura en los
accesos de lectura a la zona de memoria en la que la aplicación
guarda los datos leídos del registro para "pescar" al código
encargado de desencriptar los datos leídos y calcular los días
restantes de evaluación. Una vez localizado el objetivo no queda
más que desenfundar el editor hexadecimal y disparar...
Pero lo primero es localizar en dónde carga la aplicación
los datos cargados del registro. La solución lógica es colocar
un breakpoint de ejecución en la función RegQueryValueEx
que haga saltar el SoftIce para ver que valor se está consultando
y dónde se almacena el resultado. Esta opción sería
válida para otros programas, pero no para los que nos ocupan, con
cientos de accesos al registro antes de arrancar la aplicación.
Tenemos que limitar el breakpoint para que solo salte cuando a nosotros
nos interesa, es decir, cuando lea el valor del registro que contiene la
fecha de instalación y de última ejecución.
Para ello el SoftIce permite la introducción
de breakpoints condicionales, que solo salten cuando se cumplan las condiciones
que se especifican. La mayoría de los lectores sabrán a lo
que me refiero aunque para otros puede resultar una novedad.
Consultando la ayuda de la función RegQueryValueEx
no encontramos la siguiente cabecera:
LONG RegQueryValueEx ( |
|
HKEY hKey, |
// handle de la clave a consultar |
LPTSTR lpszValueName, |
// puntero al nombre del valor |
LPDWORD lpdwReserved, |
// reservado (NULL) |
LPDWORD lpdwType, |
// puntero al tipo de valor |
LPBYTE lpbData, |
// puntero al buffer de recepción |
LPDWORD lpcbData ); |
// puntero al tamaño del buffer |
De todos estos parámetros los que nos interesan son lpszValueName
que apuntará al nombre del valor, en nuestro caso debe apuntar a
'MiscStatus', y lpbData que apuntará a la zona de
memoria en la que la función devolverá el valor leído
en el registro de Windows. En esa zona es donde colocaremos nuestro breakpoint
de acceso de lectura para ver qué código es el que accede
a él.
El punto de ruptura será de la forma:
BPX RegQueryValueExA IF **(esp+8)=='Misc'
DO "d*(esp+14)"
Este punto de ruptura se divide en tres partes:
BPX RegQueryValueEx |
Definimos donde queremos colocar el breakpoint. |
IF **(esp+8)=='Misc' |
Definimos la condición de disparo del breakpoint. |
DO "d*(esp+14)" |
Definimos las acciones a realizar cuando salte el breakpoint. |
En la condición de disparo referenciamos a la DWORD apuntada
por el contenido de ESP+8. Para los que no lo sepan, el sistema de paso
de parámetros utilizado en las llamadas en las funciones de la API
se basa en la pila. En ella se vuelcan todos los parámetros, comenzando
por el último y terminando con el primero. Cuando se produce una
llamada a una función mediante la instrucción 'CALL' se añade
a la pila la dirección de retorno a utilizar cuando finalice la
función invocada con la instrucción 'RET'. Esta dirección
será la de la siguiente instrucción al 'CALL'. Así,
el contenido de la pila justo en la entrada de la función RegQueryValueEx
será el siguiente:
*(ESP) ------> |
Dirección de retorno. |
*(ESP+4) ----> |
Primer parámetro: hKey |
*(ESP+8) ----> |
Segundo parámetro: lpszValueName |
*(ESP+C) ----> |
Tercer parámetro: lpdwReserved |
*(ESP+10) ---> |
Cuarto parámetro: lpdwType |
*(ESP+14) ---> |
Quinto parámetro: lpbData |
*(ESP+18) ---> |
Sexto parámetro: lpcbData |
Por lo tanto, si *(ESP+8) es el puntero al nombre del valor,
**(ESP+8) será el nombre del valor. Puesto que por defecto
el SoftIce trabaja con DWORD, la DWORD apuntada por **(ESP+8)
serán los cuatro primeros caracteres del nombre del valor, en este
caso 'Misc'.
Las acciones a realizar en caso de activación del punto de ruptura
se especifican tras la instrucción DO indicando
entre comillas dobles los comandos queremos que ejecute el SoftIce, separados
por ';'. En este caso sencillo solo vamos a mostrar el contenido de la
zona de memoria donde la función va a devolver lo que ha leído
del registro, aunque podría completarse con más cosas como
un 'G *SS:ESP', que equivale a ejecutar hasta el final
de la función y parar en la instrucción siguiente al 'CALL'
que invocó a la función.
Lógicamente esto funcionará si el primer valor que lee
el FrontPage que comienza por 'Misc' es el que a nosotros
nos interesa. En caso contrario nos saltará el breakpoint en momentos
que a nosotros no nos interesa...
BPX RegQueryValueExA IF **(esp+8)=='Misc'
DO "d*(esp+14);g *SS:ESP"
Este será el breakpoint que usaremos como pista de aterrizaje
en el código del FrontPage. En el caso del PhotoDraw usaríamos,
según lo que vimos en la primera parte del tutorial:
BPX RegQueryValueExA IF **(esp+8)=='Hand'
DO "d*(esp+14);g *SS:ESP"
Lo colocamos en el SoftIce, y desde Windows arrancamos al conejillo
de indias...
... y acción.
Como era de esperar el breakpoint condicional nos ha dejado justo donde
queríamos. Hemos aterrizado en la dirección 6782A528 (en
el PhotoDraw 300B3F31), perteneciente al código de la librería
'FPCUTL.DLL' (archivo 'PHOTODRW.EXE' del PhotoDraw). Además en la
ventana de datos tenemos los datos leídos del registro, a partir
de la dirección 6784B0A8 (30146140 en el PhotoDraw), perteneciente
a la sección de datos de la misma librería.
Primero vamos a intentar localizar en que lugar del programa se verifica
si se ha leído algo del registro o no. Empezamos a ejecutar paso
a paso...
Lo primero que hace la aplicación tras haber leído el
valor del registro es ver si ha ocurrido un error, en cuyo caso el contenido
del registro EAX será 2. Independientemente de que se haya producido
el error o no, la aplicación cierra el manejador a la clave del
registro con RegCloseKey.
Una vez finalizada la función actual, justo después del 'CALL'
de la rutina encargada de leer el registro nos encontramos lo siguiente:
6782A6BF: CMP [6784B0A8], EBX
y en el caso del PhotoDraw:
300B41CE: CMP [30146140], EBX
Lo que está haciendo el programa es comparar
la primera DWORD leída del registro de Windows con el contenido
de EBX, que en este caso es cero. Está es la comprobación
que realiza el programa para ver si existía o no el valor en el
registro. Si repetimos la operación pero borrando previamente el
valor del registro comprobaremos que tras la lectura fallida esa zona de
memoria se encuentra a cero.
En caso de que se haya leído correctamente
el valor del registro el salto condicional situado a continuación
de la comparación se producirá. Para confundir al programa
podemos hacer dos cosas: anular el salto para que nunca se produzca o bien
modificar la comparación para que compare el registro EBX con alguna
zona de memoria que esté siempre a cero.
Para ser un poquito originales vamos a olvidarnos
de la opción de nopear el salto. La zona de memoria que sigue al
final de los valores leídos del registro se mantiene a cero. El
valor leído del registro tiene un tamaño de 80 bytes, por
lo que la zona de memoria situada a partir de 6784B0F8 (30146190 en el
PhotoDraw) se mantiene a cero cuando el programa realiza estas verificaciones.
Para realizar este cambio no tenemos más que sustituir el byte A8
(situado en el offset 0x5B6C1 de 'FPCUTL.DLL') por F8. Con esto obtenemos
la siguiente instrucción:
6782A6BF: CMP [6784B0F8], EBX
Para el PhotoDraw sustituiremos el byte 40h (situado
en el offset 0x0B41D0 de 'PHOTODRW.EXE') por 90h obteniendo la siguiente
instrucción:
300B41CE: CMP [30146190], EBX
A partir de ahora la aplicación, a pesar de leer correctamente
el valor del registro, interpretará que éste no se ha encontrado
por lo que volverá a crearlo (más bien lo sobreescribirá)
sin realizar ninguna comprobación sobre la caducidad de la versión
de evaluación.
Conclusiones.
Existe la tendencia a pensar que una aplicación cuanto más
grande y compleja es, más difícil es derribar las protecciones
que trae. Esto no es cierto en todos los casos, como en estos dos ejemplos
de Microsoft.
Ambas aplicaciones basan su protección en unos datos guardados
en el registro que, si no se encuentran, son inicializados ya que los programadores
han supuesto que si dichos datos no se encuentran presentes es porque la
aplicación se acaba de instalar. Estas aplicaciones no son las únicas
cuyo periodo de evaluación de reinicializa mediante el borrado de
una determinada clave del registro. Este "truco" funciona en muchas otras
aplicaciones que basan su control de caducidad de la licencia en la fecha
de instalación almacenada en el registro de Windows.
El problema se reduce en hallar qué valor de los que se acceden
del registro es el que guarda dicha información. Una vez hallada
puede explotarse tal y como se ha mostrado en la segunda parte del tutorial,
haciendo creer al programa que la clave de registro no existe.
Para los más curiosos puede complicarse el asunto, llegando a
descubrirse cosas interesantes. Por ejemplo, si en los dos ejemplos tratados
seguimos el flujo del programa controlando los accesos de lectura a los
bytes leídos del registro podremos descubrir que tanto en el FrontPage
como en el PhotoDraw existen unos bytes que controlan la duración
del periodo de evaluación, a partir de cuantos días comienza
a aparecer la nag avisando de que quedan X días de evaluación
e incluso un byte que indica si se debe controlar la caducidad del periodo
de evaluación o no...
En el caso del FrontPage estos bytes se encuentran en el fichero 'FPCUTL.DLL':
RVA 678312C0 OFFSET 0x622C0: 01 2D 00 00 00 0E
El último byte indica a partir de cuándo debe avisarse
al usuario de que quedan X días de evaluación. En el caso
del FrontPage es a partir de que resten 15 días de evaluación.
El segundo byte indica cuántos días dura el periodo de evalaución,
45 en el FrontPage. Y el primer byte indica si debe controlarse la caducidad
(01) o no (00). En caso de que no se controle (00), la aplicación
niiquiera accede a la fecha de instalación almacenada en el registro.
En el PhotoDraw estos bytes se encuentran en el fichero 'PHOTODRW.EXE':
RVA 301412FC OFFSET 0x1412FC: 01 1E 00 00 00 05
Con estos datos y un editor hexadecimal os podreis construir un periodo
de evaluación a medida y de acuerdo con vuestras necesidades: más
o menos días de evaluación, avisos de caducidad más
o menos cerca del fin del periodo, evaluación indefinida... ;-)
Por supuesto, estas modificaciones del periodo de evaluación
debéis consultarlas antes con Microsoft, que es la empresa a la
que pertenecen los derechos de ambos programas... no vayais a hacer nada
ilegal... XDD
Mr. Blue / [WkT ! 2000] |