29 enero 2014

Ingeniería inversa del juego Pixel Dungeon para Android

He desempolvado uno de mis juegos favoritos de Android, el rogue like Pixel Dungeon.



La propuesta del juego es sencilla: elige una clase de personaje y profundiza a lo largo de múltiples niveles de mazmorras enfrentándote a trampas, enemigos y jefes de mazmorra cada vez más complicados mientras que por el camino recolectas armas, armaduras, pociones, pergaminos y todo tipo de objetos que intentarán servirte en tu ayuda, pero que sólo dilatarán una muerte que llegará tarde o temprano (más temprano que tarde, por cierto).
Y después de esta introducción vamos a lo que nos ocupa, una de sus últimas actualizaciones, la clase cazadora:


Como podéis leer en la descripción del personaje, el juego nos reta indicándonos que únicamente podremos jugar con esta clase una vez que hayamos superado al jefe de mazmorra del nivel 3, jefe al que no he tenido el gusto de conocer porque lo normal es una muerte prematura varios niveles antes de llegar a él..., así que intentamos resolver este problema con otro acercamiento: la ingeniería inversa.

Notas de partida

  • Si no habéis jugado previamente, además de descargarlo es necesario que juguéis y desbloqueéis algún logro para que se genere un fichero que necesitaremos. Los logros más sencillos se obtienen al matar 10 enemigos o recolectar 100 monedas. Se puede confirmar si has desbloqueado logros desde la opción "Badges" del menú principal.
  • El dispositivo Android tiene que estar rooteado ya que accederemos a ubicaciones del sistema de ficheros a los que de otro modo no tendremos acceso
  • El dispositivo Android tiene que encontrarse en modo depuración y con su driver correctamente configurado para que ADB sea capaz de identificarlo:


Paso a paso

1. Cerrar el proceso del juego en el dispositivo Android en caso de que se encuentre en ejecución.

2. Lo primero que tendremos que hacer es obtener el nombre del APK del juego, utilizaremos ADB para esto:


3. Obtenemos la ruta de instalación del APK utilizando el paquete que nos ha indicado la salida el anterior comando:


4. Descargamos el APK desde la ruta que hemos obtenido:


5. El siguiente paso es convertir el fichero APK en JAR, para ello utilizaremos la herramienta dex2jar:


6. En el JAR tenemos los ficheros .class, para decompilarlos podemos utilizar la herramienta gráfica jd-gui. Es suficiente con abrirla y arrastrar sobre ella el fichero .JAR:



7. Ahora que tenemos el código en nuestro poder vamos a buscar parte de la cadena de texto que nos muestra el juego y en la que nos indica que la cazadora está bloqueada, para ello hacemos lo siguiente: Menú Search > Search > Seleccionamos todas las opciones y escribimos "unlock":



8. El resultado de esta búsqueda nos muestra la clase StartScene, donde revisando las variables encontramos "TXT_UNLOCK", donde está la cadena que leemos en el juego;  y "private boolean huntressUnlocked", que suena bastante prometedor ¿verdad?:



9. Al ser "huntressUnlocked" una variable privada, buscamos sus asignaciones dentro de la clase y encontramos el siguiente código en el metodo create():


10. Como vemos, se invoca el método isUnlocked(...) de la clase Badge. Si le damos un vistazo a la clase  Badge encontramos en una de las variables el fichero de logros:



11. Descargamos el fichero de logros de la ubicación por defecto para los ficheros creados por una aplicación Android ( /data/data/<app>/files/ ), en nuestro caso "/data/data/com.watabou.pixeldungeon/files/":


12. ¡Perfecto!, abrimos el fichero en un bloc de notas y nos encontramos el siguiente formato:


13. Como vimos en el paso 9, se realizaba una comprobación de si se disponía del logro "Badges.Badge.BOSS_SLAIN_3", asi que volvemos a jd-gui y buscamos en la clase Badges el enumerado "public static enum Badge":


14. En el recuadro en rojo tenemos el logro que espera el juego para activar el booleano de "huntressUnlocked", así que añadimos el valor a la colección de logros del fichero "badges.dat" que hemos descargado y lo guardamos:


15. Hacemos push del fichero modificado al sistema de ficheros del dispositivo:


16. Iniciamos el juego y nos vamos al menú de personajes y... ¡habremos desbloqueado a la cazadora!


Happy hunting ;)


Artículo cortesía de Miguel Ángel García

11 comments :

Miguel Ángel García dijo...

Muchas gracias!


Si le vas a dar un vistazo te hago sólo un apunte adicional que no comento en el análisis por no destripar demasiado el juego: en el mismo directorio donde tenéis el "badges.dat" se crean otros ficheros en base a la clase con la que tienes partidas en curso: "warrior.dat", "huntress.dat"..., verás que cruzando su información y la de algunos packages del JAR se pueden hacer cosas muy curiosas :P

Miguel Ángel García dijo...

Me alegro de que haya quedado bien explicado aunque todo hay que decirlo, gran parte de la facilidad de este análisis se debe a que el desarrollador del juego ha hecho un muy buen trabajo con el código (está bien estructurado, con un buen nombrado de variables y una programación muy limpia) y a la vez ha cometido el error de no ofuscarlo con una herramienta como ProGuard.

masticover dijo...

Exacto, eso quería comprobar, "jugar" con el juego de otra forma a la habitual XD
Gracias por el apunte. Saludos :)

Diego Romero dijo...

Si sin duda es algo a tener en cuenta. Pero como él deben haber más que no ofusquen, eso da para divertirse por un buen tiempo :)

snake dijo...

Genial el artículo!
Una duda, para los que no tenemos root, hay alguna forma de hacer override del paquete en /data/data//files/ copiándolo a /sdcard/Android/data//files o algo por el estilo para poder hacer modificaciones sobre él?


Gracias!

dexafree dijo...

En principio no hay forma de poder realizar modificaciones sobre el directorio /data/data/nombredelpaquete/, ya que esos directorios se consideran "privados" de la aplicación, por lo cual una aplicación que no sea la propia, u otra con permisos de root no será capaz de realizar ninguna acción.

Anonimo dijo...

Tio, te sigo desde hace mucho, incluso te he visto muchas veces en la televisión y vídeos que busco. Me ha encantado esta publicación, ojalá hubiera un par de ellas cada semana, cosas así para ir abriendo aún más nuestra mente y aprendiendo cosas nuevas que den pie a otras muchas.


Me encantaría, y supongo que a todos, poder leer cste tipo de publicaciones más a menudo. Genial, de verdad. Gracias.

Alejandro Ramos dijo...

Hola anónimo, no se a cual de los editores del blog te refieres, pero somos varios los que publicamos en el blog. En este caso en concreto, el autor es Miguel Angel Garcia como contribución.


Eso si, estoy completamente de acuerdo contigo en que el artículo es muy bueno y esperamos poder leerle mucho más :-)


Un saludo.

Alguienzo Algonza dijo...

Disculpen mi ignorancia pero no me quedo muy claro el paso 6, en esta parte: Es suficiente con abrirla y arrastrar sobre ella el fichero .JAR:

Donde esta el fichero JAR para arrastrarlo a jd-gui?

Alguienzo Algonza dijo...

ya aprendí para que sirven y como usar los coman-dos xD
en el caso de adb pull solo me faltaba indicarle la ruta de destino o en su ausencia saber que los archivos se guardan en C:/android-tools/ (en mi caso).

Gracias por la guía! me ayudo demasiado a entender varias cosas para experimentar y hacer cambios en las APPs :D.

Zer0 dijo...

Eres un manco tt, me pasaron este enlace porke no tienen webos a decirtelo, pero nosotros conseguimos el amuleto de yendor sin problemas...