Contents

Ingeniería inversa Android Crackme02 de deurus

Introducción

  • crackme01 de deurus solucionado aquí
  • Pueden descargar el crackme02 de deurus aquí

Comportamiento de la app

Como siempre, antes de empezar a trastear con el código vamos a ver como es el programa.

Tenemos dos campos a introducir, uno el nombre y otro el serial, dos botones (Check! y otro Exit). Procedo a introducir un nombre y un serial cualquiera, y pulsamos Check!

Como podemos ver, al introducir un nombre y un serial que no es correcto, la pantalla se muestra en rojo. Pues vayamos al código, ¡a ver que encontramos!

Decompilado y análisis

Decompilado

Lo primero será pasarlo a smali. Tras comprobar un rato el smali, sé podría ver el código desde el smali pero sería bastante laborioso, así que mejor lo pasamos a un nivel más alto, y con la herramienta dex2jar, pasamos el classes.dex a un empaquetado .jar del cual extraeremos los archivos .class. Para decompilar los archivos .class he utilizado esta herramienta. Y analizamos el código, hasta que observamos dónde se genera el serial:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Object Button1_check$Click()
{
	runtime.addToCurrentFormEnvironment(Lit12, Lit28);
	runtime.addToCurrentFormEnvironment(Lit14, Lit11);
	if (runtime.isYailEqual(runtime.sanitizeComponentData(Invoke.invoke.apply2(runtime.lookupInCurrentFormEnvironment(Lit37), Lit22)), "") != Boolean.FALSE)
	{
		runtime.callComponentMethod(Lit53, Lit54, LList.list3("Please Enter the Name", "Error", "Ok"), Lit55);
		runtime.$PcSetAndCoerceProperty$Ex(runtime.lookupInCurrentFormEnvironment(Lit37), Lit22, "", Lit20);
		return runtime.$PcSetAndCoerceProperty$Ex(runtime.lookupInCurrentFormEnvironment(Lit47), Lit22, "", Lit20);
	}
	while (runtime.callYailPrimitive(Scheme.numLEq, LList.list2(runtime.lookupInCurrentFormEnvironment(Lit12), runtime.callYailPrimitive(strings.string$Mnlength, LList.list1(runtime.sanitizeComponentData(Invoke.invoke.apply2(runtime.lookupInCurrentFormEnvironment(Lit37), Lit22))), Lit56, "length")), Lit57, "<=") != Boolean.FALSE)
	{
		runtime.addToCurrentFormEnvironment(Lit10, runtime.callComponentMethod(Lit58, Lit59, LList.list1(runtime.callComponentMethod(Lit58, Lit60, LList.Empty, LList.Empty)), Lit61));
		runtime.addToCurrentFormEnvironment(Lit14, runtime.callYailPrimitive(AddOp.$Pl, LList.list2(runtime.lookupInCurrentFormEnvironment(Lit14), runtime.callYailPrimitive(MultiplyOp.$St, LList.list2(runtime.lookupInCurrentFormEnvironment(Lit10), runtime.lookupInCurrentFormEnvironment(Lit12)), Lit62, "*")), Lit63, "+"));
		runtime.addToCurrentFormEnvironment(Lit14, runtime.callYailPrimitive(AddOp.$Pl, LList.list2(runtime.lookupInCurrentFormEnvironment(Lit14), runtime.callYailPrimitive(numbers.modulo, LList.list2(runtime.lookupInCurrentFormEnvironment(Lit14), Lit64), Lit65, "modulo")), Lit66, "+"));
		runtime.addToCurrentFormEnvironment(Lit12, runtime.callYailPrimitive(AddOp.$Pl, LList.list2(runtime.lookupInCurrentFormEnvironment(Lit12), Lit28), Lit67, "+"));
	}
	if (runtime.isYailEqual(runtime.sanitizeComponentData(Invoke.invoke.apply2(runtime.lookupInCurrentFormEnvironment(Lit47), Lit22)), runtime.lookupInCurrentFormEnvironment(Lit14)) != Boolean.FALSE) {
		return runtime.$PcSetAndCoerceProperty$Ex(runtime.lookupInCurrentFormEnvironment(Lit0), Lit16, Lit68, Lit18);
	}
	return runtime.$PcSetAndCoerceProperty$Ex(runtime.lookupInCurrentFormEnvironment(Lit0), Lit16, Lit69, Lit18);
}

Análisis

Lo primero de todo tenemos esto:

1
2
runtime.addToCurrentFormEnvironment(Lit12, Lit28);
runtime.addToCurrentFormEnvironment(Lit14, Lit11);

Que es equivalente a dar valores a las variables. De tal forma:

1
2
tam_nombre = 1
temp = 0

Lit12 es tam_nombre y lit28 es IntNum.make(1). De la misma manera, será la variable temp.

Podemos apreciar la condición if, la cual nos mostrará un error ('Please Enter the Name') si no introducimos un nombre.

Luego podemos ver otra condición, while. numLEq significa <=. La condición es la siguiente:

1
while(tam_nombre <= len(nombre))

Es decir, si no utilizamos la variable nombre sino la longitud de ese string, el serial sólo dependerá de esta longitud, por lo que el nombre deja de ser relevante.

De la misma manera, atribuímos valores dentro de las condición while. Las operaciones se sacan leyendo la secuencia. La variable ano es el año actual.

1
2
3
temp += ano * tam_nombre
temp += temp % 1638 # temp mod(1638)
tam_nombre += 1

Una vez que sale del bucle while. El serial es el resultado guardado en la variable temp.

Solución

Por lo que el keygen que hice sería así:

 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
#!/usr/bin/python

import sys
from datetime import date

tam_nombre = 1
temp = 0
ano = date.today().year

print '''
######################################
#                                    #
#    Keygen for crackme02 deurus     #
#                                    #
######################################

Author: @_mgp25 - github.com/mgp25 - mgp25.com

'''

if len(sys.argv) < 2:
	sys.exit("Usage: python serial.py <name>\n")
else:
	nombre = sys.argv[1]

def generateSerial(nombre, tam_nombre, ano, temp):
	if nombre == "":
		sys.exit("Enter a valid name")
	else:
		while tam_nombre <= len(nombre):
			temp += ano * tam_nombre
			temp += temp % 1638
			tam_nombre += 1

		print "Your serial is",  temp

generateSerial(nombre, tam_nombre, ano, temp)

Ahora utilizamos el keygen para generar un serial válido para mi nombre. Ya que hemos obtenido el serial, lo probamos en el crackme02.

¡Perfecto! Ya hemos completado el crackme02 :) Pero esto no termina aquí, ahora a parte del keygen, modificaremos el bytecode para que acepte cualquier serial.

Si nos fijamos, después del bucle while, hay una condición if, que comprueba si el serial es correcto. Ahora nos vamos al archivo Screen1.smali, y buscamos esa condición:

1
2
3
4
5
6
Linea 4282


sget-object v1, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;

if-eq v0, v1, :cond_2

Esta es la condición que hace que vaya a mostrar la pantalla roja, o la pantalla verde. Solo cambiamos el FALSE por un TRUE. Quedaría así:

1
sget-object v1, Ljava/lang/Boolean;->TRUE:Ljava/lang/Boolean;

Construímos el nuevo .apk, lo firmamos y lo instalamos. Y ahora probamos, el serial y el nombre que probamos al comienzo…

¡Perfecto! :D Pues ya hemos completado totalmente este crackme02 de deurus.

Un saludo y hasta la próxima :)