Reto para «Código & Colegas»

Un grupo de alumnos ha organizado para esta tarde en la FISS (Facultad de Informática de San Sebastián), un pequeño meeting bajo el nombre «Código & Colegas», para compartir código, ideas y conocer a más gente interesada en programación. La verdad es que le podían haber dado más bombo a la iniciativa y ser más concretos en los objetivos, pero , ¡hey! están empezando y por tanto, más que criticar, mejor aplaudir y colaborar 🙂 Ahí va mi granito de arena: un reto de programación, cortito y entendible, de los que provocan necesidad de una buena dosis de cafeína para resolverlo: Sean los Strings <strong><em>números</em></strong> y <strong><em>dígitos</em></strong>. Cada una de esas cadenas contiene únicamente dígitos entre 1 y 9, inclusive. Para cada ocurrencia de un dígito en el String <em>dígitos</em> , debes eliminar una única ocurrencia de ese dígito en <em>números</em>. Objetivo: terminar - tras haber eliminado los dígitos necesarios - con una cadena que represente el número más alto posible. Devolver este número como String

Ejemplo 1: números: «12345» dígitos: «513» Devuelve: «24» Si eliminamos los dígitos ‘5’, ‘3’, ‘1’ obtenemos el número 24.

Ejemplo 2: números: «654321123456» dígitos: «612534» Devuelve: «654321» (Eliminando de la segunda mitad de números )

Ejemplo 3: números: «2654982765982365» dígitos: «2345978» devuelve: «698265265»

NOTA: el ejercicio está sacado de una de las competiciones de TopCoder. Que saber de dónde viene no provoque la pérdida de interés por intentar resolverlo por vuestra cuenta 😉

Asociar extensiones con aplicaciones en GNOME

Me ha llevado más de 2 minutos encontrar la opción, por lo que considero que no es evidente y que hay que documentarla. El problema: en una carpeta con ficheros .doc, al hacer doble click sobre ellos se abre una ventana en la que se me indica que las mejores aplicaciones para abrirlo son AbiWord y KWord (ninguna de las dos está instalada en mi PC). OpenOffice no sale como alternativa (tengo instalado OpenOffice 2.4, desde paquetes .deb). ¿Cómo hacer que la extensión .doc esté asociada con OpenOffice.org Writer? Abrir Nautilus / Situarse en la carpeta donde está el .doc / botón derecho sobre el .doc / Ficha Abrir con. Elegir OpenOffice Writer o teclear la ruta hacia esa aplicación. ¡Listo!

Plantilla en Java del patrón Singleton para Eclipse

Parafraseando a Groucho Marx: éstos son mis títulos de post. Si a usted no le gustan, tengo otros 😉

Warning: post muy técnico.

Si programas en un lenguaje orientado a objetos, es probable que te hayas encontrado a menudo con el patrón singleton. Últimamente me he encontrado tecleando las líneas de este patrón en Java una y otra vez, con lo que se me ha encendido la bombilla de la pereza: «y ésto… ¿no lo podré automatizar?» Por supuesto. En Eclipse es sencillo:

Window->Preferences->Java->Editor->Templates. Click en New e insertar el siguiente código. Darle un nombre («singleton» podría ser una decisión acertada 😉 . Cuando quieras usar el patrón en tus clases Java, escribe «singleton» y pulsa Ctrl+Espacio. ¡Magia! private static ${enclosing_type} instance; private ${enclosing_type}(){} public static ${enclosing_type} getInstance(){ if(null == instance){ instance = new ${enclosing_type}(); } return instance; }

Nota: ojito, que esta forma de implementación del patrón parece sufrir una condición de carrera en el bloque if … una posible solución sería inicializar la referencia estática en la propia sección de declaraciones.

HackIt! 2007 : nivel 6: solución

Reverse Engineering. Cracking. Un poco de conocimientoso sobre ambos temas serán imprescindibles para pasar este nivel (os he dejado muchos días para que fuerais sacándolo por vuestra cuenta, ¿qué tal os ha ido?) Vamos a por ello. Tras descargarnos el fichero .zip que indica la página, lo descomprimimos e intentamos ejecutar (txipi nos dijo que no había virus ni código maligno en los ejecutables, por eso nos atrevemos a ejecutar un binario a pelo…). La salida es bastante pobre, por decir algo:

sh-3.2$ ./program sh-3.2$

Así que toca depurar con GDB.

$ gdb ./program warning: no loadable sections found in added symbol-file /tmp/program (no debugging symbols found)

Vaya, si no hay tabla de símbolos, la depuración puede ser un infierno. Así que comprobemos primero que el ejecutable no haya sido comprimido con UPX o similares (UPX es el compresor más conocido de ficheros ELF en Linux):

$ upx -l ./program Ultimate Packer for eXecutables Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007 UPX 3.00 Markus Oberhumer, Laszlo Molnar & John Reiser Apr 27th 2007 . File size Ratio Format Name</p> <hr /> <p>532432 -> 52404 9.84% linux/elf386 ./program

¡Tachán! Efectivamente el ejecutable está comprimido con UPX. Descompresión al canto:

$ sh-3.2$ upx -d ./program Ultimate Packer for eXecutables Copyright (C) 1996,1997,1998,1999,2000,2001,2002,2003,2004,2005,2006,2007 UPX 3.00 Markus Oberhumer, Laszlo Molnar & John Reiser Apr 27th 2007 . File size Ratio Format Name</p> <hr /> <p>532432 <- 52404 9.84% linux/elf386 program Unpacked 1 file.

Y ahora volvemos a pasarlo por GDB, a ver qué nos dice:

sh-3.2$ gdb ./program GNU gdb 6.6-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"... Using host libthread_db library "/lib/libthread_db.so.1". (gdb)

Mejor 🙂 Bien, vamos a poner un punto de ruptura en main():

(gdb) break main Breakpoint 1 at 0x8048455 (gdb)

Ejecutemos hasta el punto de ruptura y pidamos un desensamblado de la zona: (gdb) run Starting program: /tmp/program Breakpoint 1, 0x08048455 in main () (gdb) disassemble Dump of assembler code for function main: 0x08048444 <main+0>: lea 0x4(%esp),%ecx 0x08048448 <main+4>: and $0xfffffff0,%esp 0x0804844b <main+7>: pushl 0xfffffffc(%ecx) 0x0804844e <main+10>: push %ebp 0x0804844f <main+11>: mov %esp,%ebp 0x08048451 <main+13>: push %edi 0x08048452 <main+14>: push %esi 0x08048453 <main+15>: push %ebx 0x08048454 <main+16>: push %ecx 0x08048455 <main+17>: sub $0x78,%esp 0x08048458 <main+20>: movl $0x0,0xffffffec(%ebp) 0x0804845f <main+27>: lea 0xffffffd0(%ebp),%eax 0x08048462 <main+30>: mov %eax,(%esp) 0x08048465 <main+33>: call 0x804837c <a href="mailto:times@plt">times@plt</a> 0x0804846a <main+38>: movl $0x0,0xffffffe8(%ebp) 0x08048471 <main+45>: jmp 0x8048477 <main+51> 0x08048473 <main+47>: addl $0x1,0xffffffe8(%ebp) 0x08048477 <main+51>: cmpl $0x98967e,0xffffffe8(%ebp) 0x0804847e <main+58>: jle 0x8048473 <main+47> 0x08048480 <main+60>: lea 0xffffffc0(%ebp),%eax 0x08048483 <main+63>: mov %eax,(%esp) 0x08048486 <main+66>: call 0x804837c <a href="mailto:times@plt">times@plt</a> <a href="mailto:/times@plt">/times@plt</a><a href="mailto:/main+66></main+63></main+60></main+47></main+58></main+51></main+47></main+51></main+45></main+38></times@plt">/main+66></main+63></main+60></main+47></main+58></main+51></main+47></main+51></main+45></main+38></times@plt</a></main+33></main+30></main+27></main+20></main+17></main+16></main+15></main+14></main+13></main+11></main+10></main+7></main+4></main+0>

Bien, se ve la zona de paso de variables a la pila (guardar el contexto) y primera llamada call (por el nombre de la función, times, parece que hace algún tipo de comprobación) y en función del resultado, en main+45 saltamos a main+51. Ahí nueva comparación… saltamos (hacia atrás) a main+47. Parece un bucle, ciclando hasta que se cumpla cierta condición. Posteriormente, se hace una nueva llamada a «times», haciendo alguna nueva comprobación:

0x08048486 <main+66>: call 0x804837c <a href="mailto:times@plt">times@plt</a> 0x0804848b <main+71>: mov 0xffffffc0(%ebp),%edx 0x0804848e <main+74>: mov 0xffffffd0(%ebp),%eax 0x08048491 <main+77>: mov %edx,%ecx 0x08048493 <main+79>: sub %eax,%ecx 0x08048495 <main+81>: mov %ecx,%eax 0x08048497 <main+83>: mov %eax,0xffffffe8(%ebp) 0x0804849a <main+86>: cmpl $0xa,0xffffffe8(%ebp) 0x0804849e <main+90>: jle 0x8048586 <main+322> 0x080484a4 <main+96>: movl $0x80c89a8,(%esp) 0x080484ab <main+103>: call <a href="mailto:printf@plt">printf@plt</a> <a href="mailto:/printf@plt">/printf@plt</a><a href="mailto:/main+103></main+96></main+322></main+90></main+86></main+83></main+81></main+79></main+77></main+74></main+71></times@plt">/main+103></main+96></main+322></main+90></main+86></main+83></main+81></main+79></main+77></main+74></main+71></times@plt</a></main+66>

En main+90 hay alguna condición que tras comprobarla hace que el programa termine (salta a main+322). Vamos a cortocircuitar este último salto . a ver qué pasa 😉 : (gdb) j *0x080484a4 Continuing at 0x80484a4. username? jota error: wrong serial number!Program exited normally. (gdb) Vaya, algo hemos avanzado, porque al menos nos pide username… Pero poniéndole cualquier cosa (jota, p.ej.), vemos que no le gusta. Mmmmmhhh… volvamos a la carga:

(gdb) run Starting program: /tmp/program . Breakpoint 1, 0x08048455 in main() (gdb) disassemble ... 0x080484ab <main+103>: call 0x804835c 0x080484b0 <main+108>: lea 0xffffffac(%ebp),%eax 0x080484b3 <main+111>: mov %eax,(%esp) 0x080484b6 <main+114>: call 0x804833c <a href="mailto:gets@plt">gets@plt</a> 0x080484bb <main+119>: mov $0x303c48b1,%eax 0x080484c0 <main+124>: sub 0xffffffec(%ebp),%eax 0x080484c3 <main+127>: mov %eax,0xffffffec(%ebp) 0x080484c6 <main+130>: addl $0x2faf080,0xffffffec(%ebp) 0x080484cd <main+137>: cmpl $0x0,0xffffffec(%ebp) 0x080484d1 <main+141>: jne 0x804857a <main+310> 0x080484d7 <main+147>: movb $0x9b,0xffffffe0(%ebp) ... <a href="mailto:/main+147></main+310></main+141></main+137></main+130></main+127></main+124></main+119></gets@plt">/main+147></main+310></main+141></main+137></main+130></main+127></main+124></main+119></gets@plt</a></main+114></main+111></main+108></main+103> Entre el printf («username») y el gets (recogida del parámetro por teclado) no hay nada que destacar. Lo siguiente termina en un jne a main+310. Volvamos a cortocircuitar (no queremos saltar en main+141… queremos ir a la siguiente instrucción) : (gdb) j *0x080484d7 Continuing at 0x80484d7. correct! password = d3nb0r4 Program exited normally. (gdb) ¡Qué bonito password! 🙂