Ingeniería inversa Android Crackme04 de deurus

  • crackme01 de deurus solucionado aquí

  • crackme02 de deurus solucionado aquí

  • crackme03 de deurus solucionado aquí

  • Pueden descargar el crackme04 de deurus aquí

¡Por fin llegó el día! La solución del crackme04 de deurus. Recuerdo que cuando empecé con estos crackme me parecieron sencillos, hasta que llegó Pro Guard y empezará a hard codear, y obfuscar todo el código y clases. Por un tiempo no sabía poder donde meterle mano a este crackme, pero este último mes, he estado investigando mucho sobre aplicaciones y haciéndoles ingeniería inversa. Esto me ha llevado a entender y manejar mucho mejor con smali y aunque Pro Guard no nos lo pone fácil, me defiendo con ello. ¡Empecemos!

Tras arrancar el programa, la primera pantalla que vemos es:

pantalla-splash

Y tras unos segundos, aparece el menú de registro en el que aparece los ‘name’ y ‘serial’:

menu

Hay que decir que la música en 8-bit, mola bastante :) Al darle a registar los valores que vienen por defecto o cualquier otros que pongamos, vuelve a la primera pantalla durante unos segundos, y de nuevo aparece el menú de registro.

Ahora viene la parte divertida. Tengo que decir que es más que recomendable no solo pasarlo a smali sino también a java, para ver mucho mejor el código. La única diferencia en este crackme es que han utilizado Pro Guard, pero no pasa nada, podemos con ello :P

Analizando el código llegamos a la parte realmente importante que es esta:

        try
        {
          i = c(this.f);
          if (i.equals(str2))
          {
            localTextView1.setText(str5 + str1);
            localTextView1.setVisibility(0);
          }
        }

Es básicamente donde se comprueba si el número de serie introducido y el que debería ser, son iguales. En caso de ser iguales muestra un texto compuesto por 2 strings, los cuales son:

    String str1 = this.a.getString("Name", "");
    String str2 = this.a.getString("Code", "");
...
String str5 = a(new byte[] { 82, 101, 103, 105, 115, 116, 101, 114, 101, 100, 32, 116, 111, 58, 32 });

El str2 es definitivamente el serial introducido que sera comprobado con el generado. El str1 es el nombre, que es justo una de las variables que aparece justo al validar el serial. ¿Pero que es el str5? Hice una función en python que nos descifra este misterio:

def aBytes(Bytes):
	localStringBuilder = ""
	i1 = 0
	while True:
		if(i1 >= len(Bytes)):
			return str(localStringBuilder)
		localStringBuilder = str(localStringBuilder) + chr(Bytes[i1])
		i1 = i1 + 1

Lo que da como resultado que srt5 es: “Registered to:”. Lo cual tiene sentido, pues si validamos el serial saldrá que lo hemos registrado con el nombre que hayamos puesto.

Una vez identificado lo que tenía que modificar, lo pasé a smali lo cual me llevó a este fragmento de código:

    iget-object v5, v0, Lcom/deurus/androidcrackme4/crackme;->f:Ljava/lang/String;

    invoke-static {v5}, Lcom/deurus/androidcrackme4/crackme;->c(Ljava/lang/String;)Ljava/lang/String;

    move-result-object v5

    sput-object v5, Lcom/deurus/androidcrackme4/crackme;->i:Ljava/lang/String;
    :try_end_0
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0

    :goto_4
    sget-object v5, Lcom/deurus/androidcrackme4/crackme;->i:Ljava/lang/String;

    invoke-virtual {v5, v11}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

    move-result v5

    if-eqz v5, :cond_6

    new-instance v5, Ljava/lang/StringBuilder;

    invoke-static {v13}, Ljava/lang/String;->valueOf(Ljava/lang/Object;)Ljava/lang/String;

La solución fue sencilla, fue cambiar esto:

    invoke-virtual {v5, v11}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

a esto otro:

    invoke-virtual {v5, v5}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

Volví a construir el apk y lo instalé, para mi sorpresa al intentar abrir el apk se cerraba todo el rato. Se me ocurrió ver el log y no vi nada interesante, pero mirando un poco más a fondo el código, di con esta función:

  private void d()
  {
    String str = getApplicationContext().getString(2131099650);
    if (!e(String.valueOf(new ZipFile(getPackageResourcePath()).getEntry(this.e).getCrc())).equals(str))
      finish();
  }

Esa función, lo que hace es comprobar el MD5 de las classes.dex para comprobar que este no ha sido modificado, y como vemos, si es modificado termina la aplicación, que es justo lo que me ocurría. En este punto, podia hacer 2 cosas. Por un lado podía modificar la igualdad, o una vez ya construida el archivo classes.dex modificar el archivo string.xml que contiene el MD5 y cambiarlo por el nuevo, para que sea aceptado por la aplicación. Yo cambié la igualdad para que haga la modificación que haga siempre me lo aceptara. Cambié esto:

    invoke-virtual {v1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

a esto:

    invoke-virtual {v1, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z

Una vez modificado, lo volví a instalar y seguía cerrándose. ¿Cual sería el problema esta vez? La verdad que el mismo, lo único que esta vez había que hacerlo en el archivo splash.smali.

También hice una modificación respecto a la primera pantalla que salía (que no me gustaba nada), así que hice cambie lo siguiente en el archivo splash.smali.

	invoke-virtual {p0, v0}, Lcom/deurus/androidcrackme4/Splash;->setContentView(I)V

a esto otro:

	nop

y esto:

	const-wide/16 v2, 0x3000L

a esto otro:

	const-wide/16 v2, 0x001

Ahora lo instalamos de nuevo, y finalmente, ¡lo hemos conseguido! Ya acepta cualquier nombre y serial.

exito

También empecé a hacer el keygen, y la verdad que es sencillo de sacar el código a parte de los archivo .java, pero como es necesario datos que se encuentran dentro del sistema en un archivo, el IMEI, IMSI, ect etc.. Lo dejé sin terminar aunque os dejaré el boceto que tengo del script, realmente una vez parcheado el apk, el keygen no es necesario. Pero aquí os dejo lo que hice:

#!/usr/bin/python
# coding=utf-8

import sys

print '''
######################################
#                                    #
#    Keygen for crackme04 deurus     #
#                                    #
######################################

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

'''
h = [ "m", "n", "a", "G", "&", "¿", "G", "8", "5", "-", "q", "H", "N", "X", "A", "B", "X", "Z", "l", "L", "3", "2", "8", "7", "4", "5", "7", "E", "N", "J", "8", "a", "s", "d", "Q", "P", "v", "4", "9", "5", "z", "l", "g", "h", "-", "n", "m", "e", "w", "6", "x", "f", "$", "R", "U", "I", "C", "S", "e", "2", "c", "]", "{", "s", "d", "f", "g", "h", "t", "0", "b", "x", "}", "a", "#", "A", "S", ":", "y", "7", "%", "=", "@", "J", "/", "¡", "m", "n", "b", "5", "r", "*", "+", "?", "8", "4", "q", "e", "k", "4" ]
g = [ "01", "02", "03", "04", "05", "06", "07", "08", "09", "99", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100", "11", "27", "28", "37", "54", "55", "72", "73", "97", "98", "12", "26", "29", "38", "53", "56", "71", "74", "94", "95", "13", "25", "31", "39", "52", "57", "69", "75", "93", "96", "14", "24", "32", "41", "51", "58", "68", "76", "91", "92", "15", "23", "33", "42", "49", "59", "67", "77", "88", "89", "16", "22", "34", "43", "48", "61", "66", "78", "86", "87", "17", "21", "35", "44", "47", "62", "65", "79", "84", "85", "18", "19", "36", "45", "46", "63", "64", "81", "82", "83" ]

name = sys.argv[1]
code = sys.argv[2]
#str10 = sys.argv[3] # imei
# str11 = sys.argv[4] #imsi
# str13 = sys.argv[5] #mac sin : y en minusculas

def rshift(val, n):
	return (val % 0x100000000) >> n

#da la vuelta al string: marcos lo escribe socram
def a(string):
	str1 = string.strip() + " "
	str2 = " "
	str3 = " "
	i1 = 0

	while True:
		if(i1 >= len(str1)):
			return str2.strip()

		str3 = str1[i1] + str3
		if(str1[i1] == ' '):
			str2 = str2 + str3
			str3 = ""
		i1 = i1 +1

def b(string):
	strb = ""
	i1 = 0
	if (i1 >= len(string)):
		return strb
	if (len(string) % 2 != 0):
		i3 = rshift(ord(string[i1]) ** i1, 2)
	strb = strb + str(i3)
	while True:
		i1 = i1 + 1
		break
		i2 = string[i1] | i1 << 1
		strb = strb + i2

def c(string):
	i1 = 0
	str0 = string
	str1 = ""
	str2
	if ((len(str0) % 2 != 0) and (len(str0) > 201)):
		str2 = str0[50:150]
		g.sort()
		h.sort()
	while True:
		if(i1 >= len(str2)):
			return str1
			break
			str2 = str0[75:175]
		str3 = str2[i1, i1 + 2]
		#i2 = Arrays.binarySearch(g, str3)
		str4 = h[i2]
		str1 = str1 + str4
		i1 += 2

def d(string):
    strd = ""
    i1 = 0
    while True:
    	if (i1 >= len(string)):
    		return strd
		i2 = string[i1]
		strd = strd + i2
		i1 = i1 + 1

def aBytes(Bytes):
	localStringBuilder = ""
	i1 = 0
	while True:
		if(i1 >= len(Bytes)):
			return str(localStringBuilder)
		localStringBuilder = str(localStringBuilder) + chr(Bytes[i1])
		i1 = i1 + 1

str1 = a(name)
str5 = aBytes([82, 101, 103, 105, 115, 116, 101, 114, 101, 100, 32, 116, 111, 58, 32 ]) #Registered to:
str6 = aBytes([48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48 ]) # 000000000000000
str7 = aBytes([49, 55, 57, 52, 56, 51, 52 ]) # 1794834
str8 = aBytes([97, 98, 58, 52, 53, 58, 101, 97, 58, 50, 54, 58, 49, 49, 58, 48, 50 ]) # ab:45:ea:26:11:02
str9 = aBytes([102, 101, 58, 100, 97, 58, 97, 48, 58, 48, 48, 58, 49, 50, 58, 102, 98 ]) # fe:da:a0:00:12:fb
j = aBytes([77, 68, 53 ]) # MD5
e = aBytes([99, 108, 97, 115, 115, 101, 115, 46, 100, 101, 120 ]) # classes.dex
k = aBytes([78, 111, 78, 97, 109, 101 ]) # NoName
l = aBytes([78, 111, 83, 101, 114, 105, 97, 108 ]) # NoSerial
test = aBytes([99, 114, 52, 46, 100, 97, 116 ])

¡Otro crackme completado con éxito! Os dejo el archivo apk modificado. crackme04-solved.apk - Descargar

SHA1 Checksum: 8fc5515112f349be39ca189c8fe197c3232b1ae7

Un saludo y hasta la próxima :)

Updated: