Solución al reto 14 del crackme#2 para Android

Nuestro segundo crackme para Android (y último reto del año) consistía en evadir la implementación del proceso de licenciamiento de Android o Android License Verification Library (ALVL). La aplicación era muy sencilla pero, eso sí, tenía alguna trampa ;)

El ganador del reto, Jose Ayerdis (Necronet) de Nicaragua, ha publicado un completo solucionario en
su blog , que recomendamos que visitéis para ver más tips adicionales y otros interesantes artículos.

También quería dar las gracias a todos los que habéis intentado solucionar el reto. Por último, os dejo con el procedimiento seguido por Necronet:

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 código decompilado backsmali.
Luego de seguir la instalación del APKTool procede a ejecutar el comando para extraer el APK.

1
$apktool d cracme2hpys.apk out


*out es el directorio donde se descomprime el apk.

Husmeando el directorio extraído

Lo primero que pensé es bypasear cambiando la clase en el AndroidManifest.xml que es la que comienza la aplicación, pero no tuve éxito.

Así que tuve que husmear el código 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.


Así que puedes revisar en la carpeta com/hpys/crackmes/LicenseCheck.smali. Veras mucho código, si tienes alguna experiencia con lenguaje ensamblador no te parecerá tan raro, sino estudia un poco y veras que sencillo que es.

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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 un doCheck este llama a un método en la misma clase LicenseCheck pero que hace realmente este método veamos:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.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 método doCheck hace la revisión, 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 lógico 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 invocación del método checkAccess que es el que invoca la clase LicenseCheck recuerdas?.

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

Probablemente este método nos proporcione mejores pista que cualquier otro por que es donde se lleva acabo la revisión de la licencia.

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 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 condición es muy importante por que en caso de cumplirse te envía 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 fácil de hacer esta condición tiene una operación antagónica la cual es: if-nez vx,target así que al cambiar las operaciones debería 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 obstáculo 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 qué no funciona es clara: la clase MyAndroidAppActivity no tiene la invocación al metodo onCreate y hará falta agregárselo con backsmali.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
.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 código anterior demuestra muchas faltas en la clase MyAndroidAppActivity, siendo un código pequeño y sencillo es fácil reconocerlas todas:
  • El método onCreate no llama al super.onCreate.
  • El método onCreate tampoco tiene layout asignado debería tener main.xml con setContentView.
  • La clase MyAndroidAppActivity hereda de LicenseCheck en vez de Activity y tambien en el método init se hace la invocación especial a este metodo.
Aquí el código del MyAndroidAppActivity con las fallas anteriores resueltas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
.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 invocación del método 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 único que falta es recompilarlo y empaquetarlo para regresarlo a un APK.

De regreso a un APK

A como mencione inicialmente apktool también 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 teléfono. Para recompilar la aplicación ejecuta el comando:


1
$ apktool b out crackemecracked.apk
Tambien necesitas firmarlo, para ello necesitas de un keystore, puedes crearlo facilmente con keytool

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

Ahora ya tienes tu keystore para firmar tu aplicación


1
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.

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

Si todo esta bien, la imagen que debería aparecer es la siguiente:


Y LISTO!!

Comentarios