Share on Google+Share on FacebookTweet about this on Twitter

Como en alguna ocasión he dicho, prácticamente todos los países del mundo es completamente legal la ingeniería inversa de un programa. ¿En que consiste? Bueno, los programadores escriben su código, este luego se compila y enlaza y esto origina el programa resultante, en conjunto por supuesto del resto de los recursos que use. El resultado es un archivo o una serie de archivos que la plataforma para el cual fue compilado pueda ejecutarlo. La ingeniería inversa es simplemente intentar darle la vuelta a este esquema, que a partir de un programa compilado se pueda obtener un código que manipular, es decir, decompilar.

Cuando se habla de parquear todos ya piensan en piratear o saltarse medidas de seguridad pero esto no es para nada una norma. Es más, dentro de Android se usa más estos procesos (al menos parte de ellos) para modificar el aspecto visual de las aplicaciones, los iconos de estas, los colores, música, textos… lo que podríamos llamar los “recursos” de esta. Para hacer este tipo de cosas es necesario poder acceder a los recursos de la aplicación, editarlos y dejarlo todo tal y como estaba… o al menos dejarlo de forma que podamos usarlo después. Pero las dotes de Photoshop prefiero dejarlas a manos de buenos amigos que son realmente unos genios en el diseño gráfico (envidia sana desde luego), y yo prefiero encargarme de los bajos fondos. Por cierto, un saludo para ellos desde aquí, tanto amigos como desconocidos. En este caso por tanto no será cambiar una skin o cambiar el aspecto de una aplicación, sino que sí… en este caso si vamos a ver como saltarnos una restricción que nos imponen.

El material que vamos a usar será el siguiente:

  • Windows 7 (Se puede usa otro OS)
  • El SDK de Android instalado, conjuntamente con el JDK (SDK) de Java
  • Un editor Hexadecimal
  • Un dispositivo Android con acceso a la carpeta /System, ya sea porque está rooteado o por medio de un recovery y usar ADB pull
  • La aplicación APKtool para decompilar/compilar (parte 1, parte 2)

Bien, dado que hablamos de Android hay que conocer un poco cual es el modelo de programación que existe, y a la par que es potente es simple. Ya he dicho que Android usa un sistema de programación JAVA (que no significa que el código generado para Android pueda ejecutarse por una VM JAVA). Básicamente, todo el código compilado para Android se empaqueta en un archivo apk (que no es más que un archivo zip) que contiene por un lado los recursos de la aplicación (imágenes, sonidos, archivos binarios…), por otro lado lo que podría verse como la interfaz de dicha aplicación, la firma y certificado de la aplicación y por último las librerías y el código principal de la misma, generalmente en formato DEX. DEX es por así decirlo el código compilado puro y duro de Android, la compilación para la máquina virtual Dalvik, digamos por así decirlo que es el “ejecutable” de las aplicaciones. Las firmwares de algunos fabricantes usan un sistema híbrido que permiten a las aplicaciones por así decirlo compartir código y otras cuestiones y se las conoce como ODEX. En la práctica lo que supone es que cada aplicación apk posee externa a ella otro archivo que la acompaña en formato ODEX, que contendrá tanto el propio código que contenía el archivo DEX como el código necesario para enlazar a esta el código que no se encuentra ni en el apk ni en el propio archivo ODEX, por ejemplo código compartido por muchas aplicaciones y que se reutiliza (lo que serían algo así como librerías dinámicas).

Es importante conocer si estamos ante el escenario DEX o el escenario ODEX, puesto que es diferente. Si estamos ante una aplicación DEX, tan solo tendríamos que realizar una decompilación, modificación, compilación firma y alineamiento. Pero en el caso de una aplicación ODEX antes de decompilar tendríamos que asegurarnos que podemos suministrar al decompilador la ubicación de esas “librerías externas” que requiere dicha aplicación, para que así al modificarlo y volver a compilarlo tengamos como resultado una sola aplicación DEX completamente independiente de otras librerías (por eso es necesario tener las librerías de “apoyo” de esta aplicación, código que será incluido en el propio DEX cuando compilemos de nuevo, y así poder prescindir del archivo ODEX e incluir el archivo DEX dentro de la aplicació). Al final de la conversión básicamente lo que tendremos será una aplicación DEX.

Vale, ya sabemos como se estructura una aplicación, y parece evidente los procesos de decompilar y volver a compilar (luego vemos como). Pero hay dos procesos que hay que realizar de igual forma, el firmado y el alineamiento. El firmado es obligatorio el alineamiento opcional (aunque recomendable).Cada uno será explicado a la par que vamos a usarlos. Empecemos, para no aburrir, con lo que tenemos entre manos.

El mini proyecto a realizar será parchear la aplicación gratuita del Market “SSHDroid”. Esta aplicación no es más que un servidor SSH para nuestro dispositivo, para poder tener acceso por SSH (Putty) o SCP (WinSCP) para poder copiar, borrar, mover… archivos de forma fácil. Es compatible (no de obligado uso) con teléfonos rooteados, lo que permite entonces un acceso total al sistema por los medios mencionados. Pues bien, esta aplicación se encuentra en dos versiones, la versión gratuita con publicidad y la versión de pago sin ella. Pues bien, iba a instalar la aplicación en el Market cuando leo en la descripción que la aplicación automáticamente no se arrancará si detecta que se ha usado algún sistema para bloquear la publicidad. Aquello la verdad es que me mosqueó, no porque me parezca bien o mal el uso de la publicidad, sino porque de igual forma ninguna aplicación tiene que decirme que tengo o no tengo que hacer. Así que después de que efectivamente comprobase que la aplicación se cerraba nada más abrirla me puse manos a la obra.

 

Al ser una aplicación del Store, es DEX, con lo cual es mucho más sencillo todo. Pero bueno, lo primero es evidentemente extraer la aplicación del dispositivo. Si este está rooteado, tan solo hay que acceder a la carpeta /data/app/ y copiar desde ahí el archivo “berserker.android.apps.sshdroid-1.apk” a la SD y de la SD al PC. O hacerlo si se prefiere por SSH o por ADB. El como extraerlo es indiferente, y pegarla en cualquier sitio que podamos manejarla de forma simple. Yo por comodidad tengo todas las utilidades que voy a usar en el Path de Windows, con lo que puedo invocarlas desde cualquier ubicación, lo que me permite no tener que estar usando las rutas completas mientras trabajo de forma sencilla desde mi directorio de trabajo.

Lo primero es suponer que sistema usa la aplicación para cerrarse, lo cual no es que sea muy complicado adivinarlo. Primero porque el sistema de bloquear la publicidad que uso es puramente un archivo Hosts, lo cual ya me dice que realiza la aplicación para detectarlo. Segundo porque esta aplicación y posiblemente muchas otras usan la plataforma de anuncios admob, la cual ya conozco bien por iPhone OS, y no es la primera ni será la última que parchee  (aunque el sistema para iPhone OS difiere al de Android). Básicamente la aplicación verifica si en el archivo hosts existe alguna entrada “admob”. Si existe la aplicación se cierra. Pues bien. hay una y mil manera para sortear esto, desde la más típica que consiste en continuar la ejecución exista lo que exista en el archivo hosts (sería cambiar básicamente un True por un false o un 1 por un cero) hasta la que voy a usar en esta ocasión, que se basa simplemente en engañar a la aplicación Básicamente con este sistema la aplicación si realiza una comprobación… pero no verificará el archivo /etc/hosts, sino un archivo que no existe, por ejemplo /etc/hostt (cambiando la s última por una t)

Si la suposición es correcta, en algún lugar del código de la aplicación quedará tipificado perfectamente el archivo “/etc/hosts”. Este sistema tiene la ventaja en que localizar el punto exacto es simple, solo tengo que buscar en el código por “/etc/hosts”. Claro que si el código está compilado, dicha cadena de texto no tiene porqué siquiera aparecer. Lo  interesante de este sistema es que tanto en iPhone OS como en Android esta cadena es visible sin necesidad siquiera de decompilar!! En el caso de iPhone es tan simple como editar hexadecimalmente el binario de la aplicación, buscar dicha cadena, modificarla y revalidad la firma. Además, dado que tan solo sustituyo un carácter por otro no corro el riesgo de tener que aumentar o disminuir el tamaño del archivo. Otra ventaja es que ni iPhone OS ni Android poseen sistema de verificación de hash dentro de los propios binarios, lo cual significa que puedo sin temor alguno cambiar una ‘s’ por una ‘t’ y quedarme tan pancho. Eso sí!! al realizar esto la firma en cualquiera de los dos casos se destruye!! No poseen verificación dentro del propio archivo (lo cual sería un “problema” porque habría que reconstruir el hash afectado) pero os recuerdo que todas las aplicaciones tienen que tener una firma válida, y como vimos en el artículo de firma digital cuando se modifica algo que esté bajo una firma digital, esta se destruye y se invalida, lo cual nos obliga a realizar una nueva firma. En Android podemos hacerlo porque Android nos permite firmar nosotros mismos lo que queramos, con iPhone OS podemso hacerlo porque el JB valida también las firmas que realizamos nosotros, no solo las de Apple.

Volviendo al asunto en cuestión, tendríamos dos opciones. La opción rápida sería descomprimir el contenido del apk, eliminar la firma anterior eliminando la carpeta “META-INF”, editar hexadecimalmente el archivo “classes.dex”, buscar la cadena citada y modificarla, guardar el archivo y volver a empaquetarlo en el apk (comprimirlo). Pero esto es posible por el truco explicado de la cadena de texto, que evidentemente es un caso excepcional y generalmente no puede hacerse así. Así que vamos a detallar el proceso ligeramente más largo (tampoco tiene mayor complejidad) q pasa por decompilar antes la aplicación.

Una vez tengamos la aplicación a mano, usaremos APKTool para decompilar toda la aplicación. APKTool decompilará el código para Dalvik y nos lo mostrará en digamos lo que podría ser el ensamblador de este (evidentemente no nos devuelve el código fuente .java):

E:\Android\Apps>apktool d berserker.android.apps.sshdroid-1.apk carpeta-decompilada
I: Baksmaling…
I: Loading resource table…
I: Decoding resources…
I: Loading resource table from file: C:\Users\Theliel\apktool\framework\1.apk
I: Copying assets and libs…

Como sabemos que tenemos que buscar exactamente, ni siquiera vamos a perder el tiempo cotilleando cada archivo decompilado. Para ello tenemos al buen amigo “grep”:

#grep -r hosts ./carpeta-decompilada

Lo cual nos devuelve casi de forma automática que el archivo implicado es “./carpeta-decompilada/smali/berserker/android/corelib/e.smali”. Por tanto podemos automáticamente ir a dicha ruta, abrir el archivo con un editor de texto cualquiera, buscar “hosts” y encontraremos rápidamente lo que buscábamos, la cadena “/etc/hosts”. Tan solo tendríamos que modificarla a “/etc/hostt” por ejemplo, y guardar el archivo. A partir de aquí es muy simple, ya que la misma aplicación apktool va a hacer todo el trabajo por nosotros:

E:\Android\Apps>apktool b carpeta-decompilada apk_sinfirmar_sinalineal.apk
I: Checking whether sources has changed…
I: Checking whether resources has changed…
I: Building apk file…

Llegado a este momento tendremos la aplicación correctamente compilada en un archivo apk completamente funcional, el problema es que antes de que podamos usarlo tendremos que firmarlo previamente y opcionalmente firmarlo.

Si recordamos, el iPhone OS tan solo puede ejecutar aplicaciones o contenido que ha sido firmado expresamente por Apple y por nadie más. Esto evidentemente impedía que cualquier usuario pudiese ejecutar alguna aplicación casera si así lo desease, y de ahí (entre otras muchas razones) el motivo por el cual se realiza el JB al iPhone OS. Pero Google no es Apple, afortunadamente. Google sí permite la ejecución de cualquier aplicación aunque no haya sido firmada por ellos. Es más, la única firma que se verifica es la del autor de la aplicación, la cual si es obligatoria claro. ¿Por qué? Bueno, básicamente es la garantía de que dicha aplicación no ha sido alterada por ningún intermediario, a la vez que la legitimiza. Pero cualquiera puede crear una aplicación para Android, compilarlar, firmarla e instalarla en su dispositivo!! y para ello no hace falta absolutamente nada, ni que el dispositivo esté rooteado ni nada.

Para firmarlo usaremos las herramientas estándares de JAVA: keytool para la generación del almacén de claves y nuestras claves y jarsigner para firmar la aplicación. Vamos a suponer que el usuario no tiene aun claves para firmar la aplicación:

E:\Android\Apps>keytool -genkey -v -keystore almacen-android -alias Theliel -keyalg RSA -keysize 2048 -validity 3650

Dicho comando nos creará en la carpeta actual un almacen de claves llamado almacen-android, el alias de dicha clave en dicho almacen, el tipo de key (RSA-2048) y la validad de la clave (10 años). Tendremos que introducir una serie de parámetros, como nuestro nombre, contraseña para el almacén y para nuestra clave… etc. El caso es que cuando terminemos tendremos lista el par de claves necesarias. Ya solo queda firmar la aplicación:

E:\Android\Apps>jarsigner -verbose -sigalg MD5withRSA -digestalg SHA1 -keystore ./almacen-android ./apk_sinfirmar_sinalineal.apk Theliel
Enter Passphrase for keystore:
Enter key password for Theliel:
adding: META-INF/MANIFEST.MF
adding: META-INF/THELIEL.SF
adding: META-INF/THELIEL.RSA
signing: assets/dropbear/dropbear

Una vez terminado el proceso, podemos renombrar dicho archivo a apk_sinalineal.apk, puesto que en ese momento ya estaría firmado.

 

Por otro lado el alineamiento se realiza por una cuestión de mejora de rendimiento. Por diseño, Dalvik es super eficiente a la hora de gestionar el consumo de recursos, intentando en la medida de lo posible usar la mínima RAM posible (entre otras cosas). El alineamiento lo que realiza es asegurarse de que todo el contenido (que no esté comprimido) dentro del APK comience en un offset (un desplazamiento) concreto, particularmente se desea que el comienzo de cada archivo en el APK comience en un bloque de 4 Bytes (32 bits). El alineamiento es indiferente al contenido comprimido dado que antes de poder acceder a este debe de ser extraído del paquete apk, pero el contenido en el apk no comprimido puede ser accedido directamente sin necesidad de extraerse siquiera. Se podría pensar que todo el contenido dentro del apk (que básicamente es un archivo zip… por así decirlo) estará comprimido, pero la mayoría de compresores ignoran ciertos archivos cuando estos son claramente no compresibles (o mínimamente) como por ejemplo imágenes, audio, video… Esto dicho así es complicado de entender, pero es mucho más fácil si se muestra con un ejemplo.

Las dos siguientes imágenes muestran la ubicación interna de la imagen key.png dentro del apk abierto por un editor hexadecimal, el mismo apk, uno antes de alinearse y el otro alineado. Como se puede apreciar, aunque el contenido es exactamente igual, uno parece estar desplazado 6 Bytes hacia la derecha:

APK No Alineado

 

APK Alineado

Una imagen PNG comienza exactamente con los bytes hexadecimales “89 50 4E 47”. Si vemos las imágenes anteriores, en el APK no alineado la imagen comienza en el offset 2E1E6. mientras que el alineado en el offset 2E1EC. Básicamente todo lo que comenté antes, es que para que el APK esté alineado, todo los contenidos que no estén comprimidos dentro del APK (como esta imagen) deben de poseer un offset múltiplo de 4 bytes), es decir, el último valor hexadecimal de su offset global deberá ser 0, 4 8 o C. El primero efectivamente comienza en el offset relativo 6, que no pertenece a un inicio de bloque de 4 Bytes y por tanto no está alineado. Una vez que se alinea el APK, este pasa a estar en un offset relativo C, el cual es el comienzo de un bloque de 4 Bytes y por ende está alineado.

Esta alineación permite al sistema Android acceder directamente a la posición en la que se encuentran dichos datos, minimizando el uso de RAM (de lo contrario, posiblemente haría falta extraer el APK completo, no solo los archivos comprimidos, sino que para acceder a los archivos descomprimidos habría que extraerlos también del APK, mientras que de este modo los elementos no comprimidos pueden ser accedidos directamente desde el APK).

Por algún sitio en la web he leído que la alineación hay que hacerla antes de la firma. No!! La firma va dentro del propio APK, con lo que si se firma después de alinear, la firma destruirá el alineamiento, haciendo la aplicación menos eficiente. No significa que Android no funcione con aplicaciones no alineadas, ojo, significa que usará sensiblemente más RAM. La pregunta que todos se harán en este momento es si realmente las aplicaciones que descargamos del Market o no están correctamente alineadas… o no. Invito a cada cual que investigue, pero de ya le digo que muchas están sin alinear.

¿Como se alinea? Con la aplicación zipalign, especificando el offset relativo que se desea dar a la alineación. En este caso deseamos bloques de 4 Bytes, por tanto el valor a especificar será de 4. Tenemos dos comandos igualmente útiles, uno para alinear y otro para comprobar el alineamiento (como he dicho útil para ver como de despistados son los programadores, y eso que cuando se configura bien el IDE para programar en Android el proceso debería de ser completamente transparente para el programador… curioso como las malas prácticas en la programación se extienden aun cuando es algo simple de realizar y que nos otorga un buen pellizco de RAM extra)_

E:\Android\App>zipalign -v 4  apk_sinalineal.apk apk_alineada.apk
Verifying alignment of apk_sinalineal.apk (4)…
50 META-INF/MANIFEST.MF (OK – compressed)
906 META-INF/THELIEL.SF (OK – compressed)
1819 META-INF/THELIEL.RSA (OK – compressed)

Si quisiésemos simplemente verificar el alineamiento lo realizaríamos con la linea “zipalign -c -v 4 apk_paraverificar.apk. Los archivos comprimidos se mostrarán como OK – Compressed, los archivos alineados como OK y los archivos no alineados como (BAD – X) siendo X el número de desplazamientos necesarios para su alineamiento… es decir, que no se ha alineado la aplicación.

A este paso ya podemos renombrar nuestra aplicación modificada a su nombre de origen por ejemplo: berserker.android.apps.sshdroid-1.apk

Si queremos estar completamente seguro de nuestro éxito, lo mejor será primero desinstalar primero la aplicación que ya tenemos en nuestro dispositivo. Una vez realizado, podemos proceder a instalar nuestra aplicación modificada con ADB: “adb install berserker.android.apps.sshdroid-1.apk”, y ya solo nos quedará verificar si la aplicación se abre o no se abre:

 

Como ya he dicho, ni que decir tiene que esto no es sino un ejemplo de como se podría realizar ingeniería inversa a cualquier aplicación que deseemos. Recordar, que esto es una práctica completamente legal (al menos aquí en España), y que esto nos abre la oportunidad no solo de realizar como hemos visto aqui un pequeño “parche” para poder abrir la aplicación sin tener que renunciar a nuestros anti-publicidad, sino que imaginar que hubiésemos aprovechado para cambiar el color de la interfaz, o el icono de la aplicación, o los textos de esta o… cuestiones que bien conocen los que les gusta el modding,que no el parching. Por ejemplo, mi Market no es verde, es Lavanda

Theliel.