Cimatron 10 BETA - Tutorial

http://www.cimatron.com/ - Pagina Web.
Versión de instalacion(65Mb's).

Cimatron es un software muy caro y su proteccion parece formidable cuando se la ve por primera vez, nunca ha sido practico eliminar el dongle usado mediante emparchado solamente, en vez de eso debemos emular la rutina principal del HASP. El programa tambien utiliza varios codigos para habilitar opciones(esto es solamente una llave del registro - el aptly llamado KeyCode controla la ejecucion del modulo). Comenzaremos por localizar la marca de fabrica de la rutina HASP (Yo utilizo mi propio programa de marca de fabrica para este proposito, pero no es exactamente dificil codificar su propio findhasp.exe :) ). 2 lugares estan descubiertos, pero donde nos enfocaremos primero es en haspms32.dll.

Afortunadamente usted puede desensamblar facilmente este archivo y localizar la exportacion del 'hasp', esta es la rutina de llamada al dongle que recibe y acciona los codigos de servicio. Me he enfocado en cimit.dll como conteniendo la mayoria de los chequeos simplemente porque todos los modulos Cimatron son realmente cargadores (loaders) para cimit.dll. Cimit.dll tambien aloja el chequeo del KeyCode que no tratare aqui, aunque sea digno de alguna discusion. Cimit.dll pesa unos 6.6Mb's y el desensamblado con el W32Dasm era la unica opcion en mi PC.

2hrs despues, efectivamente si chequean las funciones importadas encontraran haspms32.hasp (estas son buenas noticias para nosotros), el numero de CALL's sin embargo es evidentemente NO TAN grande. Unos 22 chequeos separados, muchos servicios diferentes pero todos localizados muy proximos en terminos de direccion. Los listaremos aparte a causa de los argumentos, recuerden el formato de los parametros {colocados}.

Basic & servicios Hasp Especificos para el Dongle

===============================================================================
Servicio: Nom./No.   No. de parametros retornados   Otras direcciones X-ref
===============================================================================
IsHasp() - 1         1 parametro de status (Par1).  1C53E115, 1C53E1C7
HaspCode() - 2       4 parametros retornados.       1C53E164, 1C53E4FA
ReadWord() - 3       2 retornos (word + status).    1C53E3E9
WriteWord() - 4      1 parametro de status.         1C53E288, 1C53E2F1
ReadBlock() - 32     1 parametro de status.         1C53E372
SetTime() - 46       1 parametro de status.         1C53E8A4
GetTime() - 47       4 parametros retornados.       1C53E98B
SetDate() - 48       1 parametro de status.         1C53E908
GetDate() - 49       4 parametros retornados.       1C53EA20
WriteByte() - 4A     1 parametro de status.         1C53E5E9, 1C53E66F, 1C53E6C6
                                                    1C53EAC7, 1C53EB24.
ReadByte() - 4B      2 retornos (byte + status).    1C53E73C, 1C53E7C1, 1C53E81B 
                                                    1C53EB80, 1C53EBDE.

Todos estos servicios y chequeos parecen desalentadores, 22 chequeos en total, a los diseñadores de Cimatron realmente les gusta estar leyendo y escribiendo en el dongle, tambien hacen uso del reloj de tiempo real. Tristemente esta maravillosa estructura API tiene una debilidad inherente, los que hacemos ingenieria inversa podemos ver lo que el programa debe devolver exactamente, obviamente en el caso del servicio 2 usaremos un generador HASP de UCL's para encontrar los codigos de retorno reales (aunque podemos estar en problemas si otras dll's llaman a los sevicios con "seeds" diferentes), sin embargo todos los parametros lucen bastante faciles de recuperar. Podemos cargar la exportacion haspms32.dll con el Loader de SoftICE, esto sera util para localizar los chequeos iniciales, pero finalmente querremos rastrear el .dll y codificar nuestra rutina emuladora alli.

Hagamos un bpx para hasp(). Nuestro primer break es en la direccion 1C53E4B1 (servicio 1), parece como si el DWORD a ser verificado debiera ser no-cero. Despues de modificar este chequeo en memoria el servicio 2 es llamado en 1C53E4FA, aqui podemos ver los parametros que deseabamos :), Password 2 es 713D, Password 1 es 2209 y el "seed" code es 64 (el resultado de los parametros retornados es este - 3FA1, FF26, 5EE7 & 4955). Los diseñadores de Cimatron sin embargo, evidentemente no son estupidos, ellos no verifican solo el codigo de retorno, sino que lo manipulan (aunque unicamente una subtraccion)y utilizan el resultado como flags, esto sin embargo no sera un problema cuando "emulemos" estos retornos dentro de la rutina principal del hasp().

Un bpx final ocurre con una llamada al Servicio 4B, esto devuelve error -28 (El HASP con el password especifico no fue encontrado), evidentemente este deberia ser 0. Asi que iniciamente podemos ver lo que es necesario hacer, pero luego encontraremos solo 3 de los servicios, recuerden que no es dificultoso revertir estos procedimientos de verificacion en cada caso, heche una mirada al servicio 2.

:1C53E4FA CALL hasp() Servicio 2.
:1C53E4FF MOV ECX, DWORD PTR [1D33D7C8] <-- Codigo de retorno 1 (3FA1).
:1C53E518 MOV EAX, DWORD PTR [1D33D7CC] <-- Codigo de retorno 2 (FF26).
:1C53E530 MOV EDX, DWORD PTR [1D33D7D0] <-- Codigo de retorno 3 (5EE7).
:1C53E549 MOV ECX, DWORD PTR [1D33D7D4] <-- Codigo de retorno 4 (4955).

Los diseñadores de Cimatron actualmente restan estos valores de otras posiciones de memoria, los resultados deseados deben ser siempre 0. Para que funcione el servicio 4B necesitamos el valor del byte de retorno (sabemos que el estado del Par3 debe ser 0). Antes de que hagamos todo esto, les dare un KeyCode para el registro. Realmente su valor es echar una mirada a la matematicas detras de estos codigos que se validan usando IDIV's firmados.

"25077193,24980256,22023906,14541172,14488979,6305836,6222823,4336014"

Retornemos a nuestro emulador HASP . Cuando comenzamos el bpx para hasp() encuentra que se hacen llamadas a los servicios 1,2 & 4B (x2). Echemos una mirada dentro de haspms32.dll (MEMORIZE ESTE PATRON DE CODIGOS).

:10002E46 CMP BH,32 <-- BH es SIEMPRE el codigo del servicio.
:10002E50 JB 10002E50 <-- el Handle del servicio basico es levemente diferente.
:10002E56 CALL 10002C64 <-- Comunicacion con el HASP.

Despues de la llamada para comunicarse con el HASP nuestros parametros estan listos para se retornados Par1=AX, Par2=BX, Par3=CX & Par4=DX, ahora bien, el objetivo de nuestro emulador es i) identificar el codigo de servicio, ii) responder de acuerdo a el & iii) emparchar los valores correctos de acuerdo con el servicio. Hacer espacio para nuestra rutina no es demasiado dificil (al menos es lo que pense al principio), simplemente podemos escibrir sobre la llamada al hasp() con nuestro emulador CALL (CALL es preferible a JMP porque podemos (hacer) RET mas facilmente).

Nuestro primer problema es evidentemente identificar el servicio, simple realmente, es siempre el valor de BH. Hablemos un poco sobre el manejo de los servicios, el servicio 1 no es un problema, donde el servicio 2 podria haber sido mas tramposo es si el "proteccionista" hubiese emitido 2 llamadas al servicio 2 con un codigo "seed" diferente, pienso sin embargo que este no es el caso. Para el servicio 3 conocemos CX (Par3) necesitamos que sea 0 y BX (Par2) deberia ser una palabra del dongle, sin embargo no puedo ver como el servicio 3 puede ser llamado, ciertamente la creacion de una pagina del stack en 1C53E3B0 no podria ser alcanzada nunca, nosotros lo emularemos solo en caso de que. Los servicios 4/32/4A mostraran ser triviales (Par3=0). Miraremos los servicios especificos de Time/Date luego, el service 4B podria darnos un dolor de cabeza si los parametros de retorno se verifican o se utilizan como variables.

Con el servicio 4B estoy contando con un poco de suerte, si usted piensa un poco en terminos de lenguaje de alto nivel, comprendera lo dificil que es no traicionar el valor del codigo de retorno esperado (Par2) si usted lo utiliza como variable o flag, la necesidad de un manejador de error mostraria eso, obviamente los puntos a considerar son donde cualquier flag podria ser chequeado. Mirando cada llamada al servicio 4B, ambos Par2/Par3 siempre parecen ser verificados, pero no estoy seguro de si Par2 se usa realmente como alguna otra cosa que un flag indicador, pero eso puede ser verificado usando SoftICE. Hagamos un boceto de nuestro codigo de emulacion.

Incio del Emulador

:CMP BH,01 <-- Es el servicio 1 (80 FF 01).
:JNZ proximo_servicio
:MOV EAX,00000001 <-- EAX (Par1=1) HASP encontrado.
:RET <-- Retorno del emulador.
:CMP BH,02 <-- Es el servicio 2 (80 FF 02).
:JNZ proximo_servicio
:MOV EAX, 00003FA1 <-- Par1 (B8 A1 3F 00 00).
:MOV EBX, 0000FF26 <-- Par2 (BB 26 FF 00 00).
:MOV ECX, 00005EE7 <-- Par3 (B9 E7 5E 00 00).
:MOV EDX, 00004955 <-- Par4 (BA 55 49 00 00).
:RET <-- Retorno del emulador.
:CMP BH,03 <-- Es el servicio 3 (80 FF 03).
:JNZ proximo_servicio
:XOR ECX,ECX <-- Limpiar el Par3 (Par3=0).
:MOV EBX,00000001 <-- Creo que el Par2 deberia ser 1 .
:RET
:CMP BH,04 <-- Es el servicio 4 (80 FF 04).
:JNZ proximo_servicio .....

No completare esto, usted debe profundizar la idea para los codigos de servicio restantes. Debemos, sin embargo, chequear los servicios 46-49, (46 & 48) ambos probaran ser triviales, simplemente coloque Par3=0 i.e. (status good) y estos chequeos estaran muertos. Las llamadas a los servicios 47 & 49 sin embargo retornan 4 parametros, Par3 parece ser el unico que podemos fijar confiablemente. Como este HASP evidentemente tiene un reloj de tiempo real que no podemos emular facilmente, tendremos que descifrar que parametros son chequeados, podemos inferir los resultados del servicio 47 porque el servicio 49 deberia ser llamado (sino habria un pequeño punto que lo incluyera). Traslademos todos estos parametros a los 16 bytes del espacio de direcciones del dongle - nosotros establecimos esto chequeando nuestra primer llamada al hasp() 1 & 2).

Par1 = 1D33D7C8 (AX)
Par2 = 1D33D7CC (BX)
Par3 = 1D33D7D0 (CX)
Par4 = 1D33D7D4 (DX)

Esta translacion nos muestra que los servicios 47 & 49 chequean unicamente Par3 (el status). Podemos, por supuesto que solo por razones esteticas, emparchar algunos valores de fecha ficticios, aun cuando estos nunca parecen ser verificados. Yo emparche todo esto en mi rutina de emulacion, pero ay!, hay un tornillo suelto, hago correr Cimatron y suelta "No Protection Device", la razon de esto es un truco realmente solapado de los diseñadores de Cimatron, usted puede encontrarlo en la direccion 1C002F40. Una segunda rutina HASP esta incluida dentro de cimit.dll, por consiguiente no llama a nuestro maravilloso emulador, de alli el fracaso, hay tambien cuatro referencias cruzadas (X-ref)(afortunadamente para nosotros podemos emular la rutina una vez mas, que es exactamente la misma que en haspms32.dll).

Esta nueva rutina tiene 4 xrefs, 1C50A276, 1C50A2C1, 1C54FC40 & 1C54FC8B. Los servicios 3, 4B, 3 & 4B respectivamente, sin embargo los servicios 4B nunca son llamados (son trampas caza-bobos), si el programa esta contento con los resultados del servicio 3 entoces usted puede saltarse siempre esos chequeos secundarios. Sin embargo la presencia de esta rutina incorporada dentro de un dll es una noticia miserable para nosotros, en primer lugar, debemos buscar por cualquier otra ocurrencia y en segundo lugar debemos chequear todos los dll's mayores usados por el programa (aunque cimit.dll haga la mayoria del trabajo). La instruccion 'cmp bh, 32' sera un criterio lo suficientemente estrecho para realizar la busqueda, a menos que usted tenga su propia utilidad para hacerlo :).

Como sugeri, otros dll's llaman tambien a nuestro ahora emulado hasp() tambien, catia.dll (22 chequeos), chkcod.dll (20 chequeos), chkddf.dll (20 chequeos) - necesito continuar?. Pero (y este es un gran pero) todavia hay otro gran tornillo suelto, yo agregue mi codigo emulador y espere que todo funcionara, pero el programa se colgo muy poco ceremoniosamente, mi primer pensamiento:) (mala codificacion por mi parte), hice bpx al inicio del codigo de emulacion, despues de los primeros 5 opcodes podia ver alguna clase de corrupcion del codigo operativo, este es el verdadero asunto de los "reverser's", la rutina principal hasp() esta protegida de alguna forma contra modificaciones, algo mayor de 5 bytes o asi y alguna rutina patea (evidentemente no es un problema con la encripcion porque el archivo esta completamente desencriptado).

Esta rutina me produjo los mayores dolores de cabeza, salgo ahora de la pedagogia, probe haciendo bpx-ing para las APIs de apertura de archivos (BoundsChecker), buscando alguna clase de rutina de CRC/Paridad pero nada aparecio, probe re-dirigiendo la llamada a mas alto nivel (seguia corrupto), muchas mas cosas ademas pero todavia no podia emular eficazmente. Al final yo aprendi varias cosas, primero, que no importaba donde re-dirigia las llamadas o donde agregaba el codigo, la rutina pateaba, sin embargo podia emparchar 5 bytes y evitar la deteccion, esto casi seguramente excluye checksumming y CRC, quizas lo que tenemos es un verificador de paridad de orden (?), la solucion puede involucrar agregar codigo al final del archivo.

Agarren ustedes mismos una copia de una utilidad PE Dump,( el Dumppe.exe incluido con el Sourcer es perfectamente adecuado). Vuelquen el contenido a un archivo de texto usando una simple re-direccion de salida D.O.S. e.g.

dumppe haspms32.dll > dumphasp.txt

Consideren la siguiente información:

Tamaño de Imagen     : 18000
Tamaño Virtual       : 568  (de la seccion .reloc).
Offset del Raw Data  : 12200 (de .reloc).
Tamaño del Raw Data  : 600 (de .reloc).
Caracteristicas      : 42 00 00 40

Podemos chequear rapidamente 12200h+600h=18200h o 75.776d (el tamaño del archivo), 600-568=98 bytes de espacio libre la seccion .reloc que no es bastante espacio para nuestro emulador. En cambio nosotros alargaremos el archivo (yo elegi 250h), entonces podemos agregar esto al Tamaño de Imagen (18250), Tamaño Virtual (818) y Tamaño de Raw Data (850). Para realizar estos cambios necesitara editar los header's de los archivos PE (busque por .reloc usando HIEW (HVIEW?) y encontrara estos valores en estrecha proximidad). Tambien necesitaremos cambiar la caracteristica de 42 00 00 40 a 42 00 00 E2 (ver la documentacion de M$ sobre archivos PE para comprender esto). Finalmente con nuestros nuevos valores necesitaremos alargar el archivo (12,200h+850h=12A50h) = 76,368 bytes.

Este cambio nos dara espacio suficiente para iniciar un CALL a nuestro emulador y realizar las acciones necesarias, esto de veras trabaja y podemos realizae facilmente un RET al final completando asi el crack ([btw?] el instalador hace una llamada al servicio 5 que puede tambien gustarle emular). Si usted quiere echar una mirada a un haspms32.dll emulado, entonces envieme un e-mail y le dare la URL.

Pequeño Addendum - agregado el 08/04/99

Durante la prueba de este programa llamo mi atencion que aun despues de todo este trabajo Cimatron se cuelga despues de 10 minutos de uso, esto es porque cimit.exe tambien tiene una rutina hasp() interna independiente del emulador. Los servicios son triviales, (4 & 4A), limpiar Par3 (XOR ECX,ECX - Par3=0) sera suficiente para matar estos chequeos (este seguro de rastrear el codigo con un INT 3 bien puesto).

Revision de la Proteccion

Yo realmente recomiendo esta proteccion, el numero de chequeos obligara a cualquier cracker serio a emular la rutina principal, mis unicas leves criticas son la confianza puesta en el chequeo de Par3 (los regulares de HASP habran tomado nota de esto) y la falta de chequeos de los servicios 2 & 3 . De hecho yo pienso que la falta del chequeo del servicio 3 es mas debido al hecho de que Cimatron eligio un dongle barato sin demasiada memoria. Esta es una buena proteccion y en mi opinion es segura frente a cracker's *normales*, por esta razon elegi no entregar un parche "listo para usar" o mi emulador.


greenball.gif (835 bytes) Dongles Return to Main Index


(c) 1999 CrackZ. 15/16th March, 2nd April 1999.
www.000webhost.com