Antes de comenzar dos puntos a tener bien en cuenta: 1º. Esto tan solo es una muestra educativa o para ser usado con aplicaciones gratuitas. Cualquiera interesado podría hacerlo en casa sin mayores problemas, 2º. En internet abundan, al igual que pasaba con el antiguo parche de SpringBoard, diferentes versiones de MobileInstallation. JAMAS JAMAS se debe de usar una versión diferente del archivo que estamos usando!! esto es muy importante. Los parches que existen son todos normalmente EL MISMO, basado en el MobileInstallation 2.0. Usar versiones diferentes tan solo da lugar a problemas, fallos, relentizaciones... por eso es importante saber como fueron creados y adaptarlos a las nuevas versiones. En todo mi ejemplo yo estoy usando la versión 2.0.2. 3º. Existe un problema añadido de las distribuciones ilegales. Dado que los archivos deben de autofirmarse para que puedan ser usados, si le pasas un archivo creado pro ti mismo a cualquier otra persona, no solo estarás incurriendo en algo ilegal, que a muchos no les importa demasiado... sino que además estarás enviando un archivo que puede contener información personal. Por ejemeplo, en uno de los parches que existen por la red ilegales, contiene información personal de su creador. Esto es un error!!Hace mucho tiempo hicimos una
pequeña guia de como tan solo con un editor Hexadecimal, IDA o algún otro desensamblador se podía modificar de forma teórica el SpringBoard de nuestro dispositivo en los iPod Touch para activar las aplicaciones del iPhone, allá para la versión 1.1.4
Con la versión 2.0 nos sucede algo similar.
Cualquier Aplicación gratuita o de pago descargada desde AppStore se encuentra almacenada en un archivo .IPA. Este archivo no es más que un archivo zip que contiene la aplicación en sí, la firma y otros datos personales del comprador.
En teoría por lo tanto se podrían construir entonces paquetes .IPA de aplicaciones realizadas por la comunidad que previamente instalábamos por Cydia/Installer y tendríamos la opción también de instalarlos desde el mismo gestor de iTunes. Esto tiene algunas ventajas, aunque también algunos inconvenientes.
Ventajas:
Las puedes tener predescargadas en el PC, luego en el caso de restaurar tan solo con darle a un botón sería suficiente para sincronizarlas todas.
La velocidad de transferencia por cable es mucho mayor que por WIFI
Inconvenientes:
Las actualizaciones se tendrían que hacer también manualmente, es decir, descargando la aplicación al PC y después añadiéndola de nuevo a iTunes
No sería posible instalar aplicaciones que necesitasen tareas específicas como modificación de archivos de sistemas u otros. Es decir, no tendríamso script de instalación
Este es el método también que usan algunos piratas para poder sincronizar de manera simple las aplicaciones hackeadas del AppStore. Evidentemente no es el espíritu de esta entrada ni de este blog. Aquí solo informamos.
Como pasaba con el SpringBoard, no es posible publicar (dado que es ilegal) material de Apple modificado. No obstante esto no es una entrada de distribución, sino de aprendizaje. En este mini titurial aprenderemos como y por qué, cada cual puede modificar por su propia mano el archivo en cuestión: MobileInstallation
¿Y por qué tenemos que modificar nada? Bueno, Apple protege como es natural su negocio, y tan solo permite usar iTunes y sincronizar con nuestro dispositivo aplicaciones que han sido previamente firmada por ellos. Cualquier intento de sincronizar cualquier otra aplicación fallará. Aquí hay que hacer un pequeño parentesis para explicar que es esto de la firma digital, para quien no lo sepa ya:
Digamos que un archivo digital, el que sea, se puede firmar de forma que sea 100% seguro (hasta la fecha) que el destinatario final sepa si quien emitió dicho archivo es o no es genuino o si a sido modificado o no. Lo mejor es siempre un ejemplo. Pongamos por ejemplo una aplicación del Store, llamémosla Aplicación "X".
Apple emite por Store la aplicación X. A la aplicación X se le realiza el proceso de firma común. Esto normalmente es:
1º. Se calcula el Hash del archivo o los archivos. Una función hash devuelve siempre un valor alfanumérico de longitud fija, de forma que cualquier modificación, por mínima que fuese (aunque fuese la modificación de un solo byte) modificaria completamente este valor Hash. Este valor Hash es el que se usa para saber si la aplicación ha sido modificada o no. Imaginemos entonces que esta aplicación X tiene por Hash: 666777888 (por ejemplo).
2º. Este Hash se puede calcular en cualquier máquina final, tan solo hay que realizar la misma función de hash sobre el mismo archivo y el valor debería de ser el mismo que el que se calcula al inicio. Si el Hash calculado inicialmente coincide con el calculado por el usuario final podemos garantizar que el archivo no ha sido modificado, puesto que de hacerse, este tendría un Hash diferente. ¿Pero como sabe el usuario final cual fue el Hash calculado inicialmente?
3º. El hash se podría introducir en el mismo archivo, a lo mejor al final del archivo. Pero de ser así, cualquiera podría modificar el archivo y modificar también el hash, de forma que el usuario final vería que el hash calculado por él es el mismo que el calculado al inicio. Lo que se realiza es firmar. Cuando se habla de firma, lo que se hace es proteger el Hash inicial con un certificado de firma del emisor.
4º. Apple por tanto calcula el Hash de la aplicación X: 666777888 y lo protege con su certificado de firma, con una clave privada que solo Apple tiene y conoce. Pero como lo protege con su clave privada, cualquier usuario con la clave pública puede abrirlo y ver el Hash. La clave publica es algo que se conoce y tenemos en nuestro dispositivo. ¿Pero entonces que sentido tiene esto? Facil. Apple firma que el Hash es 666777888. Si cualquiera modifica el archivo el hash final cambiaría, con lo que debería de cambiar el hash protegido por la firma de Apple. Cualquiera puede ver el hash calculado por Apple, esto no es complicado, pero nadie puede reconstruir la firma de Apple. Un hacker debería modificar el archivo, recalcular el hash, eliminar la firma de Apple (hasta aquí todo perfecto), y firmar el nuevo Hash CON EL CERTIFICADO DE APPLE. Como no dispone de este certificado, lo único que podría hacer sería firmarlo con un mismo certificado o una autofirma. Pero entonces que pasaría en el usuario final?
5º. El usuario final reciviría el archivo, calcularía el Hash e iría a mirar el Hash calculado por Apple. Se encontraría que el certificado no pertenece a Apple!! si, el Hash puede ser correcto, pero la firma no es la de Apple, con lo que el usuario final intuye o imagina que el archivo ha sido modificado en el origen, puesto el archivo final que él tiene NO ES DE APPLE, si fuera de Apple, estaría firmado por ellos.
Bueno a priori puede resultar un poco confuso pero si se lee dos o tres veces se comprende. iTunes usa un sistema similar. Cuando introduces una aplicación en iTunes esta se la cree. Al intentar sincronizar con el iPod o el iPhone, estos verifican si está firmada por Apple. Al detectar evidentemente que no está firmada por Apple se impide la sincronización. El proceso de firma no se puede imitar, luego el vector de ataque es diferete.
Comprendemos ahora la necesidad de modificar este archivo de nuestro dispositivo. Este archivo es el que rechaza o acepta la aplicación, dependiendo de la verificación de la firma. Un simple salto incondicional en su código haría que fuese correcta o no la firma de la aplicación, se realizaría la sincronización.
Pero modificar este archivo tiene el mismo problema. Desde la versión 2.0 TODOS los archivos que se ejecutan dentro del dispositivo de Apple (incluyendo este archivo) tienen que estar firmados y autentificados. Así evita Apple que se pueda ejecutar cualquier tipo de archivo que no pertenezca a Apple. Estos chequeos los realiza el Kernel del dispositivo. Con lo que si modificásemos en principio este archivo, nuestro dispositivo sabría que ha sido modificado e impediría su ejecución, lo que nos daría de resultado final un dispositivo que no pasa del logo de Apple.
Gracias al JB del Dev-Team, estos al hacer el JB lo primero que se hace es parchear el kernel de Apple para que permita la ejecución de cualquier archivo. En realidad lo que se hace es saltarse el chequeo de la firma de Apple. Es decir, se verifica la firma sí, pero sea la de Apple o no lo sea se acepta. Es decir, nosotros en el archivo MobileInstallation vamos a hacer algo similar que lo que hicieron Dev-Team en el kernel. Aun así, la aplicación tendrá que estar firmada, aunque la firma no sea de Apple.
Esto nos dice que la modificación tiene dos partes. La primera será la búsqueda del salto incondicional en el código de la aplicación que nos permita pasar siempre la firma, sea de Apple o no lo sea. La segunda parte será firmar todo el proceso. Recordar que aunque podamos modificar lo que queramos, si no realizamos la firma, sea esta original o no lo sea, no podremos hacer nada. Cualquier archivo que se ejecute en el dispositivo tiene que está firmado. Con JB da igual que sea de Apple o de pepito grillo, pero tiene que estar firmado.
Manos a la obra.
Lo primero es obtener el archivo original, este archivo se encuentra en la ruta:
/System/Library/PrivateFrameworks/MobileInstallation.framework
el archivo se llama:
MobileInstallation (recordar que en MAC los archivos sin extensiones son ejecutables, equivalen a los .exe en windows)
Lo podemos sacar de nuestro dispositivo por SFTP sin problema alguno como es natural.
Lo siguiente es tener conocimientos de desensamblador para ARM (Que es el procesador que usa nuestro dispositivo). Con un buen desensamblador esto no sería problema. Desensamblar nos devuelve el código máquina del ejecutable, es decir, las instrucciones que se ejecutarán en la CPU. Mirando este código si se tiene experiencia, paciencia y un poco de prueba error (y suerte), es posible encontrar el lugar exacto. En mi caso he usado IDA. Nada mas abrir el archivo con IDA tenemos algo así, esto puede asustar un poco:

(click para agrandar)
En realidad asusta más de lo que es ;). En esas ventanas tenemos desde el código desensamblado como muchas otras cosas. Nos vamos a fijar tan solo en el código desensamblado.
Lo primero es tener imaginación. El problema a abordar como hemos dicho es la firma... el problema es saber exactamente el lugar donde se verifica la firma, y así poder actuar sobre dicho archivo. Pero es un archivo relativamente grande como para analizarlo poco a poco (que también es posible hacerse).
Todos los programadores al codificar usamos tanto comentarios de código como textos descriptivos para ciertas tareas. Estos son completamente necesarios, más aun cuando trabajamos con otros compañeros. Sin estas herramientas sería muchas veces casi imposible comprender el código de algo. Estas cadenas de texto son siempre un buen punto de partida. Si estas cadenas noo nos diesen una solución, tendríamos que pasar a entornos simulados, puntos de ropturas y una mayor compresión de ensamblador. Es decir, en nuestro caso, estoy seguro que si nuestro dispositivo detecta una firma no válida, este de una forma u otra tendría asociado algún error.
Nota: Antes de editar la entrada dejé puesto que estos comentarios los dejaban los mismos programadores por error. No me refería literalmente a eso. Los comentarios de código, al compilar una aplicación son ignorados, no forman parte del código. Siento la confusión.
En este caso lo tenemos muy muy facil ;). El código de Apple está bien comentado (cosa además que no es que sea un fallo de programación, es lo ideal).
Nota 2: Cuando hago referencia aquí a que el código está bien comentado, me refiero no a comentario de código, sino a textos descriptivos de ciertas partes del código.
Luego lo primero es la imaginación. El problema está con la firma... firmar en ingles es to "sign", y la firma en sí "signature". Pues lo primero será buscar en todo el código por "signature".
Curiosamente tenemos dos entradas. Una parece ser la función buscada claramente, mientras que la segunda entrada parece hacer referencia a precisamente lo que comentamos anteriormente, un registro de error interno de la aplicación. Este registro es necesario, sino que que pasaría? la aplicación simplemente dejaría de funcionar? siempre tiene que mostrar algún error, aunque sea a nivel interno. Como decía nos encontramos con lo siguiente:

Si no se aprecia hacer click para agrandar la imagen
No hace falta saber demasiado de ensamblador para ver lo que hace esa función. Es la función que se pasará al detectar uan firma no válida. Es decir, el código se ejecutará y en algún momento se pedirá que se verifique la firma. Se entrará en dicha función y dependiendo de si es correcta la firma o no, se modificará en este caso el valor del registro R4. El registro R4 según vemos en esta imagen, y esta otra:

debe de ser "Cero". De no ser Cero dicho registro, se entraría en las subsiguientes rutinas haciendo que la aplicación fuese rechazada. La comparación final la hace como vemos en la imagen con el código:
CMP R4, #0
BEQ loc_33245F1C
Es decir, compara el contenido de R4 con cero. Si es cero, entonces se ejecutará la instrucción de salto BEQ (Que realiza el salto si la comparación anterior es igual). De no ser cero el contenido en R4, la instrucción BEQ se omitiría y se ejecutaría la siguiente instrucción, que sería en este caso
LDR R0, =(___FUNCTION__.14048 - 0x33244EBC)
Que sería ya una de las funciones que al final nos devolverá el error de instalación de aplicación.
Sabiendo esto hay muchas formas de realizar la modificación. La primera forma y siempre clásica sería cambiar el salto BEQ por un salto incondicional BAL. Modificando por ejemplo tan solo esto, BEQ por BAL, sea cual sea el valor de R4 saltará a la dirección loc_33245F1C.
Otra solución sería forzar que en R4 hay un cero llegado a este punto, para ello lo más simple sería situarnos en la parte del código donde se elimina este valor (cuando se verifica la firma y esta es incorrecta). Esto se encuentra un pelin más arriba:
ADD R0, PC, R0 ; "verify_executable"
ADD R1, PC, R1 ; "Could not validate signature: %x"
BL _installlog
MVN R4, #0
Si vemos esa última instrucción, MVN R4, #0 lo que está forzando es que en R4 jamás haya un cero. La instrucción MOV asigna un valor la instrucción MVN lo asigna pero complementado. Es decir, si en R4 antes de ejecutar esa instrucción teníamos un cero, al pasar esta instrucción en R4 aparecerá el cero en complemento a uno es decir:
00000000 (cero en binario y sin complementar) -> 11111111 (el valor una vez realizado el complemento a uno)
Es decir, los ceros se cambian a unos y los unos a ceros. En realidad es algo técnico tan solo para decir que en R4 al pasar por esa instrucción jamás se almacenará un cero.
Pero si la misma instruccíon MVN la modificásemos por MOV:
MOV R4, #0
Lo que haríamos sería que cuando la verificación de firma fracasara, en R4 se introduciría un cero. Luego a efectos prácticos... si la verificación es correcta es un cero, y si no es correcta es tambien un cero. Luego el salto BEQ que vimos al principio también se cumpliría siempre.
Como vemos hay muchas opciones de atajar el problema, todo es cuestion de ver donde deseamos colocar la modificación. Se podrían hacer soluciones más complejas, pero como después estoy hay que pasarlo a un parche o a una edición hexadecimal, es mucho mejor intentar solo modificar instrucciones como estamos viendo, un MVN por un MOV o un BEQ por un BAL. A efectos de edición, esto tan solo es un byte a cambiar en el archivo después.
Bueno, por ahora vamos bien. Hemos localizado el punto exacto que creemos es el responsable de que falle la verificación. Ahora solo toca corregirlo. Lo primero es ver que valor hexadecimal corresponde a cada instrucción, no todos se conocen el taco de instrucciones ARM y el opcode de cada instrucción. Y yo tampoco me lo sé. Así que vamos a averiguarlo. En IDA o en otros desensambladores podemos modificar el código evidentemente. Vamos a hacer el ejemplo a priori con la la modificación de MVN a MOV, pero igual se puede hacer para el otro ejemplo.
Sabemos que instrucción vamos a cambiar, nos queda por saber que opcode le corresponde a dicha instrucción, cual es el offset donde se encuentra dicha instrucción y que opcode corresponde a la nueva instrucción que vamos a cambiar por la vieja.
Para todo ello tan solo tenemos que posicionarnos en la linea de la instrucción a modificar. A la izquierda de dicha opción tenemos el Offset (el desplazamiento en memoria) de dicha instrucción:
__text:33244E98 MVN R4, #0
es decir, en mi caso 33244E98. Si cambio de pestaña y abandono la vista desensamblada y paso a vista hexadecimal, desde el mismo IDA veo esto:

y aquí si que obtengo ya lo que me interesa, el codigo hexadecimal de toda la instrucción:
00 40 E0 E3
Pero de ahí no puedo sacar el Opcode, es decirl el valor hexadecimal de la instrucción. Sé qie es uno de eos valores. Evidentemente el 00 no será, el 40 lo dudo, luego lo que queda es E0 ó E3. Por simple lógica saldría solo. Si la instrucción es MVN R4, #0 seguramente el código hexadecimal equivaldría a:
40 = R4
00 = 0
E0 = Opcode
E3 = Método de direccionamiento, en este caso direccionamiento literal, dado que se está pasando a R4 un valor directamente.
De todos modos es facil saberlo, basta con mirar alguna instrucción MVN o MOV más para estar seguro al 100% de cual es el Opcode. Igualmente es facil imaginar el opcode de MOV, que ya de por sí os puedo decir que es A0.
Luego si el código en hexadecimal que teníamos en un principio:
00 40 E0 E3
lo tendremos que modificar a
00 40 A0 E3
Lo que nos queda por saber es que offset es. Es decir, en que lugar del archivo editado hexadecimalmente tendremos que posicionarnos. Pero este dato tambien nos lo dice el programita. Tan solo hay que seleccionar la instrucción. Abajo a la izquierda en mi caso, saldrá la posición de memoria. Hay que tener claro que la posición que nos daba anteriormente, 33244E98 es relativa al archivo desensamblado, no relativa al archivo editado directamente. Hay que tener esto muy presente. La direción real de esta cadena hexadecimal nos la da el mismo programa como hemos dicho, aunque está un poco más escondida. Si miramos bien en esta captura lo veremos:

Abajo a la derecha resaltado en amarillo lo tenemos. El offset de la izquierda es el real dentro del archivo, mientras el de la derecha es el relativo al programa. Para que este sea correcto debe de estar seleccionada la instrucción MVN a modificar. Luego el offset que debemos de apuntar es:
6E98
En realidad este es el inicio de la secuencia completa hexadecimal 00 40 E0 E3. Es decir, la posición 6E98 sería para el valor 00, 6E99 para el valor 40, 6E9A para E0 y para acabar 6E9B para el valor E3. Luego si queremos ser completamente exactos, el offset sería
0x00006E9A
Todo lo que hemos visto tan solo ha sido para:
1º. Encontrar el offset, el punto preciso donde se decidirá si la firma es válida o no lo es. Este punto en nuestro ejemplo es el offset 0x00006E98.
2º. Saber el valor a modificar en dicho punto. En nuestro caso el valor almacenado en el original es E0, el valor que tomará su lugar será A0.
Con estos datos podemos cerrar ya IDA y abrir el archivo original MobileInstallation con cualquier editor Hexadecimal. Yo he usado WinHex, pero cualquier otro es igual de válido. En el editor hexadecimal tan solo tenemos que posicionarnos en la posición apuntada por el offset, localizar el valor y modificarlo por el nuevo:


Ya solo queda guardar el archivo resultante y habremos modificado correctamente el archivo. Solo queda probarlo... ¿o no? Queda firmarlo.
Al realizar las modificaciones que hemos llevado a cabo, hemos logrado que este archivo no pueda ser leído por nuestro dispositivo, dado que el Hash de el archivo original y el nuevo es completamente diferente, y la firma de Apple nos lo rechazaría. Por eso es necesario realizar un último paso de firmado.
Esto lo llevaremos a cabo dentro del mismo dispositivo. Lo primero será tener el programa para firmar. Este lo podemos obtener desde el mismo Cydia o lo podemos descargar directamente desde:
AQUIEste archivo lo copiamos por ejemplo dentro de nuestro dispositivo en /bin y evidentemente le asignamos permisos 0755
copiarlo en dicha carpeta es para evitar tener que desplazarnos a la carpeta que sea para invocarla.
Para acabar copiaremos el mismo MobileInstallation a nuestro dispositivo, sustituyendo el original. Evidentemente antes de hacer esto guardamos una copia de seguridad del otro. Una vez copiado a su carpeta de sistema tambien debemos de modificar los permisos a 0755.
Ya solo queda ejecutar esta instrucción por SSH para realizar el firmado:
ldid -s [archivo]
es decir, en nuestro caso:
ldid -s /System/Library/PrivateFrameworks/MobileInstallation.framework/MobileInstallation
Tras ejecutar esto por SSH o por terminal de nuestro dispositivo, se realizará el firmado de la aplicación modificada y podrá ser perfectamente ejecutada por nusetro iPod Touch o nuestro iPhone.
Reiniciaremos y listo. Tendremos un MobileInstallation completmaente funciona, actualizado para la versión 2.0.2 o la versión que sea y todo por nosotros mismos, que es lo que tiene más merito de todo.
Otro día, más.
Un saludo.