Crackeando
SecuROM (Pedro))
Primero,
una explicación simple sobre SecuROM
SecuROM es una protección del CD-ROM que no
puede copiarse cuando duplicas un CD. Es usado
por muchos juegos, con el propósito de este
tutorial yo probé Conflict Freespace (versión
inglesa), Grim Fandango (versión
italiana) y Might and Magic VI (versión
italiana). Este es el primer tutorial que yo
escribo, así que me perdonas si no estoy claro o
si mi inglés es malo. Decidí escribir este
tutorial porque el crack genérico para SecuROM
de Laxity no funciona en los juegos que yo
probé. También encontré que parte del código
de SecuROM está encriptada, y es descifrada por
una lectura de la clave del CD. Mi objetivo es
permitir a la protección descifrarse, entonces
poner el código descifrado en el ejecutable y
quitar la comprobación del CD-ROM por SecuROM.
Necesitas SoftICE, Adump 1.0, ProcDump32 1.1 y el
gratuito & grandioso DJGPP32 C++ -
Compilador para usar este
tutorial.
Primero, miremos al ejecutable principal.
Conflict Freespace y Might and Magic VI no están
empaquetados, mientras que Grim Fandango está
empaquetado con Petite. Así que la primera cosa
que nosotros tenemos que hacer es desempaquetar
el ejecutable de Grim Fandango. Por suerte es
bastante fácil si nosotros usamos ProcDump32,
este tiene apoyo para desempaquetar los programas
comprimidos por Petite.
Ok, empecemos con el cracking real ;-)
Cuando arrancamos estos juegos, vemos que cargan
algo del disco y que simplemente terminan (porque
pusimos una copia, no el original dentro del
lector :-) Bien,podemos intentar un
breakpoint en la llamada usual a
SendDriverMessage, pero no pasa nada.
Hmm, parece que SecuROM no usa el Windows API
para acceder al CD-ROM. Te ahorro el esfuerzo de
encontrar cómo el programa accede al CD-ROM: Usa
INT 31 con AH=03 (DPMI 0.9+ - SIMÚLATE REAL
MODE INTERRUPT) para llamar a la
interrupción de MSCDEX
andemos un poco después del breakpoint, entramos
en CMS32_95.DLL eso es parte de la protección,
entonces alcanzamos el ejecutable principal.
¡Ahora, si buscamos algunos de los bytes del
código que nosotros vemos, no podemos
encontrarlos en el archivo ejecutable principal!
Partes del código están encriptadas, y ellas se
descifran cuando el programa se ejecuta. ¿Cómo
puede modificarse el programa durante la
ejecución?
Llamando a WriteProcessMemory. Así que ponemos
un breakpoint en esta llamada. Vemos que todos
éstos juegos lo llaman tres veces por lo menos,
aun cuando no pusimos el CD-ROM en el lector.
Así que este descifrado no depende de los datos
en el CD-ROM original. Esto es simplemente el
código que hace el último descifrado. De hecho
si nosotros pusiéramos el CD-ROM original en el
lector, el API es llamado una vez más, y el
código descifrado es correcto, mientras si
nosotros pusiéramos una copia en el lector, o
bien WriteProcessMemory no es llamado la cuarta
vez (copia mala, así que la protección
comprende que es falso) o es llamado pero el
código descifrado es basura y el programa
comprende que es erróneo y sale.
Así que el cuarto descifrado depende de la
lectura de los datos del CD-ROM original.
Si miramos la referencia de API que vemos cuando
el breakpoint es activado en WriteProcessMemory
tenemos:
ESP+08: dirección del destino
ESP+0C: dirección de la fuente
ESP+10: longitud de área a copiar
Así que, nosotros podríamos descargar los bytes
del código después del descifrado y podríamos
sustituirlos en el archivo ejecutable principal.
Primero hacemos una copia del ejecutable
principal llamado MM6_2.EXE (para este
propósito yo uso Might and Magic VI, pero los
otros funcionan de la misma manera). Es
importante usar nombres de archivo cortos, de
otra manera supcomp y supwrite no funcionarán.
La regla es: ejecutamos el original MM6.EXE para
descifrar el código y parcheamos MM6_2.exe (no
ejecutar MM6_2.EXE antes de que sea parcheado
completamente o se colgará).
Ejecutemos Adump. Con el comando ' R' veo que el
comienzo del área de memoria para descargar es
0x83651000.
Ahora ejecutemos MM6.EXE con breakpoint en
WriteProcessMemory. Ok, veo que 0x5000 bytes
serán escritos, la dirección de la fuente está
en 0xe80078 mientras la destino dirección está
en 0x4ae000. Así que copio las dos áreas (fuente
y destino) en dos áreas diferentes de
descarga de memoria::
M 4AE000 L 5000 83651000 (código original)
M E80078 L 5000 83661000 (código desencriptado)
Dejó al proceso acabar, voy al
"dumpeador" y escribo las dos áreas de
memoria a dos archivos.
W C:\ORIG1.DAT 5000 83651000
W C:\MODIF1.DAT 5000 83661000
Ahora abro ORIG1.DAT con un editor hexadecimal y
tomo los primeros 16 bytes. Busco esos bytes en
MM6_2.EXE. Los encuentro en offset 0xAD400.
Veamos si todos los 0x5000 bytes son idénticos.
Abro una ventana de DOS y ejecuto:
supcomp C:\ORIG1.DAT MM6_2.EXE 0 0xAD400 0x5000
Ok, ninguna diferencia, así que podemos
parchearlos.
supwrite C:\MODIF1.DAT MM6_2.EXE 0 0xAD400 0x5000
Pero ahora si nosotros ejecutamos MM6_2.EXE se
cuelga porque intenta descifrar datos ya
descifrado y consigue basura. Así que ejecutemos
MM6.EXE de nuevo, y cuando localizamos el
breakpoint, escribimos ' u @esp ' y subimos
algunas líneas:
:008CC2FE 8D8D64FEFFFF LEA ECX,[EBP-019C]
:008CC304 51 PUSH ECX
:008CC305 8B95C4FEFFFF MOV EDX,[EBP-013C]
:008CC30B 52 PUSH EDX ; longitud
:008CC30C 8B85E4FEFFFF MOV EAX,[EBP-011C]
:008CC312 50 PUSH EAX ; fuente
:008CC313 8B8DBCFEFFFF MOV ECX,[EBP-0144]
:008CC319 2B8DB4FEFFFF SUB ECX,[EBP-014C]
:008CC31F 51 PUSH ECX ; destino
:008CC320 8B15B87D9F00 MOV EDX,[009F7DB8]
:008CC326 52 PUSH EDX ; manipulador
:008CC327 FF15B8839F00 CALL [KERNEL32!WriteProcessMemory]
Debemos
poner longitud a 0 así que cambiamos:
MOV EDX,[EBP-13C]
PUSH EDX
a:
XOR EDX,EDX
NOP
NOP
NOP
NOP
PUSH EDX
Es
decir, nosotros buscamos los bytes (en
MM6_2.EXE):
8B 95 C4 FE FF FF 52 8B 85 E4 FE FF FF 50 8B
8D BC FE FF FF 2B 8D B4 FE FF FF 51 8B 15 B8 7D
9F 00 52 FF 15 B8 83 9F 00
y cambiamos los primeros 6 bytes a:
33 D2 90 90 90 90
(es mejor buscar varios bytes, porque hay
partes similares de código, y tenemos que
asegurarnos que hemos encontrado el lugar exacto).
Tienes que trabajar de la misma manera para los
otros dos breakpoints (cambia el código
cifrado por el código descifrado y pon a cero la
longitud para WriteProcessMemory). Alguien
podrían preguntarse por qué no escribí un
programa para hacer todo esto automáticamente.
Bien, el problema es, el código es similar pero
no es el mismo para todos los juegos que probé (por
ejemplo Grim Fandango usa ECX como el registro
para empujar el parámetro de longitud).
Ahora nosotros realmente necesitamos el CD-ROM
original (¿lo compraste?,¿ no lo tienes? :-)
para descifrar el código correctamente. Todavía
debemos repetir el procedimiento anterior para
descifrar y poner la longitud a cero cuando
localiza el cuarto WriteProcessMemory (esta
vez EDX es "XOReado" amablemente para
nosotros, así que nosotros apenas necesitamos '
NOPear' la siguiente instrucción que carga EDX).
También debemos crackear la parte de código
donde verifica el CD-ROM original y sale sin
ejecutar nuestro código parcheado (esto está en
la parte del código que desciframos antes, así
que si no lo hubiéramos descifrado, no
podríamos encontrarlo fácilmente el archivo
ejecutable).
¡De hecho si nosotros intentamos ejecutar el
programa después de cuarto descifrado pero antes
del último crack, no funcionará, incluso con el
CD-ROM original en el lector!
La última parte del crack es un poco más
difícil porque no puedes caminar en el código
del programa(F8 o F10 no funcionarán). Es
más, si el programa descubre que estás
intentando caminar en él, la próxima vez ni
siquiera se cargará hasta que reinícies
Windows, así que no debes poner breakpoints
excepto lo que yo te diga. Te ahorraré el tiempo
que yo gasté para entender lo que el hace
programa. Primero puedes poner un breakpoint en
GetDriveTypeA que es usado para encontrar el
CD-ROM. Con F11 vuelves al código del programa.
Si te desplazas en la ventana del código algunas
páginas abajo, encontrarás una serie de POPs y
un RET seguida por unos INT 03. Pon un breakpoint
en el RET.
Ejecuta el original MM6.EXE con el CD original.
Hmm, no alcanza el RET. Quita el CD del lector y
ejecútalo de nuevo. Ah, ahora alcanza el RET con
EAX=2. Pon el CD-ROM original de nuevo en el
lector pero ejecuta MM6_2.EXE (debes de
haberlo descifrado y lo debes de haber remendado
TODO las cuatro veces). Alcanza el RET con
EAX=7. Si pones una copia en el lector también
consigues EAX=7. Así que es fácil entender que
EAX contiene un código de error cuando algo sale
mal, y el RET nunca es alcanzado cuando todo va
correctos. Ahora debes ejecutar el MM6_2.EXE
modificado después de poner el cd original en el
lector y poner un breakpoint en GetDriveTypeA y
en RET. Aprieta F11 para conseguir el código
cuando alcance GetDriveTypeA. Recuerda, debes
conseguir el código de error 7 de modo que
desplázate abajo hasta que encuentres:
TEST EDX,EDX ; ¿error?
JNZ ........ ; si salta => no error
CALL [.....] ; comienzo de rutina de error
PUSH 07
CALL .......
MOV EAX,7 ; código de error
JMP ........ ; salta a los POPs y RET
Así
que el primer jnz debe cambiarse a jmp. Buscamos
los siguientes bytes:
75 17 FF 15 B0 83 9F 00 6A 07
Dentro de MM6_2.EXE y cambiamos 75 a EB.
Ejecútalo. Otro error, con retorno de código 8.
En el código encontramos:
TEST EDX,EDX ; ¿error?
JNZ ........ ; si salta => no error
CALL [.....] ; comienzo de rutina de error
PUSH 08
CALL .......
MOV EAX,8 ; código de error
JMP ........ ; salta a los POPs y RET
Busca:
75 17 FF 15 B0 83 9F 00 6A 08
Y cambia 75 a EB. Esta vez si lo ejecutas,
consigues error código 0. Encuentras este
código cerca del RET:
JNZ ........ ; salta si error
PUSH 2C ; ¡todos los controles pasados!
CALL .......
JMP ........ ; corre, baby, corre :-)
Así
que simplemente debes cambiar jnz -> nop nop.
La secuencia a buscar es 75 09 6A 2C y cambia 75
09 con 90 90.
Por fin el ejecutable modificado corre de nuevo
con el original. ¿Pero qué pasa con la copia,
oigo que preguntas? ¡Hurra, también funciona
porque quitaste los códigos de error 7, 8 y 0
que también producía la copia! Por supuesto la
copia debe tener el nombre de volumen correcto,
de otro modo conseguirás el mensaje 'Wrong disc'
(n.del t.: disco incorrecto)
Bien, yo no sé si esto es la manera más simple
de crackear SecuROM: Yo escribí todo esto
especialmente con propósitos didácticos. Debe
funcionar en cualquier juego protegido con
SecuROM. De modo que bye, bye SecuROM, nosotros
no te extrañaremos :-)
****************** código fuente para supcomp ****************************
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <mem.h>
#define AREA 32768
FILE *f,*g;
unsigned char *b1,*b2;
char notfound[]="Can't open: %s\n";
char seekerror[]="Seek error: %s\n";
char readerror[]="Read error or end of file: %s\nAborting\n";
long l,cont,ofsrc,ofdest;
void uscita(void)
{
if (b1) free(b1);
if (b2) free(b2);
if (f) fclose(f);
if (g) fclose(g);
}
void main(int argc, char *argv[])
{
if (atexit(uscita))
{
printf("Atexit error\n");
exit(EXIT_FAILURE);
}
if (argc!=6)
{
printf("Supcomp v1.0 by Pedro '98\n\n"
"Usage: supcomp <src> <dest> <offset src> <offset dest> <length>\n"
"Números deben estar en hex si precedidos por 0x\n\n"
"El programa compara <length> bytes de <src> con el correspondiente\n"
"bytes en <dest> comienzan desde el especificado offsets\n"
"Solo las diferencias son escritas en el output\n");
exit(EXIT_FAILURE);
}
if ((b1=(char *)malloc(AREA))==NULL) exit(EXIT_FAILURE);
if ((b2=(char *)malloc(AREA))==NULL) exit(EXIT_FAILURE);
if ((f=fopen(argv[1],"rb"))==NULL)
{
printf(notfound,argv[1]);
exit(EXIT_FAILURE);
}
if ((g=fopen(argv[2],"rb"))==NULL)
{
printf(notfound,argv[2]);
exit(EXIT_FAILURE);
}
ofsrc=strtol(argv[3],NULL,0);
ofdest=strtol(argv[4],NULL,0);
if (fseek(f,ofsrc,SEEK_SET))
{
printf(seekerror,argv[1]);
exit(EXIT_FAILURE);
}
if (fseek(g,ofdest,SEEK_SET))
{
printf(seekerror,argv[2]);
exit(EXIT_FAILURE);
}
if ((l=strtol(argv[5],NULL,0))==0)
{
printf("Wrong length\n");
exit(EXIT_FAILURE);
}
while (l)
{
long letti,i;
if (l>=AREA) letti=AREA;
else letti=l;
if (fread(b1,1,letti,f)!=letti)
{
printf(readerror,argv[1]);
exit(EXIT_FAILURE);
}
if (fread(b2,1,letti,g)!=letti)
{
printf(readerror,argv[2]);
exit(EXIT_FAILURE);
}
for (i=0;i<letti;i++)
{
if (b1[i]!=b2[i])
{
printf("%.8lx %.2x - %.8lx %.2x\n",cont+ofsrc+i, (unsigned
int)b1[i],cont+ofdest+i,(unsigned int)b2[i]);
}
}
l-=letti;
cont+=letti;
}
exit(EXIT_SUCCESS);
}
****************** código fuente para supwrite ****************************
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <mem.h>
#define AREA 32768
FILE *f,*g;
unsigned char *b1;
char notfound[]="Can't open: %s\n";
char seekerror[]="Seek error: %s\n";
char readerror[]="Read error or end of file: %s\nAborting\n";
char writeerror[]="Write error: %s\n";
long l,ofsrc,ofdest;
void uscita(void)
{
if (b1) free(b1);
if (f) fclose(f);
if (g) fclose(g);
}
void main(int argc, char *argv[])
{
if (atexit(uscita))
{
printf("Atexit error\n");
exit(EXIT_FAILURE);
}
if (argc!=6)
{
printf("Supwrite v1.0 by Pedro '98\n\n"
"Usage: supwrite <src> <dest> <offset src> <offset dest> <length>\n"
"Números deben estar en hex si precedidos por 0x\n\n"
"El programa escribe <length> bytes de <src> al correspondiente\n"
"bytes en <dest> comienzan desde el correspondiente offsets\n");
exit(EXIT_FAILURE);
}
if ((b1=(char *)malloc(AREA))==NULL) exit(EXIT_FAILURE);
if ((f=fopen(argv[1],"rb"))==NULL)
{
printf(notfound,argv[1]);
exit(EXIT_FAILURE);
}
if ((g=fopen(argv[2],"rb+"))==NULL)
{
printf(notfound,argv[2]);
exit(EXIT_FAILURE);
}
ofsrc=strtol(argv[3],NULL,0);
ofdest=strtol(argv[4],NULL,0);
if (fseek(f,ofsrc,SEEK_SET))
{
printf(seekerror,argv[1]);
exit(EXIT_FAILURE);
}
if (fseek(g,ofdest,SEEK_SET))
{
printf(seekerror,argv[2]);
exit(EXIT_FAILURE);
}
if ((l=strtol(argv[5],NULL,0))==0)
{
printf("Wrong length\n");
exit(EXIT_FAILURE);
}
while (l)
{
long letti;
if (l>=AREA) letti=AREA;
else letti=l;
if (fread(b1,1,letti,f)!=letti)
{
printf(readerror,argv[1]);
exit(EXIT_FAILURE);
}
if (fwrite(b1,1,letti,g)!=letti)
{
printf(writeerror,argv[2]);
exit(EXIT_FAILURE);
}
l-=letti;
}
exit(EXIT_SUCCESS);
}
|
Apéndice
de +Xoanon
------------------------------------------------------------------------------
SecuROM
Apéndice al crack de Pedro
por xOANINO [UCF/CLASS]
------------------------------------------------------------------------------
Precisamente
he leído el ensayo de Pedro para SecuROM, un
ensayo muy bueno.... Pero como todas las cosas
italianas... no es un trabajo completo. Como
nuestros increíbles políticos, él hizo cosas
fáciles más difíciles de lo que realmente son
como te mostraré en este pequeño 'apéndice' (Yo
no puedo escribir un ensayo completo, como
siempre tengo chicas que me esperan... ya sabes
:))
(n.del t.: No sé que tiene éste muchacho
contra los italianos, a mí personalmente me caen
bastante bien, aunque como es normal me gustan
más las italianas ;oÞ)
Sin embargo, un trabajo muy bueno Pedro... Aun
cuando tu trabajo no está al 100% completo,
funciona... y es bueno ver que también Italia
tiene (de hecho) algo de valor para mostrar en la
escena del cracking!!
Bien... Aquí está el problema: Como todos
pueden leer anteriormente en el ensayo de Pedro,
él descifra correctamente todo el material en la
sección .text e .idata y parchea el .exe. Ok,
así es cómo SecuROM debe ser crackeado.
Así... ¿qué está equivocado en el ensayo de
Pedro? Bien, intenta usar su método con (por
ejemplo) un juego que quieres rasgar y usar
sin el CD. No funcionará. ¿Por qué? Bien, el
EXE producido por Pedro no esta reconstruido al
100%. Todavía depende de las DLLs de SecuROM, y
si no encuentra un CD en la unidad con el nombre
y material correcto, el programa no correrá.
Y así que, aquí viene tu mega-ocupado, con
chicas, estudios, codificando xOANINO al rescate
para producir un ejecutable completamente
reconstruido trabajando al 100% :)
Simplemente sigue estos 6 pasos:
1) Sigue el ensayo de Pedro hasta el BPX
GetdrivetypeA / BPX en el RET algunas páginas
abajo.
2) Como puedes notar, algunas líneas sobre el
ret (donde Pedro remienda el último JNZ)
allí hay un JMP EAX. Ahora... ¿qué es este
JMP? Es el salto al entrypoint del programa
correcto, cuyo código ya está descifrado
siguiendo el ensayo de Pedro.
3) Apunta el valor de EAX, y simplemente escribe
este valor al campo entrypoint de la cabecera de
PE (PE+28h. debe estar en offset A8h en cada
archivo PE). Considera que debes restar de
EAX el valor del imagebase:
Ejemplo:
EAX = 418DB4h (Omnia99 entrypoint)
Imagebase = 400000h
valor a escribir = 418DB4-400000 =
018DB4h (revertido, por supuesto)
4) Ahora también puedes eliminar las secciones
no necesitadas... ¡como la sección .CMS_D y la
sección .PETITE!! (usa procdump para esto, o
hazlo a mano)
5) Wow, también puedes suprimir el CMSxx.DLL !
6) ri-Wow, el EXE corre sin más problemas !!!!
Eso es todo... es solamente un ' apéndice':)
¡Pedro hizo un trabajo muy bueno de cualquier
modo!
|
Apéndice de GrimFandango (Pedro)
Hace
algunos días yo encontré un pequeño cambio en
la protección de SecuROM que fue hecho por Sony
en un reciente juego, así que decidí escribir
de nuevo sobre esto. El ensayo anterior todavía
es válido, pero, como nosotros veremos, hay un
poco más de trabajo que hacer para crackear el
nuevo SecuROM. En primer lugar, me gustaría
agradecer a +Xoanon por escribir su ' apéndice'.
Yo realmente había hecho las cosas más
difíciles de lo que eran. Con su idea simple,
nosotros no necesitamos ya poner la longitud de
WriteProcessMemory a cero, ni tenemos que
crackear los códigos de error antes del RET,
porque simplemente saltamos todo el código de
SecuROM y nos vamos directamente al punto de
entrada real. Tengo todavía mucho que aprender
... :-)
Pero hay algo más que decir sobre SecuROM.
Bien, en mi ensayo yo era un poco impreciso.
Recuerdas que yo crackeé Might and Magic VI como
un ejemplo, y por supuesto eso funcionó (yo
leí el FAQ de Fravia+ y no quiero escribirle
diciendo ' Mi invalido crack no funciona'
después de que ha publicado mi trabajo :-).
También dije que Conflict Freespace y Grim
Fandango eran lo mismo. Es aquí, donde yo he
sido impreciso. De hecho yo sólo tenía una
copia no-funcional de esos dos juegos, pero vi
que había el mismo descifrado multi-pasos, así
que yo asumí que la protección entera era la
misma.
Después algunos días yo conseguí el original
de Grim Fandango y me sentí defraudado cuando vi
que Sony dejaba el descifrado multi-pasos, pero
se agregó algo más a la protección. Así que
yo quiero comportarme mejor que mis aborrecidos
políticos italianos y yo no quiero permitir que
este crack sea otra cosa italiana inacabada :-)
En primer lugar, tienes que hacer la copia usual
del ejecutable original descomprimido llamado
GRIM2.EXE. Entonces debes parchear las cuatro
partes del código como describí en mi primer
ensayo. Puedes saltar la parte en donde puse a
cero longitud para WriteProcessMemory o donde
crackeé los códigos de error, porque ya no
necesitaremos esa parte del código de SecuROM.
También, debes poner un breakpoint en ' jmp eax'
como +Xoanon señaló, para conseguir el punto de
entrada real, y cambiarlo en la cabecera de PE.
Pero si lo ejecutas, no funciona. ¿Por qué?
Sígueme y lo descubrirás.
Ahora deja un breakpoint de nuevo en
WriteProcessMemory y ejecuta el original
GrimFandango.exe (con la copia original en el
lector). Vemos que allí está el mismo
descifrado de las cuatro partes, pero después de
eso, WriteProcessMemory es llamado muchas más
veces durante la ejecución del juego, y sólo
son parcheados 4 bytes cada vez. Cuando estamos
dentro del breakpoint si escribimos ' u
(@esp+8)-2 ' para ver donde los datos están
siendo escritos, siempre vemos la misma LLAMADA:
CALL [008D6218] (por supuesto esto cambia de
juego a juego)
A propósito, esto es justamente el punto desde
donde se llamó el procedimiento de SecuROM en
[008D6218], y es este procedimiento el que está
llamando a WriteProcessMemory ahora. Si apretamos
F11 podemos ver lo que pasa. Esa llamada se ha
cambiado a:
CALL [KERNEL32!GetVersion]
(esto es simplemente un ejemplo, cada llamada
es cambiada a su valor original, es decir el
valor que estaba en el juego sin protección
antes de que Sony se entretuviera con él :-)
Nosotros lo hemos entendido ahora. Todo el tiempo
el juego desprotegido tenía que llamar una
rutina del sistema, o incluso a una de sus
propias rutinas, Sony guardó la dirección de la
LLAMADA en una tabla, y hizo a la llamada apuntar
a una rutina de SecuROM. Cuando esta rutina es
ejecutado, esto puede entender desde donde se
llamó mirando el valor de retorno en la pila,
entonces parchea el código y así la próxima
vez la llamada se hará directamente. Al final
debe dar el control a la rutina que sería
llamada, y logra esto con un ' jmp eax' (en
nuestro ejemplo eax contendrá la dirección de
GetVersion). Desgraciadamente esto sigue durante
la ejecución entera del programa. Pero no nos
gusta mantener a semejante aburrido vecino como
una parte del código de SecuROM, nosotros
queremos eliminarlo completamente.
En primer lugar debemos ver lo que es la
dirección real de esta rutina de SecuROM:
en la localización [8D6218] nos encontramos la
dirección 8CB050.
Ahora tomemos Wdasm y desensambla GRIM2.exe (así
el código en 8CB050 ya estará descifrado) .
Bien realmente no necesitas desensamblarlo para
hacer el crack, porque yo explicaré todos los
pasos para hacerlo después, pero es útil
aprender, porque esa es la razón real de por que
estás estudiando reversing y para lo que estás
leyendo esto, no simplemente para copiar algunos
juegos tontos, ¿o tu sí? :-)
Puedes ver simplemente el código con SoftICE
cuando estás dentro del procedimiento. Vamos a
8CB050. Aquí nosotros vemos muchas referencias
como éstas:
:008CB069 8B0DBCDF8E00 MOV ECX, DWORD PTR [008EDFBC]
......
:008CB072 890DBCDF8E00 MOV DWORD PTR [008EDFBC], ECX
......
:008CB0AA 8A8240FC8E00 MOV AL, BYTE PTR [EDX+008EFC40]
......
Así
que ahí debe estar la tabla para descifrar todas
las llamadas. Nota que no nos preocupamos de
cómo esta tabla está hecha. Apenas necesitamos
reconstruir el ejecutable de la misma manera que
hicimos con los cuatro parches. La rutina termina
así:
:008CB385 FF1518E78E00 CALL DWORD PTR [008EE718]
; llama a WriteProcessMemory
:008CB38B 61 POPAD
:008CB38C 8B45F8 MOV EAX, DWORD PTR [EBP-08]
:008CB38F 8BF0 MOV ESI, EAX
:008CB391 8B06 MOV eax, DWORD PTR [ESI]
:008CB393 5F POP EDI
:008CB394 5E POP ESI
:008CB395 5B POP EBX
:008CB396 8BE5 MOV esp, EBP
:008CB398 5D POP EBP
:008CB399 FFE0 JMP EAX ; aquí hace la llamada
; original del juego
:008CB39B 5F POP EDI
:008CB39C 5E POP ESI
:008CB39D 5B POP EBX
:008CB39E 8BE5 MOV ESP, EBP
:008CB3A0 5D POP EBP
:008CB3A1 C3 RET
Hmm,
ahora una idea interesante me viene a la mente.
En primer lugar la rutina no usa EBX, así que
nosotros podemos utilizarlo para conseguir el
control después de llamar a la rutina. Entonces
nosotros podemos ' simular' las llamadas del
programa usando el siguiente pequeño programa en
ensamblador que puede ensamblarse en SoftICE en
una zona sin usar de Adump (pobre Adump, lo
estamos cargando excesivamente :-):
:00000100 B9FA0F4F00 MOV ECX, 004F0FFA
; esto es (longitud - 6 bytes) de sección .text
; por supuesto tendrás que cambiarlo con
; los diferentes juegos
:00000105 BA00104000 MOV EDX, 00401000
; esto es dirección de comienzo de sección .text
:0000010A 803AFF CMP BYTE PTR [EDX], FF
; FF 15 18 62 8D 00 es la secuencia que
; corresponde para call [008d6218], y
; tendrás que cambiarlo según
; el juego que estés crackeando.
; Estamos buscando esta secuencia en
; el código
:0000010D 7530 JNE 0000013F
; no encontrada, continúa buscando
:0000010F 807A0115 CMP byte ptr [edx+01], 15
:00000113 752A JNE 0000013F
:00000115 807A0218 CMP byte ptr [edx+02], 18
:00000119 7524 JNE 0000013F
:0000011B 807A0362 CMP byte ptr [edx+03], 62
:0000011F 751E JNE 0000013F
:00000121 807A048D CMP byte ptr [edx+04], 8D
:00000125 7518 JNE 0000013F
:00000127 807A0500 CPM byte ptr [edx+05], 00
:0000012B 7512 JNE 0000013F
:0000012D 8D4206 LEA EAX, DWORD PTR [EDX+06]
; ¡encontrada! Ahora 'simulamos' la
; llamada: ponemos
; la dirección de retorno en la pila como
; la llamada que fue hecha por el programa
:00000130 60 PUSHAD
; ...pero primero mejor guardamos nuestros
; preciosos registros
:00000131 50 PUSH EAX
; ah, ahora la dirección de retorno
; correcta está en la pila
:00000132 BB3D010000 MOV EBX, 0000013D
; cargamos ebx con la dirección de la siguiente:
; instrucción de 'pop eax', así recobraremos
; el control después de parchear. Cambia este valor
; según tu offset in Adump
:00000137 FF2518628D00 JMP DWORD PTR [008D6218]
; y ahora damas y caballeros, infrinjamos las reglas
; SecuROM (como siempre, cambia 008d6218 según
; el juego que estés crackeando)
:0000013D 58 POP EAX
:0000013E 61 POPAD
:0000013F 42 INC EDX
:00000140 E2C8 LOOP 0000010A
; la búsqueda seguirá en...
:00000142 CC INT 03
; terminado, atrás a SoftICE
Ejecutemos
GrimFandango.exe con el breakpoint de +Xoanon
sobre ' jmp eax'. Ahora nosotros estamos a punto
de entrar en el programa (recuerdas, nosotros
siempre estamos ejecutando GrimFandango.exe
porque GRIM2.EXE no funciona). Con ' u 8cb050
' nosotros vemos nuestra rutina principalmente
odiada :-).
Desplazando algunas páginas abajo vemos el ' jmp
eax' sobre el que yo estaba hablándote. Esto
debe cambiarse a ' jmp ebx', para permitir a
nuestro diminuto (diminuto pero eficaz :-)
programa en ensamblador recobrar el control.
Entonces apuntamos el eip actual, ponemos '
i3here on' y ponemos eip al principio de nuestro
código en ensamblador. F5, entonces nosotros
esperamos un poco (si ni tu ni yo hemos cometido
ningún error, de otra manera podrías esperar
para siempre por Windows para resucitar :-) Wow,
está terminado. ¿Así que qué pasó? Bien,
todas las ' call [008d6218] ' se deben de haber
reemplazado con su colega original correcto. Para
decir la verdad es posible (pero muy
improbablemente) que una secuencia falsa FF 15 18
62 8D 00 que no representa a la antedicha llamada
se haya cambiado incorrectamente. Es
improbablemente, no obstante, porque 6 bytes son
un modelo muy específico. Así que simplemente
probémoslo primero, si todo choca -o algo peor -
simplemente investigaremos aburridamente todas
las localizaciones reemplazadas.
Debemos descargar ahora desde 401000 al final de
la sección .text la que puede verse con 'map32' (comienzo=401000,
longitud=4f1000 => fin=8f2000). El
problema es ahora que algunas páginas no están
cargadas en memoria, así cuando nosotros
escribimos el comando "m" conseguimos
un error. Lo que sigue es una idea para descargar
aun cuando algunas páginas no están cargadas en
la memoria: supón que la zona de descarga de
Adump empieza en 'pippo' (con una imaginación
italiana increíble :-). Debemos ensamblar
este pequeño programa con SoftICE cerca del
final de la zona de Adump, pon pippo+4ff000 (ah,
recuerda aumentar el área de Adump por supuesto,
si lo necesitas, como en este caso que debe ser
por lo menos 0x500000 bytes):
PUSHAD
PUSHF
CLD
REP
MOVSB
POPF
POPAD
Ok,
cuando necesitamos descargar apuntamos el eip
actual, entonces cambiamos eip a pippo+4ff000,
caminamos sobre pushad y pushf, entonces cargamos
ecx manualmente con la longitud de la descarga (en
este caso 4f1000), esi con el offset (401000)
del comienzo, y edi con el offset (pippo) de
destino y caminamos sobre las instrucciones
restantes.
¡Cuando hemos terminado de descargar nosotros
podemos restaurar el eip que antes apuntamos, y
GrimFandango continuará apaciblemente incluso
sin saber que nos lo hemos vuelto del revés! :
-) Ah, sólo una nota de advertencia: antes de
descargar o llamar a mi rutina en ensamblador,
quita TODOS los breakpoints, de lo contrario
tendrás ' int 3 ' no deseados en la descarga (como
yo hice :-).
La última parte es muy fácil: mira donde
empieza la zona descargada en el ejecutable y
reemplázalo con supwrite (bien, mi
conocimiento de ejecutables PE es limitado, pero
yo pienso que es ok para reemplazarlo todos. Por
otra parte puedes escribir un pequeño programa
en C para reemplazar sólo las FF 15 18 62 8D 00
secuencias con lo correcto desde la descarga).
Si el reemplazando completo es correcto, ni
siquiera necesitas los primeros cuatro pasos: con
este único descarga y el punto de entrada
correcto lo consigues todos.
Que cosa tan bonita: ¡nuestro GrimFandango
no-funcional está vivo de nuevo! No está tan
mal para un esqueleto :-)).
|
Outcast Apéndice (R!SC)
Herramientas
* HexEditor (Hacker's View)
* SoftICE 3.2x + Memory Dumper (IceDUMP)
* ProcDump
* RPP 1.2i
* TASM 5.0
Mi objetivo, LOADER.EXE, comprimido con ' Petite'
del juego ' Outcast' de Appeal/Infogrames (bonito
objetivo, porque 50% del tiempo no quiere correr
aun cuando tengas el CD correcto).
Yo he estado jugando con SecuROM... Me encanta el
tutorial de Pedro sobre él (http://crknotez.cjb.net), y esto
me ayudó a que crackeara algunos juegos de
SecuROM, pero, ¡ay!, pienso que Sony ha puesto
al día SecuROM, así que el tutorial de Pedro no
ayuda mucho ahora... ¿Recuerdas cómo ello
parecía que supiera que habías estado
corrigiéndolo, como y se negó ya a correr,
hasta un reseteo ? Bien, chúpate esto , y antes
había unos breakpoints seguros que podrías
usar, bpx writeprocessmemory, bpx getdrivetypea,
erm, bien, ahora los conoce, y también se niega
a correr... ¡ay!, nos encontramos con algunos
problemas.. :D
¿bien, eres un cracker o un ratón?? he, he,
nosotros no nos asustamos de NINGUN SecuROM ...
Digamos que quiero poner un bpx en
writeprocessmemory, para averiguar donde descifra
los datos en el código de los programas,
consigue una interrupción, la primera donde
descifra algún código de SecuROM, entonces
ninguna más, el programa corre en un loop
continuo, ctrl-alt-del para eliminarlo, entonces
él habitualmente correrá de nuevo... digamos
que quiero evitar la parte de descifrado del
código de él, y romper en GetDriveTypea, no no
no, si tienes cualquier punto de ruptura puesto,
él simplemente correrá, y después de
quitarlos, seguirá sin correr ...
Yo noté una cosa, CMS16.DLL, CMS32_95.DLL &
CMS32_NT.DLL están dentro del programa, y son
escritos al disco cuando es ejecutado...
probablemente para evitar que los manoseemos.. :D
si sabe que has estado corrigiéndolo, sale sin
cerrar el manipulador del archivo para CMS16.DLL.
Prueba a suprimirlo, consigues un bonito error,
'Cannot delete CMS16: The specified file is being
used by Windows'.
Bien, esto es una pista, quizá... quita todos
los breakpoints, bpx createfilea ... ejecuta el
juego protegido por SecuROM: algunas de las
primeras interrupciones no son importantes...
Windows cargando el archivo, entonces una pausa
corta, dónde se descomprime, entonces éstos son
los únicos que queremos, el primero se abre,
¿el segundo?
CMS16.DLL? el tercero, CMS16.DLL ... espera, echa
una mirada a este código ...
0137:005A1298 50 PUSH EAX <-- ptr para x:\xx\cms16.dll
0137:005A1299 FF1534685C00 CALL [KERNEL32!CreateFileA]
0137:005A129F 8945DC MOV [EBP-24],EAX
0137:005A12A2 837DDCFF CMP DWORD PTR [EBP-24],-01 <-- él ya está allí, y
0137:005A12A6 753B JNZ 005A12E3 - no puede ser abierto de nuevo..
0137:005A12A8 C70580645C0000000000MOV DWORD PTR [005C6480],00000000
0137:005A12B2 6A00 PUSH 00
0137:005A12B4 6A00 PUSH 00
0137:005A12B6 6A03 PUSH 03
0137:005A12B8 6A00 PUSH 00
0137:005A12BA 6A00 PUSH 00
0137:005A12BC 6800000080 PUSH 80000000
0137:005A12C1 8D8D38FFFFFF LEA ECX,[EBP-00C8]
0137:005A12C7 51 PUSH ECX <-- mismo ptr para cms16.dll
0137:005A12C8 FF1534685C00 CALL [KERNEL32!CreateFileA]
0137:005A12CE 8945DC MOV [EBP-24],EAX
0137:005A12D1 837DDCFF CMP DWORD PTR [EBP-24],-01 <-- oh mierda, todavía está a -1
0137:005A12D5 750A JNZ 005A12E1 - pero forzando este salto
0137:005A12D7 6A00 PUSH 00 - correrá de nuevo :D
0137:005A12D9 E8022C0000 CALL 005A3EE0
0137:005A12DE 83C404 ADD ESP,04
0137:005A12E1 EB0A JMP 005A12ED
0137:005A12E3 C70580645C0001000000MOV DWORD PTR [005C6480],00000001
0137:005A12ED 8B15C4675C00 MOV EDX,[005C67C4]
Bien,
mira, intenta crear este archivo, y si falla,
devuelve el código FFFFFFFF, y sale... si
nosotros lo engañamos, haciéndolo pensar que
puede crear este archivo, sólo forzando
cualquiera de estos saltos, corre de nuevo :D
Así no todo está perdido.. oops, nosotros
todavía no podemos bpx writeprocessmemory, o bpx
getdrivetypea, así que las cosas son más
complicadas, pero no imposible ...
Lo que noté sobre las versiones más viejas de
SecuROM, es que descifra 20 KB de código del
programa, alrededor del punto de entrada
original, entonces verifica el disco, y si el
correcto está dentro, descifra 200h bytes más
de código en el punto de entrada original... el
otro código descifrado no era tan importante,
como lo era éste código de SecuROM... teoría,
rastrea Petite hasta que haya desempaquetado el
programa, descarga la memoria, bpx en el punto de
salida del código de SecuROM, y cuando lo
alcances, descargas la memoria de nuevo, y
simplemente haces una comparación del archivo,
debes encontrar un bonito bloque de 20 KB de
código descifrado en el segundo volcado...
arrgh! ¿cómo hacemos para el bpx en el punto de
salida del código de SecuROM?? eh, no hagas
ph34r, es fácil :D
Yo espero que tengas ya jodido al programa, así
que ya no correrá, y tenemos que hacerle correr
cambiando uno de los saltos después de la
llamada a CreateFileA, ' CMS16.DLL '... good ...
Yo utilicé mi arriesgado parcheador de procesos
para hacer un loader que arregló esto para mí
...
T=10000:
F=loader.exe:
O=securomfix_cc.exe:
P=5A12A6/75/CC: ; 0137:005A12A6 753B JNZ 005A12E3
$
Cambiar esto a un EB le hace correr todo el
tiempo, pero Yo quise romper aquí, así que lo
cambie a CC, INT 03, entonces en SoftICE, bpint
03, X. ejecuta el loader... eh, cuando rompa, no
te olvides de cambiar el CC a un EB... e eip eb
... bien, cuando rompa, y hayas cambiado tu INT
03 a un JMP, simplemente haz tu ventana de
código grande y bonita, y desplázate,
ctrl-page-down.
Aquí es donde nosotros normalmente podríamos
romper, pero bpx getdrivetypea o simplemente bpx
5a25a2, el los conoce y detiene la ejecución...
malo, sigue desplazándote
0137:005A259B 52 PUSH EDX
0137:005A259C FF15E04F5C00 CALL [KERNEL32!GetDiskFreeSpaceA]
0137:005A25A2 8D8548FCFFFF LEA EAX,[EBP-03B8]
0137:005A25A8 50 PUSH EAX
0137:005A25A9 FF15004B5C00 CALL [KERNEL32!GetDriveTypeA]
0137:005A25AF 83F805 CMP EAX,05
Mira,
otro lugar donde normalmente podríamos romper,
pero, ¡ay!, no podemos nunca más... sigue
desplazándote ...
0137:005A28E0 8D9548FCFFFF LEA EDX,[EBP-03B8]
0137:005A28E6 52 PUSH EDX
0137:005A28E7 FF15E04F5C00 CALL [KERNEL32!GetDiskFreeSpaceA]
0137:005A28ED 8D8548FCFFFF LEA EAX,[EBP-03B8]
0137:005A28F3 50 PUSH EAX
0137:005A28F4 FF15004B5C00 CALL [KERNEL32!GetDriveTypeA]
0137:005A28FA 83F805 CMP EAX,05
Yippee!!
este es otro lugar donde nosotros romperíamos,
muchas muchas páginas de código han pasado ante
nosotros, y lo sabemos, aquí es donde el código
del securom termina, y salta al programa
apropiado :D
0137:005A31A8 B8A1535000 MOV EAX,005053A1
0137:005A31AD 90 NOP
0137:005A31AE 90 NOP
0137:005A31AF 50 PUSH EAX
0137:005A31B0 EB03 JMP 005A31B5
0137:005A31B2 58 POP EAX
0137:005A31B3 FFE0 JMP EAX
Hmm,
buenas noticias, podemos bpx aquí 0137:005A31B3
FFE0 JMP EAX, y todo estará bien... el programa
todavía funciona bien :D yah... ¡¡Bien!!
Aquí hay otro loader para ayudarnos en nuestra
tarea ...
T=10000:
F=loader.exe:
O=securom.cc.jmp.eax.exe:
;P=5A12A6/75/EB: ; eh, mi pc chocó por alguna
razón, así que esto no es necesario todavía :)
P=5A31B3/FF/CC: ; Solamente quiero romper en el
punto de salida de securom, el jmp eax ...
$
Bonito R!SC, casi es el momento para ser
destructivo :D
Ahora, usando este loader, cuándo el softice
rompe en el int 03, nosotros podemos conseguir
nuestro código descifrado, ¿qué sobre nuestras
arriesgadas llamadas? ¿recuerdas al viejo
securom? ¿call dword ptr [securom] para cada
importación? ¿y si remontaras encima de él, el
bonito código del securom reemplazó [securom]
con la dirección real de la importación a fuera
IAT en alguna parte en memoria? he, he, bien,
jodido si esto trabaja ahora :( Mira aquí ...
Esto es mi punto de entrada de programa original,
mira 005053C7.. eso es donde la api llama a
GetVersion, pero llama al código de securom, que
a un tiempo, jmp a GetVersion, remonta en una de
las llamadas, entonces desplaza la ventana del
código hasta un jmp eax... pon un breakpoint en
este ... y ejecútalo ...
0137:005053A1 55 PUSH EBP
0137:005053A2 8BEC MOV EBP,ESP
0137:005053A4 6AFF PUSH FF
0137:005053A6 6810EB5100 PUSH 0051EB10
0137:005053AB 68C0525000 PUSH 005052C0
0137:005053B0 64A100000000 MOV EAX,FS:[00000000]
0137:005053B6 50 PUSH EAX
0137:005053B7 64892500000000 MOV FS:[00000000],ESP
0137:005053BE 83EC58 SUB ESP,58
0137:005053C1 53 PUSH EBX
0137:005053C2 56 PUSH ESI
0137:005053C3 57 PUSH EDI
0137:005053C4 8965E8 MOV [EBP-18],ESP
0137:005053C7 FF1528C25A00 CALL [005AC228] <-- llamada [securom] ...
0137:005053CD 33D2 XOR EDX,EDX
0137:005053CF 8AD4 MOV DL,AH
0137:005053D1 891594585900 MOV [00595894],EDX
0137:005053D7 8BC8 MOV ECX,EAX
0137:005053D9 81E1FF000000 AND ECX,000000FF
0137:005053DF 890D90585900 MOV [00595890],ECX
mi
'jmp eax' estaba en 59F16F
Rompe debido a BPX #0137:0059F16F (ET=286.97
microsegundos)
:?eax
BFF9137C 3220771708 (-1074195588)
"¿ù|"
:¿qué eax?
El valor BFF9137C es (a) KERNEL32!GetVersion
<-- ha
Ahora, teoría, el petite desempaqueta el
código, y desempaqueta una tabla de dirección
de importación real :D, nosotros apenas tenemos
que encontrar el IAT real en memoria, buscamos
las importaciones correctas para nuestras
llamadas, y arreglamos las llamadas para llamar
nuestra tabla de importación en lugar del
código del securom ...
:s 400000 l ffffffff 7c 13 f9 bf
modelo encontrado en 0137:0051B110 (0011B110)
:s
modelo encontrado en 0137:005C6530 (001C6530)
:s
modelo encontrado en 0137:005CA3E8 (001CA3E8)
:s
modelo encontrado en 0137:0095067C (0055067C)
Bien, nosotros conseguimos cuatro opciones por el
momento, Yo rastreé el código desempaquetado, y
simplemente se detiene antes de que el código de
securom se ejecuta... entonces busca de nuevo ...
:s 400000 l ffffffff 7c 13 f9 bf
modelo encontrado en 0137:005CA3E8 (001CA3E8)
Yippee, sólo encuentra uno... así si cambio
esta línea ...
0137:005053C7 FF1528C25A00 CALL [005AC228]
a
0137:005053C7 FF15E8A35C00 CALL
[KERNEL32!GetVersion] ; llamada [005CA3E8]
Eso es una llamada arreglada, sólo deja
aproximadamente 300 para irse:) deja algo de
código ...
WHOOPS, Yo codifiqué algunas cosas, arreglé
todo, descargué la memoria, lo copié y lo
pegué en mi anterior volcado, y funcionó ok...
pero ... no funcionó en Win95 (Yo trabajo con
Win98) ...
Más allá el debugging reveló algún código
así ...
015F:00508FE0 55 PUSH EBP
015F:00508FE1 8B2D68B15100 MOV EBP,[KERNEL32!CloseProfileUserMapping]
015F:00508FE7 56 PUSH ESI
015F:00508FE8 57 PUSH EDI
015F:00508FE9 33DB XOR EBX,EBX
015F:00508FEB 33F6 XOR ESI,ESI
015F:00508FED 33FF XOR EDI,EDI
015F:00508FEF 3BC3 CMP EAX,EBX
015F:00508FF1 7533 JNZ 00509026
015F:00508FF3 FFD5 CALL EBP
Mira esta línea :
015F:00508FE1 8B2D68B15100 MOV EBP,[KERNEL32!CloseProfileUserMapping]
Es
realmente MOV EBX, DWORD PTR [0051B168], moviendo
una dirección del api desde el primer IAT
nosotros encontramos... lo único que no es suyo
cuando es desempaquetado... no estropearon
securom con esto, porque no es una llamada
directa al IAT.. bien, desempaqueté el
ejecutable con ProcDump... lo ejecuté con
LOADER32, y comprobé la memoria en 51B168,
contenía 72981200... obviamente, como esto es
reemplazado con la dirección lineal de la
función del api, esto era mi primera cosa
'rota'.. Yo busqué en el exe desempaquetado por
72981200, y encontré dos lugares, uno justo
antes de todos los nombres de las funciones
importadas, y el otro, uno que ya había
encontrado antes, además estudiando el exe con
mi editor hex, localicé el comienzo de la tabla
de importación.. la lista de mi
image_import_descriptors, 8 de ellos, seguidos
por 14h bytes nulos... terminando el descriptor
:D yippee!! simplemente usa ProcDumps PE Editor,
edita la estructura del directorio, dirige la
tabla de importación al real... para este
programa, era 528ed8, - la imagebase hace 128ed8
...
Bien, ejecutándolo de nuevo con LOADER32,
comprueba la dirección 51b168, sí, está
escrito encima con la dirección lineal de la
función del api correcta... grande... a medio
camino de el ...
Algo de código de nuevo ...
;------------------------------------------------------------------------------
; R!SC's dodgy call fixer for 'newer' SecuROM
; (c) august 27th 1999 risc@notme.com
;
; tasm32 /mx /m3 /z /q call_fix
; tlink32 -x /Tpe /aa /c call_fix,call_fix
;
; copia y pega el código dentro del ejecutable comprimido de securom..
;Yo prefiero la cabecera de pe ...
; Rompe en jmp eax (en el código de securom, jmp orig_entry_point)
; recodifica el 'jmp eax' en el código de la llamada [securom] a jmp ebx
; i3here on, faults on
; copia el código a una zona de memoria vacía.. m 400300 l 60 530000, r eip 530000, ejecútalo :0
;------------------------------------------------------------------------------
.486P
.Model Flat
.code
main:
call @1 ; por favor excusa mi primer esfuerzo del tipo un código reubicable
@1:
pop ebx
mov esi, ebx
add ebx, offset here-offset @1 ; devuelve dirección desde jmp [5ac228]
add esi, offset boring-offset @1
mov edx, 401000h
mov ecx, 51b000h-401000h ; mi iat empieza en 51b000, así que el código termina ,espero, antes de él
search_loop:
cmp [edx], 0c22815ffh ; busca modelo para llamada [005AC228]
jne try_again
cmp word ptr [edx+4],005ah ; -
jne try_again
lea eax, [edx+6] ; consigue la dirección que será empujada a la pila
pushad
push eax
;jmp dword ptr [5ac228h]
db 0ffh,25h,28h,0c2h,5ah,0 ; jmp blah..
here: ; k, retornamos aquí desde el código de securom, la dirección del api está en EAX
mov edx, 51b000h ; comienzo de la dirección de mi *real* IAT, primer tronco..
search_iat:
cmp [edx],eax
jz got_match
inc edx
cmp edx, 51b2a0h
jne search_iat
pop eax ; seguridad, si no puede encontrar una pareja para lo que está en EAX en nuestro IAT
popad
int 03 ; la pareja no fue encontrada, rompe en int 03, apunta la dirección en EDX
jmp try_again ; y arréglalo a mano..
got_match:
mov [esi+4],edx ; guardando la direc de la importación de la dirección conseguimos una pareja para
pop eax
popad
mov eax, [esi+4] ; recupera la dirección de la importación en nuestro IAT
mov [edx+2], eax ; pégalo encima de 005ac228 en la llamada [securom]
try_again:
inc edx
mov [esi],edx ; guarda la última dirección que estaba en EDX.. (en caso de algún problema..)
dec ecx
jne search_loop
int 03
nop
boring:
end main
;------------------------------------------------------------------------------
Correcto,
compílalo, edítalo en hex y edita en hex el exe
de SecuROM comprimido, corta y pega el código en
alguna parte del fixer de la llamada en el
ejecutable securom.. Yo escogí el offset 300h
del archivo, en la cabecera del pe ...
Ejecuta tu loader que rompe en el punto de salida
de securom. mueve el código desde la cabecera
del pe a alguna otra parte si no.. m 400300 l 100
530000 , r eip 530000 ...
Pon un bpx en JMP [005AC228], y ejecútalo.
cuando consigas una interrupción, remonta en el
jmp, y desplaza tu ventana del código hasta que
veas algún código así ...
015F:0059F15E 83C408 ADD ESP,08
015F:0059F161 61 POPAD
015F:0059F162 8B45F4 MOV EAX,[EBP-0C]
015F:0059F165 8BF0 MOV ESI,EAX
015F:0059F167 8B06 MOV EAX,[ESI]
015F:0059F169 5F POP EDI
015F:0059F16A 5E POP ESI
015F:0059F16B 5B POP EBX
015F:0059F16C 8BE5 MOV ESP,EBP
015F:0059F16E 5D POP EBP
015F:0059F16F FFE0 JMP EAX
Entonces
cambia el jmp eax que sería JMP a la llamada de
API, a JMP EBX... para saltar atrás a nuestra
rutina de llamada amañada
:a 59f16f
0137:0059F16F jmp ebx
bc*, i3here on, faults on, a causa de que
queremos atrapar cualquier error, y softice
romperá si golpea cualquiera de nuestras int
03's ...
Bien, de cualquier modo, ejecuta el código, y
cruza tus dedos... Yo conseguí algunos errores,
tres llamadas devolvieron direcciones erróneas,
así que no podría encontrarlos en mi IAT, y dos
llamadas causaron una caída... con faults on,
softice cogió la caída, y todo lo que tuve que
hacer fue mirar en la dirección de memoria al
final de mi código, donde se almacena el
contador de dirección, para ver qué llamada lo
provocó ...
502bdd lo provocó, y después de arreglar eso,
50ce1c lo provocó, así que cuando lo ejecuté
de nuevo, revisé aquéllas situaciones de
memoria, reemplazando el FF 15 con CC 15 que
detuvo el código encontrando el modelo de byte
correcto, así deteniendo la caída, e
investigando esas llamadas a mano ...
También rompió en este int 03 tres veces ...
int 03 ; la pareja no fue encontrada, rompe en el
int 03, apunta la dirección en EDX jmp try_again
; y arréglalo a mano ...
Simplemente apunta la dirección en edx, y
continúa, nosotros podemos investigar esas
llamadas a mano también ...
Las llamadas malas eran 507c39, 50a324 50e0be,
así que ejecuta el programa con tu interrupción
en el punto de salida de securom, edita el eip
para apuntar a una de esas llamadas arriesgadas,
y remonta, rompe en el jmp eax... y busca la
dirección del api en tu IAT a mano :D, después
de que todas las llamadas estén arregladas,
vuelca la memoria, pagein 400000 11b000
c:\callsfixed.dat .. copia y pega esto dentro del
archivo que desempaquetaste con ProcDump, y
arregla la dirección del IAT en la cabecera del
pe ...
Cruza tus dedos, ejecútalo :D hey, funciona :P
¡Eso es! SecuROM es un poco más divertido esta
vez por todas partes, pero si no fuera por Pedro
y +Xoanon, yo dudo que este tutorial hubiera sido
posible. En los tres días o así que este crack
me llevó, yo aprendí mucho, y espero que, tú,
aprendas algo de él también ...
R!SC 27th August '99
|
|