|
|
 |
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 |
|
|
|
|
|