27 enero 2012

Seguridad en aplicaciones IOS, Parte II

Continuación de Parte I.

El siguiente paso es descifrar el binario, ya que cuando un desarrollo es terminado por sus autores y enviado a Apple para añadirlo a la Apple Store, este es analizado y examinado. Si supera los tests, será cifrado y firmado, complicando que se haga ingeniería inversa sobre el.

Para descifrarlo tan solo hay que ejecutarlo y con gdb se vuelca la memoria del proceso. Tal y como se haría genéricamente para desempacar un binario en otra plataforma.

Antes de hacer esto, es recomendable que se conozca un mínimo la estructura de un binario Match-O, que en muy grandes rasgos se representa en este gráfico:
  • El fichero contiene una cabecera donde se identifican distintos aspectos como si es fat o en la arquitectura en la que corre. 
  • Tras la cabecera se encuentran los comandos, en los que se definen otras características como la localización de la tabla de símbolos, o los nombres de las librerías compartidas que son importadas en su ejecución.
  • Finalmente los datos que son divididos en segmentos y estos en secciones. Los dos segmentos más importantes son:
    • __TEXT, donde se encuentra el código de la aplicación y otros datos de solo lectura y 
    • __DATA, donde la aplicación podrá escribir e intercambiar datos.
Volviendo al binario, para comprobar si está cifrado se puede consultar si el flag "cryptid" del comando LC_ENCRYPTION_INFO es igual a 1, además de otros valores como el tamaño del bloque cifrado "cryptsize"

Con la herramienta otool se obtienen estos valores:

iPhone:~ root#  otool -l /private/var/mobile/Applications/58ED43CF-0D53-4DDC-864C-7F34D34E059C/StupidZombiesDLX.app/StupidZombiesDLX | grep crypt
 cryptoff  8192
 cryptsize 10133504
 cryptid   1

Como se va a descifrar el binario, hay que cambiar el valor de cryptid a 0, para esto es necesario parchearlo modificando la cadena del comando LC_ENCRYPT_INFO, que contiene los valores: 0000002100000014......................01 por 0000002100000014......................00, donde los puntos representan valores (11) que pueden variar.

La forma más sencilla de cambiar este byte es copiando el binario con DiskAid a la estación de trabajo para usar editor hexadecimal, buscar por la cadena hexadecimal 0000002100000014, contar 11 bytes más y modificar el 01 por 00. Guardar y dejar el fichero en el iPhone en un fichero temporal.


Otra opción es leer la cabecera del fichero en hexadecimal, hacer la modificación y  posteriormente guardarlo en un fichero. Esto se puede hacer con estos dos comandos dentro del teléfono:

iPhone:/tmp root# hex=`dd bs=4096 count=1 if=/private/var/mobile/Applications/58ED43CF-0D53-4DDC-864C-7F34D34E059C/StupidZombiesDLX.app/StupidZombiesDLX 2>/dev/null| od -A n -t x1 -v  | tr -d ' ','\n' | sed -e 's|0000002100000014\(.\{22\}\)01|0000002100000014\100|' | sed -e 's|\(..\)|\\\\x\1|g'`
iPhone:/tmp root# echo -ne "$hex" > /tmp/header.hex

Se verifica que cryptid ahora es igual a 0:

iPhone:/tmp root# otool -l /tmp/header.hex | grep -i crypt
          cmd LC_ENCRYPTION_INFO
 cryptoff  8192 (past end of file)
 cryptsize 10133504 (past end of file)
 cryptid   0

WIN!

Si aun no te has cansado, continuamos la semana que viene con las instrucciones necesarias para el volcado final.

5 comments :

Anónimo dijo...

Excelente tutorial!! Espero impaciente al próximo. ¿Dónde aprendistes todo esto? Muchas gracias por compartirlo con todos!

Rodolfo dijo...

Pues esperaremos a la semana que viene para el nuevo capítulo (como quien espera por Dex)
Muy buen trabajo, está todo muy bien explicado :)

Alex dijo...

Hay algo que no entiendo. Para poder usar gdb y ver el código y tal, es necesario que el ejecutable permita el depurado y se pueda hacer un attacht al proceso no?
Y en cuanto al cifrado, tan fácil cambiarlo a no cifrado?

Supongo que todo esto es posible solo para terminales rooteados (No me acuerdo como se dice para los Iphone xD)

De cualquier manera, te felicito, no tengo Iphone pero estoy siguiendo estos post.

Un saludo.

Alejandro Ramos dijo...

Buenas Alex,

Para usar gdb, el código da igual como esté, ya que gdb es un debugger. Lo que se trata aquí (y en la primera y última parte) es como descifrar el binario para desensamblarlo posteriormente y poder interpretar su contenido, independientemente de que se ejecute o adjunte con gdb el proceso.

Esto que se ha cambiado aquí, solo es un byte que indica que está cifrado, si le dices que no está cifrado y lo está, no funcionará. Falta la última parte donde se descifra definitivamente. Es decir, esto solo es la primera parte para descifrarlo "de verdad".

Gracias.

Alex dijo...

Gracias, me he confundído, pensaba que lo que se quería hacer era depurar el programa.

Un saludo.