ESTUDIO COLECTIVO DE DESPROTECCIONES
Última Actualizacion: 25/10/2001

Programa Rayman 2 W95 / W98
Descripción Juego de plataformas
Tipo C-Dilla SafeDisc
Tipo de Tutorial [X]Original, []Adaptación, []Aplicación, []Traducción
Url  
Protección SafeDisc. Encriptación de ejecutable con una firma incopiable grabada en el CD original
Dificultad 1) Principiante, 2) Amateur, 3) Aficionado, 4) Profesional, 5) Especialista
Herramientas FrogsIce, SoftIce v4.00, Gettyp, W32dasm v8.9, Hiew, ProcDump, Adump
Objetivo Obtener un ejecutable desencriptado
Cracker rik
Fecha 20 de Junio del 2000

INTRODUCCION

He decidido escribir este tutorial en base a ciertas diferencias con el tutorial de SkUaTeR (Gran Thief Auto). Es bastante recomendable que leas su tutorial antes que este. Mas que recomendable, yo diria que es imprescindible. Leelo.

Además el C.u.dilla r1 final no funciona con este programa.

Como supongo que ya lo habras leido, no voy a explicar en que se basa este sistema de protección. Únicamente decir que el ejecutable original del juego es Rayman2.icd y que el fichero Rayman2.exe es el encargado de desencriptarlo conforme a una firma digital grabada en el CD del juego.

AL ATAQUE

Lo primero que hay que hacer es cargar el FrogsICE para ocultar el debugger ya que Rayman2.exe utiliza dos sistemas de detección del SoftICE: MeltICE (SICE, NTICE y SIWVID) y int68h (si quieres saber mas sobre estos métodos de detección te recomiendo que leas los ficheros de texto que vienen con FrogsICE).

Bien, utilizando el programa Gettyp obtenemos la siguiente información del programa Rayman2.icd:

Calculated entrypoint: 389312 / 0005F0C0h (RVA: 0005FCC0h) <-------
...
Objects (object align = 00001000h):

Name Virt size    RVA Phys size Phys Ofs
.text 0009A1E8h 00001000h 0009A200h 00000400h <-- Sección de código
.rdata 00002E56h 0009C000h 00003000h 0009A600h <-- Imports
.data 000776D0h 0009F000h 00007C00h 0009D600h <-- Sección de datos

Como el Image Base es 400000h el punto de entrada al programa es 45FCC0h. Ahora debemos poner el punto de ruptura en esta dirección, pero ha de ser desde dentro de Rayman2.icd, es decir, debemos estar dentro de la ejecución de este programa para que el punto de ruptura sea válido (gracias Gádix). Si no os fiais y el Image Base no lo obteneis con el Gettyp, también podeis obtenerlo con ProcDump, W32Dasm o hiew.

Pero, ¿como sabremos que estamos en Rayman2.icd y no en Rayman2.exe si para el SoftICE ambos módulos se llaman Rayman2? (: mod ray*). Pues muy sencillo, renombramos Rayman2.exe a Rayman.exe y cuando en el SoftICE nos ponga (en la linea base de la ventana de código) Rayman es que estamos en Rayman2.exe y si pone Rayman2 es que estamos en Rayman2.icd. Entonces es cuando debemos poner el punto de ruptura:


:bpx 45fcc0

Hay dos aproximaciones distintas para entrar dentro del código de Rayman2.icd. La primera es poniendo un bpx en la función GetDriveTypeA y pulsando sucesivamente F12 y F5 hasta que entremos dentro de Rayman2.icd. La otra es ejecutar Rayman2.exe y cuando aparezca la ventanita pequeña en la ventana superior izquierda pulsamos Ctrl+D y pulsamos sucesivamente F12 hasta que desemboquemos en Rayman2.icd.

Una vez puesto el punto de ruptura en el Entry Point pulsamos F5, continuamos la ejecución del juego y salimos de él. Volvemos a ejecutar Rayman2.exe y ahora debe saltar la ventana del SoftICE en el punto de entrada de Rayman2.icd. Debemos ver algo parecido a esto:

0167:0045fcba ff25b4c14900   jmp       [MSVCRT!_access]
0167:0045fcc0 55             push      ebp                <-- Entry Point
0167:0045fcc1 8bec           mov       ebp,esp
0167:0045fcc3 6aff           push      ff
0167:0045fcc5 68d0c94900     push      0049c9d0
0167:0045fcca 6890ff4500     push      MSVCRT!_except_handler3           
0167:0045fccf 64a100000000   mov       eax,fs:[00000000]                 
0167:0045fcd5 50             push      eax
0167:0045fcd6 64892500000000 mov       fs:[00000000],esp                 
0167:0045fcdd 83c498         add       esp,-68                           
0167:0045fce0 53             push      ebx                               
0167:0045fce1 56             push      esi                               
0167:0045fce2 57             push      edi  
0167:0045fce3 8965e8         mov       [ebp-18],esp                      
0167:0045fce6 c745fc00000000 mov       dword ptr [ebp-04],00000000
0167:0045fced 6a02           push      02                
0167:0045fcef ff1510c24900   call      [MSVCRT!__set_app_type]
0167:0045fcf5 83c404         add       esp,04
  ...
  ...
0167:0045fdd9 89758c         mov       [ebp-74],esi
0167:0045fddc ebf0           jmp       0045fdce                          
0167:0045fdde c745d000000000 mov       dword ptr [ebp-30],00000000
0167:0045fde5 8d45a4         lea       eax,[ebp-5c]
0167:0045fde8 50             push      eax                               
0167:0045fde9 ff1520c04900   call      [0049c020]     <-- Primera llamada a la API
                                                             de Windows.
0167:0045fdef f645d001       test      byte ptr [ebp-30],01
0167:0045fdf3 740a           jz        0045fdff                          
0167:0045fdf5 8b45d4         mov       eax,[ebp-2c]                      

Nota: A menos que cargueis los exports de la libreria MSVCRT.DLL, bien en winice.dat, bien con el Symbol Loader no vereis las llamadas a las funciones de MSVCRT.DLL.

Bien, Lo primero que debemos hacer es volcar las secciones de código y datos, pero en vez de hacerlo por separado como pone en otros tutoriales, nosotros lo vamos a hacer todo junto, que asi da menos trabajo. Para hacer esto de la forma mas sencilla posible debemos hacerlo con ProcDump, pero primero debemos detener la ejecución en el punto de entrada. Para ello tenemos dos soluciones:

1.- Utilizando el Adump con el comando R obtenemos la dirección reservada por este programa para poder trabajar. Por ejemplo, en mi ordenador la dirección es 8305F000h. Cambiamos en el SoftICE el EIP a esta dirección y ensamblamos un jmp eip.

  :r eip 8305f000        <-- Cambiamos el puntero de programa
  :u eip                 <-- desensamblamos esta dirección
  :a eip                 <-- modificamos el codigo de esta dirección
   
  8305f000 jmp eip
  

y pulsamos F5. Ahora con ProcDump volcamos TODO el proceso a disco.

2.- Ensamblamos en el Entry Point un jmp eip y pulsamos F5. Volcamos TODO el proceso a disco y luego con un editor hexadecimal, como Hiew, nos vamos al Entry Point y cambiamos el jmp 45FCC0 por lo que teniamos al principio.

Bien, después de esto tenemos las secciones de código (.text) y la de datos (.data). Solo nos queda lo peor, extraer todos los imports !!Uhhhhh!!.

La sección .rdata ('a bitch called ...', una perla de sabiduría)

Si desensamblamos el proceso volcado a disco con W32Dasm vemos que todos los imports parecen estar bien salvo los de las librerias KERNEL32.DLL y USER32.DLL. Contamos las funciones de ambas librerias y vemos que para la libreria Kernel32 existen 60 imports y para la user tenemos 26. Estas funciones tienen mal la dirección del import. Si traceamos con F8 la primera llamada a la api de Windows de Rayman2.icd veremos codigo como este:

  
0167:0076424c 60             pushad 
0167:0076424d 68beff1394     push      9413ffbe   (*) Explicado más abajo 
0167:00764252 6804000000     push      00000004   <-- Numero de función 
0167:00764257 6801000000     push      00000001   <-- Libreria 1: Kernel32.dll                        

0167:0076425c ff1572427600  call      [00764272] <-- Llamada a Dplayerx.dll para obtener la dir. correcta 0167:00764262 83c40c         add       esp,0c 0167:00764265 61            popad 0167:00764266 ff256c427600  jmp       [0076426c] <-- deberia cambiar con la dir.                                                          de la func. del API de Windows 0167:0076426c 0000         add       [eax],al   ------ basura 0167:0076426e 0000          add       [eax],al      .              0167:00764270 0000          add       [eax],al      .              0167:00764272 d009           ror      byte ptr [ecx],1    . 0167:00764274 8f00           pop      dword ptr [eax]     . 0167:00764276 0000          add       [eax],al           .              0167:00764278 0000          add       [eax],al  ------ basura 0167:0076427a 60             pushad 0167:0076427b 68beff1394     push     9413ffbe                            0167:00764280 6805000000    push      00000005 <-- Numero de función 0167:00764285 6801000000     push     00000001   <-- Libreria 1: Kernel32.dll                      0167:0076428a ff15a0427600   call     [007642a0]    0167:00764290 83c40c         add       esp,0c 0167:00764293 61             popad 0167:00764294 ff259a427600   jmp      [0076429a]                          0167:0076429a 0000          add       [eax],al

Hasta aqui ninguna diferencia con el tutorial de SkUaTeR (Gran Thief Auto). Si seguimos sus pasos y ejecutamos el código que propone para obtener la dirección correcta del api, obtenemos un bonito fallo de protección general como sólo Microchof sabe hacer. ¿Por qué? Porque en este juego, dplayerx.dll no actua como en el juego Gran Thief Auto. Veamos las diferencias.

Traceando la llamada a Dplayerx.dll llegamos a este código:

0167:008f0b69 6858899000     push      00908958                            
0167:008f0b6e ff156cb19000   call      [KERNEL32!EnterCriticalSection]     
0167:008f0b74 8b450c         mov       eax,[ebp+0c]                        
0167:008f0b77 50             push      eax                                 
0167:008f0b78 8b4d08         mov       ecx,[ebp+08]                        
0167:008f0b7b 51             push      ecx
  ...
  ...
0167:008f1176 8b45fc         mov       eax,[ebp-04]    <-- Dir correcta de la API
0167:008f1179 50             push      eax                                 
0167:008f117a 8b4d0c         mov       ecx,[ebp+0c]    <-- No. de función
0167:008f117d 51             push      ecx 
0167:008f117e 8b5508         mov       edx,[ebp+08]    <-- No. de libreria
0167:008f1181 52             push      edx                                 
0167:008f1182 e839e5ffff     call      008ef6c0        <-- tracear esta llamada 
0167:008f1187 83c404         add       esp,04                              

Hasta aqui, nada significativo, pero veamos que hay en ese call 8ef6c0

0167:008eea4d 8b4d08         mov       ecx,[ebp+08]                        
0167:008eea50 8b54012a       mov       edx,[eax+ecx+2a] <-- eax+ecx = dir pushad 
0167:008eea54 83c201         add       edx,01 
0167:008eea57 8b450c         mov       eax,[ebp+0c]                        
0167:008eea5a 6bc02e         imul      eax,eax,2e 
0167:008eea5d 8b4d08         mov       ecx,[ebp+08]
0167:008eea60 8954012a       mov       [eax+ecx+2a],edx <-- pone un 1 en incr. 2a
                                                               desde el pushad
0167:008eea64 eb03           jmp       008eea69 
  ...
  ...
0167:008ef1a0 8b55fc         mov       edx,[ebp-04]     
0167:008ef1a3 81e2ffff0000   and       edx,0000ffff                        
0167:008ef1a9 8b4510         mov       eax,[ebp+10]     <-- dir correcta de la API
0167:008ef1ac 0bc2           or        eax,edx          <-- incrementa la dirección
                                                               en unos bytes
0167:008ef1ae 8b4d0c         mov       ecx,[ebp+0c]                        
0167:008ef1b1 6bc92e         imul      ecx,ecx,2e 
0167:008ef1b4 8b5508         mov       edx,[ebp+08]                        
0167:008ef1b7 89440a20       mov       [ecx+edx+20],eax <-- !!!!!!!!!!!!!!!!!! 
0167:008ef1bb eb07           jmp       008ef1c4                            
  

Ahora si que les hemos cazado a esos pillines. Lo primero es explicar el posicionamiento para la sustitución de código que realiza Dplayerx.dll. Esta dll tiene las direcciones de comienzo de las ristras de funciones que la llaman (ver código de más arriba), tanto para USER32.DLL (763CE0h) como para KERNEL32.DLL (764194h). Sabiendo que estas funciones tienen de longitud 2Eh, sabe también direccionar el código de la función que la esta llamando (edx: dir. base; ecx: desplazamiento de la función; 20h: desplazamiento dentro de la función determinada).

El poner un 1 en el incremento 2Eh de la función creo que es una marca para saber si la función ya ha sido desencriptada.

Ahora fijemonos en la ultima parte del código de arriba. En él, se ve claramente que la dirección (de llamada a la api) que coloca en el jmp de debajo de la lladada de Rayman2 es una dirección "adulterada" y no válida. Es decir, el código que viene en el tutorial de SkUaTeR no sirve porque entre otras cosas la dirección que recoge no nos sirve. Otra de esas 'otras cosas' es el código que viene a continuacion del de arriba:

0167:008ef34b 8b4510         mov       eax,[ebp+10]     <-- dir funcion API
0167:008ef34e a350899000     mov       [var_01],eax     <-- la coloca en una variable
0167:008ef353 eb03           jmp       008ef358
  ...
  ...
0167:008ef4ce eb02           jmp       008ef4d2
0167:008ef4d0 cd14           int       14                                  
0167:008ef4d2 ff2550899000   jmp       [var_01]         <-- !! Ejecuta la funcion del API !!    
  

Como vemos aqui, la función del API no se llama al volver de Dplayerx, en Rayman2.icd, sino que se hace desde dentro de esta dll.
!!Pillines!!

Sentencia

Culpable y a la silla electrica!!!

Bien, vamos a ver como podemos extraer las direcciones correctas del api para introducir en la sección .rdata y empezar a jugar a Rayman 2 (un juego cojonudo, por cierto) con nuestro fabuloso ejecutable desencriptado.

Lo primero de todo es volver a parar la ejecución de Rayman2.icd en su Entry Point. Una vez hecho esto debemos copiar el siguiente código, bien desde el Entry Point, bien desde un espacio de memoria reservado con Adump.

0167:0045fcc0 bf10005183     mov       edi,rva1         (*)
0167:0045fcc5 33c9           xor       ecx,ecx
0167:0045fcc7 8b07           mov       eax,[edi]                          
0167:0045fcc9 c60090         mov       byte ptr [eax],90                  
0167:0045fccc 60             pushad
0167:0045fccd ffd0           call      eax                                
0167:0045fccf 61             popad
0167:0045fcd0 8b1d00f55083   mov        ebx,[var_001]   (*)            
0167:0045fcd6 891f           mov       [edi],ebx                          
0167:0045fcd8 83c704         add       edi,04                             
0167:0045fcdb 8b07           mov       eax,[edi]                          
0167:0045fcdd 85c0           test      eax,eax                            
0167:0045fcdf 75e8           jnz       0045fcc9
0167:0045fce1 bf28025183     mov       edi,rva2         (*)    
0167:0045fce6 41             inc       ecx                                
0167:0045fce7 83f902         cmp       ecx,02
0167:0045fcea 75db           jnz       0045fcc7
0167:0045fcec ebfe           jmp       0045fcec

El código es bastante parecido al del tutorial de SkUaTeR con la diferencia de que en vez de recoger la dir. del Api de la función de Rayman2.icd la recogeremos de una variable que pongamos en memoria (var_001).

Rva1 y rva2 son las direcciones de la sección .rdata donde están las primeras llamadas a las librerias KERNEL32.DLL (libreria 1) y USER32.DLL (libreria 0). Es decir donde se llama a las funciones de Rayman2.icd que obtienen la función 0 de la libreria 0 y la función 0 de la libreria 1. En nuestro caso:

     rva1: 49c010h     (kernel32)
     rva2: 49c228h     (user32)
  

Si hacemos una copia de la seccion .rdata en alguna otra parte de la memoria no olvidemos que los rva's cambian:

     rva1: dir. inicio .rdata   +   10h
     rva2: dir. inicio .rdata   +   228h
  

Var_001 es una variable donde "amablemente" Dplayerx.dll nos dejara la dirección correcta del api de windows a llamar. Podemos ubicarla en la sección .data ya que contiene variables del programa. Por ejemplo, al principio, en 49f000h.

Ahora deberemos dotar de esa "amabilidad" a Dplayerx.dll. Para ello tendremos que modificar el código de la dirección 8f1176h.

Cambiamos este código:

0167:008f1176 8b45fc         mov       eax,[ebp-04]                       
0167:008f1179 50             push      eax                                
0167:008f117a 8b4d0c         mov       ecx,[ebp+0c]
0167:008f117d 51             push      ecx                                
0167:008f117e 8b5508         mov       edx,[ebp+08]                       
0167:008f1181 52             push      edx
0167:008f1182 e839e5ffff     call      008ef6c0                           
0167:008f1187 83c404         add       esp,04
  

                            

por este otro:

0167:008f1176 8b45fc         mov       eax,[ebp-04]                       
0167:008f1179 a300f55083     mov       [var_001],eax
0167:008f117e 58             pop       eax                                
0167:008f117f 3dbeff1394     cmp       eax,9413ffbe                       
0167:008f1184 75f8           jnz       008f117e
0167:008f1186 c3             ret
  

                                         

Con SoftICE:

    : u 8f1176
    : a 8f1176
  

Solo puntualizar que 9413ffbeh es un valor que Rayman2.icd guarda en la pila antes de hacer la llamada a Dplayerx (mirar en el código de mas arriba) y que a continuación de este valor esta la dirección de retorno.

Ejecutamos nuestro código de Rayman2.icd y ya tenemos la sección .rdata correcta. No ha sido demasiado difícil ¿no? ... Ahora tenemos que volcarla a disco. Para ello, como ya tenemos la ejecución parada utilizamos el ProcDump y, o bien volcamos solo la sección .rdata (49c000h longitud: 3000h), o bien volcamos otra vez todo el proceso Rayman2.icd y luego extraemos su sección .rdata.

Ahora solo nos queda sustituir esa sección .rdata por la del primer volcado de Rayman2.icd que hicimos, el que contenia las secciones de código y datos correcta. Para ello, utilizamos otra vez el ProcDump y ya de paso reconstruimos el ejecutable (Rebuild PE). Hacemos doble click y.... '!!A jugar!!



'!! That's all, folks !!


rik.
[ Entrada | Documentos Genéricos | WkT! Web Site ]
[ Todo el ECD | x Tipo de Protección | x Fecha de Publicación | x Orden Alfabético ]
(c) Whiskey Kon Tekila [WkT!] - The Original Spanish Reversers.
Si necesitas contactar con
nosotros , lee esto antes e infórmate de cómo puedes ayudarnos