Copy Link
Add to Bookmark
Report
SET 018 0x0c
-[ 0x0C ]--------------------------------------------------------------------
-[ CRACKING BAJO LINUX II ]--------------------------------------------------
-[ by SiuL+Hacky ]----------------------------------------------------SET-18-
Hola de nuevo,
Vayamos con la segunda entrega de contenidos. Como habreis podido
comprobar no hubo entrega en el numero anterior de set (17). Como dicen
en television fue por causas ajenas a nuestra voluntad. Si alguien se
perdio la primera ( que no es lo mismo que preguntar si alguien se
perdio en la primera entrega ), os recuerdo que en SET16 habiamos visto
como herramientas mas importantes el depurador GDB (con cualquiera de
los entornos graficos que existen; recomendando especialmente Data
Display Debugger) y un desensamblador DASM, que no era otra cosa que un
script que "trataba" adecuadamente el volcado en ensamblador que
facilita el programa OBJDUMP.
Estas dos herramientas, que en general podemos considerar de bajo nivel,
tienen el problema de que, en ocasiones, facilitan una cantidad de
informacion excesiva, y es preciso algun otro medio para localizar el
codigo interesante. En esta entrega, dividida en dos partes, vamos a ver
en primer lugar una serie de herramientas de analisis que en algunos
casos van a ser muy utiles de cara a sacar conclusiones del
comportamiento de un programa, o de cara a saber donde buscar en un
listado de ensamblador. En la segunda parte, vamos a ver un ejemplo muy
sencillo y didactico sobre protecciones con claves; que espero sirva en
el futuro para ir utilizar otro tipo de tecnicas algo mas ingeniosas
:-). Servira tambien para que los perezosos ( ¨todos? ) que no fueron
capaces de hacerse un triste programa en C y probar el depurador y el
desensamblador, se hagan una idea de lo que habria pasado. Las
herramientas que a continuacion se describen pueden ser encontradas en
los enlaces facilitados al final del articulo.
OTRAS HERRAMIENTAS IMPORTANTES --------------------------------------------
1. LTRACE & STRACE
He querido agrupar estas dos herramientas ya que presentan
comportamientos muy similares, de hecho podriamos decir que strace es un
subconjunto de ltrace (ya se que ha quedado muy matematica la
definicion). Sin embargo, ltrace es un programa en desarrollo, que en
determinadas circunstacias se comporta de forma menos fiable que strace,
especialmente cuando se trata de analizar programas que inician
subprocesos hijo.
Comencemos por el principio: strace es un interceptador de llamadas al
sistema. Para el que no sepa lo que es una llamada al sistema,
recordamos que los procesos en UNIX ( a diferencia que el dos) no
pueden en circunstancias normales acceder directamente a recursos
hardware, como memoria, puertos ... De eso se encarga el kernel, y de su
robustez dependera entonces que la maquina no se quede colgada. Pero
evidentemente, es preciso que los procesos reserven memoria, accedan a
los dispositivos, etc ... Para ello el kernel facilita un interfaz de
llamadas (que podeis ver nombradas en /usr/include/asm/unistd.h). Strace
se coloca en medio y monitoriza estas llamadas. No voy a entrar, al
menos de momento, en la forma en que strace trabaja internamente, pero
podemos decir que aprovecha servicios del kernel que permiten que un
proceso depure a otro.
Entre las llamadas que merecen la pena atender, estara las que
posibilitan la apertura ( o el intento de apertura ) de ficheros, la
lectura de los mismos, la escritura de cadenas por la salida estandar
(es decir, el monitor), etc ... Strace, como era de esperar ( y esto
vale para el resto de herramientas que vamos a comentar ), no tiene
entornos graficos ni nada por el estilo, hay que especificarlo todo en
la linea de comandos. A cambio de eso ocupa 97k y es rapido :-). No me
voy a preocupar de describiros las opciones mas importantes, ya que
coinciden con las de ltrace, que comentare a continuacion.
Si strace es una herramienta ciertamente interesante, ltrace es una
autentica joya de cara a la ingenieria inversa. Ltrace esta siendo
desarrollado por Juan Cespedes, un autentico mago de la programacion.
Ltrace se encarga de monitorizar el uso de librerias dinamicas. Esto
supone registrar las llamadas a las funciones pertenecientes a
librerias dinamicas ( CON SUS CORRESPONDIENTES PARAMETROS !! ). Esto
incluye TODAS las funciones de C, XWindows, etc, etc. Dado que a pesar
del desastre existente en linux en el tema de librerias, la mayoria de
los programas estas lincados dinamicamente, tenemos acceso a todas las
llamadas a funciones no implementadas directamente por el programador
(realmente tambien seria posible monitorizar las llamadas a funciones
internas, pero de momento no esta disponible esta opcion). Tampoco voy
a entrar en la forma en que trabaja internamente ltrace, ya que tengo la
esperanza de que leais el articulo hasta el final :-); pero en
cualquier caso se basan en principios similares y las fuentes de ambos
programas estan disponibles para todo el quiera echarles un vistazo.
No os asusteis con lo que vais a ver, voy a ense¤aros la linea de
comandos tipica cuando se ejecuta el programa. Veremos asi las opciones
mas importantes y que significan. No tiene un aspecto amigable, pero ya
comprobareis como todo tiene su razon de ser:
ltrace -s200 -e printf,scanf -i -o /tmp/salida PROGRAMA_VICTIMA
Oh, sielosss, que es esto. Prometo que se puede complicar mas, pero por
ahora es suficiente. Que significa todo esto:
-s200: indica que para cada llamada monitorizada, no se recorte hasta
haber escrito por pantalla al menos 200 caracteres. Daros cuenta que si
entre los parametros se incluyen cadenas de texto largas, la longitud se
incrementa notablemente.
-e printf,scanf: monitoriza las llamadas a las funciones printf y scanf.
Si no especificara el comando -e, se registrarian las llamadas a TODAS
las funciones, lo cual suele ser una perdida de tiempo y espacio en
disco. Para programas gordos, monitorizar todas las llamadas ralentiza
muchisimo la ejecucion de los programas.
-i: muestra junto a cada llamada, el valor de registro eip en el momento
de realizar la llamada, es decir, desde que direccion del programa se ha
hecho la llamada.
-o /tmp/salida: indica que el resultado se recoja en el fichero
/tmp/salida, en vez de volcarlo por pantalla, que es la opcion por
defecto.
Otro comentario antes de finalizar, si queremos que el registro se haga
adecuadamente, es preciso decirle a ltrace, como debe interpretar los
parametros de las funciones. Hay que especificarle si tal parametro es
un entero, un float, una cadena de texto, etc ... Esto se realiza en el
fichero de configuracion /etc/ltrace.conf, que para que os hagais una
idea tiene este aspecto:
int printf(format);
int puts(string);
int remove(string);
int snprintf(+string2,int,format);
int sprintf(+string,format);
No os impacienteis para ver cual es el resultado que facilita este
programa, ya que algunos parrafos mas abajo utilizaremos esta
herramienta con un sencillo programa.
2. LSOF
Este nombre es la abreviatura de LiSt Open Files. Se encarga de informar
de todos los ficheros que tienen abiertos los distintos procesos en el
sistema. Cuando digo ficheros, no me refiero simplemente a accesos a
discos, sino otros dispositivos, sockets, etc ...
Aunque a primera vista pueda parecer que esta utilidad no tiene potencia
que otras citadas aqui anteriormente, con el tiempo vereis como tiene un
papel importante. Cuenta con una documentacion bastante aceptable,
dando ideas de posibles utilizaciones, como monitorizacion de conexiones
de red, identicacion de que procesos acceden a un determinado fichero,
uso de ficheros en sistemas NFS, etc ...
Una de las aplicaciones mas obvias es seleccionar uno de los procesos
que se estan ejecutando y listar los ficheros que mantiene abiertos.
EJEMPLO DE APLICACION -------------------------------------------------
Aviso para navegantes (NAVEGANTES, no PASEANTES), NO hay en este articulo
ningun tipo de programa parcheador ni receta de invierno para crackearlo.
Eso si, el que quiera crackearlo, con la informacion que encontrara aqui
y su cerebro no tendra el minimo problema en hacerlo.
Bueno, supuestamente se acabo la parte aburrida y ahora vamos a ver como
todas estas cosas sirven para algo. El programa que vamos a crackear, (y
que ya utilice de ejemplo en otros tutoriales en ingles):
l3v272l.tgz
Lo podeis buscar en un ftpsearch, aunque ha venido distribuido con
algunas revistas, no ocupa mucho, o sea que lo podeis bajar de la red en
cualquier momento. Bajo ese nombre tan explicito, no podia esconderse
otra cosa que no fuera un codificador mpeg layer-3, que para el que no
lo sepa comprime ficheros de audio. No se si os habreis dado cuenta de
que hay gran abundancia de decodificadores mpeg, pero es complicado
encontrar un (buen) codificador. Esto es porque los decodificadores son
publicos, pero la forma de llegar a los parametros de codificacion no
esta definida, siendo esta la que diferenciara los buenos y los malos
algoritmos (yo creo que alguien deberia contar como funcionan estos
algoritmos, ya que son realmente interesantes ...). Es el tipico
programa con funcionalidades reducidas y que se empe¤a en pedirnos un
codigo de registro. El codificador y el decodificador, que vienen en
programas aparte (l3dec y l3enc) muestran un esquema de protecci¢n
repetido. Nos vamos a ocupar del decodificador, que una vez ejecutado
(l3dec), nos espeta:
*** l3dec V2.72 ISO/MPEG Audio Layer III Software Only Decoder ***
| |
| copyright Fraunhofer-IIS 1994, 1995, 1996, 1997 |
| |
| L3DEC/L3ENC is shareware and must be registered |
| if used for more than 30 days or if used |
| commercially (see licence agreement) |
| |
| This program is not yet registered |
| If you have already registered and got |
| a registration code, you may enter it now |
| Do you want to enter your registration code now (Y/N)?
le decimos que yes, y aparece:
| Please enter your registration code:
introducimos 12352526426 y evidentemente nos encontramos con
| This was no correct registration code. |
| Do you want to try again (Y/N)?
vaya por Dios. Los que ya sepais de tema, sentireis que practicamente ya
esta crackeado (y asi es en realidad), pero supongo que otra mucha gente
se preguntara porque, y que pasos tienen que dar. Veamos que se puede
obtener con las herramientas que hemos visto.
1) STRACE: ejecutemos
strace -i -o./salida l3dec
(no hace falta meter codigos de registro ni nada, responded que no y
punto). Si mirais el listado creado (./salida), que normalmente conviene
empezar a mirarlo por el final, reparareis en estas curiosas lineas:
[40029bf8] open("/usr/sbin/l3dec", O_RDONLY) = -1 ENOENT (No such file ...
[40029bf8] open("/usr/bin/l3dec", O_RDONLY) = -1 ENOENT (No such file ...
[40029bf8] open("/usr/X11R6/bin/l3dec", O_RDONLY) = -1 ENOENT (No such file
[40029bf8] open("/usr/local/bin/l3dec", O_RDONLY) = -1 ENOENT (No such file
[40029bf8] open("/usr/X11/bin/l3dec", O_RDONLY) = -1 ENOENT (No such file or
[40029bf8] open("./l3dec", O_RDONLY) = 4
[40028f02] close(4) = 0
[40029bf8] open("./register.inf", O_RDONLY) = -1 ENOENT (No such file or ...
[4002ad34] write(2, "| L3DEC/L3ENC is sharew"..., 71) = 71
[4002ad34] write(2, "| if used for more t"..., 71) = 71
[4002ad34] write(2, "| commercially (se"..., 71) = 71
las primeras corresponden a una forma bastante "sui generis" de buscar
el directorio en el que se encuentra el ejecutable, y que en este caso
se trata del directorio "actual". Una vez localizado, busca en ese
directorio (donde se encuentra l3dec), un fichero llamado
"register.inf". No hace falta ni el graduado escolar para saber que este
fichero tiene mucho que ver con el proceso de registro. En concreto es
donde se guarda el numero de registro (el bueno), para no tenerlo que
preguntar cada vez; lo cual es un detalle para todos aquellos sufridos
usuarios que hayan pagado 400 marcos alemanes por el programa. Si os
fijais bien en el listado generado, vereis que este intento de acceder
al fichero register.inf, se produce antes de preguntar si se desea
introducir el codigo. Posteriormente a esta pregunta se vuelve a
intentar el acceso. A nosotros nos interesa la primera aparicion, que es
de la que dependera que se haga o no la pregunta posterior.
El fichero, en mi caso, evidentemente no existe, de ahi que la llamada
al sistema open, devuelva -1 (si existiera devolveria un entero
positivo). Podiamos pensar en localizar la parte de codigo que intenta
abrir el fichero de registro, ya que presumiblemente despues de abrirlo
se llevara a cabo algun tipo de validacion de su contenido. El numero
que aparece entre corchetes en el listado refleja que la llamada al
sistema "open" se produce desde una direccion 4xxxxxxx, que corresponde
(otro dia el por que ...) a una llamada desde una libreria. El espacio
de direcciones de usuario suele ser el 8xxxxxxx. ¨ como se explica esto
? Bueno, las llamadas al sistema no las suelen realizar los programas,
los programas utilizan funciones de C, y estas funciones de la libreria
C son las que realizaran las llamadas al nucleo. Es una forma de que los
programas sean mas portables ...
Rollos aparte, que significa. Pues significa que el programa realizara
una llamada a una funcion de C que abra ficheros, y luego esa funcion
hara la llamada al sistema open. Hay que localizar entonces esa llamada
en C ... ¨ sera "fopen" ;-) ? Es aqui donde llega ltrace:
2) LTRACE: ejecutemos
ltrace -e fopen -o./salida -i l3dec
y oh, magia, examinemos lo que aparece (con lagrimas de emocion :)
[08058f2d] fopen("/usr/X11/bin/l3dec", "rb") = 0
[08058f2d] fopen("./l3dec", "rb") = 0x08075e30
[08058b1f] fopen("./register.inf", "rt") = 0
[4007d9d8] --- SIGALRM (Alarm clock) ---
[4007d9d8] breakpointed at 0x4007d9d7 (?)
[08058f2d] fopen("/sbin/l3dec", "rb") = 0
[08058f2d] fopen("/bin/l3dec", "rb") = 0
[08058f2d] fopen("/usr/sbin/l3dec", "rb") = 0
[08058f2d] fopen("/usr/bin/l3dec", "rb") = 0
[08058f2d] fopen("/usr/X11R6/bin/l3dec", "rb") = 0
[08058f2d] fopen("/usr/local/bin/l3dec", "rb") = 0
[08058f2d] fopen("/usr/X11/bin/l3dec", "rb") = 0
[08058f2d] fopen("./l3dec", "rb") = 0x08075e30
[08058a53] fopen("./register.inf", "rt") = 0
Cantidad de fopens, y entre ellos el que a nosotros nos interesa. La
direccion de llamada que aparece ahora: 0x8058b1f, si que corresponde al
espacio de direcciones del programa l3dec. Para que veais que casi nunca
hay un unico enfoque posible vamos a examinar el codigo del programa
primero mediante el depurador (para ir cambiando cosas de forma
interactiva) y a continuaci¢n mediante el desensamblador que ofrecera
una serie de nuevas posibilidades.
3) GDB & DDD:
ddd l3dec
vamos a examinar la ejecucion del programa a partir de la direccion que
hemos obtenido con ltrace, es cecir 0x8058b1f. Para ello ejecutamos en
la ventana de comandos del DDD (o el que se lo quiera currar con botones
y menus, es muy libre):
(gdb) br *0x8058b1f
este y otros comandos aparecian descritos en el articulo anterior
(set16), y en cualquier caso siempre estan las no siempre amigables
paginas info del gdb. Este comando fija un punto de ruptura en dicha
posicion, que sera el punto de retorno tras ejecutar la llamada a fopen.
Ejecutamos entoces el programa:
(gdb) run
Starting program: /tmp/l3v272.linux/l3dec
[ ... mensajes y mensajes ]
*** l3dec V2.72 ISO/MPEG Audio Layer III Software Only Decoder ***
| |
| copyright Fraunhofer-IIS 1994, 1995, 1996, 1997 |
| |
Breakpoint 1, 0x8058b1f in free ()
(gdb)
Si estais usando DDD como entorno del GDB, os aparecera una maravilloso
listado en ensamblador (Menu View->Machine Code Window) del codigo
correspondiente a la direccion en la que hemos detenido el programa, es
decir, transcribo:
0x8058b1f <free+65255>: movl %eax,%ebx
0x8058b21 <free+65257>: addl $0x1c,%esp
0x8058b24 <free+65260>: testl %ebx,%ebx
0x8058b26 <free+65262>: je 0x8058b70 <free+65336>
0x8058b28 <free+65264>: pushl %ebx
Os iba a castigar con 20 lineas de listado en ensamblador, pero voy a
ser indulgente y solo van a ser 5. Os pongo en situacion, retornamos de
una funcion (fopen) que devuelve 0 (al no existir el fichero
register.inf). Y ese valor lo devuelve en el registro EAX (esto os lo
creeis). Repetimos ahora el listado comentando lo que esta pasando en
cada linea:
movl %eax,%ebx <-- copia el valor devuelto a ebx
addl $0x1c,%esp <-- retira parametros de la pila
testl %ebx,%ebx <-- comprueba si ebx es igual a cero (nuestro caso)
je 0x8058b70 <-- salta a la direccion 8058b70 si es igual a cero
pushl %ebx <-- instruccion que se ejecutara si existe register.inf
si avanzamos instruccion a instruccion (comando "si"), vemos como el
programa saltara a la direccion 8058b70. De aqui en adelante
etiquetaremos como "chico_malo" a esa direccion, o sea,
...
testl %ebx,%ebx
je chico_malo
...
Mediante el comando "cont" reanudamos la ejecucion del programa, que
mostrar unos mensajes y finalizara. Manteniendo abierto el DDD, vamos
a crear ahora un archivo, desde otra shell, llamado register.inf, que
contenga lo que querais: "pepe", "hola", "no se me ocurre nada", etc.
Volvamos ahora a ejecutar el programa de nuevo en el DDD, debera pararse
en el mismo punto de ruptura de antes. Notareis que ahora el registro
eax, no contiene un valor nulo, esto es debido a que el fichero existe y
se ha abierto exitosamente. El resultado del salto condicional que
aparece en la direccion 8058b26, sera negativo y podremos ejecutar el
codigo situado a partir de la direccion 8058b28. Veamos un listado con
ese codigo. Va a ser un listado mas largo que el anterior, pero esta
comentado y no es preciso que sepais lo que hacen todas y cada una de
las instrucciones:
0x8058b28 pushl %ebx <-- salvar parametro 1 (stream), devuelto por fopen
0x8058b29 pushl $0x50 <-- parametro 2 (size), tama¤o de la cadena
0x8058b2b leal 0x18(%esp,1),%esi
0x8058b2f pushl %esi <-- parametro 3 (s), buffer de lectura
0x8058b30 call 0x8048a08 <fgets>
<-- declaracion de la funcion fgets:
<-- char *fgets(char *s, int size, FILE *stream)
<-- es decir, lee una cadena de texto de hasta 0x50 caracteres procedente
<-- del fichero (register.inf) recien abierto
<-- el puntero al buffer (s), es devuelto asi mismo en eax
0x8058b35 addl $0xc,%esp <-- retira los tres parametros de la pila
0x8058b38 testl %eax,%eax <-- es eax nulo ? es decir, fichero vacio ?
0x8058b3a jne 0x8058b4c <-- saltar si fichero no vacio
0x8058b3c pushl %ebx ;
0x8058b3d call 0x8048b48 <fclose> ;
0x8058b42 movl $0xfffffffe,%eax ; CODIGO EJECUTADO SI
0x8058b47 addl $0x4,%esp; ; FICHERO VACIO
0x8058b4a jmp 0x8058b5c ;
0x8058b4c pushl %ebx
0x8058b4d call 0x8048b48 <fclose> <-- cierra el fichero register.inf
0x8058b52 pushl %edi
0x8058b53 pushl %esi <-- buffer donde se guarda el texto del fichero
0x8058b54 call 0x8058fa8 <-- funcion misteriosa
0x8058b59 addl $0xc,%esp
0x8058b5c testl %eax,%eax <-- ha devuelto 0 la funcion misteriosa?
0x8058b5e jne chico_malo <-- saltar si ha devuelto !=0
0x8058b60 xorl %eax,%eax <-- eax = 0
0x8058b62 popl %ebx
0x8058b63 popl %esi
0x8058b64 popl %edi
0x8058b65 popl %ebp
0x8058b66 addl $0x2b0,%esp
0x8058b6c ret <-- fin de la funcion
En resumen:
* Existe fichero register.inf ?
NO: salta a chico_malo. Fin.
SI: Leer contenido fichero
* El contenido es nulo ?
SI: salta a chico_malo. Fin.
NO: Pasar contenido del fichero a la funcion misteriosa
* Devuelve 0 ?
SI: devolver el valor cero y fin de funcion
NO: salta a chico_malo. Fin.
Evidentemente, tal como lo hemos preparado, se leera el fichero, pero la
funcion misteriosa situada en la direccion 8058b54 NO devolvera cero en
el registro eax. ¨ Que ocurriria si modificamos el valor que devuelve y
lo fijamos a cero ? Para ello, por ejemplo, fijamos un punto de ruptura
en la direccion 8058b59 (comando br *0x8058b59) y una vez detenido el
programa en esta posicion, modificamos el registro eax, de esta forma:
(gdb) set $eax=0
(gdb) cont
y oh !, el mensaje de registro NO APARECE ! Efectivamente, la funcion
misteriosa parece que valida la cadena que contenga el fichero
register.inf (que podeis comprobar que es el numero de registro puro y
duro). Voy a mostraros como se pueden asociar comandos a un punto de
ruptura. La idea es que cuando el programa llegue a la direccion que le
indiquemos, cambie automaticamente el registro eax, poniendolo a cero:
(gdb) br *0x8058b59
(gdb) commands
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>silent
>set $eax=0
>cont
>end
de esta forma evitamos que el programa se detenga y hagamos el cambio a
mano. (Quede como ejercicio voluntario, ver que representa la variable
0x805c06e y que valores puede tomar).
4) DASM:
dasm l3enc l3enc.dasm
Vamos a destripar ahora otras partes del programa, con un enfoque
completamente distinto. Nos basaremos en los mensajes que muestra el
programa, en este caso el mensaje:
"Please enter your registration code"
para localizar la parte del codigo que la referencia. Vereis entonces
toda la potencia del analisis de listados, y como facilmente se
comprueban los pasos que va siguiendo el programa. Yo he elegido esta
cadena, pero podeis elegir el mensaje de error que mas rabia os de.
Entonces, una vez creado el listado en ensamblador (l3enc.dasm),
busquemos la cadena de texto antes indicada ("Please enter your
registration code") y vereis lo que aparece:
Possible reference to string:
"| Please enter your registration code: "
08058d27 pushl $0x805adfe
08058d2c pushl $0x80696c0
Reference to function : fprintf <-- se imprime el mensaje
08058d31 call 08048a58
08058d36 leal 0x170(%esp,1),%esi
08058d3d pushl %esi <-- este el puntero a la cadena de
<-- caracteres donde se guardar el
<-- numero que introduzcamos
Possible reference to string:
"%14s"
08058d3e pushl $0x805ae26
Reference to function : scanf <-- funcion que lee el codigo de
<-- registro. Fijaros que en principio
<-- parecen 14 caracteres los que lo
<-- conforman (parametro %14s)
08058d43 call 08048ba8
08058d48 leal 0x2c8(%esp,1),%eax
08058d4f pushl %eax
08058d50 pushl %esi <-- el puntero al codigo de registro.
08058d51 call 08058fa8 <-- funcion a la que se le pasa el cod.
<-- NUESTRA FUNCION MISTERIOSA !!!!
08058d56 addl $0x20,%esp
08058d59 testl %eax,%eax <-- devuelve cero ?
08058d5b je 08058dfc <-- saltar si ha devuelto cero
<-- (el codigo es valido)
08058d61 leal 0x0(%esi),%esi <-- esto se ejecuta cuando devuelve un
<-- valor distinto de cero.
Referenced from jump at 08058dca ;
Possible reference to string:
"| |"
08058d64 pushl $0x805ac50
08058d69 pushl $0x80696c0
Reference to function : fprintf
08058d6e call 08048a58
Possible reference to string:
"| This was no correct registration code. |"
08058d73 pushl $0x805ae2b
08058d78 pushl $0x80696c0
Reference to function : fprintf <-- parece ser que el codigo introducido
<-- no ha sido del todo bueno.
08058d7d call 08048a58
Possible reference to string:
"| Do you want to try again (Y/N)? "
Es evidente por los mensajes que si la funcion llamada en la
direccion 08058d51, y que recordemos ha recibido el codigo de registro
como parametro, devuelve un valor (en el registro eax) distinto de cero,
eso implica que el codigo de registro no es valido. Esto no nos es
nuevo, ya que es exactamente la misma funcion misteriosa que nos
habiamos encontrado antes y que validaba el codigo del fichero
register.inf. No deberia ser tan evidente que si devuelve cero, eso
corresponda con que el codigo ha sido completamente aceptado, ya que se
produce un salto a la direccion 08058dfc, pero no sabemos que acciones
lleva a cabo a partir de esa direccion. Podria darse el caso de que
hubiera posteriores comprobaciones de validez. No os voy a poner el
listado de lo que hace para no hacer esto pesado, pero el que quiera,
puede comprobar que crea el fichero register.inf con el numero
introducido (y validado) y no muestra ningun mensaje de error.
Algunas aclaraciones mas: parchear la instruccion (parchearla de forma
permanente modificando el fichero ejecutable con un editor hexadecimal,
como los comentados en el articulo de set16):
08058d5b je 08058dfc
con una del tipo
08058d5b jmp 08058dfc
no es en general la mejor solucion. La razon es simple, este parche
eliminara ciertamente los mensajes de error, pero NO significara que al
programa le parezcan validos. Se entiende ? Veamos, es diferente que
enga¤emos a un programa para que todos los codigos sean validos, a que
lo manipulemos para evitar los mensajes de error una vez que ha
detectado que son invalidos. La consecuencia principal de eso es que la
siguiente vez que ejecutaramos el programa, el fichero register.inf NO
seria validado (asi como cualquier otra validacion que pudiera realizar
al vuelo en otro momento anterior o posterior)y nos volveria a salir en
pantalla el famoso
"| Please enter your registration code: "
La alternativa es parchear la funcion que valida el codigo de forma que
devuelva siempre cero. Esta es siempre una forma mas segura y elegante
de realizar los parches.
Queda la tarea (para proximas entregas) de ver la forma de conseguir
codigos de registro validos. En este caso la proteccion se basa en que
digamos, hay no se cuantos miles de millones de combinaciones, de las
cuales, solo un reducido numero cumplen alguna propiedad que los
convierte en validos. Ante eso hay dos soluciones, analizar la rutina y
desentra¤ar cual es esa (compleja) propiedad o condicion que pueden
cumplir, o simplemente realizar un ataque por fuerza bruta, es decir,
probar numeros aleatoriamente hasta conseguir uno valido.
Para que entendais un poco como funciona globalmente el programa, las
instrucciones que acabamos de ver arriba, y que solicitan introducir un
codigo de registro, son continuacion de la rutina que buscaba el
fichero register.inf. Recordareis que si manipulabamos el valor de
retorno de la funcion misteriosa, la rutina acababa enseguida con un
"ret". Si algo no iba bien, se producia un salto, bueno, pues este salto
ira a desembocar en las instrucciones que solicitan el codigo de
registro. Queda claro ?
Llegamos al final. Como conclusion os recuerdo a los que empezais, que
lo importante hasta este momento, no es tanto dar una receta de lo que
hay que hacer para crackear un programa, sino ver como funcionan las
herramientas de analisis y empezar a interpretar a partir del listado en
ensamblador lo que esta haciendo el programa. Y experimentad por
vosotros mismos, EXPERIMENTAD. En cualquier caso si hay algun aspecto
que queda confuso, siempre podeis preguntar ENCRIPTADO POR SUPUESTO:
SiuL+Hacky
si_ha@usa.net
----------------------------------------------------------
Referencia de programas:
STRACE: esta incluido en cualquier distribucion
LSOF: ftp://ftp.cert.dfn.de/pub/tools/admin/lsof
LTRACE: http://www.cs.us.es/pub/debian/dists/slink/main/binary-i386/utils/
L3DEC: ftp://ftp.gui.uva.es/pub/linux.new/software/apps/mpeg
----------------------------------------------------------