lunes, 19 de diciembre de 2011

Hacking Android - Crackeando LVL

Este es probablemente el ultimo post del año que sea tecnico/tutorial, y es mi regalo de despedida hasta el 2012, espero que lo disfruten. 

Para hacer un poco de prologo revisa el Reto 14: Android crackme #2, es una interesante publicación que  te retan a pasarte el licenciamiento de Android de un APK; así que es la excusa perfecta para aprender un poco de decompilado de Android, el cual hasta el momento sabia de manera teorica y nunca habia tenido una motivación mas alla de simplemente ser "el sabiondo teorico" para pasar a ser un curioso pragmatico, así que gracias a Hackerplayers por el reto.

Aclaraciones

Este post no es un explicativo profundo acerca de la forma que Android compila las clases y empaqueta el APK. Tampoco voy a explicar que es Smali/Backsmali (código decompilado) para ello ya hay buenos blogs , como Dissasembling Dex File  y claro Android Cracking el ultimo un cracker de Android te cuenta muchos secretos y trucos acerca de código decompilado.

Este post es más acerca de como te pasas el licenciamiento de seguridad resolviendo el Reto 14: Android crackme #2. Asi que empezemos a divertirnos aqui esta mi respuesta (la cual fue aprobada por el autor del blog):

Descarga el APKTool

El apktool es una herramienta maravillosa, si te quieres hacer de esto de cracking aplicaciones android el apktool es un fiel compañero, permite realiza muchas cosas entre ellas:
  • Extraer y decompilar fuentes de empaquetados Android(apktool), esto incluye recursos(res), manifiesto(AndroidManifest), y fuentes decompiladas.
  • Recompilar dichas fuentes y volverlas a empaquetar.
  • Depurar codigo decompilado backsmali.
Luego de seguir la instalacion del APKTool procede a ejecutar el comando para extraer el APK.

$apktool d cracme2hpys.apk out


*out es el directorio donde se descomprime el apk.

Husmeando el directorio extraido

Lo primero que pense es bypasear cambiando la clase en el AndroidManifest.xml que es la que comienza la aplicacion, pero no tuve exito.

Asi que tuve que husmear el codigo decompilado. El AndroidManifest.xml siempre te da la pista de donde comenzar en general procuro que sea la Actividad inicial, por que es donde probablemente se de la invocación del ALVL.





Asi que puedes revisar en la carpeta com/hpys/crackmes/LicenseCheck.smali. Veras mucho codigo, si tienes alguna experiencia con lenguaje ensamblador  no te parecera tan raro, sino estudia un poco y veras que sencillo que es. 

Busca en el onCreate de LicenseCheck.smali el codigo donde revisa la licencia se realiza una invocacion a un metodo doCheck().

    invoke-direct {v1, p0, v2, v3}, Lcom/android/vending/licensing/LicenseChecker;->(Landroid/content/Context;Lcom/android/vending/licensing/Policy;Ljava/lang/String;)V

    .line 120

    iput-object v1, p0, Lcom/hpys/crackmes/LicenseCheck;->mChecker:Lcom/android/vending/licensing/LicenseChecker;

    .line 123

    invoke-direct {p0}, Lcom/hpys/crackmes/LicenseCheck;->doCheck()V

    .line 125

    return-void


Si miras bien en la linea 123 hay hay un doCheck este llama a un metodo en la misma clase LicenseCheck pero que hace realmente este metodo veamos:

.method private doCheck()V

    .locals 2



    .prologue

    .line 106

    iget-object v0, p0, Lcom/hpys/crackmes/LicenseCheck;->mChecker:Lcom/android/vending/licensing/LicenseChecker;



    iget-object v1, p0, Lcom/hpys/crackmes/LicenseCheck;->mLicenseCheckerCallback:Lcom/android/vending/licensing/LicenseCheckerCallback;



    invoke-virtual {v0, v1}, Lcom/android/vending/licensing/LicenseChecker;->checkAccess(Lcom/android/vending/licensing/LicenseCheckerCallback;)V



    .line 107

    return-void

.end method

Ahora es claro que el metodo doCheck hace la revision, y llama a la clase LicenseChecker y aparentemente le pasa un callback LicenseCheckerCallback probablemente para informar que el licensamiento se realizo de forma correcta. Entonces el paso mas logico ahora sera ir a explorar la clase

com/android/vending/licensing/LicenseChecker


Explorando del LicenseChecker



Antes de empezar a leer todo el LicenseChecker y sus derivados, mejor ve directamente a la invocacion del metodo checkAccess que es el que invoca la clase LicenseCheck recuerdas?.

    invoke-virtual {v0, v1}, Lcom/android/vending/licensing/LicenseChecker;->checkAccess(Lcom/android/vending/licensing/LicenseCheckerCallback;)V

Probablemente este metodo nos proporcione mejores pista que cualquier otro por que es donde se lleva acabo la revision de la licencia.

El metodo checkAccess, es bastante grande así que procurare resumir las partes relevantes, por ejemplo:

# virtual methods

.method public declared-synchronized checkAccess(Lcom/android/vending/licensing/LicenseCheckerCallback;)V

    .locals 9

    .parameter "callback"

    .prologue

    .line 133

    monitor-enter p0

    :try_start_0

    iget-object v1, p0, Lcom/android/vending/licensing/LicenseChecker;->mPolicy:Lcom/android/vending/licensing/Policy;

    invoke-interface {v1}, Lcom/android/vending/licensing/Policy;->allowAccess()Z

    move-result v1

    if-eqz v1, :cond_0

    .line 134

    const-string v1, "LicenseChecker"

    if-eqz v1, :cond_0 esta condicion es muy importante por que en caso de cumplirse te envia a cond_0 y mas abajo indica que es la instanciacion del validador de licencias. Si nos saltamos esta parte tendremos el ejercicio terminado!!. Es bien facil de hacer esta condicion tiene una operacion antagonica la cual es: if-nez vx,target  asi que al cambiar las operaciones deberia funcionar.


Pero no funciona.... ¬¬

Si, si, cambiamos esta linea y ejecutamos el paso de recompilación de codigo y reempaquetamiento (lo explicare luego), el reto nos regala un obstaculo adicional,  al pasar la licencia el MyAndroidAppActivity no parece invocar al metodo onCreate, y te genera el siguiente error en logcat:



¿Así que pasa? ¿por que no funciona?, Bueno la respuesta del por que no funciona es clara la clase MyAndroidAppActivity no tiene la invocación al metodo onCreate hara falta agregarselo con backsmali.

.class public Lcom/hpys/crackmes/MyAndroidAppActivity;

.super Lcom/hpys/crackmes/LicenseCheck;

.source "MyAndroidAppActivity.java"

# direct methods

.method public constructor ()V

    .locals 0

    .prologue

    .line 6

    invoke-direct {p0}, Lcom/hpys/crackmes/LicenseCheck;->()V

    return-void

.end method

# virtual methods

.method public onCreate(Landroid/os/Bundle;)V

    .locals 0

    .parameter "savedInstanceState"

    .prologue

    .line 11

    invoke-super {p0, p1}, Lcom/hpys/crackmes/LicenseCheck;->onCreate(Landroid/os/Bundle;)V

    .line 15

    return-void

.end method


El codigo anterior demuestra muchas faltas en la clase MyAndroidAppActivity, siendo un codigo pequeño y sencillo es facil reconocerlas todas:
  • El metodo onCreate no llama al super.onCreate.
  • El metodo onCreate tampoco tiene layout asignado deberia tener main.xml con setContentView.
  • La clase MyAndroidAppActivity hereda de LicenseCheck en vez de Activity y tambien en el metodo init se hace la invocacion especial a este metodo.
Aqui el codigo del MyAndroidAppActivity con las fallas anteriores resueltas:

.class public Lcom/hpys/crackmes/MyAndroidAppActivity;
.super Landroid/app/Activity;
.source "MyAndroidAppActivity.java"


# direct methods
.method public constructor ()V
    .locals 0

    .prologue
    .line 6
    invoke-direct {p0}, Landroid/app/Activity;->()V

    return-void
.end method


# virtual methods
.method public onCreate(Landroid/os/Bundle;)V
    .locals 1
    .parameter "savedInstanceState"

    .prologue
    .line 15
    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V

    .line 17
    const/high16 v0, 0x7f03
    invoke-virtual {p0, v0}, Lcom/hpys/crackmes/MyAndroidAppActivity;->setContentView(I)V

    .line 20
    return-void
.end method


Podemos realizar los siguientes apuntes, como la invocacion del metodo onCreate() y de setContentView con la variable v0, en este tienes que tener sumo cuidado en declarar locals 1 que es el numero de variables que utilizaras.

¿Ya terminamos?, algo así lo unico que falta es recompilarlo y empaquetarlo para regresarlo a un APK.

De regreso a un APK


A como mencione inicialmente apktool tambien te permite recompilar la aplicación y regresarla a un APK, pero vas a necesitara un poco mas de eso para colocarla devuelta en el telefono. Para recompilar la aplicacion ejecuta el comando:

$ apktool b out crackemecracked.apk

Tambien necesitas firmarlo, para ello necesitas de un keystore, puedes crearlo facilmente con keytool

$ keytool -genkey -v -keystore my-release-key.keystore


Ahora ya tienes tu keystore para firmar tu aplicacion


jarsigner -verbose -keystore keystore.keystore crackemecracked.apk crackmecracked


Si deseas saber mas de como firmar aplicaciones android desde consola no te olvides pasar por la documentación oficial.


Ahora si tenemos la aplicacion firmada, es muy sencillo desintalarla y volverla instalar con el adb.

$ adb uninstall com.hpys.crackmes 
$ adb install crackemecracked-za.apk
 

Si todo esta bien, la imagen que deberia aparecer es la siguiente:




Y LISTO!! 



¿Algunos Tips adicionales ?

En lo particular nunca habia realizado esto, y la informacion de como modificar codigo backsmali no es muy amplia, pero es posible y no es tan dificil.

- Te recomiendo que veas algo de codigo Assembler por que es similar al backsmali basicamente son operadores y cambios registros.

- Cuando estaba perdido con alguna operación acudia a Dalvik OpCode el cual fue como un diccionario para mí.

- Tambien cree clases sencillas, como el hola mundo o una imagen para ver como se decompilaba y como se veia.

- No hay necesidad de revisar todas las clases como LicenseChecker$ResultListener$1 por que se refiere a clases internas (anonimas o declaradas) en el archivo, así que obviemos eso por un momento y dediquemonos al metodo doCheckAccess en la clase LinceseChecker.

- Para la parte de la recompilación y empaquetamiento me hice un shellscript por si quieres utilizarlo.

- Te recomiendo dos excelentes presentaciones de I/O.

Google I/O 2008 - Dalvik Virtual Machine Internals



 Google I/O 2010 - A JIT Compiler for Android's Dalvik VM



1 comentario:

Paolo Martínez dijo...

tienes un muy buen blog amigo, felicidades, lastima que no tengas mucha difucion :S