Après avoir maîtrisé les outils mentionnés ci-dessus, vous pouvez commencer à développer des plug-ins. Tout d’abord, vous devez réfléchir aux besoins que le plug-in doit implémenter et trouver les vues et les contrôleurs appropriés. La première étape consiste à utiliser class-dump pour analyser l'application shellée. Class-dump peut analyser tous les noms de classe et déclarations de méthodes en fonction de la table des symboles dans le fichier Mach-O.
~ » ./class-dump -H -o /header/path WeChat
Après avoir exporté les fichiers d'en-tête, extrayez ces fichiers d'en-tête dans un projet XCode pour faciliter les recherches ultérieures.
Une fois que vous disposez du fichier d’en-tête, vous devez le verrouiller sur la classe de contrôleur de l’interface appropriée. Utilisez OpenSSH pour vous connecter à l'iPhone jailbreaké avec Mac, utilisez Cycript pour injecter le processus, appelez la méthode [[UIApp keyWindow] recursiveDescription]
pour obtenir la hiérarchie de la vue actuelle, obtenez l'adresse d'une vue et appelez à plusieurs reprises le [#0x2b2b2b00 nextResponder]
, vous obtiendrez enfin la classe du contrôleur d'interface.
À ce stade, il existe généralement deux points d’entrée pour poursuivre l’analyse inverse.
Si l'on souhaite que l'opération Hook soit déclenchée par l'événement touch d'une vue sur l'interface, il existe généralement deux manières de localiser l'action :
[button allTargets]
pour obtenir l'adresse des cibles, [button actionsForTarget: Targets forControlEvent: [button allControlEvents]]
pour obtenir la méthode d'action correspondante.La source de données du contrôleur peut être utilisée lors de l'écriture du plug-in. La source de données peut être obtenue en analysant la liste des variables membres de la classe de contrôleur ou en analysant la méthode proxy de la source de données de certaines vues (telles que TableView). Avec une source de données, des informations plus riches sur le contrôleur peuvent être obtenues.
Un simple plug-in peut verrouiller la cible et la connecter en utilisant uniquement Cycript, mais dans de nombreux cas, la fonction cible a plusieurs paramètres et la logique est relativement complexe. Si vous souhaitez connaître les détails de son implémentation interne, vous devez le faire. utilisez l'artefact d'analyse statique IDA ---- Il peut décompiler le code écrit en Objective-C en code assembleur, et les détails d'implémentation de la fonction seront clairs en un coup d'œil. Lors de l'analyse des résultats du désassemblage, en raison des caractéristiques de transmission de messages d'Objective-C, l'envoi général de messages appelle en fait la fonction objc_msgSend
. Rappelez-vous simplement la signification de chaque paramètre de objc_msgSend
et la convention d'appel de l'architecture ARM, et vous pourrez restaurer avec succès l'appel de fonction à partir du code assembleur. La convention d'appel est que les quatre premiers paramètres de la fonction sont transmis à l'aide des registres généraux R0-R3, que davantage de paramètres sont placés sur la pile et que la valeur de retour est stockée dans le registre R0. Ensuite, [aObject aMessage: arg1];
correspond à objc_msgSend(aObject, aMessage, arg1);
R0 stocke l'adresse du destinataire du message, R1 stocke le sélecteur et R2 stocke l'adresse du premier paramètre. Le format de message pour plus de paramètres est le suivant :
objc_msgSend(R0, R1, R2, R3, *SP, *(SP + sizeOfLastArg), …)
Grâce au format ci-dessus, la logique d'une certaine fonction peut être analysée étape par étape. Si cela est complété par le débogage dynamique en une seule étape de LLDB et le débogage du suivi des points d'arrêt à l'adresse d'une certaine instruction d'assemblage, cela aidera à comprendre les détails de l'implémentation de la fonction.
Il existe de nombreuses solutions pour la fonction cible Hook, mais en principe elles sont toutes basées sur les caractéristiques dynamiques d'Objective-C et utilisent la méthode Swizzling pour remplacer l'implémentation d'origine. Cette fois, nous présenterons en détail l’utilisation de la bibliothèque CaptainHook. Cette bibliothèque est implémentée sur la base de MSHookMessageEx()
dans Cydia Substrate. La déclaration de cette fonction est :
void MSHookMessageEx(Class _class, SEL message, IMP hook, IMP *old);
Une fois iOSOpenDev installé, vous pouvez utiliser le modèle CaptainHook Tweak pour créer un projet. La bibliothèque CaptainHook introduit une nouvelle syntaxe pour écrire des fonctions Hook. Tout d’abord, chargez la classe où se trouve la fonction à Hooker dans CHConstructor()
, telle que CHLoadLateClass(UIView)
. Enregistrez ensuite la fonction CHHook(argNumber, className, arg1, arg2)
pour qu'elle soit Hooked. La définition macro de CHConstructor
est la suivante :
#define CHConcat(a, b) CHConcat_(a, b)
#define CHConstructor static __attribute__((constructor)) void CHConcat(CHConstructor, __LINE__)()
Le contenu après __attribute__((constructor))
est garanti pour s'exécuter lorsque le dylib est chargé, généralement au démarrage du programme. De même, d'autres symboles sont introduits via des définitions de macros.
Permettez-moi de vous présenter comment utiliser CaptainHook pour déclarer la fonction Hook et l'implémenter, directement dans le code.
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 */
}
Une fois l'écriture terminée, connectez-vous à l'iPhone disponible pour la compilation afin de vous assurer qu'une bibliothèque dynamique correspondant à l'architecture est générée.
Utilisez l'outil yololib pour injecter le fichier dylib compilé dans la liste Load Commands du fichier exécutable Mach-O.
~ » ./yololib [binary] [dylib file]
La structure des fichiers Mach-O se compose principalement de trois parties. La partie frontale est la structure d'en-tête, qui enregistre le type de plate-forme de Mach-O, le type de fichier, le nombre de LoadCommands et d'autres informations ; suivi de l'en-tête est la partie des commandes de chargement. En analysant cette partie, la structure logique du fichier et. son emplacement dans le fichier virtuel peut être déterminé. L'outil yololib modifie les informations dans la section Load Commands pour charger dylib. Le processus spécifique de mise en œuvre est le suivant :
Étant donné que les informations sur les commandes de chargement changent, ncmds et sizeofcmds dans la structure d'en-tête correspondante changeront, l'en-tête doit donc être modifié en premier :
// 取出 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);
Modifiez ensuite la section Load Commands et ajoutez les informations de chargement 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);
Les dernières données écrites dans dylib.
fwrite([data bytes], [data length], 1, newFile);
Utilisez la commande codesign
pour re-signer la bibliothèque dynamique générée et tous les fichiers exécutables dans l'APP (y compris l'extension APP dans le dossier Plugin). Utilisez la commande xcrun -sdk iphoneos PackageApplication -v
pour empaqueter la bibliothèque dynamique et tous les fichiers ensemble. Vous comprendrez tout le processus. S'il existe un certificat d'entreprise, l'application signée et packagée peut être installée sur un iPhone non jailbreaké qui fait confiance au certificat. Une star de l'aviron !