-[ 0x0C ]-------------------------------------------------------------------- -[ CRACKING BAJO LINUX - IV ]------------------------------------------------ -[ by SiuL+Hacky ]----------------------------------------------------SET-20- 1. PROTECCIONES COMERCIALES ------------------------------------------- Vamos a tener en esta entrega una mezcla de teoria y practica, donde por un lado conoceremos detalles de una proteccion comercial bastante extendida en el mundo linux/unix; y por otro aplicaremos conceptos generales que si observais con cuidado se suelen repetir con frecuencia en este tipo de protecciones. Afortunada o desafortunadamente linux esta todavia bastante libre de aplicaciones comerciales, o al menos de aquellas que no permiten mediante una licencia especial su uso gratuito (tal como al final han hecho WordPerfect, StarDivision y otra gente). Por ello tampoco es de esperar un desarrollo de soluciones de tipo comercial para proteger programas. Incluso en Windows donde la explosion de software comercial es tremenda, el hecho de licenciar un sistema de proteccion/gestion de licencias es minoritario y predominan sistemas mas o menos caseros (y mas o menos eficaces) de proteccion). Un ejemplo claro de protecciones comerciales en Windows serian la populares mochilas o llaves hardware. Haciendo un inciso, hay que señalar que un uso adecuado de una mochila, debe hacer un programa practicamente seguro, a no ser de que se disponga de una de ellas y se pueda hacer un estudio para su emulacion. Bueno, pues a pesar de esto, una gran parte de ellos son crackeables de forma sencilla. Uno de los problemas fundamentales en el uso de soluciones comerciales: la desatencion hacia la proteccion es tal, que no solo no se invierte el menor medio humano (que no economico) en desarrollar la proteccion, sino que la forma de "empalmar" la proteccion al resto del programa, lo suele hacer altamente vulnerable. De alguna forma es de esperar que si uno paga por un sistema de proteccion, no tenga que perder un segundo en adaptar el programa. Es aqui donde llegamos a esos chicos tan raros que programan aplicaciones en unix, generalmente sobre cosas tan esotericas como simulaciones quimicas, estudio geofisicos, etc ... El panorama hasta ahora es muy poco ingenioso. Este tipo de aplicaciones cuestan muchisimo dinero, y como generalmente su ambito de uso es muy reducido, tampoco les debe importar demasiado que no se proteja. Eso no significa que lo dejen en plan "copien y disfruten"; por el contrario compran un programa/conjunto de utilidades que venden los chicos de Globetrotter Software, y que casi todo el mundo que trabaja con estaciones de trabajo habra oido alguna vez: Flexlm. Si, flexlm (Flex License Manager) parece ser la forma mas generica de proteger, bien sea un compilador de Sun, una aplicacion para SGI, u otras aplicaciones que ultimamente se han dignado a portar a linux. Por cierto, me cuentan por ahi, que hay aplicaciones de NT (New Testament) que tambien lo usan. Para el que no lo conozca, estos chicos de Globetrotter Software han hecho un autentico negocio. Su lista de clientes es importante, y la cantidad de bazofia comercial que podeis encontrar en su web (www.globetrotter.com) es tremenda. Vamos que saben vender bien la moto esta gente. Esto en web, por lo que supongo que una presentacion en directo de sus productos debe ser como para perder el control. Si esto lo mezclais con licencias, patentes y todo eso, da la impresion de que lo que estas comprando es algo asi como el Gate-keeper que salia en "La Red". Ya juzgareis vosotros mismos, pero a mi me parece que, en general, es el timo de la estampita aun sin conocer lo que cuesta. Y digo en general, porque puede ser un buen esquema, siempre que no se implemente por cuenta propia algunas funciones que permite FlexLM. Pero entonces, estamos pagando por algo que no protege, lo que protege es la parte generada por el comprador. 2 . FlexLM. Cuestiones generales ------------------------------------- Ya esta bien de hablar vaguedades. ¿ que es y como funciona FlexLM ? Hay dos modos principales de funcionamiento: 1) En el primero de ellos hay una especie de esquema cliente/servidor. El cliente estaria integrado dentro del programa a proteger, y el servidor seria un programilla o demonio generado via flexlm (y donde cada empresa puede introducir variantes y mejoras). En este modo aparece la utilidad "lmgrd" que hace las veces de inetd y se encarga de lanzar diversos demonios. Cada demonio perteneceria a una empresa/producto y obviamente se encargarian independientemente de dar acceso a los clientes. Cada demonio lee la informacion de un fichero de licencia con un formato estandard que genericamente (hay peque~as variaciones) incluye: nombre del programa a proteger, numero de licencias, fecha de caducidad y un hash que verifica los contenidos anteriores. 2) En el otro, el cliente lee directamente el fichero de licencia. Si nos ponemos la camiseta de Globetrotter, diriamos que con la primera de las opciones, es posible tener licencias flotantes, es decir n licencias a usar por n ordenadores, pero sin fijar cuales. Bueno, blah, blah. La cuestion es ¿ como de seguro es FlexLM ? Lo que opina Globetrotter de la la vulnerabilidad de su producto es (traducido): *** ATAQUE SENCILLO *** Ejecutando la aplicacion sobre un depurador si esta conserva informacion de depurado (unstripped para ser tecnicos). => es cierto. Es una ataque sencillisimo, y es el gran problema de todo el producto: al final todo se reduce a: llamar funcion_check_supercomplicada es correcta la licencia ? y esta decision suele ser sencilla de localizar (vistosos mensajes de error) y mas facil todavia de parchear. Otro inciso, no hace falta que haya informacion de simbolos para crackear un programa asi. Si esta informacion existe, lo que facilita es que se reviente completamente (como veremos) el sistema FlexLM. *** ATAQUE DIFICIL, DEPENDIENTE DE LA CONFIGURACION *** "Matando los demonios. Si, a pesar de todo, no se usa uno de los temporizadores incorporados, y no se llama a la funcion HEARTBEAT(), entonces la proteccion software puede ser puenteada por alguien que mate los demonios, ya que las aplicaciones nunca detectarian que el demonio esta caido." => de dificil nada. Debe estar en el grupo de los faciles, ya que la funcion heartbeat puede ser parcheada y se acabo el invento. O mejor se parchea todo ;) *** ATAQUE MUY DIFICIL *** (...suena bien eh ?) "Adivinando los hash que pertenecen al fichero de licencia.[...] The algorithm used is a proprietary one-way block chaining encypherment of all the input data". => me niego a traducir esta obra de arte. Por lo demas, el hash tiene 48 bits. sigamos "Escribiendo un nuevo demonio que emule el original. FlexLM encripta el trafico entre el cliente y el demonio para hacer mas dificil este tipo de ataques". => en cuanto a ingenieria inversa me parece con diferencia el tema mas interesante, aunque las debilidades anteriores ya desacreditan el sistema. "Ejecutando el depurador en un ejectuble sin simbolos de depurado. Esto requiere que alguien encuentre la llamadas-FlexLM sin ningun conocimiento en la tabla de simbolos". => guau, o sea que vamos a utilizar el ataque muy dificil. Pero que buenos que somos. 3 . Funcionamiento interno de FlexLM --------------------------------- Bueno hasta aqui mucho de blah, blah, blah, propaganda, marketing y esas cosas. Ahora bien, las bases sobres la que se asienta son solidas ? Para ello, lo mejor es irse al web de Globetrotter (www.flexlm.com creo que tambien valia) y pillar el kit de desarrolladores. Hace ya unos cuantos meses pille el de la version 6.1, que es el que incorporan la mayoria de los programas (si, flexlm se actualiza, ya se sabe hay que sacar nuevas versiones cueste lo que cueste). Este kit es una autentica mina de oro, ya que facilita aparte de una documentacion bastante completa (hay, logicamente funciones interesantes no documentadas), los siguientes elementos: 1. Gestor de licencias (lmgrd) 2. Codigo fuente de un ejemplo demonio-cliente 3. Utilidades para generacion de licencias 4. Tres librerias que contienen todo el API de FlexLM Dentro del 3er punto esta incluida una utilidad llamada lmcrypt. Esto, lo creais o no, es una generador de licencias. Esto es un nuevo concepto, resulta que la empresa que vende la proteccion facilita un generador de licencias. Ya no es necesario crearte tu propia programa que destripe las entrañas del algoritmo de generacion, no, te lo dan ellos. Hombre, hay que matizarlo, pero es asi como vereis. Bueno nos habiamos bajado el kit de desarrollo. Este viene pseudoencriptado, con lo cual es preciso perder unos segundos (pocos eso si) en sacar la clave. No voy a entrar en mucho detalle, ya que creo que es justo que si alguien quiere acceder a este material se lo trabaje un poco. De todas formas, es muy sencillo y utilizando ltrace se consiguen las pistas para sacar la clave facilmente. Aun asi si alguien no es lo suficientemente capaz, siempre puede arrastrarse a los chicos de Globetrotter y que te manden la clave (que es lo que supuestamente todo el mundo deberia hacer). Es facil al principio perderse entre la documentacion, por lo cual, os voy a resumir en que se basa el sistema. Hay 7 elementos clave para cada demonio comercial (entendiendo por demonio comercial, todo el sistema de gestion de licencias utilizado para un programa determinado ... que hay mucho mal pensado): * 2 semillas de encriptacion exclusivas. * 5 claves de 32 bits. Para la generacion de los hash que autentifican las licencias, son fundamentales estas dos semillas, ya que conociendolas es posible autentificar tantas licencias como uno quiera. Las cinco claves son utiles para poder instalar el kit y para generar un demonio. Estos datos, son logicamente utilizados por las funciones del API, por lo que se empaquetan en la siguiente estructura de datos: typedef struct { short tipo; unsigned long semillas[2]; unsigned long claves[4]; short flexlm_version; short flexlm_revision; char flexlm_patch[2]; char behavior_ver[LM_MAX_BEH_VER + 1]; } codigos; Supongamos que hemos comprado la cosa esta y nos han pasado las 2 semillas y las 5 claves. El programa de instalacion del kit nos pide todos estos datos, y nos genera un cliente, un demonio y otras cosas (ya, ya se que no teneis ninguno de estos datos, pero estamos hablando en un caso generico). Hay una utilidad llamada lmcrypt, que recibe como entrada una licencia en la que el hash esta a cero, y nos devuelve el fichero de licencia con el hash adecuado. La forma en que genera el hash, es mediante esta funcion: int lc_cryptstr(job, STR, STR_RETURN, *CODIGOS, flag, filename, errors) Esta funcion esta documentada en los manuales, y os he puesto en mayusculas los parametros importantes: STR: recibe la cadena de texto donde se especifica sobre que programa se aplica la licencia, si caduca y cuando, y el numero de licencias permitidas. Uno de los campos es el hash de autenticacion, y que en este caso se pone a cero. Por ejemplo: "FEATURE word MSOffice 1.0 permanent 4 0" CODIGOS: es una estructura como la de arriba, con las semillas y las claves, que puestamente se usaran para crear el hash STR_RETURN: devuelve una cadena de texto equivalente a STR, pero donde el hash ya no es cero, sino son los 48 bits correspondientes. Vale, la licencia esta generada. ¿Porque no la genera cualquiera ? Hombre, pues porque hacen falta esos 7 datos ? ¿7 datos ? bueno, en realidad solo son 6, ya que en la estructura CODIGOS solo aparecen 4 claves (ya veremos que pasa con la quinta). 4 . Debilidades del sistema ------------------------------------------ Ahora resulta que el usuario Pepito se ha comprado su programa carisimo, y le han dado un fichero de licencia que podria ser asi: SERVER localhost.localdomain ANY VENDOR khoral /usr/local/flexlm/v6.0/i86_l1/khoral FEATURE xprismpro khoral 1.0 permanent 4 XXXXXXXXXXXX las XXX son el hash eh !! Arrancara el gestor de licencias (lmgrd) que ira a buscar ese fichero y tendra que comprobar el hash. Bien, por sentido comun, y aunque uno no sepa nada de cracking, o el sistema es muy bueno o de alguna forma para comprobarlo hay que generarlo otra vez y comparar. ¿ Como comprueba el hash el demonio ? Tomando el demonio de ejemplo que viene en el kit (y al igual que hacen la mayoria de los demonios comerciales que circulan) primero se incializa el sistema llamando a la funcion lc_init: lc_init(--, --, CODIGOS*, --) Je, je, luego efectivamente en el demonio hay una copia de la estructura CODIGOS, con la semillas y las 4 claves. QUE FRAUDE ! pensareis. Aqui es donde FlexLM dice: Ah, pero la seguridad esta garantizada ya que las semillas estan encriptadas (xoreadas) con la clave numero 5 que no esta incluida en el demonio (y asi es efectivamente). Esta funcion de inicializacion recibe las 4 claves y las dos semillas xoreadas con la clave numero 5. Recapacitemos, nos dicen que NO estan en el demonio todos lo elementos que nos permitirian generar licencias. Yo, desde luego, no crei que comprobaran el hash sin recurrir a los datos originales con el que fue generado, luego la clave numero 5 debian generarla de alguna forma al vuelo (o bien estaba "escondida" en el demonio). Si analizamos mas detenidamente el codigo del demonio de ejemplo, vemos que la comprobacion del hash se hace con una llamada a la funcion lc_crypt, que obviamente no esta documentada lc_crypt (--, --, --, CODIGOS*) pero en la que la estructura codigos recibe ya las semillas originales (desxoreadas). Je, je, luego han generado la clave numero 5. En definitiva y ahorrandonos listados en ensamblador (ya llegaran) , la clave numero 5 se genera mediante una llamada a la funcion, tambien indocumentada l_svk (secret vendor key ?) int l_svk(char*,CODIGOS*) el parametro de entrada (y co-responsable de su generacion) es nada mas y nada menos que el nombre del demonio :D. Ciertamente curioso. Esta funcion l_svk esta disponible en las librerias del kit de desarrollo, luego podemos hacernos un programita en el que rellenando los datos correspondientes a las semillas xoreadas y las 4 claves, nos facilite la clave numero 5, y lo que es mas importante las semillas originales con las que generar licencias en serie. Resumiendo, en el codigo del demonio (y en su nombre) estan los datos para generar todas las licencias del mundo. El unico problema es identificar la llamada a la funcion lc_init, por ejemplo, en la que se pasa como parametro una estructura de tipo CODIGOS. Pero eso no es un gran problema una vez que sabemos como es (en el demonio del kit de desarrollo, si que hay tabla de simbolos y es inmediato localizar la funcion en el listado en ensamblador). Mi opinion es que si se dispone de un fichero de licencia operativo (sabiendo asi las sintaxis de cada campo), es bastante sencillo generear licencias adicionales o modificar las existentes. En el resto de los casos se complica algo. 5 . Ficheros de licencias. Ejemplo practico --------------------------- Despues de esta introduccion general a lo que es FlexLM, que no pretende ser demasiado exhaustiva por cuestiones de espacio (si acaso en otras entregas, trataremos un caso concreto de FlexLM que sea especialmente didactico), vamos a ver un ejemplo practico que si bien esta flexlm metido por medio, no es exactamente flexlm. El programa se llama IDL 5.2, y significa algo asi como Interactive Data Language. Es una especie de lenguaje de programacion muy orientado a la presentacion/visualizacion grafica de datos, y que segun cuenta la gente es bastante potente. Lo podeis encontrar en el web de rsi: www.rsinc.com Tienen una demo muy curiosa. Supuestamente es completamente funcional si exceptuamos salvar datos e imprimir. Eso si solo durante 7 minutos, transcurridos los cuales te echa sin el menor miramiento. Vereis que cada vez afinan mas. Existe la posibilidad de que te den una licencia temporal para evaluarlo (gratuita), y luego si lo compras, la licencia va por FlexLM. Curiosamente la forma de obtener una licencia temporal de evaluacion, no es con FlexLM. No se muy bien, si es que no se fian del sistema (en cuyo caso parece deberian cambiarlo), o bien no quieren pagar mas a los chicos de Globetrotter. Se han hecho su propio programita que genera un fichero de licencia que desde luego no esta en modo texto. Este programa, llamado genver, genera un numero aleatorio que tu le comentas al comercial de turno por telefono, y este te genera otro numero que introduces al programa para que genere la licencia. Hay claramente tres opciones: 1) Evitar que caduque a los 7 minutos (hay una bonita ventana que nos comunica que el tiempo se ha acabo). 2) Generar una licencia temporal. 3) Generar un licencia Flex. Vamos a descartar la primera y tercera opcion. La primera porque el programa esta hecho en Motif, y no conocemos nada sobre ello, ni sobre Toolkits, mensajes, widgets ni nada de eso. Ademas el programa es gigantesco y los listado en ensamblador son enormes (de mas de 60 Mb). Hasta ahora no hemos tocado nada sobre Motif, ya que algo me dice que esta cayendo en deshuso y es en general bastante farragoso (esta basado en objetos, es decir, codigo gigantesco). La tercera es por una cuestion preventiva, ya que ya me ha ocurrido varias veces de estar mucho tiempo forzando funcionalidades que luego no estaban disponibles en el cliente, con lo cual manipular licencias sin tener una referencia de que el cliente las va a saber manejar, puede ser una perdida de tiempo. En cualquier caso, con lo expuesto aqui podreis obtener facilmente tanto las semillas como la 5 claves (el que lo consiga que me las mande, y tendra soporte on-line de flexlm. A ver si os animais). Manos a la obra, ejecutamos el programa genver (que se encuentra en el directorio idl/bin/bin.linux/genver) y nos dice: Your number for today is: 6239 Enter length of trial period in days : si le ponemos que 1 millon, nos echara rapidamente, pero si ponemos algo razonable como 100, nos respodera: Enter RSI supplied installation key: esta es la que supuestamente nos dan por telefono. Respondiendo lo que sea, nos dice que es invalida. Ejectando el maravilloso programa ltrace que hemos descrito ampliamente en entregas anteriores, obtenemos este interesante listado: [080490e2] fopen("./idl.genver", "w") = 0x0804b028 [08049122] chmod("./idl.genver", 0666) = 0 [080493ca] ftime(0xbfffe5dc, 0, 0x40005ff0, 0x08048940, 0xbfffe5dc) = 0 [0804946b] printf("\nYour number for today is: %lu\n"..., 14098) = 34 [08048e24] fileno(0x0804afc8) = 0 [08048e2f] isatty(0) = 1 [08048e4a] printf("Enter %s: ", "length of trial period in days ") = 39 [08048e68] fgets("100\n", 1024, 0x0804afc8) = 0xbfffe164 [08048f1c] sscanf("100\n", "%ld", -1073749672, -1073749672) = 1 [08048f54] sscanf("100\n", "%ld", -1073749672, -1073749672) = 1 [08048e24] fileno(0x0804afc8) = 0 [08048e2f] isatty(0) = 1 [08048e4a] printf("Enter %s: ", "RSI supplied installation key") = 37 [08048e68] fgets("235253453536346"..., 1024, 0x0804afc8) =0xbfffe164 [08048f1c] sscanf("235253453536346"..., "%ld", -1073749672,-1073749672) = 1 [08048f54] sscanf("235253453536346"..., "%ld", -1073749672,-1073749672) = 1 [08048db1] fprintf(0x0804af70, "genver: %s\n\n", "invalid key") = 21 [08048dde] exit(1) = hay dos secuencias muy parecidas, una la que lee el numero de dias, y otra la que lee la llave. La secuencia es printf -> fgets -> sscanf -> sscanf. Si os fijais en el puntero de programa, la localizacion de estas funciones es la misma, es decir, hay una especie de funcion que hace las labores de "imprime cartel, lee caracteres, pasalo a numero". No parece muy sensato que la funcion sscanf (que es similar a scanf) se llame dos veces con los mismos argumentos, pero ... Generemos un listado en ensamblador (con el script dasm, cuya ultima actualizacion acompa~o al final. Por cierto disculpad que los mensajes esten en ingles) del programa genver, y examinemos las proximidades de la secuecia fgets -> sscanf: Reference to function : fgets 0x08048e63 call 0x08048798 0x08048e68 addl $0xc,%esp 0x08048e6b movl %eax,%eax 0x08048e6d movl %eax,0xfffffbf8(%ebp) 0x08048e73 cmpl $0x0,0xfffffbf8(%ebp) 0x08048e7a jne 0x08048e90 fgets devuelve en eax el puntero a la cadena leida, luego esta comprobando que se ha leido algo. Posteriormente comprueba que no es ningun caracter exotico (CR, tabulador, ...), y hace una llamada (dos veces, solo os pongo la segunda) a sscanf: int sscanf( const char *cadena, const char *formato, &numero) 0x08048f38 leal 0xfffffbf0(%ebp),%eax 0x08048f3e pushl %eax; <---- salva el puntero al numero 0x08048f3f leal 0xffffeb17(%ebx),%edx 0x08048f45 movl %edx,%eax 0x08048f47 pushl %eax <---- salva el punt. a la cadena de formato 0x08048f48 movl 0xfffffbf8(%ebp),%eax 0x08048f4e pushl %eax <---- salva la cadena origen Reference to function : sscanf <---- sscanf devuelve en eax la cantidad <---- de caracteres convertidos 0x08048f4f call 0x08048848 0x08048f54 addl $0xc,%esp 0x08048f57 movl %eax,%eax 0x08048f59 cmpl $0x1,%eax <---- comprueba que se ha convertido un car. 0x08048f5c je 0x08048f92 <---- salta si es cierto 0x08048f5e leal 0xfffffbfc(%ebp),%eax 0x08048f64 pushl %eax [...] 0x08048f8a call 0x08048d80 0x08048f8f addl $0x8,%esp Referenced from jump at 08048f5c ; 0x08048f92 movl 0xfffffbf0(%ebp),%eax <--- mueve el numero devuelto por <--- sscanf a eax 0x08048f98 jmp 0x08048fa0 0x08048f9a leal 0x0(%esi),%esi Referenced from jump at 08048e7e ; 08048f98 ; 0x08048fa0 movl 0xfffffbec(%ebp),%ebx 0x08048fa6 movl %ebp,%esp 0x08048fa8 popl %ebp 0x08048fa9 ret luego toda esta funcion, lo que hace es devolver en eax la llave introducida por el usuario. Para averiguar a donde retorna esta funcion, lo mejor es ejecutar el programa en el depurador y al encontrarnos en este punto del programa (tras introducir la llave, ya que esta parte de codigo tambien se ejecuta cuando introducimos el numero de dias), ejecutar el comando gdb llamado "back", que devuelve el arbol de llamadas en ese punto: #0 0x8048f1c in strlen () #1 0xbfffe38c in ?? () #2 0x80495a6 in strlen () #3 0x804899b in strlen () luego venimos del codigo con direccion 0x80495a6. Examinemoslo: 0x080495a1 call 0x08048e00 <--- esta es la funcion que hemos visto <--- y que devuelve la llave leida 0x080495a6 addl $0x4,%esp 0x080495a9 movl %eax,0xffffeaf4(%ebp) 0x080495af movl 0xffffeaf4(%ebp),%edi 0x080495b5 movl %edi,0xffffeb4c(%ebp) 0x080495bb movl 0xffffeb4c(%ebp),%eax 0x080495c1 movl %eax,0xffffeaf4(%ebp) 0x080495c7 movl 0xffffeaf4(%ebp),%edi 0x080495cd cmpl %edi,0xffffeb48(%ebp) nunca habia visto algo tan ineficiente, estas lineas solo valen para que finalmente se compare el numero introducido con el contenido en ebp+0xffffeb48, que es un numero que cambia cada vez, y que para vuestra informacion es la clave correcta. Pongo el resto del codigo. 0x080495d5 pushl $0x1 <--- esto se ejecuta si la clave es erronea 0x080495d7 leal 0xffffeee2(%ebx),%edx 0x080495dd movl %edx,0xffffeaf4(%ebp) 0x080495e3 movl 0xffffeaf4(%ebp),%eax 0x080495e9 pushl %eax 0x080495ea call 0x08048d80 <--- aqui se llamara al printf con el <--- mensaje de llave invalida Ya sabemos donde esta la llave buena, ahora os voy a ense~ar una forma sencilla de que nos aparezca mientras se ejecuta el programa. Para ello sabemos que cuando nos solicita la clave, el programa ya la ha generado internamente, y la idea es usar el printf donde nos la solicita para mostrar ese numero en claro. Vuelvo a poner la forma del printf donde se solicita introducir la llave: printf("Enter %s: ", "RSI supplied installation key") que os parece si lo cambiamos a: printf ("Enter %x: ", puntero_a_la_llave_que_el_ha_calculado); Para cambiar la cadena de formato, no hay mas que editar el fichero binario (sigo sugiriendo dosemu+hiew) y cambiar la "s" por la "x". Y por ultimo habra que cambiar el puntero a "RSI ..." por un puntero a la llave. Para esto ultimo si ejecutamos genver bajo un depurador y detenemos el programa en la instruccion 0x080495a9 (ver listado de arriba), podemos averiguar la direccion absoluta de la llave, es decir cuanto vale ebp+0xffffeb48 en ese punto. Se apunta, llamemosla direccion_llave. Volviendo al printf que vamos a modificar, esta es su estructura: 0x08048e38 movl 0x8(%ebp),%eax 0x08048e3b pushl %eax <--- salvar "RSI ..." 0x08048e3c leal 0xffffeb0c(%ebx),%edx 0x08048e42 movl %edx,%eax 0x08048e44 pushl %eax <--- salvar "Enter %s" Reference to function : printf 0x08048e45 call 0x08048778 si en este punto vemos el valor de ebp, veremos que solo se diferencia en 0x6c unidades de la direccion_llave, es decir modificando: 0x08048e38 movl 0x8(%ebp),%eax por 0x08048e38 movl 0x6c(%ebp),%eax apuntara a la llave correcta, que nos aparecera claramente, es decir, en vez de preguntar: Enter RSI supplied installation key: preguntara Enter llave_correcta: elegante no ;-) ? Como daño colateral, en vez de solicitarnos el numero de dias tambien aparecera un numero, pero bueno, creo merece la pena el cambio. Hasta la proxima entrega, SiuL+Hacky si_ha@iname.com ------------------------------ listado de dasm ------------------------ #!/usr/bin/perl ;############ MODIFY THIS LINE WITH YOUR PERL LOCATION ############ push(@INC,"/usr/lib/perl5"); require("flush.pl"); ;################################################################## ;######## LINUX DISASSEMBLER 2.0799 ;######## (C) SiuL+Hacky Jul 1999 ;######## You may copy, modify, distribute this program and ;######## is up you to keep this header here ;######## Usage: dasm exe_file dasm_file ;################################################################## $f_input=$ARGV[0]; $f_output=$ARGV[1]; &printflush(STDOUT, "\nCreating disassembled file ..."); $return=system("objdump -d -T -x --prefix-addresses ".$f_input.">".$f_output."2"); if ($return!=0){ print "\nERROR OPENING OBJDUMP $return"; print "\nUsage: dasm exe_file dasm_file"; print "\nBe sure to get objdump in your path. Check also file permissions\n"; exit(1); } open(INPUT, "<".$f_output."2"); &printflush(STDOUT, "\nReading strings ..."); $_=; while (!/.rodata/){ $_=; } ($rubbish, $rest)=split(/.rodata/,$_,2); ($rubbish, $rest)=split(/0/,$rest,2); @numbers=split(/ /,$rest,5); $size=hex($numbers[0]); $starting_address=hex($numbers[1]); $end_address=$starting_address+$size; $offset=hex($numbers[3]); open(CODIGO, "<".$f_input); seek(CODIGO,$offset,0); read(CODIGO,$cadena,$size); close(CODIGO); SEARCH: while (){ last SEARCH if (/SYMBOL TABLE/); } if (/SYMBOL TABLE/){ &printflush(STDOUT, "\nProcessing symbol table ..."); $_=; while (!/^\n/){ @st_element=split(/ /, $_); $_=$st_element[$#st_element]; chop; $symbol_table{$st_element[0]}=$_; $_=; } } else { seek(INPUT,0,0); } while (!/\.text/){ $_=; } &printflush(STDOUT, "\nProcessing jmps and calls ..."); ######### the regex gets rid of possible line information ############# while (){ $_=~ s/0x//g; $_=~ s/<.*?>//g; $_=~s/ / /g; if (/j/){ ($direccion,$inst,$destino)=split(/ /,$_,3); $destino=~s/ //g; chomp($destino); $salto{$destino}.=($direccion." \; "); } elsif (/call/){ ($direccion,$inst,$destino)=split(/ /,$_,3); $destino=~s/ //g; chomp($destino); $call{$destino}.=($direccion." \; "); } } seek(INPUT,0,0); &printflush(STDOUT, "\nWritting references ...\n"); open(OUTPUT, ">".$f_output) || die print "\nError opening write file\n"; print OUTPUT "FILE REFERENCED\n\n"; while (!/Disassembly of section .text:/){ $_=; print OUTPUT; } $char="."; $counter=0; while(){ $counter++; if ( ($counter % 400)==0){ printflush(STDOUT,$char); if ( ($counter % 4000)==0){ printflush(STDOUT,"\r"); if ($char eq "."){ $char=" ";} else { $char=".";} } } $copia=$_; $_=~s/0x//g; $_=~s/<.*?>//ge; $_=~s/ / /g; ($direccion, $inst, $destino)=split(/ /,$_,3); if ( defined( $symbol_table{$direccion} )){ print OUTPUT "\n"; print OUTPUT "---- Function : ".$symbol_table{$direccion}." ----\n"; } if (/call/){ $destino=~s/ //g; chomp($destino); if ( defined( $symbol_table{$destino} )){ print OUTPUT "\n"; print OUTPUT "Reference to function : ".$symbol_table{$destino}."\n\n"; } } if ( defined( $salto{$direccion} )){ print OUTPUT "\n"; print OUTPUT "Referenced from jump at ".$salto{$direccion}."\n\n"; } if ( defined( $call{$direccion} )){ print OUTPUT "\n"; print OUTPUT "Referenced from call at ".$call{$direccion}."\n\n"; } if (/\$/){ ($instruccion, $operand)=split(/\$/,$_,2); if (!/push/){ ($operand, $rest)=split(/\,/,$operand,2); } chomp($operand); $offset=hex($operand); if ( ($offset <= $end_address) && ($offset >= $starting_address ) ){ $auxiliar=substr($cadena, $offset-$starting_address); $length=index($auxiliar, pack("x") ); $auxiliar=substr($auxiliar, 0, $length); $auxiliar=~s/\n//g; print OUTPUT "\n"; print OUTPUT "Possible reference to string:"; print OUTPUT "\n\"$auxiliar\"\n\n" } } print OUTPUT $copia; } close(INPUT); close(OUTPUT); print "\n"; system("rm ".$f_output."2");