[ Mochilas, una aproximación genérica ]


Básicamente una mochila no es más que una caja de plastico que contiene un circuito (algunos tipo ASIC) que puede variar en complejidad según el tipo de mochila. Algunas mochilas constan de memoria (unos pocos bytes) en los cuales se almacenan datos usados por la própia mochila. Las mochilas más utilizadas suelen ser las Sentinel Pro de Rainbow Technologies o las HASP de bullet.gif (296 bytes)Aladdin Systems. Estas últimas tiene fama de ser las mejores en cuanto a protección se refiere, pero esto resuelta cierto sólo en la medida en la cual el programador haya sabido implementar la protección.
En la mayoría de caso resulta penoso comprobar que todo el sistema depende de tan sólo la modificación de un sólo byte del ejecutable y demuestra que el programador ni tan siquiera se molestó en leer los manuales.


[ Introducción ]
E
ste tutorial pretende ser una pequeña introducción al basto mundo de las mochilas, mi prósito es dar una idéa general sobre como atacar estos sistemas de protección y no se centra en ningún tipo de mochila en concreto. De ningún modo este documento pretende ser la biblia sobre las mochilas, mi único propósito es ofrecer una visión general de lo que normalmente podremos encontrarnos. Para el seguimiento de este tutorial utilizaremos el debugger bullet.gif (296 bytes)SoftIce.


[ Cómo actuar ante cualquier mochila ]

Los pasos a dar antes de empezar a desproteger una aplicación protegida con mochila son los siguientes:

marcador.gif (1024 bytes) 1.- Identificar el tipo de mochila a la cual nos enfrentamos. No suele ser dificil en la mayoría de los casos, mira en la mochila (si tienes acceso a esta), comprueba los ficheros instalados por la aplicación, busca archivos DLL o VXD que contengan un copyright diferente al del autor del programa protegido. Algunos de los nombres más comunes que puedes encontrar:

HASP*.VXD
HASP*.DLL
SENTINEL.VXD

marcador.gif (1024 bytes)2.- Recoger toda la información posible en las páginas del fabricante de la mochila, manuales, FAQ's (todo lo que encuentres puede ser de gran ayuda). Encontraras el API de la mochila con la documentación de las operaciones que realiza cada función (conocer a tu enemigo es básico para derrotarle). No te asustes por lo que diga el manual: encriptación RSA, anti-debugging etc. Efectivamente todo esto puede estar presente, pero la mayoría de veces no es así, además se trata de crackear la aplicación y no la mochila.

marcador.gif (1024 bytes)3.- A veces hay kits de desarollo de evaluación disponibles por un módico precio y adivina, !!incluyen una mochila!!

marcador.gif (1024 bytes)4.- Ahora ya disponemos de todo lo necesario, solo nos queda pensar un poco. Imaginemos que los programadores son bastantes vagos y estupidos, y su esquema de protección es de los más fáciles que podamos encontrar (primero siempre intenta una aproximación fácil) del tipo.

call CheckDonglePresence
test eax,eax
jz NoDongle
...
...
@NoDongle:

Aunque te parezca increible que esto pueda ser así, es cierto en la mayoría de los casos. Muchos programadores tan solo utilizan una función que devuelve TRUE o FALSE para comprobar si la mochila esta conectada. A veces realizan más de una comprobación por lo que deberemos parchear la rutina de comprobación en vez del salto jz NoDongle.

En la mayoría de casos, la función CheckDonglePresence está contenida dentro de una DLL o VXD (los que previamente hemos encontrado), estas DLL o VXD están programados por los fabricantes de la mochila y suelen estar bastante bién protegidos: técnicas anti-debugging, código auto-modificalble, encriptación de datos etc. Mi consejo es no modificarlos si no es absolutamente necesario, ya que suele ser más fácil modificar la aplicación protegida.

La primero es encontrar la llamada (a veces más de una) al API de la mochila, para ello desensamblaremos la DLL que contenga el API con el W32Dasm o el IDA. En la lista de funciones exportadas, buscaremos cualquier nombre que levante sospechas estos pueden ser algunos:

IsDonglePresent
CheckDongle
ReadDongle
WriteDongle

Con el manual del API ahora podremos investigar las funciones una a una e identificar donde se se guardan y que operaciones se realizan sobre los parámetros pasados a cada una de las funciones. A partír de aquí, deberemos de establecer nuestro plan de ataque, he aquí algunos de los sistemas más utilizados para atacar mochilas:

[ Metodo 1- Trazar a lo retro ]

Los programadores tienen la mania de habisarnos cuando no se detecta la mochila, normalmente utilizan un cuadro de mensaje, por lo que procederemos de la manera siguiente:

marcador.gif (1024 bytes) 1.- Establecer un breakpoint sobre la función que crea el cuadro de mensaje, generalmente MessageBoxA.
marcador.gif (1024 bytes) 2.- Cuando el breakpoint surta efecto, pulsaremos F12 para volver a la función que lo llamó y examinaremos el código previo a la llamada en busca de algún salto condicional que este cercano. Si no encontramos nada pulsaremos de nuevo F12 y procederemos de la misma forma. Si encontramos algún salto sospechoso, prueba a invertirlo y ejecuta de nuevo el programa haber lo que sucede, te sorprenderas de lo que puedes llegar a crackear con este metodo tan simple.

[ Metodo 2- Capturar los accesos a puertos ]

Establecer un punto de ruptura en los accesos a puerto.Voy a tratar exclusivamente las mochilas que se colocan en uno de los puertos paralelo, es decir un LPT, por ser estas las más extendidas. Sabemos que un PC tiene 65535 puertos distintos y que los puertos paralelo corresponden a:

LPT1 -> 378h
LPT2 -> 278h

Uno de los métodos más utilizados para encontrar donde se envian datos a la mochila, se basa en utilizar un punto de ruptura (breakpoint) sobre el puerto donde se encuentra instalada la mochila, si utilizamos el debugger SoftIce, esto lo haremos con el comando:

> BPIO 378
> BPIO 278

Esto hace que el SoftIce detenga la ejecución del programa cuando este intenta enviar datos a la mochila en alguno de los dos puertos. A partir de aquí, podremos trazar el código paso a paso comprobando que es lo que hace el programa. Este metodo tiene ciertas desventajas: nos dejará justo en el API de la mochila con lo cual si el API esta preparado para detectar un debugger, podremos acabar con un cuelgue del sistema. Se obtienen mejores resultados con este método si la aplicación es de MS-DOS.

[ Metodo 3- Forzar un BPX en el API ]

Establecer un punto de ruptura forzado en el API. Podemos colocar el código de operación CCh (INT 3) al inicio de cualquier función del API (esto implica modificar el archivo con editor hexadecimal) y establecer un punto de ruptura (breakpoint) sobre la INT 3 en SoftIce con:

> BPINT 3

Ahora ejecutaremos SoftIce y este se detendrá (si se llama a esa función), ahora reemplazaremos el byte CCh por el que había originalmente con el comando A del SoftIce (A dirección_codigo). Traza paso a paso y comprueba que sucede cuando no hay mochila. Con suerte encontrarás una comprobación seguida de un salto tipo: jz NoDongle. Invierte el salto y comprueba lo que sucede (quizás tu aplicación comienze a funcionar :) ). Puedes intentar parchear el código para que emule la mochila retornando un valor que satisfaga el proceso que llamó a la función. En la mayoría de los casos esto resulta suficiente.

Si la aplicación se resiste, quizás se este usando más de una función del API y estas también deberán de ser emuladas/crackeadas.

[ Metodo 4- Anticiparse al API ]

Establecer un punto de ruptura antes de cargar el API de la mochila. Este metodo de ataque se basa en interrumpir la ejecución del programa cuando se carga la DLL o el VXD que maneja la mochila. Para realizar esto podemos utilizar los siguentes breakpoints:

> BPX LoadLibraryA
> BPX LoadLibraryExA
> BPX CreateFileA

Nota: la A final significa que son las funciones en su versión de 32Bits.

Las funciones LoadLibrary se encargan de cargar una DLL en memoria, y la función CreateFile se utiliza para establecer una via de comunicaciones con un dispositivo VXD (se utiliza para muchas más cosas, ver la guía de referencia del API de Windows ) la descripción de estas funciones es como sigue:

HINSTANCE LoadLibrary(
    LPCTSTR lpLibFileName     // dirección que contiene un nombre de un fichero DLL
);

Parametros:

lpLibFileName

Apunta a una cadena terminada en nulo que nombra al módulo ejecutable (puede ser una .DLL o .EXE ). El nombre especificado es el nombre del fichero y no tiene nada que ver con el nombre del módulo almacenado en el fichero como se especifica por la palabra clave LIBRARY en el fichero de definición del módulo (.DEF)

Si la cadena especifica un camino y el fichero no existe en el directorio especificado, la función falla. Si no se especifica un camino y se omite la extensión, la extensión por defecto es .DLL. Aún así el nombre del fichero puede incluir el caracter punto (.) para indicar que el módulo no tiene extensión. Cuando no se especifica un camino, la función buscael fichero mediante el siguiente criterio:

    1.    El directorio desde donde se cargó la aplicación.
    2.    El directorio actual.
    3.    Windows 9x: El directorio system the Windows. Windows NT: El directorio system de 32 bits de windows SYSTEM32.
    4.    Windows NT: El directorio system de 16-bit de windows SYSTEM.
    5.    El directorio de Windows.
    6.    Los directorios que aparecen en la variable de entorno PATH.

Retorna:

    Si hay éxito, retorna el handle del módulo.
    Si no hay éxito, retorna NULL.

HANDLE CreateFile(

    LPCTSTR lpFileName,   // puntero al fichero
    DWORD dwDesiredAccess,    // tipo de acceso
    DWORD dwShareMode,    // modo de compartición
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,    // puntero a los atributos de seguridad
    DWORD dwCreationDistribution,    // metodo de creación
    DWORD dwFlagsAndAttributes,    // atributos del archivo
    HANDLE hTemplateFile    // handle al fichero con los atributos a copiar
);

Parametros que nos interesan:

lpFileName

Apunta a una cadena terminada en nulo que contiene el nombre del objeto a crear o abrir. (archivo,directorio,dispositivo de disco,recurso de comunicaciones, consola etc.)

Retorna:

    Si hay éxito, un handle abierto al objeto especificado.
    Si falla, el valor es igual a INVALID_HANDLE_VALUE = -1

Este método presentado de esta manera suele ser bastante inútil, ya que las dos funciones anteriores son muy utilizadas y establecer un simple breakpoint sería bastante engorroso ya que deberiamos de ir comprobando si realmente se está pasando como argumento el módulo que contiene el API de la mochila para cada interrupción que haga el breakpoint (puede haber bastantes interrupciones). La solución es bastante sencilla, utilizaremos un breakpoint condicional, el cual surtirá efecto cuando se pase como argumento las cuatro primeras letras del módulo, para ello deberemos saber que el primer argumento de la función se encuentra en ESP->4 y como es un puntero que apunta a una cadena quedará *(esp->4), por lo tanto para la función LoadLibrary haremos:

BPX LoadLibraryA IF *(ESP->4)=='SENT'
BPX LoadLibraryExA IF *(ESP->4)=='SENT'

Esto detendrá la ejecución cada vez que se intente carga una DLL la cual se llame sent*.*, con toda probabilidad si la DDL se llama sentinel.dll, este breakpoint surtirá efecto. Para realizar la misma operación con un VXD el breakpoint variará un poco debido a que cuando se desea abrir un VXD, el nombre de este debe de estar precedido de los 4 caracteres "\\.\", por lo tanto haremos:

BPX CreateFileA IF *(ESP->4+4)=='\\.\HASP'

El +4, se suma a la dirección en ESP->4 para saltarse los 4 caracteres que preceden a "HASP". Esto detendrá la ejecución cada vez que se intente abrir un VXD cuyo nombre comienze por "HASP". Nota: Este breakpoint es de mucha utilidad cuando tratamos con mochilas tipo HASP :-)

A partir de aquí si se ha abierto el VXD, la comunicación con la mochila se realizará a traves del VXD mediante el handle devuelto por CreateFile, usando la función DeviceIOControl.

BOOL DeviceIoControl(

    HANDLE hDevice,        // handle al dispositivo de interés
    DWORD dwIoControlCode,   // código de control de la operación a realizar
    LPVOID lpInBuffer,        // puntero a un buffer que contiene información de entrada
    DWORD nInBufferSize,   // tamaño del buffer de entrada
    LPVOID lpOutBuffer,        // puntero a un buffer que recibe información de salida
    DWORD nOutBufferSize,    // tamaño del buffer de salida
    LPDWORD lpBytesReturned,    // puntero a una variable que recibirá el conteo de los bytes de salida
    LPOVERLAPPED lpOverlapped    // puntero a una estructura superpuesta para operaciones asincronas
);

Con lo cual podremos establecer otro breakpoint cada vez que intente enviar datos al VXD de la mochila con:

BPX DeviceIOControl if (esp->4)==hDevice

Donde hDevice será el valor devuelto por CreateFile. Ahora podremos ir examinando los buffers que la función DeviceIOControl pasa al VXD y lo que esta retorna después de ser ejecutada.

En definitiva la idea general es encontrar algún punto donde se comparen los datos devueltos por el API con los que la aplicación espera e invertir el salto. A veces no es posible y la única solución es emular el funcionamiento de la mochila mediante el parcheado del código.Bueno, espero haber cubierto los aspectos más genéricos a los que un cracker se enfrenta cuando debe desproteger este tipo de protecciones. Cualquier contribución/ampliación/corrección de lo aquí expuesto será bienvenida.


You are inside Reversed Minds pages.

por Mr. Silver / WKT!
La información aquí vertida es exclusivamente para uso educacional, no puedo hacerme responsable del uso que se haga de esta, por lo que atiendo a la honradez de cada uno :), recuerda que debes comprar el software que utilices :)