Después de dominar las herramientas mencionadas anteriormente, puede comenzar a desarrollar complementos. Primero, debe pensar en las necesidades que debe implementar el complemento y encontrar las vistas y controladores relevantes. El primer paso es utilizar class-dump para analizar la aplicación desgranada. Class-dump puede analizar todos los nombres de clases y declaraciones de métodos según la tabla de símbolos en el archivo Mach-O.
~ » ./class-dump -H -o /header/path WeChat
Después de exportar los archivos de encabezado, incorpore estos archivos de encabezado a un proyecto XCode para facilitar las búsquedas posteriores.
Una vez que tenga el archivo de encabezado, debe bloquearlo en la clase de controlador de la interfaz correspondiente. Use OpenSSH para conectarse al iPhone con jailbreak con Mac, use Cycript para inyectar el proceso, llame al método [[UIApp keyWindow] recursiveDescription]
para obtener la jerarquía de la vista actual, obtenga la dirección de una vista y llame repetidamente al [#0x2b2b2b00 nextResponder]
, finalmente obtendrá la clase de controlador de interfaz.
En este punto, generalmente hay dos puntos de entrada para continuar el análisis inverso.
Si queremos que la operación Hook se active mediante el evento táctil de una vista en la interfaz, generalmente hay dos formas de ubicar la acción:
[button allTargets]
para obtener la dirección de destinos, [button actionsForTarget: Targets forControlEvent: [button allControlEvents]]
para obtener el método de acción correspondiente.La fuente de datos del controlador se puede utilizar durante la escritura del complemento. La fuente de datos se puede obtener analizando la lista de variables miembro de la clase de controlador o analizando el método proxy de la fuente de datos de algunas vistas (como TableView). Con una fuente de datos se puede obtener información más rica sobre el responsable del tratamiento.
Un complemento simple puede bloquear el objetivo y engancharlo usando solo Cycript, pero en muchos casos la función de destino tiene múltiples parámetros y la lógica es relativamente compleja. Si desea conocer los detalles de su implementación interna, debe hacerlo. utilice el artefacto de análisis estático IDA ---- Puede descompilar el código escrito en Objective-C en código ensamblador, y los detalles de implementación de la función serán claros de un vistazo. Al analizar los resultados del desmontaje, debido a las características de transmisión de mensajes de Objective-C, el envío general de mensajes en realidad llama a la función objc_msgSend
. Simplemente recuerde el significado de cada parámetro de objc_msgSend
y la convención de llamada de la arquitectura ARM, y podrá restaurar con éxito la llamada a la función desde el código ensamblador. La convención de llamada es que los primeros cuatro parámetros de la función se pasan utilizando los registros generales R0-R3, se insertan más parámetros en la pila y el valor de retorno se almacena en el registro R0. Entonces [aObject aMessage: arg1];
corresponde a objc_msgSend(aObject, aMessage, arg1);
R0 almacena la dirección del receptor del mensaje, R1 almacena el selector y R2 almacena la dirección del primer parámetro. El formato de mensaje para más parámetros es el siguiente:
objc_msgSend(R0, R1, R2, R3, *SP, *(SP + sizeOfLastArg), …)
A través del formato anterior, la lógica de una determinada función se puede analizar paso a paso. Si esto se complementa con la depuración dinámica de un solo paso de LLDB y la depuración de seguimiento de puntos de interrupción en la dirección de una determinada declaración de ensamblaje, ayudará a comprender los detalles de la implementación de la función.
Existen muchas soluciones para la función de destino Hook, pero en principio todas se basan en las características dinámicas de Objective-C y utilizan Method Swizzling para reemplazar la implementación original. Esta vez presentaremos en detalle el uso de la biblioteca CaptainHook. Esta biblioteca está implementada en base a MSHookMessageEx()
en Cydia Substrate. La declaración de esta función es:
void MSHookMessageEx(Class _class, SEL message, IMP hook, IMP *old);
Una vez instalado iOSOpenDev, puede utilizar la plantilla CaptainHook Tweak para crear un proyecto. La biblioteca CaptainHook introduce una nueva sintaxis para escribir funciones Hook. Primero, cargue la clase donde se encuentra la función que se va a enganchar en CHConstructor()
, como CHLoadLateClass(UIView)
. Luego registre la función CHHook(argNumber, className, arg1, arg2)
para que se enganche. La definición macro de CHConstructor
es la siguiente:
#define CHConcat(a, b) CHConcat_(a, b)
#define CHConstructor static __attribute__((constructor)) void CHConcat(CHConstructor, __LINE__)()
Se garantiza que el contenido después de __attribute__((constructor))
se ejecutará cuando se cargue dylib, generalmente cuando se inicia el programa. De manera similar, otros símbolos se introducen mediante definiciones de macros.
Permítanme presentarles cómo usar CaptainHook para declarar la función Hook e implementarla directamente en el código.
CHDeclareClass ( BXViewController );
CHOptimizedMethod ( 0 , self , void , BXViewController , viewDidLoad ) {
CHSuper ( 0 , BXViewController , viewDidLoad );
/* HERE TO WRITE YOUR CODE */
}
CHDeclareMethod0 ( void , BXViewController , addFriends ) {
/* HERE TO WRITE YOUR CODE */
}
Una vez completada la escritura, conéctese al iPhone que tiene a mano para compilarlo y asegurarse de que se genere una biblioteca dinámica correspondiente a la arquitectura.
Utilice la herramienta yololib para inyectar el archivo dylib compilado en la lista de comandos de carga del archivo ejecutable Mach-O.
~ » ./yololib [binary] [dylib file]
La estructura de los archivos Mach-O consta principalmente de tres partes. La parte frontal es la estructura del encabezado, que guarda el tipo de plataforma de Mach-O, el tipo de archivo, el número de LoadCommands y otra información, seguida del encabezado es la parte de los comandos de carga. Al analizar esta parte, se analiza la estructura lógica del archivo. Se puede determinar su ubicación en el archivo virtual. La herramienta yololib cambia la información en la sección Cargar comandos para cargar dylib. El proceso de implementación específico es el siguiente:
Debido a que la información de los comandos de carga cambia, tanto ncmds como sizeofcmds en la estructura del encabezado correspondiente cambiarán, por lo que primero se debe modificar el encabezado:
// 取出 Header
fseek(newFile, top, SEEK_SET);
struct mach_header mach;
fread(&mach, sizeof(struct mach_header), 1, newFile);
NSData* data = [DYLIB_PATH dataUsingEncoding:NSUTF8StringEncoding];
// 计算 dylib 的大小
uint32_t dylib_size = (uint32_t)[data length] + sizeof(struct dylib_command);
dylib_size += sizeof(long) - (dylib_size % sizeof(long));
// 修改 cmds 和 sizeofcmds
mach.ncmds += 1;
uint32_t sizeofcmds = mach.sizeofcmds;
mach.sizeofcmds += dylib_size;
// 写回修改后的 Header
fseek(newFile, -sizeof(struct mach_header), SEEK_CUR);
fwrite(&mach, sizeof(struct mach_header), 1, newFile);
Luego cambie la sección Cargar comandos y agregue información de carga de dylib:
fseek(newFile, sizeofcmds, SEEK_CUR);
// 创建一个 dylib 类型的 command
struct dylib_command dyld;
fread(&dyld, sizeof(struct dylib_command), 1, newFile);
// 修改 dyld 结构体数据
dyld.cmd = LC_LOAD_DYLIB;
dyld.cmdsize = dylib_size;
dyld.dylib.compatibility_version = DYLIB_COMPATIBILITY_VERSION;
dyld.dylib.current_version = DYLIB_CURRENT_VER;
dyld.dylib.timestamp = 2;
dyld.dylib.name.offset = sizeof(struct dylib_command);
// 写回修改
fseek(newFile, -sizeof(struct dylib_command), SEEK_CUR);
fwrite(&dyld, sizeof(struct dylib_command), 1, newFile);
Los últimos datos escritos en dylib.
fwrite([data bytes], [data length], 1, newFile);
Utilice el comando codesign
para volver a firmar la biblioteca dinámica generada y todos los archivos ejecutables en la APLICACIÓN (incluida la extensión de la APLICACIÓN en la carpeta de complementos). Utilice el comando xcrun -sdk iphoneos PackageApplication -v
para empaquetar la biblioteca dinámica y todos los archivos juntos. Comprenderás todo el proceso de. Si hay un certificado empresarial, la aplicación firmada y empaquetada se puede instalar en un iPhone sin jailbreak que confíe en el certificado. ¡Una estrella del remo!