martes, 26 de mayo de 2015

Resolviendo un crackme con Cheat Engine

Cheat Engine. Una 'navaja suiza' para reversers.

En una entrada anterior ya apareció un extenso tutorial sobre esta pequeña gran herramienta. En aquella ocasión se utilizó para exponer las bondades de esta utilidad lo que viene a ser el objetivo natural de esta herramienta, un videojuego.

Pero como con muchas otras utilidades, podemos seguir sacándole punta a Cheat Engine en otros campos de aplicación distintos de los propios para los que ha sido concebido: hoy vamos a resolver un crackme. Si la entrada anterior fue una introducción bastante extensa a la herramienta, en esta entrada vamos a 'liarla parda', como aquel que dice.

Empezamos por el final. Esto es lo que vamos a perpetrar:


Ya puestos a buscar formas de hacer lo mismo pero con distintos sabores, vamos a juguetear con un crackme que ha aparecido hace poco en la web de SecurityByDefault como conejillo de indias para ilustrar el funcionamiento de WinAPIOverride. El crackme en cuestión data del año 2001, realizado por 'SantMat' del extinto grupo  'Immortal Descendants'. Dicho grupo echó el cierre en octubre de 2001, al igual que muchos otros que existían desde los 90 y que justamente con la llegada del nuevo milenio se extinguieron casi todos prácticamente a la vez, como por ejemplo los muy recordados por estas tierras 'Whiskey Kon Tekila!', o 'WkT!' para los amigos.

El crackme puede descargarse de varios sitios:
En esos tres sitios los binarios del crackme son exactamente iguales y no parecen tener virus. Y lo digo porque la librería 'canyou.dll' que le acompaña hace saltar a algunos antivirus. He desemsamblado tanto la librería como el ejecutable y ni la librería tiene nada raro, ni el ejecutable importa las funciones necesarias para poder extraer algo raro de la librería... De todas maneras, los más precavidos pueden usar una máquina virtual.

¿Can you CrackMe?

Solo con ver el signo de apertura de interrogación inicial en una pregunta escrita en inglés, se puede suponer que la lengua nativa del autor no es el inglés. El crackme incluye una librería, el ejecutable y un fichero de texto en el que nos retan a crear un generador de claves válido.

'Manual de uso'
El crackme está fechado a agosto de 2001, es decir, meses antes del lanzamiento de Windows XP. Por las funciones que importa del sistema operativo, se tuvo que desarrollar para ejecutarse en un Windows 2000 o un Windows ME. La antigüedad del programa va a provocar que tengamos que tocarlo un poco para que funcione correctamente en Windows 7.

La aplicación muestra una sencilla ventana en la que se nos invita a introducir el clásico usuario y número de serie, junto con un botón para validar la información introducida. Si la información proporcionada es correcta nos aparecerá el consabido mensaje de éxito y en caso contrario el típico mensaje de error tras el que la aplicación finaliza. Simple y básico.

Ventana principal del crackme
No se va a entrar demasiado en el análisis del código del crackme, y se centrará el enfoque en cómo usar características avanzadas de Cheat Engine que no se vieron en la entrada anterior. Por acortar esta entrada, ya de por sí extensa, el análisis sistemático del crackme se ha realizado en un anexo aparte.

Resumiendo, para el cálculo del número de serie el crackme utiliza:
  1. El nombre de usuario tecleado en el control de edición superior
  2. Las coordenadas del cursor del ratón al pulsar el botón 'Gain Access!'
  3. El número de volumen del disco duro desde el que se ejecuta el crackme
  4. La clave de producto de Windows
Al pulsar el botón inferior, antes de nada el crackme valida que ninguno de los dos campos es nulo. Sobre los datos anteriores el crackme realiza una serie de operaciones e interrelaciones entre ellos que dan como fruto dos valores numéricos. Ambos números se convierten a cadena, y la concatenación de ambas cadenas forma el número de serie. Dicho número de serie se compara con el introducido por el usuario en el control de edición inferior. Si no son iguales se muestra un mensaje de error y se cierra la aplicación, y en caso contrario se muestra una felicitación.

Los valores numéricos que forman las dos partes del número de serie se transforman a cadena utilizando la función 'wsprintf'. Ambos números se almacenan en los registros EDX (dirección 'canyou.exe+1218') y ECX (dirección 'canyou.exe+1224'), para posteriormente invocar dos veces las función 'wsprintf', en 'canyou.exe+1235' para convertir el valor numérico del registro EDX y en 'canyou.exe+1248' para convertir el valor en ECX.

El problema es que tras la primera invocación de la función 'wsprintf' en 'canyou.exe+1235' el registro ECX no preserva su valor, con lo que el parámetro numérico que se pasa a la segunda invocación de 'wsprintf' en 'canyou.exe+1248' no es el valor numérico previamente calculado, si no un valor inicialmente aleatorio.

En sistema operativos anteriores, tras ejecutarse 'wsprintf' el valor del registro ECX siempre era un valor fijo, con lo que podría generarse un keygen a pesar de este fallo (o puede que no) de desarrollo. En Windows 7 dicho valor es completamente aleatorio, con lo que si queremos generar un keygen tendremos que cambiar algunas líneas de código de sitio.

La idea es trasladar las dos instrucciones que cargan el registro ECX con el valor numérico adecuado ('canyou.exe+121E' y  'canyou.exe+1224') por debajo de la primera llamada a la función 'wsprintf' en 'canyou.exe+1235'. El cambio propuesto es el que se muestra:

Situación original. El registro ECX no conserva su valor tras la primera llamada a 'wsprintf'
Tras el citado cambio, la situación final queda así:

Situación final. El registro ECX carga su valor justo tras la primera llamada a 'wsprintf'
Este cambio puede realizarse con un desemsamblador/ensamblador que permita modificar un ejecutable, o bien directamente con un editor hexadecimal sobrescribiendo el contenido a partir del desplazamiento 0x61E tal y como se muestra:

Modificación del código. Arriba versión original y abajo versión modificada
Al final de esta entrada hay un enlace para descargar, entre otras cosas, la versión modificada del crackme con esta pequeña corrección.

Desatando el potencial de Cheat Engine

Como ocurre en la mayor parte de estos casos, hay decenas de maneras de resolver el problema. Anteriormente se expusieron las posibilidades que Cheat Engine ofrece para monitorizar el contenido de determinadas variables que utiliza el programa objetivo, y como actuar sobre ellas. Además, con Cheat Engine creamos un 'trainer' con una ventana predefinida para mostrar al usuario las opciones e instrucciones de uso.

En esta ocasión crearemos una ventana completamente desde cero usando el asistente de diseño que integra la herramienta. Además de monitorizar y obtener el contenido de determinadas variables que influyen en el cálculo de un número de serie válido, inyectaremos código en el contexto del la aplicación objetivo que nos permitirá realizar distintas funciones.

Parto de que hemos arrancado Cheat Engine, hemos cargado el proceso en ejecución del crack 'canyou.exe', y que hemos curioseado el funcionamiento del código del crackme, tras lo que hemos llegado a las conclusiones que se presentan en el anexo.

Para el resto del artículo es interesante cargar el archivo .CT que se enlaza al final de la entrada desde Cheat Engine, ya que lo que sigue va a ser una descripción de su funcionamiento.

En la lista de direcciones del interfaz de Cheat Engine se encuentran listados dos direcciones de memoria y tres scripts:

'Addresslist' para nuestro keygen
La dirección etiqueta como 'randomSeed' corresponde a lo que en el anexo he nombrado como 'semilla del ratón', o SMR (ver punto 3 de la sección dedicada al ejecutable 'canyou.exe' en el anexo).

'previousSerialSeed' en el anexo aparece como 'semilla del ProductKey', o SMP (punto 1 de la sección 'canyou.exe' del citado anexo).

Hasta aquí nada nuevo respecto a lo ya visto. Las novedades están en las funcionalidades de los tres scripts. Estos scripts, que pueden activarse y desactivarse, permitirán al script principal en LUA realizar llamadas a funciones de la API de Windows a las que LUA no tiene acceso directo. En realidad, desde el script en LUA podríamos importar otros módulos de LUA que permiten llamar directamente a funciones de la API de Windows, como la extensión 'Alien'. Para usar esta extensión hay que utilizar una versión específica de la extensión para Cheat Engine.

En este caso se ha hecho de otra manera que permite de una manera sencilla no solo llamar a funciones de la API de Windows, si no que al ejecutarse dentro del contexto de la aplicación objetivo, podremos incluso llamar a funciones internas de la aplicación objetivo. Los tres scripts siguen el mismo esquema. De hecho podrían unificarse en un único script que mediante un parámetro se definiera cual de las funcionalidades se desea ejecutar.

En la sección '[ENABLE]' de cada script, que se lanza al activarlo, se realizan las reservas de zonas de memoria usando 'alloc()' necesarias para albergar en el contexto de la aplicación objetivo el código que deseamos ejecutar así como las distintas variables que dicho código precisa. En caso de desear acceder a alguna de estas variables desde el script principal en LUA debemos registrar los símbolos que correspondan con 'registersymbol()'. La sección '[DISABLE]' que se ejecutará al desactivar el script liberará las zonas de memoria reservadas anteriormente con 'dealloc()' y eliminará de la tabla de símbolos globales los símbolos que se registraron usando en esta ocasión 'unregistersymbol()'.

La novedad es la función 'CREATETHREAD(adr)'. Como su nombre da a entender, permite crear y lanzar un hilo que ejecutará el código a partir de la dirección que se le pasa como parámetro hasta encontrar una instrucción de retorno 'ret'. Este hilo se crea y se ejecuta en el contexto del proceso anfitrión, con lo que puede acceder a la memoria del mismo.

1. Script 'GetVolumeInformation'

Este script se encarga de obtener el número de volumen de la unidad de disco en la que se encuentra el directorio actual de la aplicación anfitriona. Para mostrar un script, hacemos click con el botón derecho del ratón sobre la línea que le corresponde en el listado de direcciones del interfaz principal de Cheat Engine, y en el menú contextual se selecciona la opción 'Change script'.

Script 'GetVolumeInformation'
Al activar el script anterior desde el script principal en LUA, se inyectará en el proceso anfitrión el código que obtendrá el número de volumen de la unidad de disco en la que se encuentra el directorio actual. Para ello realiza una llamada al la función de la API de Windows 'GetVolumeInformation'. Como siempre, los parámetros se pasan mediante la pila en la que se insertan en orden inverso (del último parámetro al primero). Tras la ejecución el hilo finaliza y la dirección 'volumeID' albergará el número de volumen. Esta dirección se declara como símbolo para acceder a la misma desde el script en LUA. El script en LUA activará el script, lo que lanzará el hilo, esperará unos milisegundos y después accederá a la dirección 'volumeID' en la que ya se encontrará el número de volumen. Tras recogerlo, desactivará el script lo que liberará todos los recursos utilizados. Puesto que la función 'GetVolumeInformationA' es importada por el propio ejecutable anfitrión, se encontrará ya disponible para nuestro hilo al ejecutarse en el mismo contexto.

Los otros dos scripts son similares, con la salvedad que el hilo se queda en un bucle infinito del que no finaliza hasta que el script principal en LUA activa un flag que se ha declarado como símbolo.

2. Script 'GetUserName'

Esto puede verse con claridad en el script 'GetUserName', utilizado para obtener el nombre que el usuario introduce en el crackme:

Script 'GetUserName'
La tarea del hilo es refrescar continuamente en la dirección 'username' el texto que el usuario introduce en el control destinado al nombre en el crackme. Para ello usa la función de la API 'GetDlgItemText'. El primer parámetro (que corresponde al último que se inserta en la pila) es el manejador o 'handle' de la ventana de diálogo en la que se encuentra el control del que queremos acceder al texto. Tras realizar varias búsquedas, y varias ejecuciones del crackme de la misma forma que se mostró en la entrada anterior, encontramos que el manejador de la ventana de diálogo se almacena en la dirección 0x18FF00. Con la directiva 'define()' creamos un atajo para esa dirección y mantenemos el código algo más ordenado. El refresco se realiza cada 500ms. (el carácter '#' indica que el número debe tomarse con base decimal en lugar de hexadecimal) llamando a la función 'sleep'. A diferencia de la función 'GetDlgItemText', la función 'sleep' no está previamente importada por la aplicación anfitriona, por lo que debemos indicar la librería en la que se encuentra, y ya se encarga el motor de Cheat Engine de importar la dirección correcta.

Al finalizar el periodo de 500ms. se comprueba el estado del flag 'stop'. Si es distinto de cero finaliza el hilo, y en caso contrario vuelve al inicio recargando nuevamente el texto del control.

3. Script 'SetSerial'

El último script auxiliar es el encargado de escribir en el control del número de serie del crackme el número de serie calculado. Al igual que el anterior el hilo se ejecuta en un bucle infinito, aunque en este caso en lugar de un flag que desbloquea la finalización del hilo, dispone de un segundo flag que indica si hay un nuevo número de serie que deba volcar al control de la ventana de diálogo del crackme.

Script 'SetSerial'
El hilo duerme durante 150ms. tras lo cual pasa a revisar el estado de los flags de control. El flag 'refresh_serial' distinto de cero indica que hay un nuevo número de serie pendiente de ser insertado en la ventana del crackme. De ser así, resetea el flag a cero y realiza la llamada a la función 'SetDlgItemText'. Al igual que el script anterior, el flag 'stop_serial' distinto de cero marcará al hilo que debe finalizar. El script publica con 'registersymbol' los dos flags que controlan el flujo del hilo ('stop_serial' y 'refresh_serial'), además de un buffer de memoria etiquetado como 'serial' donde el script principal en LUA volcará el número de serie que ha calculado, en forma de cadena de caracteres acabada en carácter nulo.

4. Script principal

Desde el interfaz principal de Cheat Engine, el script principal en LUA es accesible desde el menú 'Table', opción 'Show Cheat Table Lua Script'.

El script se ha escrito en forma de clase, aunque no es necesario hacerlo así. El objeto principal 'CanYou_Trainer' que inicializa al inicio, contendrá las variables y las funciones necesarias.

La clase principal tendrá cuatro funciones:
  1. Main. Inicialización de la ventana principal y de las variables internas.
  2. OnTimerRefresh. En la inicialización se ha creado un timer, y esta será la función que se invocará cada vez que se cumpla el intervalo del timer.
  3. OnFormClose. En la inicialización se ha configurado la ventana principal para que ejecute este método cuando va a cerrarse. Libera recursos ordenadamente.
  4. CalculateSerial. Se encarga de calcula un serial válido.

Main

La ventana principal del trainer es 'UDF1'. Se apunta el evento 'OnClose' de dicha ventana para que lance a la función 'OnFormClose'. Las variables internas de la clase que mantendrán la información importante para calcular el serial válido son:
  • volumeID. Número de volumen de la unidad que contiene al directorio actual.
  • randomSeed. Semilla del ratón (SMR).
  • usernameSeed. Semilla del nombre de usuario tecleado en el interfaz del crackme (SMU).
El número de volumen se leerá la primera vez que se dispara el timer, ya que no va a cambiar. Las otras dos semillas se recalcularán en cada intervalo del timer, establecido en un segundo.
Una vez configurado el timer, y cargados los cinco punteros que apuntan al contenido de la lista de direcciones, se muestra la ventana principal y finaliza la función.

OnTimerRefresh

Las misiones del timer son dos, cargar en el trainer el contexto del proceso del crackme, en caso de que el proceso del crackme se encuentre entre la lista de procesos en ejecución. Una vez enganchado al proceso del crackme, se activarán los hilos de los scripts 'GetUserName' y 'SetSerial'. A partir de ahí, y mientras el proceso del crackme siga activo, el timer se encargará de mantener actualizados con la última información disponible del crackme, lo que he llamado semillas del ratón y del nombre de usuario (SMR SMU en el anexo). La del ratón se leerá directamente de la posición de memoria del proceso del crackme definida en la lista de direcciones. La del nombre del usuario se recalculará cada vez que se detecte que el nombre del usuario tecleado en la interfaz del crackme se ha modificado, usando para ello el script 'GetUserName'.

Si alguna de las semillas cambia porque variables de las que depende es modificada, se recalcula el nuevo serial llamando a la función 'CalculateSerial'. El nuevo serial calculado es inyectado en el control correspondiente del interfaz del crackme usando el script 'SetSerial'.

Una vez que el trainer se ha enganchado al proceso del crackme, si éste se cierra, el trainer también se cerrará automáticamente.

OnFormClose

Esta función es invocada cuando el usuario cierra la ventana del trainer. Se procede a señalizar a los hilos de los scripts 'GetUserName' y 'SetSerial' que deben finalizar, y se espera medio segundo antes de deshabilitarlos para liberar sus recursos, tras lo que se cierra el trainer.

CalculateSerial

Cuando desde la función que controla los eventos del timer se detectan cambios en la posición del ratón o en el nombre de usuario introducido en la interfaz del crackme, el serial debe recalcularse. Para ello lee  de la memoria del proceso del crackme lo que denominé semilla de la clave de producto (SMP), que junto con las otras dos semillas y el número de volumen obtenidos por el evento del timer permitirán calcular un nuevo serial válido.

El nuevo serial, una vez calculado, además de mostrarse en la interfaz del trainer, se copia al portapapeles y además se envía directamente al control correspondiente de la interfaz del crackme mediante la funcionalidad ofrecida por el script 'SetSerial'.

5. Interfaz principal

Cheat Engine ofrece un completo asistente gráfico para diseñar ventanas, muy similar al que podemos encontrar en versiones algo más antiguas de entornos de desarrollo como Delphi o Visual Basic.

Ventana del 'trainer' funcionando
Con una política de 'drag & drop', crear un interfaz no requiere de mucha más explicación. Hay que tener en cuenta que a la hora de generar un ejecutable (menú 'File', opción 'Save as...', y en el desplegable de 'Tipo' opción 'Cheat Engine Trainer Standalone (*.EXE)'), hay que añadir los ficheros que contienen los recursos que el ejecutable necesite. En este caso, se han añadido dos imágenes del led que aparece en la esquina superior derecha del interfaz, y que señalan si el trainer está enganchado al proceso del crackme (led verde) o no (led rojo).

Inclusión de ficheros de recursos al generar el 'trainer'

Conclusiones

Aunque se ha ideado con el objetivo de los videojuegos en mente, esta versátil herramienta puede utilizarse para muchas otras cosas, e incluso para cosas serias. Corrección de errores en programas de los que no se dispone de los fuentes, creación de herramientas para tests automatizados de aplicaciones en desarrollo, etc... Es cuestión de echarle imaginación.

Desde luego, aunque Cheat Engine está pensada para videojuegos, no es ni mucho menos una herramienta de juguete...

Los archivos necesarios para este tutorial y los generados se encuentran en este enlace.

No hay comentarios:

Publicar un comentario