jueves, 24 de enero de 2013

Keygen for AppZapper (just for fun)

Esta entrada esta relacionada con el post de AppZapper que hice algún tiempo, donde usando el comando strings se pueden ver usuarios y seriales válidos. Me dio por revisar el algoritmo de generación de las claves y lo ví  como una oportunidad para explicar un poco el uso de IDA PRO como desensamblador "mágico" que nos ayuda a crear keygens. Esto se hace con fines académicos, realmente el AppZapper es una muy buena aplicación, así que si tiene los 13 dolares que cuesta el producto, mejor páguelos, usar seriales falsos? es ilegal.

Para empezar cargamos la aplicación en el IDA (file --> open)


Lo primero que se piensa es como llegar rápidamente a la rutina de validación de los seriales, la forma mas rápida es buscar los strings de validación que ya hemos encontrado como válidos, por ejemplo, busquemos la palabra "Pablo", para esto generamos los strings de la aplicación (view --> open subviews --> strings) y buscamos (Alt+t) la palabra "Pablo".


Para seguir la rutina hacemos doble click sobre el nombre y esto nos lleva a la rutina donde aparece el nombre buscaod, la "nueva vista" de la versión 6.3 de IDA permite ampliar o expandir solo las zonas que deseemos ver, para entrar o salir de las rutinas podemos usar las teclas + y -.


Al entrar a la rutina vemos que realmente estamos en la sección de definiciones de strings, esto es porque los nombres están definidos explícitamente en el sistema, así que tenemos que ubicar el punto desde donde este nombre es referenciado. Para esto nos paramos sobre el nombre (resaltado en amarillo) y presionamos la tecla "x", con esto aparece una ventana donde se muestra los lugares desde donde se invoca el nombre, como se puede ver en la siguiente imagen solo hay un solo punto.


Hacemos click sobre esta referencia y llegamos a la rutina verdadera.


Aquí se puede ver que la rutina se llama a_validatenameA, lo que nos esta indicando que efectivamente es la rutina donde se valida el serial de la aplicación. Algunas veces estos nombres no van a aparecer, todo depende de si la aplicación fue compilada con los símbolos reales y la depuración (debugging) habilitada.

Si aún no están acostumbrados a esta vista pueden regresar a la vista tradicional presionando la barra espaciadora.


Esta imagen nos muestra una hilera de condicionales anidados, del estilo, si este es el usuario válido entonces OK, sino es, entonces verifique el siguiente usuario ...


Si recorremos la función podemos encontrar cada uno de los usuarios válidos que hayamos con el comando string. Esto nos indica que efectivamente antes de generar el serial para un usuario particular, la rutina valida que el usuario no sea uno de los predefinidos, realmente no entiendo porque tienen todos estos usuarios ahí, seguramente tendrá algo que ver con las aficiones de los desarrolladores.

El último usuario que se valida es  "Pablo Sanchez", de modo que si ninguno de los usuarios fue el que se ingreso en el proceso de registro del software empieza una rutina de validación real sobre los datos ingresados.


En este punto entramos a la rutina que nos interesa.

Podemos ver que se carga el offset_3F788 que corresponde con la función lowercaseString(), porque lo primero que se hace es pasar el nombre de usuario a minúsculas. Podemos ir renombrando los offsets presionando la tecla "n" sobre el nombre y escribiendo algo que podamos recordar mas fácil.




El siguiente offset cargado es 3FA50, que apunta a la función mutableCopy(), esta función me genera una copia del valor entrado (con miras a modificarlo) de modo que el valor original no se vea alterado.

El siguientes es 3F530 que apunta a length() que como su nombre lo indica encontrará la longitud de la cadena ingresada.

3FEE4 apunta a la función replaceOccurrencesOfString:withString:o, esta rutina eliminará los espacios en el string que se ingresa .

Luego se carga el offset 3F754 que apunta a la función componentsSeparatedByString:, esta rutina permite dividir un string usando un delimitador. En este caso el delimitador es el carácter "-".

Una vez que el string se ha dividido se empieza a evaluar cada parte, según lo que conocemos de los seriales válidos el formato debe ser: APZP-XXX-XXX-XXX-XXX.

A cada una de las partes le llamaremos bloques, entonces el serial válido tendrá 5 bloques (APZP-Bloque1-Bloque3-Bloque4-Bloque5) como se ve en los ejemplos: APZP-101-109-198-114, APZP-105-108-206-110.

En el offset 3F528 se apunta a la rutina objectAtIndex que se encarga de seleccionar cada uno de los componentes, el primer componente empieza en cero.




BLOQUE 1

El offset 3F4C8 apunta a la función isEqualToString: que se encarga de comparar el valor del primer bloque. Para el primer bloque se compara la palabra "APZP" por lo que entendemos que el serial válido necesariamente tiene  que empezar con ese string.


Si el bloque uno es correcto se evalúa el bloque 2 y así con cada uno de los 5 bloques.

BLOQUE 2

En este punto aparecen dos rutinas mas:

3F9C0 --> intValue  , que se usa para validar que el bloque ingresado sea un número entero.
EFEE8 --> characterAtIndex: , se usa para convertir un carácter a un entero de 16 bits.

Lo que hace este bloque es tomar el usuario ingresado y convertir a un entero corto la letra que esta en la penúltima posición del usuario. Esa penúltima posición se alcanza restándole dos a la posición final del string, para restarle este valor lo que se hace es sumarle -2.

Código en ASM:   add     eax, 0FFFFFFFEh  =   add eax, -2


BLOQUE 3

Aquí se toma el primer carácter del usuario y se convierte a entero, este valor se compara con el valor ingresado en el serial.


BLOQUE 4

En este bloque se toma el segundo carácter del usuario y se convierte a entero, luego se toma el penúltimo valor y se hace lo mismo (se obtiene el mismo resultado del bloque 2), y finalmente se suman los dos valores (add     eax, ebx) para obtener el entero final que debe estar en esa posición.

BLOQUE 5
El último bloque toma la última letra del string de usuario y la convierte a un entero, la búsqueda la hace decrementando el valor  (dec eax) del registro que apunta al tamaño del string.



Al comparar este último bloque solo pueden haber dos caminos, el correcto que registra el programa o el incorrecto que hace que el programa genere un error de serial invalido.



Para automatizar este proceso generamos un script que funcione como keygen, el código es el siguiente:

[Hax0r@HOST] ~ $ cat keygen.py 
##
## Keygen for AppZapper by nonroot (c) 2013
##

import sys

def main():
  usuario=sys.argv[1]
  cusuario=usuario.lower().replace(' ','')
  lusuario=len(cusuario)
  B1="APZP"
  B2=ord(cusuario[lusuario-2])
  B3=ord(cusuario[0])
  B4=ord(cusuario[1])+B2          
  B5=ord(cusuario[lusuario-1])
  print "*********************"
  print usuario
  print str(B1)+"-"+str(B2)+"-"+str(B3)+"-"+str(B4)+"-"+str(B5)
  print "*********************"
if __name__ == '__main__':
  main()
[Hax0r@HOST] ~ $ 


Comprobemos con los usuarios por defecto (hardcoded) que trae el binario.

[Hax0r@HOST] ~ $ python keygen.py "Pablo Sanchez"
*********************
Pablo Sanchez
APZP-101-112-198-122
*********************
[Hax0r@HOST] ~ $ python keygen.py "Stephanie Lee"
*********************
Stephanie Lee
APZP-101-115-217-101
*********************
[Hax0r@HOST] ~ $ python keygen.py "MacZapper"
*********************
MacZapper
APZP-101-109-198-114
*********************
[Hax0r@HOST] ~ $ 

OK funciona, solo queda ingresar los datos generados en la ventana de registro.

[Hax0r@HOST] ~ $ python keygen.py "Intro a IDA PRO con AppZapper"
*********************
Intro a IDA PRO con AppZapper
APZP-101-105-211-114
*********************
[Hax0r@HOST] ~ $ 


Esto es todo, recuerden que la intención de crear este post no es otra que explicar un poco el uso de esta excelente herramienta usada en el mundo del reversing. En ningún momento se busca incentivar el registro de software de forma ilegal, por supuesto yo pagué mi versión de AppZapper ;)

Saludos.

No hay comentarios:

Entradas populares