Karpoff Spanish Tutor

Programa:

Análisis de técnica de protección


 

PROTECCION: Mochilas de tipo Sentinel
Objetivo: Sacar la envoltura de un archivo encriptado.
Descripcion: No se ataca un programa en especial.
Dificultad: Media, Avanzado.
DOWNLOAD: http://xxxx
Herramientas: Softice, IDA, Editor Hexadecimal, Borland Turbo debug, Borland Tdump es conveniente.
CRACKER: Spyder   FECHA: 22/01/1998

 

 CREDITOS

Cortesía del sitio de Fravia's dedicado a ingeniería inversa

Este ensayo describe un método para trabajar con protecciones complicadas como las  de las mochilas de tipo Sentinel usadas en programas de WIN32. 

 

 AL ATAKE

                                                                                   

Introducción

Esto no es un crack de un producto en especial, es solamente un trabajo que hace probable, algo que parece prácticamente improbable. Conozco poco sobre mochilas, o sobre las empresas que las hacen. La que hace ésta es llamada Sentinel, y si ves referencias a Sentinel o SSI, es que estamos hablando de lo mismo. Si cuando experimentás te encontrás con listados muertos o megabytes de basura, es un buen signo (ojo que también puede ser malo). 

También conozco poco sobre el código actual que se usa para las mochilas; jamas tuve que mirarlo. Poner breakpoints en el port de la impresora, o algo parecido, es la peor manera de comenzar a reventar programas protegidos con mochilas.

 

Ensayo

¿Por qué este crack se volvió tan difícil? Debido a que el código de las mochilas es un envoltorio para executables semi-protegidos. Cuando el envoltorio es aplicado, encripta la mayoría del código ejecutable. De hecho se fija al final del programa y se apodera del punto de entrada del programa. Cuando el programa corre, el código de la mochila adquiere el control, y si está satisfecho con la mochila, desencripta el ejecutable y le pasa el control a él. El ejecutable, puede hacer llamados en varios puntos del programa durante la ejecucion, al código de la mochila para confirmar que aún está presente. 

De manera que si crackeás el cádigo inicial de la mochila, aún es necesario entender y emparchar el ejecutable principal. El encriptado hace que sea difícil comprender y casi imposible emparcharlo. Crackquear completamente el código de la mochila es una opción casi improbable. Estos tipos de mochilas hacen cosas como retornar un valor de un string que se le pasa, y el algoritmo y claves que hacen esto, están dentro de la mochila. No hay una manera sencilla de emparchar el cádigo del driver de la mochila para tener el mismo resultado.

No he estudiado con sumo detalle la rutina de encriptación, pero no es débil. Estoy casi seguro que es del tipo "rolling encription", donde cambiar un byte afectará todo los siguientes bytes; por esto es que es tan difícil emparcharla. También estoy seguro que la clave de encriptado está fuera de la mochila, (debe trabajar de este modo si el que la suministra tiene algún sentido). Esto significa que es necesario tener la mochila por algunas horas, aunque a falta de mochilas también podés tener algo de suerte. Crackear sin suerte es tan difícil como crackear sin corazonadas.

Si el programa tiene un modo de DEMO, o tal vez alguna opción para licencias en Red, entonces puede ser desencriptada sin la presencia de la mochila, de manera que ésta puede funcionar y después decidir si realmente necesita la presencia de la mochila o de una licencia por Red. Bueno, conociendo esto ¿cómo debemos empezar a trabajar? Mi enfoque es deshacer lo que se hizo cuando se aplicó la envoltura (wrapper), es decir recrear el programa muy cerca de como estaba antes de que fuera envuelto. Podemos aplicar las técnicas normales para estos casos.  Abajo observamos algunos bits interesantes en tdump para un programa de muestra.

Entry RVA     0003D950
Image base    00400000

Object table:
#   Name      VirtSize    RVA     PhysSize  Phys off  Flags
--  --------  --------  --------  --------  --------  --------
01  .text     0001E200  00001000  0001E200  00000400  E0000020 [CERW]
02  .rdata    00000800  00020000  00000800  0001E600  C0000040 [IRW]
03  .data     00013E24  00021000  0000D200  0001EE00  C0000040 [IRW]
04  .PAD000   00000E00  00035000  00000E00  00000000  C0000080 [URW]
05  .SSINod   00007A00  00036000  00007A00  0002C000  E0000040 [IERW]
06  .rsrc     00000800  0003E000  00000800  00033A00  C0000040 [IRW]
07  .idata    00000C00  0003F000  00000C00  00034200  C0000040 [IRW]
08  .reloc    00000200  00040000  00000200  00034E00  42000040 [IDR]

SSINod es el código que se agrega para la mochila, PAD000 es donde estaba el segmento original .reloc (contiene direcciones importantes que son patcheadas en la carga del programa). Image base es donde se carga el programa para el modelo de memoria plano (la mayoría de direcciones son relativas a esta base). Entry RVA es la dirección de comienzo del programa. El siguiente paso es cargar el programa en el turbo debug y desencriptarlo. Si el programa corre y permanece corriendo mientras presenta un mensaje de error, entonces la mejor manera de proceder es disparar el TD32 en una ventana de DOS y unirlo al programa. Esto es una de las maravillas del debug en Win32: sólo elegir file | Atttach y seleccionar la ventana, después de un momento TD32 tomará control del programa y vos podrás observarlo.

Podés estar capacitado para iniciar el programa con el TD32, pero tener el control una vez que está corriendo y desencriptarlo, no es tan fácil, y menos aún tener la imagen de memoria correcta cuando el programa termine, o tal vez fijarte en el Stack para encontrar algunos lugares donde poner BPX de hardware. Estoy seguro que es bastante fácil con SICE, pero el SICE, (hasta donde yo conozco), no puede hacer lo que hace el TD32. De cualquier manera asumo que de algún modo has maniobrado para tener el control del programa usando el TD32. Para confirmar que el programa fue desencriptado, echale un vistazo a la imagen base + .text RVA, y estoy seguro que luce como código. Si no, necesitas que algún amigo te preste un mochila, para comenzar todo de nuevo. 

Ahora abrí una ventana para volcar código, ir a (Ctrl-G), dirección de image base y empezá a marcar un bloque presionando la tecla izquierda y arrastrá el mouse por los bytes que necesitás (seleccionar bloques con TD32 es un poco extraño, algunas veces el shift + el cursor no funciona). Bien, ahora andá al final del segmento .text (el cual debe expandir el bloque marcado). Seleccioná Block | Write (Ctrl-B, W) y TD32 cariñosamente volcará el encabezado del ejecutable, y el segmento .text estará completamente desencriptado para vos.

TD32 parace que escribe dichos bloques a una velocidad de 1-2KB por segundos, lo que es bastante lento. Por lo tanto te aconsejo que te tomés un café o cocktail como descanso. Perdón si estoy siendo muy específico acerca de la operación del TD32, pero la primera vez que trabajé con él, no sabía cómo hacer esto de volcado de bloques de memoria al disco, y me pasé muchas horas calcando bloques a un archivo de memoria, después los procesaba con un programa que escribí, que hacía que los volcados se convirtieran en el archivo binario. Por eso quiero estar seguro que mis lectores conocen estos trucos; "un poco de conocimiento no le hace mal a nadie". 

Ahora una cosa más antes que acabemos con el TD32. Observá el segmento PAD000. Tiene que tener la mayor parte completa de ceros. Tenés que ver un bloque de direcciones de 32 bits. Estas son las direcciones de las entradas de las importaciones que fueron emparchadas cuando se cargó el programa en el segmento .reloc, parte del código de la mochila las copió aquí, que es donde el programa principal espera que estén. El orden y número de entradas no es el mismo que en el segmento .reloc, de manera que no se puede hacer una simple copia de ellas, ni tampoco jugar con los RVAs para ponerlas en el lugar correcto. Poné un breakpoint de hardware de escritura de cualquiera de esas entradas, y entonces reiniciá el programa desde el TD32 (Ctrl-F2). TD32 debe parar en el medio del código de la mochila que copia estas entradas de un lado al otro.

Echále una mirada al código, y si es como la cosa que yo he visto, encontrarás una tabla de pares de direcciones de 32 bits apuntadas por EBX.  Estas son pares de direcciones relativas, (dentro del segmento .reloc) con la dirección relativa para el segmento .PAD000 para el mismo import. Marcá esta tabla y volcála al disco como lo hicimos antes.  

Ahora si terminamos con el TD32, y probablemete con la mochila.

 

Juntando las piezas

Ahora tenemos que insertar el dump desencriptado en el ejecutable, esto es un tipo de edición binaria, pero un poco más tramposa.  Echále otra mirada a la tabla de objeto tdump y  entendé lo que esto significa... 

Entry RVA     0003D950
Image base    00400000

Object table:
#   Name      VirtSize    RVA     PhysSize  Phys off  Flags
--  --------  --------  --------  --------  --------  --------
01  .text     0001E200  00001000  0001E200  00000400  E0000020 [CERW]
02  .rdata    00000800  00020000  00000800  0001E600  C0000040 [IRW]
03  .data     00013E24  00021000  0000D200  0001EE00  C0000040 [IRW]
04  .PAD000   00000E00  00035000  00000E00  00000000  C0000080 [URW]
05  .SSINod   00007A00  00036000  00007A00  0002C000  E0000040 [IERW]
06  .rsrc     00000800  0003E000  00000800  00033A00  C0000040 [IRW]
07  .idata    00000C00  0003F000  00000C00  00034200  C0000040 [IRW]
08  .reloc    00000200  00040000  00000200  00034E00  42000040 [IDR]

El RVA del segmento .text es 1000, significa que en la memoria éste comieza en la dirección 401000 (image base + 1000). El "Phys off" es donde está alojado el segmento .text en el archivo ejecutable. Esto significa que el dump desencriptado tiene C00h bytes entre las direcciones 400h y 1000h, que necesitan ser cortadas. 

El header (encabezado) del ejecutable no está encriptado, y en mis experiencias anteriores siempre resulta idéntico en la imagen de memoria. Con tu editor favorito corta los C00h bytes y pegále la imagen dumpeada encima del ejecutable.

 

Reparando los imports 

Nosotros hicimos el volcado de los pares de direcciones para mover la dirección de importación entre los segmentos .reloc y .PAD000 al comienzo.  Debemos tener mucho espacio para código en el segmento .SSINod, y una vez que el programa esté realmente crackeado, éste no debería pasar más dentro de este segmento. Cambié el volcado de binario a hexadecimal, después lo edité manualmente a sentencia de assembler por cualquier viejo ensamblador que pueda crear un archivo binario de salida. Agregué A1h adelante de la primera dirección para hacer un "mov eax[????]" y A3h adelante de la siguiente para hacer "mov [????], eax". Recordá agregar la dirección de carga de la Imagen (Image load) a todas las direcciones, porque son todas relativas. Acordáte que estás cargando del segmento .reloc y almacenando en el segmento .PAD000. Yo solo volqué este fragmento binario en el ejecutable al comienzo del segmento .SSINod. Después de lo anterior bien podrías querer emparchar el RVA de entrada en el encabezado del ejecutable para apuntar a este código (o emparchar un salto más tarde). 

 

¿Dónde estamos ahora?  

En este punto deberíamos tener un ejecutable que ya no está encriptado y empezara corriendo un pedazo de código que mueve direcciones de importación en donde el ejecutable original espera que estén. El punto de entrada probablemente apunta a este código pero no corre, debido a que el emparchado de la importación actualmente apunta al código basura de la mochila. El ejecutable debe tener el mismo tamaño que el original, si no es así, me temo que se te perdió algo en la edición en algún lugar. Bien ahora tenés algo para comenzar a crackear el final del listado muerto. El trabajo que tenés que hacer es buscar el entry point original y emparchar adentro un salto al final del emparchado del código de importación antes de que puedas  intentar correrlo. 

Si estás usando el IDA, puede ser que descubras el entry point como alguna de las funciones de startup de las bibliotecas de run time. Aunque el IDA no reconoce el compilador automáticamente en los ejecutables reconstruidos (selecccionar firma manualmente). La otra altenativa es debuguear otro programa construido con el mismo compilador para encontrar cómo luce el entry point. También podés observar el código para buscar un llamado a Call GetVersion, los códigos de startup casi siempre llaman al principio esta función. Una vez que encontraste esto y lo emparchaste, el trabajo está hecho. Ahora tenés el mismo ejecutable con las mismas funciones que tenía antes que fuera envuelto (wrapped).

Hay chances de que este programa aún pueda hacer llamadas al código de la mochila, pero ya no es como el autor espera: que la examinación del código sea difícil y el emparchado imposible. Es poco probable que hagan algo muy sofisticado, y si lo hacen, mejor, más desafío para nosotros.

 

Nota Final 

Bien, espero que hayas encontrado esto interesante y útil. No he visto este encriptado de Sentinel en otro lado que no sea el segmento .text, por supuesto no siempre se encripta el segmento de .text completo.  La misma técnica de recuperación puede ser usada para otro esquema basado en encriptación, pero pensá en extender el truquito del encriptado a otros segmentos además del .text. Tendrás que volcar y capturar una imagen antes de que el programa corra lo suficiente como para que "fumigue" sus propios segmentos. El segmento .reloc es particularmente doloroso porque el OS lo "corrompe" durante la carga, (sin embargo este tipo de cosas es igualmentente difícil para el protector). 

La protección HASP parece ser bastante parecida a mi Sentine. Yo sospecho que el encriptado de enrollado de Sentinel es mucho más robusto. 

Spyder

Traducción: AFTOSA

 

Karpoff Spanish Tutor: Pagina dedicada a la divulgacion de informacion en Castellano, sobre Ingenieria Inversa y Programacion. Email "Colabora con tus Proyectos"
www.000webhost.com