| 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