Análisis de '¿Can you CrackMe?'

Introducción

Esta entrada es un anexo de la entrada 'Resolviendo un crackme con Cheat Engine'. Se intentará presentar con detalle y meridiana claridad el funcionamiento interno del crackme utilizado en aquella entrada.

Tal y como se expuso entonces, el crackme consta de un una librería 'Canyou.dll' además de la propia aplicación 'Canyou.exe'.

Las direcciones de memoria que se expresan a continuación son absolutas mientras que las mostradas por el desensamblador de Cheat Engine por defecto son referidas al módulo y/o a un determinado símbolo. Para mostrar en Cheat Engine direcciones de memoria absoluta, deseleccionamos desde la ventana del desensamblador las opciones 'Show module addresses' y 'Show symbols' en el menú 'View'. Al hacer eso, Cheat Engine cambiará el módulo o símbolo mostrado por el parámetro 'ImageBase' de dicho módulo o la dirección base de dicho símbolo, y realizando la suma con el desplazamiento especificado.


Canyou.dll


Este librería que acompaña al ejecutable contiene tres funciones (se indican las direcciones de inicio de cada función). Las dos primeras son exportadas, por lo que pueden ser invocadas por una aplicación que utilice esta librería:
  1. SofticeChecker (0x1000104E)
  2. FileSearcher (0x10001077)
  3. RandomNumberGen (0x10001015)

1. SofticeChecker

SoftICE fue un gran depurador en modo kernel creado por la compañía NuMega, y que casi todos los que llevamos años en esto de la ingeniería inversa hemos utilizado. Puesto que también era utilizado por los diseñadores de cracks para saltarse protecciones en aplicaciones, enseguida comenzó la habitual guerra de medidas para detectar la ejecución de dicho depurador, y las consiguientes contramedidas. En este caso, y aunque la función tiene el nombre que tiene, en ningún momento intenta detectar si dicho depurador se encuentra en el sistema.

Esta función exportada por la librería permitirá crear un 'hook' global de ratón para monitorizar todos los eventos de ratón en el sistema y redirigirlos hacia la función 'RandomNumberGen' definida en la propia librería, usando la dunción del sistema 'SetWindowsHookEx'.

La función 'SetWindowsHookEx' permite a una aplicación definir un 'enganche' o 'hook'. Con esta funcionalidad la aplicación consigue que el sistema operativo invoque una función definida por la aplicación cada vez que se produzca un determinado evento en un determinado hilo en ejecución, o incluso en el sistema completo. El hilo a 'espiar' puede haber sido creado por el propio proceso que crea el 'hook' o bien puede haber sido creado por cualquier otro proceso.

En nuestro caso particular, el argumento que identifica el hilo que se desea monitorizar es cero, con lo que se está creando lo que se llama un 'hook' global, con lo que cada vez que se produzca el evento a monitorizar nuestra función será invocada por el sistema operativo, independientemente de la aplicación que tenga el foco en el momento de producirse el evento.

Cuando se desea crear un 'hook' sobre un hilo de un proceso distinto al nuestro (y en nuestro caso se quiere crear sobre todos los procesos del sistema), es obligatorio que la función a ser invocada por el sistema operativo esté en una librería. Esto es así ya que el sistema operativo precisa cargar la función a ejecutarse en el contexto del proceso a monitorizar, y esto la manera normal de conseguirlo es mediante una librería. El sistema operativo inyectará nuestra librería en el proceso que la hospeda. En caso de 'hooks' globales estos es válido no solo para los procesos en ejecución en el momento en el que se crea el 'hook', sino también para procesos que se comiencen a ejecutar posteriormente. A todos ellos Windows adjuntará nuestra librería como un parásito.

Chrome con la librería 'canyou.dll' inyectada tras establecer el 'hook'

Los 'hooks' han sido ampliamente usados para distintas maldades, como por ejemplo los 'keyloggers', que son aplicaciones diseñadas para capturar las pulsaciones de teclado que se realizan en el equipo, y todas las pulsaciones incluye contraseñas, número de tarjeta de crédito, etc...

La función 'SofticeChecker' tiene un único parámetro de entrada, que se almacena en la localización 0x10002004. A continuación se crea el 'hook' invocando la función 'SetWindowsHookEx'.

En nuestro caso particular no se crea un 'hook' de teclado sino de ratón con el identificador WH_MOUSE (7) que se introduce como parámetro 'idHook' . La función que se ejecutará cada vez que se mueva el ratón es 'RandomNumberGen', cuya dirección se introduce como parámetro 'lpfn'. El parámetro 'hMod' con el manejador de la librería que contiene la función apuntada por el parámetro 'lpfn' es el propio manejador de la librería actual. Finalmente el parámetro 'dwThreadId' es cero, con lo que el 'hook' se desea definir sobre todos los procesos que se ejecuten en el equipo local.

El manejador del 'hook' recién creado se almacena en la dirección 0x10002000.

2. FileSearcher

Deshace el 'hook' establecido previamente en 'SofticeChecker' con la función 'UnHookWindowsEx'. La librería se descarga de cualquier proceso al que se encuentre adherida, y en el caso de 'hooks' globales deja de engancharse a nuevos procesos.

Su único parámetro de entrada es el manejador del 'hook' que se desea desinstalar. Esté manejador es el valor devuelto por la función 'SetWindowsHookEx' al definir el 'hook'.

3. RandomNumberGen

Es la función que gestionará los eventos que monitorizamos con nuestro 'hook', que en este caso son los movimientos del ratón.

En primer lugar la función invocará al siguiente 'hook'. Puesto que distintas aplicaciones pueden crear un 'hook' del mismo tipo, el sistema operativo establece una cadena de 'hooks' en la que el sistema operativo invoca al último 'hook' que se ha definido, y a partir de ahí cada 'hook' invocará (o no) al siguiente usando la función 'CallNextHookEx'. Esta función devuelve el resultado que ha generado el siguiente 'hook'.

A continuación, a partir de la dirección 0x1000102C la función comienza su funcionalidad propiamente dicha. Puesto que la función responde a la declaración 'MouseProc', en el parámetro 'lParam' vendrá una estructura de tipo 'MOUSEHOOKSTRUCT'. Al inicio de dicha estructura tendremos una estructura 'POINT', que almacena dos variables de tipo LONG, la coordenada X del puntero del ratón seguida de la coordenada Y.

Ambas coordenadas se multiplican entre sí, y el resultado se escribe en el parámetro 'wParam' de un mensaje con código 0x406, mensaje que se envía hacia la ventana identificada por el valor que se recibió como parámetro de entrada de la función 'SofticeChecker'.


Canyou.exe


La función que se encarga de gestionar los eventos de la ventana principal del crackme, que se declara como un diálogo, comienza en la dirección 0x401029. Trata los eventos WM_CLOSE (0x010), WM_INITDIALOG  (0x110), WM_USER+6 (0x406) y WM_COMMAND (0x111). El resto de eventos se desechan y no son tratados. Un listado de los mensajes que se usan en Windows junto con sus códigos correspondientes puede encontrarse en este enlace.

Esta función cumple con la declaración de la función DialogProc, y es la que gestiona todos los mensajes que reciba la ventana principal creada con la llamada a DialogBoxParam de la dirección 0x40101D. Los parámetros de entrada de la función y su localización son los que siguen:
  1. [ebp+0x08] - HWND hwndDlg
  2. [ebp+0x0C] - UINT uMsg
  3. [ebp+0x10] - WPARAM wParam
  4. [ebp+0x14] - LPARAM lParam

1. WM_INITDIALOG (0x110)

El mensaje WM_INITDIALOG es enviado al procedimiento que gestiona la ventana de diálogo justo antes de que esta ser mostrada por primera vez. La gestión de este evento la realiza el crackme a partir de la dirección 0x401062.

En primer lugar llama a la función externa 'SofticeChecker' que se encuentra en la librería 'canyou.dll' que acompaña al ejecutable. Para asegurar que la ejecución de dicha función se realiza una única vez, su llamada se encuentra flanqueada por un flag que se almacena en 0x403000.

A continuación, se consulta en el registro el valor 'ProductKey' dentro de la rama:

'HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion'.

En caso de que se encuentre algo, cada uno de los caracteres encontrados se 'xorea' con 0x66 y el resultado se va sumando en la dirección 0x4036D4, que se ha inicializado previamente a cero.

En resumen:
  • Llamada a la función externa 'SofticeChecker' y escribe 1 en 0x403000
  • Semilla del 'ProductKey' en 0x4036D4 (abreviando, SMP)

2. WM_CLOSE (0x010)

WM_CLOSE es una indicación para cerrar la ventana. Se gestiona al incio del procedimiento de la ventana, a partir de la dirección 0x401038.

Se invoca a la función externa 'FileSearcher' de la librería 'canyou.dll' y acto seguido llama a la función 'EndDialog' de la API de Windows para destruir la ventana.

En resumen:
  • Llamada a la función externa 'FileSearcher' y fin de la aplicación

3. WM_USER + 6 (0x406)

WM_USER no es un mensaje de Windows propiamente dicho. Es un límite, con código 0x400, a partir del cual una aplicación puede definir sus propios mensajes sin temor a colisionar con alguno de los que usa el sistema operativo, cuyos códigos se encuentran siempre por debajo de dicho límite. Este mensaje es por tanto propio de la aplicación, con lo que su finalidad será la que defina la propia aplicación. Normalmente se usa como medio de comunicación entre distintos procesos, o bien entre hilos secundarios y el hilo principal del mismo proceso. Al igual que el resto de mensajes de Windows, se reciben los parámetros wParam y lParam, con contenido en este caso definidos por la propia aplicación.

Se trata a partir de la dirección 0x4010F7, donde se almacena el contenido del parámetro wParam en 0x4030CC, y además se 'xorea' con la constante 0x12345. Como se vio al examinar la librería 'canyou.dll', la función 'RandomNumberGen' es la que se encarga de enviar este mensaje, escribiendo en el parámetro wParam el producto de las coordenadas horizontal y vertical del cursor del ratón.

En resumen:
  • Al valor que se recibe en el parámetro wParam, que es el producto de las coordenadas x e y del puntero del ratón, se le aplica una operación xor con la constante 0x12345 almacenándose el resultado en 0x4030CC. Nos referimos a él como la semilla del ratón (casi tiene nombre de película de terror), abreviando SMR.

4. WM_COMMAND (0x111)

WM_COMMAND se recibe cuando el usuario ha pulsado una opción en un menú, un control (como por ejemplo un botón) o un hotkey previamente registrado. Puesto que no hay menús ni otros controles adicionales, está porción de código que comienza en la dirección 0x401062 es la que gestiona la pulsación del botón 'Gain Access!' de la ventana del crackme.

En primer lugar (0x40111B) verifica el parámetro lParam, que para el mensaje WM_COMMAND contendrá el manejador del control que ha generado el evento. Ese parámetro es cero en caso de que se haya pulsado un menú o un hotkey, y en caso de que se haya pulsado un control contendrá el manejador del control pulsado.

En caso de ser lParam cero, continua comprobando (0x40112D) que el código de notificación enviado por el botón, y almacenado en la palabra alta de wParam, sea  BN_CLICKED (0), que corresponde a un click de ratón con el botón izquierdo.

Lo siguiente es comprobar el identificador del control que envió el evento, que se almacena en la palabra baja de wParam. En el listado en ensablamblador vemos que se compara con 3 (0x401136), que corresponde con el identificador del botón 'Gain Access!' según la información que nos muestra cualquier herramienta para mostrar los recursos de un ejecutable, como por ejemplo 'Resource Hacker':

La herramienta Resource Hacker mostrando la declaración de la ventana principal

A partir de 0x401140 comienza verdaderamente el código que ha introducido el desarrollador para gestionar el evento de click de ratón sobre el botón.

Recupera los textos introducidos por el usuario en los dos controles de edición, primero el que corresponde al nombre de usuario (control con identificador 1) y después el que corresponde al número de serie (control con identificador 2). Por abreviar, denominaremos a cada cadena UNAME USERIAL. En ambos casos comprueba la longitud de la cadena introducida por el usuario con 'lstrlen', mostrando un mensaje de error con 'MessageBox' en caso de que en alguno de los dos controles no se haya introducido nada. Además la longitud del número de serie introducido se suma al valor contenido en 0x4036D4, que contenía la semilla del 'ProductKey' de Windows (ver gestión del mensaje WM_INITDIALOG).

La primera acción a tener en cuenta es el cálculo que se realiza sobre el nombre del usuario. A partir de 0x4011C4, y usando el bucle 0x4011D7 - 0x4011E8 se realiza una operación xor de la constante 0x33 con cada uno de los caracteres de UNAME, y el resultado se va acumulando en la localización 0x4036D8, generando lo que me ha dado por llamar la semilla del nombre del usuario (o SMU).

A continuación se obtiene el número de volumen (abreviando VLM) del disco 'C:' con la función 'GetVolumeInformation'. La semilla del nombre del usuario en 0x4036D8 se multiplica (en 0x401211) con la semilla del ratón en 0x4030CC (recordemos, las coordenadas del ratón que nos envía el hook global del ratón desde la función RandomNumberGen en la librería 'canyou.dll' en el evento propio de la aplicación WM_USER+6, y xoreadas con 0x12345) y al resultado se le suma el número de volumen obtenido (0x401218). Esto constituirá el primer trozo del número de serie.

La parte final del número de serie se obtiene sumando a lo que era SMP (semilla del 'ProductKey' generada en WM_INITDIALOG, y tras sumar la longitud del número de serie introducida por el usuario) con la semilla de las coordenadas del ratón en 0x4030CC.

Finalmente, el número de serie válido será el número que forma el primer trozo convertido a cadena, tras el que se concatena el segundo número convertido a cadena igualmente.

Resumiendo. El número de serie válido se forma con la concatenación de dos cadenas:
  1. (SMU * SMR) + VLM
  2. SMP + lstrlen(USERIAL) + SMR
Tras calcular el número de serie válido para el nombre de usuario introducido y las coordenadas del ratón en el momento de pulsar el botón de la ventana principal, éste se compara con la cadena introducida por el usuario como número de serie, mostrando el mensaje correspondiente según el resultado de esta comparación.

En caso de que la comparación sea incorrecta, la aplicación se cierra tras el mensaje de error, pero en caso contrario la aplicación permanece. Si se repite el proceso sin cerrar la aplicación hay que tener en cuenta que la nueva semilla del 'ProductKey', o SMP, será el anterior SMP más la longitud del número de serie que acabamos de validar, ya que solo se inicializa al crearse la ventana.

No hay comentarios:

Publicar un comentario