Apktool, Smali, Ofuscación y Proguard


Cualquier código es susceptible de ser decompilado (léase hackeado) con los recursos adecuados. En el caso de Java y de Android en particular, los recursos son herramientas como ApkTool, capaces de destripar tu aplicación en cosa de segundos.


Un par de conceptos básicos antes de comenzar:
  • Las aplicaciones Android se empaquetan en archivos "apk", que al igual que los archivos "jar" no son más que archivos "zip" sin encriptación, por lo que simplemente cambiándoles la extensión los podéis descomprimir y extraer todos sus recursos (fotos, imágenes, audio, video...).
  • La seguridad de las aplicaciones en Android, se basa en el acceso restringido a ellas en las versiones de Android oficiales que se distribuyen con los teléfonos. Pero si "rooteáis" vuestro Android o le instaláis una ROM que os permita el acceso en modo administrador (prácticamente la totalidad de las ROMs que se distribuyen por canales no oficiales de Android), ya no tenéis ese inconveniente y podéis extraer todos los "apk" de vuestras aplicaciones fácilmente.
Un ejemplo práctico (supongamos Windows XP):

  1. Necesitamos una aplicación, a ser posible una vuestra que conozcáis el código o cualquiera de la de los tests o que encontréis por Internet de ejemplo. La compiláis en Eclipse.
  2. Necesitamos estos dos archivos de ApkTools: apktool1.4.3.tar.bz2apktool-install-windows-r04-brut1.tar.bz2, si no están estos, estarán otros más actualizados en la web ApkTools Downloads.
  3. Si no utilizáis Windows sabréis descomprimirlos sin problemas, en caso contrario, podéis utilizar alguna herramienta como 7-zip.Descomprimirlos en la carpeta donde tengáis el SDK de Android instalado, en la subcarpeta donde tengáis el archivo "adb.exe" (Debe de ser algo así: "C:\Archivos de programa\Android\android-sdk\platform-tools").
  4. Lo siguiente es abrir un terminal (Inicio->Ejecutar: cmd) y navegar hasta la carpeta del punto 3. Se podría hacer de una forma más elegante con variables PATH, pero hemos supuesto Windows.
  5. Lo siguiente que necesitáis es un archivo ".apk" que podéis obtener desde vuestro proyecto de eclipse. Si no sabéis donde se guarda, en eclipse, bajo la carpeta bin de vuestro proyecto debería de aparecer (Y si hacéis clic derecho-> Propiedades aparecerá un diálogo con la ruta donde se encuentra). Copiáis ese archivo en la carpeta del punto 3, junto al resto de herramientas. Deberíais de tener los archivos: "apktool.jar", "apktool.bat", "adb.exe" y vuestro Apk en esa carpeta.
  6. Ejecutamos el siguiente comando en la consola (sustituid "MiAplicacion" por el nombre de vuestro Apk):

    apktool d MiAplicacion.apk

    La consola debería mostrar como salida algo parecido a:

    I: Baksmaling...
    I: Loading resource table...
    I: Loaded.
    I: Loading resource table from file: C\:Documents and Settings\...\apktool\framework\1.apk
    I: Loaded
    I: Decoding file-resources...
    I: Decoding values*/* XMLs...
    I: Done.
    I: Copying assets and libs...
  7. Si todo ha ido bien tendréis una nueva carpeta con el nombre de vuestra aplicación. Esta carpeta contendrá otra carpeta "res" con todos vuestros recursos (gráficos, imágenes, iconos, audio, ...). Vuestro AndroidManifest.xml perfectamente legible y otra carpeta llamada "smali". Esta última carpeta contiene todo vuestro código, sólo que en lugar de archivos Java, está escrito en un lenguaje llamado smali. Hay herramientas que lo traducen a Java, pero para hacer unos cambios y recompilar la aplicación con un par de hacks, no supone ningún problema. Es una mezcla entre la sintaxis de JNI y de lenguaje script y un HelloWorld sería algo así:

    .super Ljava/lang/Object;
    
    .method public static main([Ljava/lang/String;)V
        .registers 2
    
        sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
    
        const-string v1, "Hello World!"
    
        invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
    
        return-void
    .end method
  8. Ahora necesitamos un teléfono Android con permisos de administrador y unas cuantas aplicaciones instaladas. Creamos una carpeta llamada "apps" y  ejecutamos el siguiente comando en el terminal:

    adb pull /system/app apps/
  9. Automáticamente se descargarán todos los Apks, de todas las aplicaciones instaladas en vuestro Android, incluidas las que venían de fábrica y las de Google. Como se ha explicado en los anteriores puntos, todas las aplicaciones son archivos comprimidos, que fácilmente podéis decompilar usando herramientas como Apktool (también podéis cambiar la extensión de los archivos por ".zip" y descomprimiendo tendréis acceso directo a los recursos). Si lo podéis hacer vosotros con las aplicaciones de otros, otros podrán hacerlo con vuestras aplicaciones cuando las subáis al Market. Con esto concluye el ejemplo y evidentemente no doy más o mejor información porque el propósito de este artículo es proteger las aplicaciones y no lo contrario.
El problema ya está identificado, cualquiera puede destripar nuestras aplicaciones y aunque la solución de Richard Stallman sería que todo fuera Open Source, los programadores tenemos el defecto de querer comer todos los días (después de comer sí que participo en proyectos Open Source).

¿Qué solución tenemos? Ninguna, por lo menos de nuestra parte, de la de los desarrolladores de a pie, Google si tiene más cosas que decir en este aspecto, aunque muchas de las posibles soluciones convertirían a Google en Apple y eso tampoco lo queremos.

Lo que sí que podemos es usar algunas técnicas para dificultar (no impedir), herramientas como las anteriores:
  1.  La primera y que no me gusta es la de desarrollar en AIR de Adobe o algún framework similar que compile en flash u otro formato que no sea Java. También se puede decompilar, así que sólo fuerza a usar más herramientas.
  2. Otra idea es la de realizar parte del código en nativo a través de JNI. Se puede decompilar pero esto ya no es Smali y cosas de "Script-kiddies". Si vais a hacer algo computacionalmente intenso (por ejemplo lo he usado varias veces para algunas cosas de filtros de fotografía), puede que tenga sentido, pero es bastante más complejo y para la mayoría de las aplicaciones que se desarrollan no es necesario.
  3. La última técnica que voy a comentar es la "ofuscación". De una manera bastante ingenua, consiste en cambiarle el nombre de las variables, métodos, clases,... de vuestro código por otros nombres aleatorios y sin aparente sentido. Por ejemplo, si tenéis una variable que se llame "passwordSecretoDeLaBaseDeDatos", la ofuscación lo cambiaría por un valor aleatorio como "as3445rR". Esta técnica, al igual que las anteriores, sólo supone un retraso a la hora de destripar las aplicaciones. Sin embargo, y a diferencia de las anteriores, en el 90% de vuestras futuras aplicaciones, las podéis implementar en vuestro proyecto con una sola línea de código y esa razón ya de por si, justifica su uso. El SDK de Android incluye la herramienta ProGuard, que no sólo permite la ofuscación del código, si no que también incluye una serie de optimizaciones que harán que vuestra aplicación ocupe menos espacio y se ejecute algo más rápido.

En principio lo único necesario es incluir la siguiente línea en el archivo "project.properties" de vuestro proyecto Android:

# Turn ON obfuscation
proguard.config=proguard.cfg

El archivo "proguard.cfg" se crea automáticamente al crear un nuevo proyecto y lo podéis mover de sitio indicándolo en la línea de configuración anterior. También hay que mencionar, que esta herramienta sólo se ejecuta cuando vayáis a subir la aplicación al Market (o Google Play) y no antes. Por lo que no os preocupéis si parece que no hace nada, lo hará.

En ciertos casos, la mayoría de los cuales relacionados con uso de librerías externas como OpenCV o código JNI vuestro, habrá problemas de compilación debido a ProGuard. La explicación es que no en todos los casos se puede usar ofuscación y cambiar nombres porque se pierde la trazabilidad del código. El archivo "proguard.cfg" contiene todo lo que necesitáis saber para solucionar estos problemas, y además si usáis librerías externas suelen indicar que es lo que tenéis que añadir. Pero para solucionar de forma rápida que una o varias clases no compilen correctamente, basta con añadir al archivo "proguard.cfg":

-keep public class NombreDeLaClaseQueDaProblemas

Y esa clase se omitirá en las optimizaciones de ProGuard.