-[ 0x08 ]-------------------------------------------------------------------- -[ Java en moviles ]--------------------------------------------------------- -[ by FCA00000 ]-----------------------------------------------------SET-31-- Java en móviles *************** Conté hace unos cuantos artículos cómo opera la máquina virtual Java y cómo funciona el compilador . También hubo un apartado para explicar la manera de modificar una clase Java sin necesidad de tener acceso al código fuente original. De nuevo voy a contar algo parecido, pero siguiendo en mi línea de artículos para móviles, ahora mostraré un poco cómo funciona Java en un teléfono móvil. Para los que entendieron el artículo anterior, éste posiblemente no les proporcione nueva información, pero nunca se sabe. También quiero recomendar el genial artículo escrito en la e-zine 29A sobre virus en Java. Proporciona una visión muy detallada del funcionamiento interno con algunas ideas muy buenas. Según la ley española, hacer ésto sin consentimiento del autor puede ser un delito. Es más, la simple posesión de éste artículo es un delito. Si no quieres cometer una ilegalidad, no sigas leyendo y destruye este fichero. Hace bastante tiempo que existen en el mercado una gran oferta de dispositivos pequeños con capacidad de ejecutar programas compilados en Java. Esto incluye móviles, PDAs, reproductores de MP3, ... Como ya era tiempo de que yo me adaptara a las tecnologías mas recientes, he adquirido un teléfono Siemens (cómo no) modelo M65. Las características técnicas son similares a las de otros modelos de la misma gama: pantalla de 132x176 pixels, 16 Bits de colores, sonido polifónico MIDI-1, vibración, teclas y cursor, cámara, infrarrojos, MMS, puerto serie, ... Internamente tiene un procesador ARM de 32 bits, 16 Mg de flash, 11 Mg de RAM, stack Java de 1.5 Mg, y soporta MIDP-2.0 y CLDC-1.1 con soporte de WML y HTML sobre WAP2.0 y GPRS Clase 10. Permite video h263 . Esto lo convierte en un móvil bastante completo y muy capaz de ejecutar aplicaciones Java con un gran conjunto de funcionalidades. Dado que los móviles están restringidos en tamaño, memoria, potencia, y velocidad, sólo usan un conjunto reducido de librerías Java especifícas para dispositivos pequeños, llamado J2ME - Java2 Mobile Edition. Los sistemas soportados por el M65 son: MIDP 2.0 CLDC 1.1 JSR 120 WMA 1.0 JSR 135 MMA 1.0 JSR 179 Location API JSR 185 JTWI 1.0 El sistema base es MIDP 2.0 con el cual se pueden hacer cosas como cargar dibujos desde ficheros, dibujar sprites en la pantalla, pintar decorados, generar mapas hechos con iconos, crear formularios HTML con los elementos típicos (textos, botones, listas, botones para elegir, diálogos, fechas, tipos de letras, ...) además de generar sonidos, conectarse con la red, mandar y recibir SMS, iniciar llamadas y otras muchas cosas más. En fin, que es bastante extenso. Se ha puesto especial atención al terreno de los juegos, haciendo que vayan a una velocidad más que aceptable. Pero no quiero contar ahora cómo hacer juegos. Primero hay que aprender a modificar juegos ajenos. Lo primero es conseguir algún juego. En mi móvil vienen instalados 4 que están incluidos en el precio, así que tengo la correspondiente licencia para usarlos. También es posible meter nuevos juegos mandando mensajes a unos servidores, y, previo abono, aparecen en tu móvil. Otra opción es buscar páginas de Internet que los tengan. Algunos juegos son gratuitos, y otros ofrecen una versión de demostración. También existen otros de pago, claro. Finalmente, existen páginas web con juegos piratas, pero descargarlos de estas páginas es ilegal y supone un delito. Así que haznos un favor a todos y paga por el software que uses. Es la única manera de seguir adelante con la industria y el desarrollo. Existen por ahí colecciones extensas de más de 500 juegos. Puesto que el M65 tiene puerto de infrarrojos, puedo transferir los programas desde mi PC hasta el móvil todas las veces que deseo. Otra opción es usar el cable serie o USB. Todo lo que necesito está disponible para Windows. Algunas aplicaciones tienen versiones para otros entornos, pero lamentablemente no todas. La primera herramienta que necesito es un descompilador de Java. Yo uso "jad" hecho por Pavel Kouznetsov. A veces uso el GUI llamado "DJ Java Decompiler" hecho por Atanas Neshkov. Algo fundamental es un compilador de Java. Yo uso el JSDK 1.4.1 de Sun con las extensiones de J2ME. Existen también muy buenos entornos gráficos para usar el compilador, pero para este artículo no son necesarios. La manera oficial de probar si las modificaciones funcionan son, por supuesto, meter el programa modificado en el móvil. Sin embargo Siemens pone a disposición de todos los desarrolladores una herramienta llamada SMTK - Siemens Mobile Tool Kit entre las cuales incluye un emulador de móvil. Existen para muchas versiones de móviles, por supuesto hay una para M65. El funcionamiento es sencillo: copia el programa modificado en un directorio del disco duro, inicia el emulador, y carga dicho programa. La emulación no es 100% perfecta, y el emulador falla demasiado para mi gusto. Pero siempre queda la opción de usar el móvil real. Para los móviles Nokia existe el NDS-Nokia Developer’s Suite for J2ME. Si quieres probar que el programa original funciona perfectamente en todos los móviles, deberías considerar esta opción. En general esto es muy útil para los creadores de programas. Otra utilidad es un editor hexadecimal. Y un programa que sirve para sacar diferencias entre un archivo y otro, tanto en modo binario como en texto. Yo uso WinDiff. ******************** Voy con el primer ejercicio. El programa se llama Megablaster y es uno de estos arcades en los que naves espaciales aparecen la parte alta de la pantalla y tu nave situada en la parte baja debe destruirlas o esquivarlas. No es que sea una idea innovadora, pero el programa está muy bien realizado y es fácil de jugar. Está realizado por la empresa italiana Microforum Games. Un programador, un dibujante, algunos testers, y poco más. Lo primero es meterlo en el móvil: si usas Windows con infrarrojos, inicia la aplicacion IrFTP, pon el móvil cerca del PC, elige el archivo del disco duro, y envíalo al móvil. Entonces aparecerá una carpeta en la parte inferior derecha, y pulsando el botón de dicho menú, copia la aplicación al directorio que quieras dentro del móvil, No es posible jugar si no mueves antes el fichero. Cuando empiezo a jugar voy pasando niveles y me van matando naves. Me doy cuenta de que de vez en cuando obtengo una vida extra. Empiezo con 3, y como soy muy malo jugando, las pierdo rápidamente. Así que voy a modificarlo para no perder vidas nunca. Tengo que buscar una variable que se inicialice a 3, que se incremente algunas veces, que se decremente otras, y que se compruebe en algun momento que vale 0. El programa está en un fichero llamado Megablaster.jar de 84 Kb. Lo desempaqueto con la aplicacion jar que se instala con el JSDK, o también con el WinRAR. El paquete completo se compone de: -unos cuantos ficheros de texto en el directorio raíz, con las intrucciones en varios idiomas. -un directorio "icons" con sólo un fichero navetta.png que puedo ver incluso con Internet Explorer -un directorio "sound" con un fichero de música hotrod.mid . Muy animada. -otro directorio "pics\acc" con todos los gráficos -un fichero en META-INF\MANIFEST.MF -otro directorio "olympics" con varios ficheros *.class El fichero META-INF\MANIFEST.MF es un texto en el que se indica la versión mínima de APIs que necesita el programa. En este caso es MIDP-1.0 y CLDC-1.0 , con lo que mi móvil lo soporta prefectamente. También se indica cual es la clase que contiene la función que inicia el juego. Pero el meollo del asunto está en 2 clases: olympics\MainPRG.class y olympics\b.class Dado que las clases Java se compilan, y luego es la máquina virtual la que ejecuta el programa, es sencillo desensamblar un programa Java para obtener algo a medio camino entre el código original y un código binario. Para hacer la tarea de los "rompedores de programas" mas difícil, es común que antes de sacar el juego al público, el programa se pasa por otra aplicación llamada enrevesador (obfuscator) que simplemente hace el código mas díficil de entender. Entre las técnicas de "ofuscación" hay una consistente en sustituir los nombres de las funciones por otros consistentes en letras simples. Así, la función PintaNave() se sustituye por a() Como además Java permite que las funciones tengan el mismo nombre, con tal de que tengan distintos parametros: SumaBonus(x) se sustituye por a(x) En programas para móviles, este paso de enrevesamiento es obligado. Al reducir los nombres de las funciones y las clases se ahorran algunos bytes que no afectan al funcionamiento del programa pero hacen que ocupe menos espacio. Incluso se ejecutan más rápido. Hay otro paso necesario y es la pre-verificación de las clases. La maquina virtual Java de los móviles debe ser pequeña así que el verificador de clases es mínimo, dejando esta tarea en manos del programador, el compilador, y otra herramienta llamada pre-verificador. Pero esto no afecta a la descompilación de programas. Lo primero que hago es desensamblar los programas con la utilidad jad . Una clase de 25 Kb se convierte en un código fuente de 45 Kb con 2.500 lineas. Me pongo a buscar cuándo algo se iguala o se compara con el valor 3. En la clase MainPRG : a) funcion _mthif(int i1, int j1) ----> if(i1 == 3) b) funcion _mthnew() ----------> l = 3; c) funcion _mthnew() ----------> if((_fldtry == 5) & (i <= 3)) d) funcion a(String s1, int i1) ----> if(l1 < 3) l1 = 3; e) _mthif(int i1) -----------> if(E[i1]._fldgoto == 3) f) _mthif(int i1) --------> if(E[i1].a == 3) g) _mthfor(int i1) -------> E[i1]._fldgoto = 3; h) _mthlong(int i1) ----> if(i1 == 3) I.a(4, "/pics/acc/fondo_03a"); i) _mthnull(int i1) ----> _fldlong[i1]._fldfor += 3 + n; Voy a analizarlos uno por uno. En la rutina a) el valor de i1 se compara con 1, 2, 3, 4, y 5. Como no creo que haya una lógica distinta dependiendo del numero de vidas, supongo que ésta no es la rutina que me interesa. En b) hay muchas cosas que comento más tarde. En c) veo que tanto las comparaciones anteriores y posteriores involucran números como 220, 765 , ... En d) querría decir: si el numero de vidas ya es 3, no añadas vidas extra. Pero eso no ocurre, así que esta comparación no tiene nada que ver. En e) f) y h) creo que no tiene sentido mirar si el número de vidas es exactamente igual a 3. En g) parece mas prometedor, pero esta función se llama con argumentos 0 y 100, que no parece que tengan nada que ver. De todos modos la dejo en reserva por si acaso. En b) veo que usa la variable l. Esta variable se usa en: i) _mthnew() para hacer if(w > G) { l = l + 1; G = G + 50000; } ii) _mthgoto(int i1) para hacer if(l == 0) k=0; iii) _mthdo(int i1) para hacer l = l - 1; Por cierto, que hay otra funcion con distinto numero de argumentos: _mthdo() { if(k == 0) I.a(1, I.Q, I.B, "GAME OVER", 0); } O sea, que la variable k dice cuándo se acaba la partida. Recordar que esta variable se ha puesto en _mthgoto(int i1) Vamos, yo creo que la variable l contiene el número de vidas. En b) haré que ponga l = 4; y veré si empiezo con 4 vidas. Desensamblando con jad -a MainPRG.class obtengo k = 1; // 37 69:aload_0 // 38 70:iconst_1 // 39 71:putfield #32 l = 3; <-***********--******-- // 40 74:aload_0 // 41 75:iconst_3 <-***********--******-- // 42 76:putfield #35 d = 1; Asi que intento cambiar 41 75:iconst_3 por 41 75:iconst_4 Para ello lo desensamblo con java -d y veo que en la posición 1FB7 tengo que cambiar el byte 0x06 , que significa iconst_3 por 0x07 , que significa iconst_4 Para verificarlo, desensamblo de nuevo y veo que efectivamente queda l = 4; Ahora llega el momento de comprobarlo. Meto el MainPRG.class dentro del Megablaster.jar con WinRAR, y lo transfiero al móvil. Inicio el juego, y veo que ahora tengo 4 vidas al empezar. Bien, así que mis pesquisas eran correctas. Una vida extra es una ayuda, pero no muy grande. Podría sustituir la instrucción anterior por iconst_100 para tener 100 vidas, pero hay un problema: la instrucción iconst_4 se codifica como un único byte: 07 Pero si quiero cargar un valor mayor de 6 debo usar la instrucción iconst_NN que ocupa 3 bytes. Como no puedo hacer hueco dentro del código, lo mejor es modificar el programa para no perder ninguna vida. Existe otra rutina que usa la variable l en MainPRG.class void _mthdo(int i1) { l = l - 1; // 0 0:aload_0 // 1 1:aload_0 // 2 2:getfield #35 // 3 5:iconst_1 // 4 6:isub // 5 7:putfield #35 E[i1].a = 6; // 6 10:aload_0 // 7 11:getfield #13 // 8 14:iload_1 // 9 15:aaload // 10 16:bipush 6 // 11 18:putfield #135 // 12 21:return } Este es el único sitio donde se decrementa la variable l , seguramente cuando un misil enemigo alcanza a la nave. Así que pretendo cambiar l = l - 1; por l = l - 0; Es decir, cambiar // 3 5:iconst_1 por // 3 5:iconst_0 Vuelvo a desensamblar con java -d porque esta aplicación me dice los códigos binarios, y veo que en la posición 0x1F41 hay que cambiar el byte 0x04 que significa iconst_1 por 0x03 que significa iconst_0 Vuelvo a meterlo en el Megablaster.jar , inicio el juego, dejo que me maten, y veo con satisfacción que el número de vidas nunca decrece. Ahora ya puedo jugar sin preocuparme. Realmente el juego pierde todo el aliciente, pero mi objetivo era aprender a hacer la modificación, no el juego en sí. ************* Voy con otro caso. Otro de los juegos que vienen con el móvil se llama Turrican2004 y es uno de estos de scroll horizontal y vertical en los que un soldado va recorriendo escenarios matando extraterrestres, con plataformas. Los gráficos son muy buenos y es entretenido. No he recorrido muchos niveles porque es muy complicado para mí. Pero ya digo que soy muy mal jugador. Lo que me fastidia de este juego es que antes de empezar muestra durante 3 segundos una pantalla de la compañía que hizo el juego, y luego otra con el logotipo de Siemens. Despues se para otros 2 segundos en una pantalla con las instrucciones. Y sólo después de esto carga el juego, lo cual lleva otros 5 segundos. Mi objetivo es eliminar esas molestas pantallas de publicidad. El programa Turrican2004.jar ocupa 328 Kb. Al descomprimirlo genera 140 ficheros en Turrican\* La mayoría tienen extensión *.ptrx y *.ptr_optb , que no sé lo que contienen. En todo caso parecen ficheros binarios. Hay 70 ficheros con extensión *.png que contienen los gráficos del programa. Uno de los ficheros se llama splashSIEMENS.png que contiene la imagen de una de esas pantallas de publicidad. No ha sido difícil encontrarlo, ?eh? Mi primera tentativa, siguiendo la ley del mínimo esfuerzo, es borrar el fichero o simplemente cambiarle en nombre. Esto cambirá el tamaño del fichero empaquetado Turrican2004.jar pero en los programas para moviles el tamaño no se verifica. Bueno, se verifica si hay un fichero con el mismo nombre y extensión .jad Dado que no existe Turrican2004.jad estoy seguro que que puedo cambiar el tamaño sin mayor preocupación. Lamentablemente con este cambio lo único que obtengo es una excepción tan grande como un caballo que impide que el juego inicie correctamente. También hay 25 ficheros *.class en Turrican2004\com\thq\Turrican\ que desensamblo rápidamente con jad -a Turrican2004\com\thq\Turrican\*.class y busco cual de ellos contiene la palabra "splashSIEMENS" : Clase s.class public void b() { if(v) return; v = true; if(z) return; switch(s) { default: break; case 0: // '\0' try { v.bA = Image.createImage("/splashTHQ.png"); v.ap = Image.createImage("/splashSIEMENS.png"); } catch(Exception exception) { } break; y muchas líneas más Esto significa que en la clase v.class hay un objeto llamado ap : public static Image ap; Ojo, no confundirlo con public static int aP = -1; Como ya he comentado antes, normalmente existen otros objetos con el mismo nombre en otras clases: a.class -> int ap; d.class -> int ap; o.class -> int ap; Desensamblo v.class para ver dónde se usa y me encuentro con que ap simplemente está declarada en esta clase, pero no se usa. O sea, que únicamente carga la imagen en la memoria; no la muestra. Esto quiere decir que se usa desde otras clases. Así que busco dónde se usa v.ap Obviamente sale s.class pero también q.class Clase q.class public void a(Graphics g1, int i1) { if(v.o == 0L) v.o = System.currentTimeMillis(); if(System.currentTimeMillis() - v.o <= (long)g) goto _L2; else goto _L1 _L1: L = true; if(p.a != null) { g1.setColor(0); g1.fillRect(0, 0, 132, 176); g1.drawImage(p.bC, 66, 0, 17); g1.drawImage(p.bs, 66, 0, 17); g1.drawImage(p.a, 0, 57, 20); } else { g1.setColor(0); g1.fillRect(0, 0, 132, 176); } g1.setColor(255, 255, 255); g1.fillRoundRect(16, 160, i1, 8, 8, 8); g1.setColor(75, 75, 175); g1.drawRoundRect(16, 160, 100, 8, 8, 8); goto _L3 _L2: if(System.currentTimeMillis() - v.o <= T || System.currentTimeMillis() - v.o >= g) goto _L5; else goto _L4 _L4: .............. _L12: Thread.yield(); Thread.sleep(25L); goto _L3 _L10: if(System.currentTimeMillis()-v.o > ae && System.currentTimeMillis()-v.o < W) { g1.setColor(255, 255, 255); g1.fillRect(0, 0, 132, 176); if(v.ap != null) g1.drawImage(v.ap, 66, 88 - (v.ap.getHeight() >> 1), 17); <-******--******- } else if(System.currentTimeMillis() - v.o < (long)ae) { g1.setColor(255, 255, 255); g1.fillRect(0, 0, 132, 176); if(v.bA != null) g1.drawImage(v.bA, 66, 88 - (v.bA.getHeight() >> 1), 17); <-******--******- } goto _L3 Exception exception; exception; _L3: } Lo que se extrae de esto es que se dibuja la imagen v.ap o v.bA en función de la diferencia de tiempos entre System.currentTimeMillis() y v.o , que es el tiempo calculado inicialmente cuando se entra en esta rutina. Estos tiempos se comparan con: T (linea _L2, a mediados de línea) g (linea _L2, antes de saltar a _L5) y justo antes de L1 ae (linea _L10, a mediados de línea) W (linea _L10, al final de la línea) Veo que podría poner al entrar en la rutina: goto _L3 o también return Pero quizás esta rutina hace algo más. En particular veo que usa p.a!=null, y pone L=true así que creo que lo mejor es tomar otro camino. Las variables T, g, ae, W están usadas en esta misma clase en el constructor q() public q() { super(false); N = false; Z = true; J = false; l = false; ae = 4000; W = 8000; T = 13000; g = 18000; s = 19000; .......... Muy bien: todas las variables están juntitas. Los valores, escritos es hexadecimal, son: ae = 0x0FA0; W = 0x1F40; T = 0x32C8; g = 0x4650; s = 0x4A38; En la clase q.class busco esos bytes y los encuentro a partir de 0x16CD 0x16CD: 11 0F A0 0x16D4: 11 1F 40 0x16DB: 11 32 C8 0x16E2: 11 46 50 0x16E9: 11 4A 38 Notar que en Java el byte más significativo va delante. Lo más sencillo es cambiarlos todos a valores mas pequeños, por ejemplo 258 = 0x0102 Parece un golpe a ciegas, pero estas cosas funcionan así, Si funciona a la primera, ?para qué voy a perder el tiempo? Como antes, descompilo de nuevo la clase modificada para ver que los valores son correctos. Meto la clase en el Turrican.jar , lo transfiero al móvil, empiezo a jugar y compruebo que las pantallas de presentación ya no aparecen. Por supuesto que sigue tardando un poco porque tiene que cargar todas las clases del juego y los gráficos, pero ahora es mucho más rápido. ************* En este mismo juego hay una modalidad para jugar una campaña: se empieza por un escenario simple, y a medida que adquieres más experiencia puedes probar con otros escenarios. Pero desde el comienzo no es posible acceder a los escenarios avanzados. En el menú de selección sólo estan disponibles 4 niveles, mientras que hay otros 20 que dice que estan bloqueados-LOCKED Busco la palabra "LOCK" y la encuentro en la clase TurricanMidlet.jad : public void startApp() { if(c == null) { v.m = getAppProperty("MIDlet-Version"); v.l = System.getProperty("microedition.locale"); v.bI = getAppProperty("language_override"); System.out.println("READ IN: " + v.bI); if(v.m == null) v.m = "V?.??"; v.bK = getAppProperty("CHEAT_UNLOCK") != null; c = this; b = new q(); b.setFullScreenMode(true); f = Display.getDisplay(this); f.setCurrent(b); e = new Thread(b); e.start(); b.a(); } Esto es un típico inicio de juego. Toma algunas variables del entorno, por ejemplo para saber el idioma. Otra de las propiedades se llama "CHEAT_UNLOCK" y debería estar en uno de los ficheros dentro del Turrican2004.jar, posiblemente en META-INF\MANIFEST.MF que contiene la variable "MIDlet-Version" O quizas en Turrican2004.jad , el cual contiene otras variables tales como "language_override" Es decir, que los propios programadores han instalado una puerta trasera que parece hacer algún tipo de trampa, quizás incluso para poder acceder a los otros niveles sin necesidad de haber pasado los anteriores. ?Y porqué no puedo usar yo la misma puerta? La línea v.bK = getAppProperty("CHEAT_UNLOCK") != null; quiere decir: si la propiedad "CHEAT_UNLOCK" está definida, haz v.bK=true Si no, haz v.bK=false Para seguir con el método anterior, voy a ver dónde se usa la variable v.bK : La encuentro en q.class public void run() { n = 0; M.n(); if(v.bK) M.a(true); ..... } O sea, que llamará al metodo a del objeto M , pero sólo si v.bK=true Como yo no voy a crear la propiedad "CHEAT_UNLOCK" , la solución es cambiar if(v.bK) por if(v.bK==false) que es lo mismo que if(!v.bK) Desensamblando: if(v.bK) //* 4 10:getstatic #69 //* 5 13:ifeq 23 M.a(true); // 6 16:getstatic #18 // 7 19:iconst_1 // 8 20:invokevirtual #70 la instrucción 5 13:ifeq (significa "si es igual") la voy a sustituir por 5 13:ifne (significa "si NO es igual") o sea, en la posición 0x17E0 pongo 0x9A en vez de 0x99 Transfiero el juego, accedo al menú, y tengo todos los escenarios liberados ! Claro que los más avanzados son demasiado difíciles para mí. Al menos he cumplido el objetivo de modificarlo, que era lo que quería probar. ************* Otro juego que tengo es AdamsAppleV3 hecho por la compañia SoftexDigital. Es un juego en el que hay que mover plataformas hasta encontrar la salida. Incluye 5 niveles pero hay que pagar para acceder a los niveles superiores. El pago se realiza accediendo a una página web desde el móvil. Bueno, es el propio teléfono el que se conecta así que no hay que hacer mucho. Con mi operador no es posible realizar estos pagos, así que sale un mensaje al principio indicando que sólo puedo jugar al modo básico: 'Teaser' Level. Por supuesto que como mi red no lo soporta, no puedo bajarme nuevos niveles. Pero me gustaría probar los 5 escenarios ya incluídos, no sólo el primero. Al menos, me gustaría eliminar ese mensaje. El fichero AdamsAppleV3.jar ocupa 62Kb y al descomprimirlo genera, entre otros, 6 ficheros *.class en el directorio raiz. El mensaje que se queja de que el operador no soporta el pago está en el fichero AdamsAppleV3.class public void startApp() { try { if(q != null) { if(p) { n = true; i = q; a("Payment", "Payment not supported by the operator. You can Play only 'Teaser' Level ", (byte)3); p = false; } else { a.setCurrent(q); } q.m = true; } } Así que comprueba si p tiene un valor o no. Si tiene algún valor, se queja. O sea, que habría que hacer que p sea false. Pero no encuentro la definición de la variable p en esta clase. En la cabecera veo que public class AdamsAppleV3 extends b Ah, esto quiere decir que puede estar en b.class Efectivamente public b() { ....... try { D.checkOperator(); } catch(SecurityException securityexception) { p = true; } } Muy bien: si hay algún fallo de permisos, entonces p se pone a true. Lo más sencillo sería saber cuales permisos son los que faltan. Si los programadores hubieran hecho un simple System.out.println("Error=" + exception); entonces yo sería capaz de saber si el problema es que la red no está disponible, o que no tengo correcto el perfil de WAP, o que ha expirado mi sesión. Lamentablemente no puedo modificar el programa lo suficiente como para incluir un simple mensaje explicatorio. Pero es fácil engañar al programa para que crea que puedo realizar pagos: Tengo que cambiar p = true; por p = false; Desensamblando: { D.checkOperator(); // 242 548:aload_0 // 243 549:getfield #10 // 244 552:invokevirtual #70 } //* 245 555:goto 564 catch(SecurityException securityexception) //* 246 558:astore_1 { p = true; // 247 559:aload_0 // 248 560:iconst_1 <-***********--******-- // 249 561:putfield #72 } esto es, cambiar 248 560:iconst_1 por 248 560:iconst_0 o sea, en la posición 0x1510 pongo 0x03 en vez de 0x04 Esta es una modificación que no he podido comprobar en el emulador, ya que éste es siempre incapaz de comunicar con la red. La excepción no es "SecurityException" sino "NotImplemented" , la cual no es interceptada por el comando catch. Pero en el propio móvil funciona perfectamente y ya no se queja. ************* En este mismo juego hay otra modificación que me gustaría hacer: el juego consiste en mover bloques hasta que la protagonista (Eva) llega a la salida y se encuentra con Adán. No es típicamente un laberinto, sino que se trata más bien de mover los bloques sabiamente. Como no sé la manera de colocarlos para conseguir el objetivo, decido que es mejor manipular el programa para que no aparezcan bloques. El mapa es de 14 columnas y 10 filas, según veo en la pantalla del móvil. Dado que no hay ficheros binarios que contengan los mapas, supongo que están dentro de las propias clases. Tras mirarlas todas, descubro que en d.class hay una estructura: public static final byte h[][][] = { { {10,10,10,10,10,10,10,10,10,10,10,10,10,10 }, { 2, 0, 0, 0, 4, 4, 1, 1, 0, 0, 0, 0, 0, 0 }, { 1, 1, 3, 3, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0 }, { 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 0 }, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } } repetida de modo parecido 5 veces (los cuales podrían ser los niveles) Nota: los valores están en decimal. Bueno; no tiene 10 filas sino 8. Quizás la fila de abajo y la de arriba son siempre iguales. En la pantalla del juego hay 2 trofeos (corazones) en la segunda línea. Comparándolo con esta matriz, deduzco que se corresponden con el valor 4. Análogamente el valor 5, que sólo aparece una vez, debe de ser un pequeño árbol. Y el valor 6, que está en la fila 7, columna 13, debe de ser el dibujo de Adán que está al otro lado del muro (valor 1) El valor 2 que aparece en la fila 2, columna 1 debe de ser el dibujo de Eva. Para hacer que el juego sea realmente sencillo de terminar, simplemente destruyo el muro que me impide el paso en la fila 7, columna 12, justo antes del dato '6'. Al llegar la hora de modificar el fichero d.class me llevo la sorpresa de que los valores no están así seguidos: 0x0A, 0x0A, 0x0A, 0x0A ... como correspondería a una secuencia de bytes, sino que cada uno ocupa 5 bytes: 0x59 0x03 0x10 0X0A 0x54 0x59 0x04 0x10 0X0A 0x54 0x59 0x05 0x10 0X0A 0x54 0x59 0x06 0x10 0X0A 0x54 Por eso supongo que Java guarda los elementos como "objetos byte", y a cada instancia le asigna un número único. Una pérdida de espacio, desde mi humilde opinión. No sólo eso, sino que algunos números ocupan menos bytes. El descompilador hace todo el trabajo de organizarlos y yo no tengo que preocuparme. Sólo anotar cuidadosamente cuáles bytes deseo cambiar. Para cambiarlo, sustituir en la posición 0x43A7 el valor 0x04, que significa "muro" , por 0x07 , que significa "corazón" Como siempre: modifico la clase, la meto en AdamsAppleV3.jar , lo transfiero al móvil, y lo pruebo. Ahora el primer nivel es un simple paseo. **************** Bueno, como ha quedado comprobado, modificar los programas Java para móviles es posible con un poco de tiempo y conocimientos simples de aprender. Si es legal, moral, o engorda, no es tarea mía juzgarlo. *EOF*