La mise à jour 2020.1.14 ne nécessite pas d'installation de jailbreak
Cet article est un tutoriel sur l'inversion du fichier binaire WeChat pour réaliser le transfert de courtes vidéos dans Moments Du code d'assemblage initial à l'installation de re-signature finale et à d'autres opérations, il vous apprendra étape par étape comment jouer avec WeChat ! Une fois que vous l’aurez appris, il sera facile de faire de l’ingénierie inverse sur d’autres fonctions de WeChat.
Cet article est divisé en deux parties en raison de sa longueur. La première partie explique le travail inverse, c'est-à-dire comment trouver les fonctions et méthodes pertinentes à implémenter. La deuxième partie explique comment re-signaturer l'installation sur des machines non jailbreakées. peaufiner l'installation sur les machines jailbreakées. Processus détaillé.
La deuxième partie du texte fournit également le code permettant à WeChat de récupérer automatiquement les enveloppes rouges et de modifier le nombre d'étapes WeChat. Celles-ci peuvent être trouvées étape par étape en suivant la routine de cet article et ne seront pas répétées ici.
Avant de vous entraîner, vous devez préparer un téléphone mobile jailbreaké puis installer tous les outils répertoriés ci-dessous. IDA et Reveal sont tous deux des versions crackées. La version authentique d'IDA coûte plus de 2 000 dollars. Cela vaut vraiment la peine pour un outil d'ingénierie inverse aussi génial. Cependant, si vous n'êtes pas une entreprise spécialisée dans l'ingénierie inverse, il n'y en a pas. je dois utiliser la version authentique. La prochaine version crackée de Windows sera OK, je ne la trouve pas encore sur Mac. Vous pouvez utiliser Hopper pour remplacer IDA sur Mac, qui est également un outil de rétro-ingénierie très puissant. Sans plus tarder, commençons !
Remarque : Le fichier binaire WeChat inversé dans cet article est la version 6.3.28. S'il s'agit d'une version différente de WeChat, l'adresse de base dans le fichier binaire est également différente.
Environnement inversé pour machine jailbreakée MacOS + iPhone5S 9.1 <br> Utilisez d'abord dumpdecrypted pour briser le shell de WeChat (si vous ne savez pas comment, veuillez lire ce tutoriel écrit par moi), obtenez un fichier WeChat.decrypted et jetez d'abord ce fichier dans IDA pour analyse (un fichier binaire d'environ 60 Mo , il faut près de 40 minutes à IDA pour analyser End), utilisez class-dump pour exporter tous les fichiers d'en-tête
LeonLei-MBP:~ gaoshilei$ class-dump -S -s -H /Users/gaoshilei/Desktop/reverse/binary_for_class-dump/WeChat.decrypted -o /Users/gaoshilei/Desktop/reverse/binary_for_class-dump/class-Header/WeChat
J'ai une belle-mère ! Il y a un total de 8 000 fichiers d'en-tête, et WeChat a vraiment énormément de travail ! Calmez vos émotions, rassemblez vos pensées et continuez. Pour obtenir le lien de téléchargement d'une courte vidéo, recherchez la vue qui lit la vidéo et suivez les indices pour trouver l'URL de la courte vidéo. Utilisez Reveal pour afficher la fenêtre de lecture de la courte vidéo. Vous pouvez voir que l'objet WCContentItemViewTemplateNewSigh est la fenêtre de lecture de la courte vidéo. Ses sous-vues incluent WCSightView, SightView et SightPlayerView. Lorsque vous enregistrez une vidéo dans vos favoris, appuyez longuement sur la vidéo pour faire apparaître l'option. Il peut donc y avoir des méthodes liées aux gestes dans la classe WCContentItemViewTemplateNewSight. Recherchez des indices dans le fichier d'en-tête que vous venez d'exporter.
- (void)onLongTouch;
- (void)onLongPressedWCSight:(id)arg1;
- (void)onLongPressedWCSightFullScreenWindow:(id)arg1;
Ces méthodes sont liées au geste d'appui long. Recherchez ces fonctions dans IDA et affichez-les une par une. onLongPressedWCSight et onLongPressedWCSightFullScreenWindow sont relativement simples, onLongTouch est relativement long et j'ai trouvé que la méthode Favorites_Add est appelée en interne, car lorsque vous appuyez longuement sur la vidéo, une option qui apparaît est Favoris, et j'ai vu cet appel de fonction
ADRP X8, #selRef_sightVideoPath@PAGE
LDR X1, [X8,#selRef_sightVideoPath@PAGEOFF]
J'ai eu l'adresse de la courte vidéo ici. On peut supposer que cette fonction est liée à la collection. Brisons le point et testons-le.
(lldb) im li -o -f
[ 0] 0x000000000003c000 /var/mobile/Containers/Bundle/Application/2F1D52EC-C57E-4F95-B715-EF04351232E8/WeChat.app/WeChat(0x000000010003c000)
Vous pouvez voir que l'ASLR de WeChat est 0x3c000. Recherchez les adresses de base de ces trois fonctions dans IDA et définissez respectivement les points d'arrêt.
(lldb) br s -a 0x1020D3A10+0x3c000
Breakpoint 1: where = WeChat`___lldb_unnamed_symbol110094$$WeChat + 28, address = 0x000000010210fa10
(lldb) br s -a 0x1020D3370+0x3c000
Breakpoint 2: where = WeChat`___lldb_unnamed_symbol110091$$WeChat + 8, address = 0x000000010210f370
(lldb) br s -a 0x1020D33E4+0x3c000
Breakpoint 3: where = WeChat`___lldb_unnamed_symbol110092$$WeChat + 12, address = 0x000000010210f3e4
Revenez à WeChat et appuyez longuement sur la courte vidéo pour voir comment le point d'arrêt est déclenché.
Process 3721 stopped
* thread #1: tid = 0x658fc, 0x000000010210f370 WeChat`___lldb_unnamed_symbol110091$$WeChat + 8, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
frame #0: 0x000000010210f370 WeChat`___lldb_unnamed_symbol110091$$WeChat + 8
WeChat`___lldb_unnamed_symbol110091$$WeChat:
-> 0x10210f370 <+8>: add x29, sp, #16 ; =16
0x10210f374 <+12>: mov x19, x0
0x10210f378 <+16>: adrp x8, 4968
0x10210f37c <+20>: ldr x0, [x8, #744]
(lldb) c
Process 3721 resuming
Process 3721 stopped
* thread #1: tid = 0x658fc, 0x000000010210fa10 WeChat`___lldb_unnamed_symbol110094$$WeChat + 28, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x000000010210fa10 WeChat`___lldb_unnamed_symbol110094$$WeChat + 28
WeChat`___lldb_unnamed_symbol110094$$WeChat:
-> 0x10210fa10 <+28>: add x29, sp, #96 ; =96
0x10210fa14 <+32>: sub sp, sp, #96 ; =96
0x10210fa18 <+36>: mov x19, x0
0x10210fa1c <+40>: adrp x8, 4863
……
Il a été constaté que le point d'arrêt 2 a été déclenché en premier, puis le point d'arrêt 1 a été déclenché, puis les points d'arrêt 2 et 1 ont été déclenchés une fois que chacun a été très silencieux. Vous pouvez exclure la connexion entre onLongPressedWCSightFullScreenWindow et la collection de courtes vidéos. Les traces de courtes vidéos doivent être retrouvées dans les deux méthodes restantes. Trouvez C à V et trouvez M en suivant les indices qui ont fait leurs preuves ! Utilisez cyript pour injecter WeChat et obtenir le contrôleur où se trouve la vue qui lit la courte vidéo.
cy# [#0x138c18030 nextResponder]
#"<WCTimeLineCellView: 0x138c34620; frame = (0 0; 319 249); tag = 1048577; layer = <CALayer: 0x138362ba0>>"
cy# [#0x138c34620 nextResponder]
#"<UITableViewCellContentView: 0x138223c70; frame = (0 0; 320 256); gestureRecognizers = <NSArray: 0x1384ec480>; layer = <CALayer: 0x138081dc0>>"
cy# [#0x138223c70 nextResponder]
#"<MMTableViewCell: 0x138c9f930; baseClass = UITableViewCell; frame = (0 307; 320 256); autoresize = W; layer = <CALayer: 0x1382dcd10>>"
cy# [#0x138c9f930 nextResponder]
#"<UITableViewWrapperView: 0x137b57800; frame = (0 0; 320 504); gestureRecognizers = <NSArray: 0x1383db660>; layer = <CALayer: 0x138af20c0>; contentOffset: {0, 0}; contentSize: {320, 504}>"
cy# [#0x137b57800 nextResponder]
#"<MMTableView: 0x137b8ae00; baseClass = UITableView; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x138adb590>; layer = <CALayer: 0x138956890>; contentOffset: {0, 99.5}; contentSize: {320, 3193}>"
cy# [#0x137b8ae00 nextResponder]
#"<UIView: 0x138ade5c0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x138ac9990>>"
cy# [#0x138ade5c0 nextResponder]
#"<WCTimeLineViewController: 0x1379eb000>"
Recherchez le contrôleur auquel WCContentItemViewTemplateNewSight appartient via la chaîne de répondeurs et il s'agit de WCTimeLineViewController. Aucun indice précieux n'a été trouvé dans le fichier d'en-tête de cette classe, mais nous avons remarqué que la vue où se trouve la courte vidéo appartient à MMTableVIewCell (voir le graphique d'analyse Reveal ci-dessus), qui est la TableView et les données de cellule les plus familières pour chaque iOS. Il est attribué via la méthode proxy de UITableViewDataSource - tableView:cellForRowAtIndexPath:
Grâce à cette méthode, vous pouvez certainement connaître l'ombre de M. Recherchez [WCTimeLineViewController tableView:cellForRowAtIndexPath:]
dans IDA et localisez l'adresse de base 0x10128B6B0 :
__text:000000010128B6B0 ADRP X8, #selRef_genNormalCell_indexPath_@PAGE
La fonction ici est la méthode de génération de cellules dans WCTimeLineViewController. En plus de cette méthode, il existe trois autres méthodes pour générer des cellules dans cette classe :
- (void)genABTestTipCell:(id)arg1 indexPath:(id)arg2;
- (void)genRedHeartCell:(id)arg1 indexPath:(id)arg2;
- (void)genUploadFailCell:(id)arg1 indexPath:(id)arg2;
Au sens littéral, on peut deviner que la méthode normale devrait être la génération de petites cellules vidéo. Continuez à chercher des indices dans IDA
__text:0000000101287CC8 ADRP X8, #selRef_getTimelineDataItemOfIndex_@PAGE
La méthode ci-dessus se trouve dans genNormalCell:IndexPath:
:. Vous pouvez hardiment deviner que cette méthode est une méthode pour obtenir des données TimeLine (moments). Les données de la courte vidéo doivent également être obtenues via cette méthode, et IDA peut voir les données. appelez cette méthode. Une méthode appelée selRef_getTimelineDataItemOfIndex_
, l'obtention de DataItem semble être la source de données de la cellule ! Ensuite, utilisez LLDB pour définir des points d'arrêt afin de vérifier la conjecture. Vous pouvez trouver l'adresse de base correspondant à cette méthode via IDA : 0x101287CE4. Imprimez d'abord le décalage ASLR de l'exécution de WeChat.
LeonLei-MBP:~ gaoshilei$ lldb
(lldb) process connect connect://localhost:1234
(lldb) im li -o -f
[0] 0x0000000000050000 /var/mobile/Containers/Bundle/Application/2DCE8F30-9B6B-4652-901C-37EB1FF2A40D/WeChat.app/WeChat(0x0000000100050000)
L'emplacement de notre point d'arrêt est donc 0x50000+0x101287CE4
(lldb) br s -a 0x50000+0x101287CE4
Breakpoint 1: where = WeChat`___lldb_unnamed_symbol63721$$WeChat + 252, address = 0x00000001012d7ce4
Imprimer la valeur de x0
(lldb) po $x0
Class name: WCDataItem, addr: 0x15f5f03b0
tid: 12393001887435993280
username: wxid_z8twcz4o18fg12
createtime: 1477360950
commentUsers: (
)
contentObj: <WCContentItem: 0x15f57d000>
Obtenez un objet WCDataItem. La valeur de x0 est ici la valeur de retour après l'exécution de selRef_getTimelineDataItemOfIndex_
, puis modifiez la valeur de x0.
(lldb) register write $x0 0
(lldb) c
À ce stade, vous constaterez que le contenu de la petite vidéo que nous souhaitons actualiser est entièrement vide.
À ce stade, nous avons trouvé la méthode pour obtenir les données sources de la courte vidéo. La question est de savoir comment obtenir ce WCDataItem ? Continuez à regarder l'état d'appel de la fonction d'analyse IDA :
WCTimeLineViewController - (void)genNormalCell:(id) indexPath:(id)
__text:0000000101287BCC STP X28, X27, [SP,#var_60]!
__text:0000000101287BD0 STP X26, X25, [SP,#0x60+var_50]
__text:0000000101287BD4 STP X24, X23, [SP,#0x60+var_40]
__text:0000000101287BD8 STP X22, X21, [SP,#0x60+var_30]
__text:0000000101287BDC STP X20, X19, [SP,#0x60+var_20]
__text:0000000101287BE0 STP X29, X30, [SP,#0x60+var_10]
__text:0000000101287BE4 ADD X29, SP, #0x60+var_10
__text:0000000101287BE8 SUB SP, SP, #0x80
__text:0000000101287BEC MOV X19, X3
__text:0000000101287BF0 MOV X22, X0
__text:0000000101287BF4 MOV W25, #0x100000
__text:0000000101287BF8 MOVK W25, #1
__text:0000000101287BFC MOV X0, X2
__text:0000000101287C00 BL _objc_retain
__text:0000000101287C04 MOV X28, X0
__text:0000000101287C08 MOV X0, X19
__text:0000000101287C0C BL _objc_retain
__text:0000000101287C10 MOV X20, X0
__text:0000000101287C14 STR X20, [SP,#0xE0+var_98]
__text:0000000101287C18 ADRP X8, #selRef_row@PAGE
__text:0000000101287C1C LDR X1, [X8,#selRef_row@PAGEOFF]
__text:0000000101287C20 BL _objc_msgSend
__text:0000000101287C24 MOV X26, X0
__text:0000000101287C28 ADRP X8, #selRef_section@PAGE
__text:0000000101287C2C LDR X19, [X8,#selRef_section@PAGEOFF]
__text:0000000101287C30 MOV X0, X20
__text:0000000101287C34 MOV X1, X19
__text:0000000101287C38 BL _objc_msgSend
__text:0000000101287C3C STR X0, [SP,#0xE0+var_A8]
__text:0000000101287C40 MOV X0, X20
__text:0000000101287C44 MOV X1, X19
__text:0000000101287C48 BL _objc_msgSend
__text:0000000101287C4C MOV X2, X0
__text:0000000101287C50 ADRP X8, #selRef_calcDataItemIndex_@PAGE
__text:0000000101287C54 LDR X1, [X8,#selRef_calcDataItemIndex_@PAGEOFF]
__text:0000000101287C58 MOV X0, X22
__text:0000000101287C5C BL _objc_msgSend
__text:0000000101287C60 MOV X21, X0
__text:0000000101287C64 STR X21, [SP,#0xE0+var_C0]
__text:0000000101287C68 ADRP X8, #classRef_MMServiceCenter@PAGE
__text:0000000101287C6C LDR X0, [X8,#classRef_MMServiceCenter@PAGEOFF]
__text:0000000101287C70 ADRP X8, #selRef_defaultCenter@PAGE
__text:0000000101287C74 LDR X1, [X8,#selRef_defaultCenter@PAGEOFF]
__text:0000000101287C78 STR X1, [SP,#0xE0+var_B8]
__text:0000000101287C7C BL _objc_msgSend
__text:0000000101287C80 MOV X29, X29
__text:0000000101287C84 BL _objc_retainAutoreleasedReturnValue
__text:0000000101287C88 MOV X19, X0
__text:0000000101287C8C ADRP X8, #classRef_WCFacade@PAGE
__text:0000000101287C90 LDR X0, [X8,#classRef_WCFacade@PAGEOFF]
__text:0000000101287C94 ADRP X8, #selRef_class@PAGE
__text:0000000101287C98 LDR X1, [X8,#selRef_class@PAGEOFF]
__text:0000000101287C9C STR X1, [SP,#0xE0+var_B0]
__text:0000000101287CA0 BL _objc_msgSend
__text:0000000101287CA4 MOV X2, X0
__text:0000000101287CA8 ADRP X8, #selRef_getService_@PAGE
__text:0000000101287CAC LDR X1, [X8,#selRef_getService_@PAGEOFF]
__text:0000000101287CB0 STR X1, [SP,#0xE0+var_A0]
__text:0000000101287CB4 MOV X0, X19
__text:0000000101287CB8 BL _objc_msgSend
__text:0000000101287CBC MOV X29, X29
__text:0000000101287CC0 BL _objc_retainAutoreleasedReturnValue
__text:0000000101287CC4 MOV X20, X0
__text:0000000101287CC8 ADRP X8, #selRef_getTimelineDataItemOfIndex_@PAGE
__text:0000000101287CCC LDR X1, [X8,#selRef_getTimelineDataItemOfIndex_@PAGEOFF]
__text:0000000101287CD0 STR X1, [SP,#0xE0+var_C8]
__text:0000000101287CD4 MOV X2, X21
__text:0000000101287CD8 BL _objc_msgSend
__text:0000000101287CDC MOV X29, X29
__text:0000000101287CE0 BL _objc_retainAutoreleasedReturnValue
__text:0000000101287CE4 MOV X21, X0
__text:0000000101287CE8 MOV X0, X20
......
Le paramètre transmis à selRef_getTimelineDataItemOfIndex_
est x2. Vous pouvez voir que x21 transmis à x2 est la valeur de retour de la fonction selRef_calcDataItemIndex_
, qui est un type de données long non signé. Poursuivant l'analyse, l'appelant de selRef_getTimelineDataItemOfIndex_
est la valeur de retour de selRef_getService_
à l'étape précédente. Après analyse du point d'arrêt, il s'avère qu'il s'agit d'un objet WCFacade
. Trions les appels à selRef_getTimelineDataItemOfIndex_
:
L'appelant est la valeur de retour de selRef_getService_
; le paramètre est la valeur de retour de selRef_calcDataItemIndex_
<br> Tournons notre attention vers ces deux fonctions et utilisons le même principe pour analyser comment elles implémentent l'appel.
selRef_getService_
:MMServiceCenter
. En recherchant, x19 est attribué à la position 0x101287C88. est le retour de [MMServiceCenter defaultCenter]
[WCFacade class]
.selRef_calcDataItemIndex_
:WCTimeLineViewController
son appelant x0 à la position 0x101287C58.selRef_section
0x101287C4C, on constate que selRef_section
paramètre entrant vient de selRef_section
ou est x3.WCTimeLineViewController - (void)genNormalCell:(id) indexPath:(id)
, donc le paramètre de selRef_calcDataItemIndex_
est [IndexPath section]
.getTimelineDataItemOfIndex:
peut transmettre [[MMServiceCenter defaultCenter] getService:[WCFacade class]]
Pour l'obtenir, ses paramètres peuvent être obtenus grâce à la fonction suivante
[WCTimeLineViewController calcDataItemIndex:[indexPath section]]
Vous avez toujours l'impression qu'il manque quelque chose ? Nous n’avons pas encore obtenu l’indexPath ! L'étape suivante consiste à obtenir l'indexPath. C'est relativement simple, car nous sommes dans [WCContentItemViewTemplateNewSight onLongTouch]
, nous pouvons donc obtenir MMTableViewCell, MMTableView et WCTimeLineViewController en séquence via [self nextResponder]
, puis obtenir l'indexPath via [MMTableView indexPathForCell:MMTableViewCell]
.
Après cela, nous avons obtenu l'objet WCDataItem. Le prochain focus devrait être sur WCDataItem, et enfin obtenir la courte vidéo que nous voulons. Recherchez des indices dans le fichier d'en-tête de cette classe. Étant donné que la vidéo ne peut être lue qu'après le téléchargement, le chemin de la vidéo doit être obtenu ici, alors faites attention aux attributs ou aux méthodes liés à l'URL et au chemin, puis recherchez ce qui suit. suspects.
@property(retain, nonatomic) NSString *sourceUrl2;
@property(retain, nonatomic) NSString *sourceUrl;
- (id)descriptionForKeyPaths;
- (id)keyPaths;
Revenez à LLDB et imprimez ces valeurs avec des points d'arrêt pour voir ce qu'il y a là.
(lldb) po [$x0 keyPaths]
<__NSArrayI 0x15f74e9d0>(
tid,
username,
createtime,
commentUsers,
contentObj
)
(lldb) po [$x0 descriptionForKeyPaths]
Class name: WCDataItem, addr: 0x15f5f03b0
tid: 12393001887435993280
username: wxid_z8twcz4o18fg12
createtime: 1477360950
commentUsers: (
)
contentObj: <WCContentItem: 0x15f57d000>
(lldb) po [$x0 sourceUrl]
nil
(lldb) po [$x0 sourceUrl2]
nil
Il n'y a aucun indice précieux, mais j'ai remarqué qu'il y a un WCContentItem dans WCDataItem. Il semble que la seule façon de commencer soit ici. Jetons un coup d'œil au fichier d'en-tête !
@property(retain, nonatomic) NSString *linkUrl;
@property(retain, nonatomic) NSString *linkUrl2;
@property(retain, nonatomic) NSMutableArray *mediaList;
Imprimez-le dans LLDB
(lldb) po [[$x0 valueForKey:@"contentObj"] linkUrl]
https://support.weixin.qq.com/cgi-bin/mmsupport-bin/readtemplate?t=page/common_page__upgrade&v=1
(lldb) po [[$x0 valueForKey:@"contentObj"] linkUrl2]
nil
(lldb) po [[$x0 valueForKey:@"contentObj"] mediaList]
<__NSArrayM 0x15f985e10>(
<WCMediaItem: 0x15dfebdf0>
)
Il y a un objet WCMediaItem dans le tableau mediaList. Le média est généralement utilisé pour représenter la vidéo et l'audio. Je suppose que c'est ça ! Trouvez rapidement le fichier d'en-tête et recherchez-le.
@property(retain, nonatomic) WCUrl *dataUrl;
- (id)pathForData;
- (id)pathForSightData;
- (id)pathForTempAttachVideoData;
- (id)videoStreamForData;
Parmi les attributs et méthodes ci-dessus, pathForSightData
est le plus susceptible d'obtenir le court chemin vidéo. Continuer à vérifier.
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] dataUrl]
type[1], url[http://vweixinf.tc.qq.com/102/20202/snsvideodownload?filekey=30270201010420301e020166040253480410d14adcddf086f4e131d11a5b1cca1bdf0203039fa00400&bizid=1023&hy=SH&fileparam=302c0201010425302302040fde55e20204580ebd3602024eea02031e8d7d02030f42400204d970370a0201000400], enckey[0], encIdx[-1], token[]
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] pathForData]
/var/mobile/Containers/Data/Application/7C3A6322-1F57-49A0-ACDE-6EF0ED74D137/Library/WechatPrivate/6f696a1b596ce2499419d844f90418aa/wc/media/5/53/8fb0cdd77208de5b56169fb3458b45
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] pathForSightData]
/var/mobile/Containers/Data/Application/7C3A6322-1F57-49A0-ACDE-6EF0ED74D137/Library/WechatPrivate/6f696a1b596ce2499419d844f90418aa/wc/media/5/53/8fb0cdd77208de5b56169fb3458b45.mp4
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] pathForAttachVideoData]
nil
(lldb) po [[[[$x0 valueForKey:@"contentObj"] mediaList] lastObject] videoStreamForData]
nil
Vous avez l'URL du réseau et le chemin local de la courte vidéo ! Ici, vous pouvez utiliser iFunBox ou scp pour copier ce fichier depuis le bac à sable afin de voir s'il s'agit d'une courte vidéo que cette cellule doit lire.
LeonLei-MBP:~ gaoshilei$ scp [email protected]:/var/mobile/Containers/Data/Application/7C3A6322-1F57-49A0-ACDE-6EF0ED74D137/Library/WechatPrivate/6f696a1b596ce2499419d844f90418aa/wc/media/5/53/8fb0cdd77208de5b56169fb3458b45.mp4 Desktop/
8fb0cdd77208de5b56169fb3458b45.mp4 100% 232KB 231.9KB/s 00:00
Ouvrez-le avec QuickTime et constatez qu'il s'agit bien de la courte vidéo que nous recherchons. Vérifiez à nouveau que l'URL est correcte. Ouvrez le dataUrl imprimé ci-dessus dans le navigateur et constatez qu'il s'agit également de cette courte vidéo. En analysant cette classe, nous pouvons tirer les conclusions suivantes :
À ce stade, l'analyse du chemin et de la méthode d'acquisition de la courte vidéo est terminée. Pour réaliser le transfert, nous devons continuer à analyser la sortie de WeChat Moments.
Cette section est un détour que j'ai fait en recherchant la fonction de transfert vidéo courte. En fin de compte, je n'ai pas trouvé de moyen de la mettre en œuvre. Cependant, elle fournit également quelques idées et méthodes couramment utilisées en ingénierie inverse. Si vous ne voulez pas le lire, vous pouvez passer à la deuxième section.
Ouvrez l'interface de tournage de la courte vidéo et injectez-y le cyscript. Nous devons découvrir quelle méthode est utilisée pour publier la courte vidéo, puis vérifier quelles fenêtres se trouvent dans la fenêtre actuelle (car le tournage de la courte vidéo n'est pas terminé. dans la keyWindow de UIApplication)
cy# [UIApp windows].toString()
(
"<iConsoleWindow: 0x125f75e20; baseClass = UIWindow; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x125f77b70>; layer = <UIWindowLayer: 0x125df4810>>",
"<SvrErrorTipWindow: 0x127414d40; baseClass = UIWindow; frame = (0 64; 320 45); hidden = YES; gestureRecognizers = <NSArray: 0x12740d930>; layer = <UIWindowLayer: 0x1274030b0>>",
"<MMUIWindow: 0x127796440; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x1278083c0>; layer = <UIWindowLayer: 0x127796750>>",
"<UITextEffectsWindow: 0x1270e0d40; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <UIWindowLayer: 0x1270b4ba0>>",
"<NewYearActionSheet: 0x127797e10; baseClass = UIWindow; frame = (0 0; 320 568); hidden = YES; userInteractionEnabled = NO; layer = <UIWindowLayer: 0x1277d5490>>"
)
On constate que la page actuelle comporte un total de 5 fenêtres, parmi lesquelles MMUIWindow est la fenêtre dans laquelle la courte vidéo a été tournée. Imprimez l'arborescence de son interface utilisateur.
cy# [#0x127796440 recursiveDescription]
Le résultat imprimé est assez long, je ne le publierai donc pas. Trouvez ce bouton qui est le bouton pour filmer de courtes vidéos
| | | | | | <UIButton: 0x1277a9d70; frame = (89.5 368.827; 141 141); opaque = NO; gestureRecognizers = <NSArray: 0x1277aaeb0>; layer = <CALayer: 0x1277a9600>>
| | | | | | | <UIView: 0x1277aa0a0; frame = (0 0; 141 141); userInteractionEnabled = NO; tag = 252707333; layer = <CALayer: 0x1277aa210>>
| | | | | | | | <UIImageView: 0x1277aa2e0; frame = (0 0; 141 141); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1277aa490>>
puis exécutez
cy# [#0x1277a9d70 setHidden:YES]
J'ai constaté que le bouton de tir avait disparu, ce qui a confirmé mes soupçons. Pour trouver l'événement de réponse du bouton, vous pouvez le trouver via target
cy# [#0x1277a9d70 allTargets]
[NSSet setWithArray:@[#"<MainFrameSightViewController: 0x1269a4600>"]]]
cy# [#0x1277a9d70 allControlEvents]
193
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:193]
null
J'ai trouvé que le bouton n'a pas d'action correspondante, ce qui est étrange ! UIButton doit avoir une cible et une action, sinon le Button ne peut pas répondre aux événements. Essayons d'autres ControlEvents
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchDown]
@["btnPress"]
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchUpOutside]
@["btnRelease"]
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchUpInside]
@["btnRelease"]
Il s'avère que ces trois ControlEvents ont des actions correspondantes. Jetons un coup d'œil aux valeurs de ces trois énumérations.
typedef enum UIControlEvents : NSUInteger {
UIControlEventTouchDown = 1 << 0,
UIControlEventTouchDownRepeat = 1 << 1,
UIControlEventTouchDragInside = 1 << 2,
UIControlEventTouchDragOutside = 1 << 3,
UIControlEventTouchDragEnter = 1 << 4,
UIControlEventTouchDragExit = 1 << 5,
UIControlEventTouchUpInside = 1 << 6,
UIControlEventTouchUpOutside = 1 << 7,
UIControlEventTouchCancel = 1 << 8,
......
} UIControlEvents;
On voit que UIControlEventTouchDown correspond à 1, UIControlEventTouchUpInside correspond à 128, UIControlEventTouchUpOutside correspond à 64, et la somme des trois est exactement 193 ! Il s'avère que lors de l'appel de [#0x1277a9d70 allControlEvents]
, la valeur renvoyée doit être une énumération. S'il y a plusieurs énumérations, ajoutez leurs valeurs. N'est-ce pas un peu déroutant ? Je ressens la même chose ! Nous venons d'imprimer les actions correspondant aux trois ControlEvents et avons continué avec LLDB+IDA pour l'analyse dynamique.
Parce que nous devons trouver un moyen de publier de courtes vidéos, nous ne nous soucions pas de la fonction btnPress
correspondante. Nous nous concentrons sur btnRelease
, une méthode qui sera appelée après le relâchement du bouton de prise de vue. Recherchez cette méthode dans IDA et définissez le prochain point d'arrêt après l'avoir trouvé.
(lldb) br s -a 0xac000+0x10209369C
Breakpoint 4: where = WeChat`___lldb_unnamed_symbol108894$$WeChat + 32, address = 0x000000010213f69c
Process 3813 stopped
* thread #1: tid = 0xf1ef0, 0x000000010213f69c WeChat`___lldb_unnamed_symbol108894$$WeChat + 32, queue = 'com.apple.main-thread', stop reason = breakpoint 4.1
frame #0: 0x000000010213f69c WeChat`___lldb_unnamed_symbol108894$$WeChat + 32
WeChat`___lldb_unnamed_symbol108894$$WeChat:
-> 0x10213f69c <+32>: bl 0x1028d0b60 ; symbol stub for: objc_msgSend
0x10213f6a0 <+36>: cmp w0, #2 ; =2
0x10213f6a4 <+40>: b.ne 0x10213f6dc ; <+96>
0x10213f6a8 <+44>: adrp x8, 5489
Filmez une courte vidéo avec votre téléphone portable, puis relâchez-la, déclenchant le point d'arrêt, ce qui montre que notre supposition est correcte. En poursuivant l'analyse, nous avons constaté que le code se trouve sur le côté droit de l'image ci-dessus. Après l'avoir regardé, il n'y a aucun moyen de passer à la version vidéo. Cependant, si vous regardez attentivement, il y a un bloc, qui est le. bloc de retard du système et est situé à 0x102093760. Ensuite, nous suivons le point d'arrêt et passons à l'adresse stockée dans x16 à 0x1028255A0
(lldb) si
Process 3873 stopped
* thread #1: tid = 0xf62c4, 0x00000001028d9598 WeChat`dispatch_after, queue = 'com.apple.main-thread', stop reason = instruction step into
frame #0: 0x00000001028d9598 WeChat`dispatch_after
WeChat`dispatch_after:
-> 0x1028d9598 <+0>: adrp x16, 1655
0x1028d959c <+4>: ldr x16, [x16, #1056]
0x1028d95a0 <+8>: br x16
WeChat`dispatch_apply:
0x1028d95a4 <+0>: adrp x16, 1655
(lldb) po $x2
<__NSStackBlock__: 0x16fd49f88>
On constate que le paramètre x2 transmis est un bloc. Revoyons la fonction dispatch_after.
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
Cette fonction a trois paramètres, à savoir dispatch_time_t, dispatch_queue_t et dispatch_block_t. Le x2 imprimé ici est le bloc à transmettre, nous supposons donc qu'il y aura un délai après le tournage de la courte vidéo, puis exécuterons le bloc qui vient d'être transmis, donc x2 doit être S'il y a d'autres appels de méthode, l'étape suivante consiste à connaître l'emplacement de ce bloc.
(lldb) memory read --size 8 --format x 0x16fd49f88
0x16fd49f88: 0x000000019f8fd218 0x00000000c2000000
0x16fd49f98: 0x000000010214777c 0x0000000102fb0e60
0x16fd49fa8: 0x000000015da32600 0x000000015ea1a430
0x16fd49fb8: 0x000000015cf5fee0 0x000000016fd49ff0
0x000000010214777c est l'emplacement du bloc. Bien sûr, le décalage ASLR actuel de WeChat doit être soustrait. L'adresse finale dans IDA est 0x10209377C. Soudain, on découvre qu'il s'agit du sous-programme de btnRelease
sub_10209377C. Ce sous-programme est très simple et n'a qu'une seule méthode selRef_logicCheckState_
qui peut être notre cible. Voyons d'abord qui a appelé cette méthode
(lldb) br s -a 0xb4000+0x1020937BC
......
Process 3873 stopped
* thread #1: tid = 0xf62c4, 0x00000001021477bc WeChat`___lldb_unnamed_symbol108895$$WeChat + 64, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
frame #0: 0x00000001021477bc WeChat`___lldb_unnamed_symbol108895$$WeChat + 64
WeChat`___lldb_unnamed_symbol108895$$WeChat:
-> 0x1021477bc <+64>: adrp x8, 5489
0x1021477c0 <+68>: ldr x1, [x8, #1552]
0x1021477c4 <+72>: orr w2, wzr, #0x1
0x1021477c8 <+76>: ldp x29, x30, [sp, #16]
(lldb) po $x0
<MainFrameSightViewController: 0x15d1f0c00>
J'ai trouvé qu'il était toujours appelé par l'objet MainFrameSightViewController. Ensuite, selRef_logicCheckState_
doit également être dans le fichier d'en-tête de cette classe. Je l'ai recherché et trouvé.
- (void)logicCheckState:(int)arg1;
Recherchez [MainFrameSightViewController logicCheckState:] dans la fenêtre de gauche d'IDA et constatez que cette méthode est super compliquée et a trop de logique, alors ne vous inquiétez pas et parcourez-la lentement. Nous avons trouvé un saut de commutateur à 0x102094D6C, et l'idée était très claire. Il nous suffit de trouver la ligne où la courte vidéo a été tournée et de regarder vers le bas, cela nous aidera à regarder la ligne. Définissez un point d'arrêt à 0x102094D6C. Ce point d'arrêt sera déclenché plusieurs fois lors du tournage de courtes vidéos. Vous pouvez désactiver le point d'arrêt avant le tournage, activer le point d'arrêt avant de lâcher prise et imprimer la valeur x8 à ce moment.
(lldb) p/x $x8
(unsigned long) $38 = 0x0000000102174e10
x8 est un pointeur et l'adresse vers laquelle il pointe est 0x102174e10. Utilisez cette adresse moins le décalage ASLR actuel pour trouver l'adresse de base dans IDA. Elle s'avère être 0x102094E10. La ligne de traitement logique pour terminer la prise de vue est trouvée et continue. jusqu'au bout. Après la position 0x102094E24, passez à 0x1020951C4. Cette branche a moins de contenu et contient trois fonctions.
loc_1020951C4
ADRP X8, #selRef_hideTips@PAGE
LDR X1, [X8,#selRef_hideTips@PAGEOFF]
MOV X0, X19
BL _objc_msgSend
ADRP X8, #selRef_finishWriter@PAGE
LDR X1, [X8,#selRef_finishWriter@PAGEOFF]
MOV X0, X19
BL _objc_msgSend
ADRP X8, #selRef_turnCancelBtnForFinishRecording@PAGE
LDR X1, [X8,#selRef_turnCancelBtnForFinishRecording@PAGEOFF]
MOV X0, X19
BL _objc_msgSend
B loc_102095288
Parmi elles, selRef_finishWriter
et selRef_turnCancelBtnForFinishRecording
doivent se concentrer sur ces deux méthodes qui semblent signifier la fin de l'enregistrement vidéo court. Les indices sont très probablement dans ces deux fonctions. En examinant l'appelant, nous avons constaté que ces deux méthodes appartiennent à MainFrameSightViewController. Continuez à vérifier ces deux méthodes dans IDA. J'ai trouvé une méthode appelée f_switchToSendingPanel
vers la fin de selRef_finishWriter
à 0x102094248, défini un point d'arrêt, puis filmé la vidéo, j'ai constaté que cette méthode n'était pas déclenchée. L'interface de publication ne doit pas être appelée via cette méthode, continuez donc à revenir à selRef_finishWriter
; appelez la méthode selRef_stopRecording
à l'emplacement 0x1020941DC, et l'appelant qui l'imprime découvre que cette méthode appartient à SightFacade
et continue de rechercher le mise en œuvre de cette méthode dans IDA. selRef_stopRecord
est appelé à nouveau à la position 0x101F9BED4 de cette méthode. L'appelant découvre également que cette méthode appartient à SightCaptureLogicF4. C'est un peu comme éplucher un oignon et continue de chercher l'implémentation de cette méthode. selRef_finishWriting
est à nouveau appelé à la position 0x101A98778 à l'intérieur de cette méthode. En utilisant le même principe, on constate que cette méthode appartient à SightMovieWriter. Trois couches ont été décollées, continuons :
Deux lignes sont divisées à la position 0x10261D004 dans SightMovieWriter - (void)finishWriting
Un point d'arrêt est défini à cette position, puis le point d'arrêt est déclenché après le tournage de la courte vidéo et la valeur de x19 est imprimée.
(lldb) po $x19
<OS_dispatch_queue: CAPTURE.CALLBACK[0x13610bcd0] = { xrefcnt = 0x4, refcnt = 0x4, suspend_cnt = 0x0, locked = 1, target = com.apple.root.default-qos.overcommit[0x1a0aa3700], width = 0x0, running = 0x0, barrier = 1 }>
Par conséquent, le code ne sautera pas à loc_10261D054 mais ira vers la gauche. Dans le code de gauche, on constate qu'un bloc est activé. Ce bloc est le sous-programme sub_10261D0AC et l'adresse est 0x10261D0AC. comme indiqué ci-dessous :
On peut voir qu'il est principalement divisé en deux lignes. Nous définissons un point d'arrêt à la fin de la première case, qui est 0x10261D108. Une fois le point d'arrêt déclenché une fois la prise de vue terminée, la valeur de x0 est imprimée comme 1. le code d'assemblage est ici
__text:000000010261D104 CMP X0, #2
__text:000000010261D108 B.EQ loc_10261D234
B.EQ passera à loc_10261D234 uniquement lorsque le résultat de l'étape précédente est 0, mais le résultat ici n'est pas 0. Modifiez la valeur de x0 à 2 pour que le résultat de l'étape précédente soit 0.
(lldb) po $x0
1
(lldb) register write $x0 2
(lldb) po $x0
2
À ce stade, relâchez le point d'arrêt et attendez de passer à l'interface de publication vidéo courte. Le résultat est qu'il est bloqué dans cette interface sans aucune réponse, donc je suppose que la logique pour implémenter le saut devrait être sur la ligne de droite, et continuez à chercher le long de la ligne de droite : Il a été constaté que la méthode suivante a été appelée à la bonne position 0x10261D3AC
- (void)finishWritingWithCompletionHandler:(void (^)(void))handler;
Cette méthode est une méthode dans AVAssetWriter fournie par le système. C'est une opération à effectuer une fois l'écriture vidéo terminée. Ici, un bloc est transmis. Comme il n'y a qu'un seul paramètre, la variable correspondante est x2 et la valeur. de x2 est imprimé.
(lldb) po $x2
<__NSStackBlock__: 0x16e086c78>
(lldb) memory read --size 8 --format x 0x16e086c78
0x16e086c78: 0x00000001a0aa5218 0x00000000c2000000
0x16e086c88: 0x00000001026d94b0 0x0000000102fc98c0
0x16e086c98: 0x0000000136229fd0 0x000000016e086d00
0x16e086ca8: 0x00000001997f5318 0xfffffffec9e882ff
Et trouvez la position du bloc 0x10261D4B0 via la mémoire de la pile (le décalage ASLR doit être soustrait)
sub_10261D4B0
var_20= -0x20
var_10= -0x10
STP X20, X19, [SP,#var_20]!
STP X29, X30, [SP,#0x20+var_10]
ADD X29, SP, #0x20+var_10
MOV X19, X0
LDR X0, [X19,#0x20]
ADRP X8, #selRef_stopAmr@PAGE
LDR X1, [X8,#selRef_stopAmr@PAGEOFF]
BL _objc_msgSend
LDR X0, [X19,#0x20]
ADRP X8, #selRef_compressAudio@PAGE
LDR X1, [X8,#selRef_compressAudio@PAGEOFF]
LDP X29, X30, [SP,#0x20+var_10]
LDP X20, X19, [SP+0x20+var_20],#0x20
B _objc_msgSend
; End of function sub_10261D4B0
Seules deux méthodes sont appelées, l'une est selRef_stopAmr
pour arrêter amr (un format audio) et l'autre est selRef_compressAudio
pour compresser l'audio. L'opération suivante une fois la prise de vue terminée ne doit pas être placée dans ces deux méthodes. depuis si longtemps et je n'en ai toujours aucune idée. Cette route semble morte, ne vous attirez pas d'ennuis, reculez stratégiquement et cherchez d'autres entrées.
La joie de faire marche arrière, c'est d'éprouver la joie du succès sur le chemin de la recherche de la vérité. Vous pouvez aussi aller dans la mauvaise direction et vous éloigner de plus en plus de la vérité. Ne vous découragez pas, ajustez la direction et continuez. en avant !
(Puisque WeChat a été secrètement mis à niveau en arrière-plan, le contenu suivant est l'ASLR de la version WeChat 6.3.30, et l'analyse ci-dessus est basée sur la version 6.3.28)
Notez que lorsque vous cliquez sur le bouton de l'appareil photo dans le coin supérieur droit du cercle d'amis, une feuille apparaîtra en bas. La première est la courte vidéo Sight. Commencez ici et utilisez cycript pour voir à quel événement correspond le bouton Sight. à.
iPhone-5S:~ root# cycript -p "WeChat"
cy# [UIApp windows].toString()
`(
"<iConsoleWindow: 0x14d6ccc00; baseClass = UIWindow; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x14d7df110>; layer = <UIWindowLayer: 0x14d7d6f60>>",
"<SvrErrorTipWindow: 0x14eaa5800; baseClass = UIWindow; frame = (0 0; 320 45); hidden = YES; gestureRecognizers = <NSArray: 0x14e9e8950>; layer = <UIWindowLayer: 0x14e9e6510>>",
"<UITextEffectsWindow: 0x14ec38ba0; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <UIWindowLayer: 0x14ec39360>>",
"<UITextEffectsWindow: 0x14e9c67a0; frame = (0 0; 320 568); layer = <UIWindowLayer: 0x14d683ff0>>",
"<UIRemoteKeyboardWindow: 0x14f226e40; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <UIWindowLayer: 0x14d6f4de0>>",
"<NewYearActionSheet: 0x14f1704a0; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x14ef9bf90>; layer = <UIWindowLayer: 0x14ef61a20>>"
)`
cy# [#0x14f1704a0 recursiveDescription].toString()
La feuille en bas est NewYearActionSheet, puis imprimez l'arborescence de l'interface utilisateur de NewYearActionSheet (c'est trop long donc je ne le publierai pas). Trouvez ensuite que le UIButton correspondant à Sight est 0x14f36d470
cy# [#0x14f36d470 allTargets]
[NSSet setWithArray:@[#"<NewYearActionSheet: 0x14f1704a0; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x14ef9bf90>; layer = <UIWindowLayer: 0x14ef61a20>>"]]]
cy# [#0x14f36d470 allControlEvents]
64
cy# [#0x14f36d470 actionsForTarget:#0x14f1704a0 forControlEvent:64]
@["OnDefaultButtonTapped:"]
L'événement lié au bouton peut être trouvé via actionsForTarget:forControlEvent:
de UIControl. La méthode de déclenchement du bouton Sight est OnDefaultButtonTapped:
Revenez à IDA et recherchez cette méthode dans NewYearActionSheet. Nous continuons à analyser uniquement cette méthode selRef_dismissWithClickedButtonIndex_animated
, en imprimant son appelant, nous constatons qu'il s'agit toujours de NewYearActionSheet. Continuez à cliquer pour trouver la méthode newYearActionSheet_clickedButtonAtIndex
. Il semble que ce ne soit pas NewYearActionSheet lui-même. En imprimant l'appelant x0, nous constatons qu'il appartient à la classe WCTimeLineViewController. Suivez le point d'arrêt et appelez la méthode #selRef_showSightWindowForMomentWithMask_byViewController_scene
à la position 0x1012B78CC. Grâce à l'observation, nous avons constaté que l'appelant de cette méthode est la valeur de retour x0 à la position 0x1012B78AC. Il s'agit d'une classe SightFacade, je suppose que cette méthode est dans SightFacade. je suis allé dans le fichier d'en-tête pour le trouver et j'ai trouvé cette méthode.
- (void)showSightWindowForMomentWithMask:(id)arg1 byViewController:(id)arg2 scene:(int)arg3;
Cette méthode devrait être la méthode pour accéder à la petite interface vidéo. Imprimez ses paramètres séparément ci-dessous
(lldb) po $x2
<UIImage: 0x14f046660>, {320, 568}
(lldb) po $x3
<WCTimeLineViewController: 0x14e214800>
(lldb) po $x4
2
(lldb) po $x0
<SightFacade: 0x14f124b40>
Parmi eux, x2, x3 et x4 correspondent respectivement à trois paramètres. x0 est l'appelant. Accédez à l'intérieur de cette méthode pour voir comment l'implémenter. On constate que l'interface de tournage vidéo courte est initialisée dans cette méthode. Tout d'abord, un MainFrameSightViewController est initialisé, puis un UINavigationController est créé pour y placer le MainFrameSightViewController, puis un MMWindowController est initialisé pour appeler.
- (id)initWithViewController:(id)arg1 windowLevel:(int)arg2;
La méthode insère le UINavigationController et termine tous les travaux de création d'interface utilisateur pour l'interface de tournage vidéo courte. Une fois la prise de vue terminée, entrez dans l'interface de publication. À ce stade, utilisez cyscript pour constater que le contrôleur actuel est SightMomentEditViewController. À partir de là, j'ai eu une idée, ne suffirait-il pas de sauter l'interface de prise de vue précédente et directement. entrer dans l'interface de publication ? Nous créons nous-mêmes un SightMomentEditViewController et le plaçons dans le UINavigationController, puis plaçons ce contrôleur de navigation dans le MMWindowController. **(J'ai écrit le réglage ici pour vérification, et les idées de réglage spécifiques sont écrites plus tard)** Le résultat est que l'interface de publication peut effectivement apparaître, mais la NavgationBar de la barre de navigation couvre l'original et toute l'interface. est transparent, c'est moche, et une fois la publication terminée, l'intégralité du MMWindowController ne peut pas être détruite et il reste toujours dans l'interface de publication. Ce n'est pas le résultat que nous souhaitons, mais nous y gagnons beaucoup. Au moins, nous pouvons appeler directement l'interface de publication et les petites vidéos peuvent être transmises normalement. Mon hypothèse personnelle est que la raison pour laquelle l'interface actuelle ne peut pas être détruite est parce que MMWindowController a créé une nouvelle fenêtre, qui n'est pas la même que la keyWindow où se trouve TimeLine. La méthode de déclenchement par bouton de SightMomentEditViewController ne peut pas détruire cette fenêtre, j'ai donc un. supposition audacieuse. Puis-je simplement afficher SightMomentEditViewController directement sur le WCTimeLineViewController actuel ?
[WCTimelineVC presentViewController:editSightVC animated:YES completion:^{
}];
Ne serait-il pas agréable de l'afficher ainsi ? Cependant, en observant le fichier d'en-tête de SightMomentEditViewController, combiné avec les éléments de l'interface lors de la sortie de la courte vidéo, il est supposé que la création de ce contrôleur nécessite au moins deux attributs, l'un est le chemin de la courte vidéo et l'autre est la vignette de la courte vidéo. Ces deux clés Si l'attribut est donné à SightMomentEditViewController, il doit être affiché normalement.
SightMomentEditViewController *editSightVC = [[%c(SightMomentEditViewController) alloc] init];
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
UIImage *image = [[self valueForKey:@"_sightView"] getImage];
[editSightVC setRealMoviePath:localPath];
[editSightVC setMoviePath:localPath];
[editSightVC setRealThumbImage:image];
[editSightVC setThumbImage:image];
[WCTimelineVC presentViewController:editSightVC animated:YES completion:^{
}];
L'interface de publication vidéo courte peut être affichée normalement et toutes les fonctions peuvent être utilisées normalement. Le seul problème est que le bouton de retour n'a aucun effet et que SightMomentEditViewController ne peut pas être détruit. Utilisez cycript pour vérifier l'actionEvent du bouton gauche et constater que sa fonction de réponse est - (void)popSelf;
Cliquer sur le bouton gauche pour retourner déclenche la méthode pop, mais ce contrôleur n'est pas dans le navgationController, il n'est donc pas valide. besoin de refaire cette méthode.
- (void)popSelf
{
[self dismissViewControllerAnimated:YES completion:^{
}];
}
À ce stade, cliquez sur le bouton Retour pour quitter normalement. De plus, une méthode appelée - (void)sendSightToFriend;
a été trouvée dans WCContentItemViewTemplateNewSight, qui peut directement transférer de courtes vidéos à des amis. .
Le transfert de courtes vidéos prend en charge 4 fonctions : transfert vers Moments, transfert vers des amis, enregistrement dans un album photo local et copie du lien vidéo court vers le presse-papiers. Si la courte vidéo n'est pas téléchargée, un appui long affichera uniquement le lien URL de la courte vidéo.
Ici, nous devons accrocher deux classes, à savoir WCContentItemViewTemplateNewSight et SightMomentEditViewController. Accrochez la méthode onLongTouch dans WCContentItemViewTemplateNewSight, puis ajoutez le menu contextuel et ajoutez les méthodes de réponse tour à tour.
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
UISaveVideoAtPathToSavedPhotosAlbum(localPath, nil, nil, nil);
}
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
UISaveVideoAtPathToSavedPhotosAlbum(localPath, nil, nil, nil);
SightMomentEditViewController *editSightVC = [[%c(SightMomentEditViewController) alloc] init];
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
UIImage *image = [[self valueForKey:@"_sightView"] getImage];
[editSightVC setRealMoviePath:localPath];
[editSightVC setMoviePath:localPath];
[editSightVC setRealThumbImage:image];
[editSightVC setThumbImage:image];
[WCTimelineVC presentViewController:editSightVC animated:YES completion:^{
}];
[self sendSightToFriend];
UIMenuController *menuController = [UIMenuController sharedMenuController];
if (menuController.isMenuVisible) return;//防止出现menu闪屏的情况
[self becomeFirstResponder];
NSString *localPath = [[self iOSREMediaItemFromSight] pathForSightData];
BOOL isExist =[[NSFileManager defaultManager] fileExistsAtPath:localPath];
UIMenuItem *retweetMenuItem = [[UIMenuItem alloc] initWithTitle:@"朋友圈" action:@selector(SLRetweetSight)];
UIMenuItem *saveToDiskMenuItem = [[UIMenuItem alloc] initWithTitle:@"保存到相册" action:@selector(SLSightSaveToDisk)];
UIMenuItem *sendToFriendsMenuItem = [[UIMenuItem alloc] initWithTitle:@"好友" action:@selector(SLSightSendToFriends)];
UIMenuItem *copyURLMenuItem = [[UIMenuItem alloc] initWithTitle:@"复制链接" action:@selector(SLSightCopyUrl)];
if(isExist){
[menuController setMenuItems:@[retweetMenuItem,sendToFriendsMenuItem,saveToDiskMenuItem,copyURLMenuItem]];
}else{
[menuController setMenuItems:@[copyURLMenuItem]];
}
[menuController setTargetRect:CGRectZero inView:self];
[menuController setMenuVisible:YES animated:YES];
J'ai mis le fichier de réglage spécifique sur github WCSightRetweet
@interface WCUrl : NSObject
@property(retain, nonatomic) NSString *url;
@end
@interface WCContentItem : NSObject
@property(retain, nonatomic) NSMutableArray *mediaList;
@end
@interface WCDataItem : NSObject
@property(retain, nonatomic) WCContentItem *contentObj;
@end
@interface WCMediaItem : NSObject
@property(retain, nonatomic) WCUrl *dataUrl;
- (id)pathForSightData;
@end
@interface MMServiceCenter : NSObject
+ (id)defaultCenter;
- (id)getService:(Class)arg1;
@end
@interface WCFacade : NSObject
- (id)getTimelineDataItemOfIndex:(long long)arg1;
@end
@interface WCSightView : UIView
- (id)getImage;
@end
@interface WCContentItemViewTemplateNewSight : UIView{
WCSightView *_sightView;
}
- (WCMediaItem *)iOSREMediaItemFromSight;
- (void)iOSREOnSaveToDisk;
- (void)iOSREOnCopyURL;
- (void)sendSightToFriend;
@end
@interface SightMomentEditViewController : UIViewController
@property(retain, nonatomic) NSString *moviePath;
@property(retain, nonatomic) NSString *realMoviePath;
@property(retain, nonatomic) UIImage *thumbImage;
@property(retain, nonatomic) UIImage *realThumbImage;
- (void)makeInputController;
@end
@interface MMWindowController : NSObject
- (id)initWithViewController:(id)arg1 windowLevel:(int)arg2;
- (void)showWindowAnimated:(_Bool)arg1;
@end
@interface WCTimeLineViewController : UIViewController
- (long long)calcDataItemIndex:(long long)arg1;
@end
@interface MMTableViewCell : UIView
@end
@interface MMTableView : UIView
- (id)indexPathForCell:(id)cell;
@end
THEOS_DEVICE_IP = 192.168.0.115//手机所在的IP
include $(THEOS)/makefiles/common.mk
ARCHS = arm64//支持的CPU架构
TWEAK_NAME = WCTimelineSightRetweet
WCTimelineSightRetweet_FILES = Tweak.xm
WCTimelineSightRetweet_FRAMEWORKS = UIKit CoreGraphics//导入系统的framework
include $(THEOS_MAKE_PATH)/tweak.mk
after-install::
install.exec "killall -9 WeChat"//安装完成杀掉的进程
Le fichier de contrôle n'a pas besoin d'être modifié, puis exécutez la commande make package install
pour l'installer sur le téléphone mobile, puis ouvrez à nouveau WeChat. La fonction de transfert de courtes vidéos a été ajoutée.
Installez macports (le processus d'installation nécessite une connexion VPN, sinon l'installation ne réussira pas)
Après avoir installé MacPorts, ouvrez le terminal et entrez sudo port -v selfupdate
pour mettre à jour MacPorts vers la dernière version, ce qui peut prendre beaucoup de temps.
Après avoir mis à jour MacPorts, installez le fichier DPKG et entrez sudo port -f install dpkg
dans le terminal.
Téléchargez et installez iOSOpendev Si l'installation échoue, vous pouvez utiliser Command + L
pour rechercher des problèmes lors de l'installation.
PackageKit: Install Failed: Error Domain=PKInstallErrorDomain Code=112 "运行软件包“iOSOpenDev-1.6-2.pkg”的脚本时出错。" UserInfo={NSFilePath=./postinstall, NSURL=file://localhost/Users/ice/Downloads/iOSOpenDev-1.6-2.pkg#iodsetup.pkg, PKInstallPackageIdentifier=com.iosopendev.iosopendev162.iod-setup.pkg, NSLocalizedDescription=运行软件包“iOSOpenDev-1.6-2.pkg”的脚本时出错。} {
NSFilePath = "./postinstall";
NSLocalizedDescription = "U8fd0U884cU8f6fU4ef6U5305U201ciOSOpenDev-1.6-2.pkgU201dU7684U811aU672cU65f6U51faU9519U3002";
NSURL = "file://localhost/Users/ice/Downloads/iOSOpenDev-1.6-2.pkg#iodsetup.pkg";
PKInstallPackageIdentifier = "com.iosopendev.iosopendev162.iod-setup.pkg";
}
Voici une solution : Téléchargez le dossier Spécifications dans iOSOpenDevInstallSolve
5. Pour résoudre le problème d'échec de l'installation, ouvrez le dossier Spécifications téléchargé à l'étape 4. Il devrait contenir 8 fichiers. Si vous avez plusieurs xcodes installés, veuillez les placer dans les xcodes correspondants.
(1) Les quatre fichiers commençant par iPhoneOS sont placés dans le dossier /Application/Xcode/Content/Developer/Platforms/IphoneOS.platform/Developer/Library/Xcode/Specifications (sinon, veuillez créer vous-même un dossier Spécifications)
(2) Les quatre autres fichiers commençant par iPhone Simulator sont placés dans le dossier /Application/Xcode/Content/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications (sinon, veuillez en créer un également)
(3) Créez un dossier usr sous le dossier /Application/Xcode/Content/Developer/Platforms/iPhoneSimulator.platform/Developer/ et créez un dossier nommé bin sous le dossier usr.
Remarque : Parfois, une invite indiquant que l'installation a échoué. Ouvrez un nouveau projet dans Xcode. S'il y a iOSOpenDev dans le menu d'options du projet, cela signifie que l'installation a réussi.
Pour installer le package ipa, vous pouvez également utiliser des outils tels que itool, mais ideviceinstaller peut voir le processus d'installation, ce qui nous permet de trouver plus facilement la cause de l'erreur.
exécuter la commande
brew install ideviceinstaller
Si vous êtes invité à indiquer que la commande Brew est introuvable, cela signifie que Homebrew n'a pas été installé sur votre Mac.
Messages d'erreur courants :
ERROR: Could not connect to lockdownd, error code -5
Pour le moment, réinstallez simplement libimobiledevice (car ideviceinstaller s'appuie sur de nombreux autres plug-ins)
Exécutez la commande suivante :
$ brew uninstall libimobiledevice
$ brew install --HEAD libimobiledevice
Téléchargez l'outil de re-signature iOS App Signer* (économisez de nombreuses opérations de ligne de commande, re-signez en un seul clic !)*
(3) Téléchargez l'application WeChat piratée
Étant donné que le package AppStore est crypté (shellé) et ne peut pas être re-signé, vous devez en utiliser un shellé. Vous pouvez utiliser dumpdecrypted pour vider le shell vous-même, ou vous pouvez utiliser directement l'assistant PP ou l'assistant itool pour télécharger la version jailbreakée. de l'application WeChat qui a été décortiquée.
(4) Installez Yololib
yololib peut injecter dylib dans le fichier binaire WeChat, afin que votre Hook puisse être efficace. Après le téléchargement, compilez et obtenez yololib.
#####(1) Générer une bibliothèque statique. iOSOpendev a été installé à l'étape précédente. Ouvrez maintenant un nouveau projet dans Xcode. Le projet iOSOpendev apparaîtra dans l'interface de sélection de projet. Ici, nous devons sélectionner le projet CaptainHook Tweak. Il n'y a qu'un seul projet nouvellement créé .mm, il nous suffit d'écrire toutes les méthodes de hook dans ce fichier.
Étant donné que les machines non jailbreakées ne peuvent pas installer de plug-ins de réglage pour accrocher des applications originales telles que les machines jailbreakées, CaptainHook utilise le mécanisme Runtime pour implémenter des fonctions telles que la définition de classe et le remplacement de méthodes à l'aide de commandes de macro. Voici une brève introduction à son utilisation :
CHDeclareClass(WCContentItemViewTemplateNewSight);
CHDeclareClass(ClassName)
indique la classe à accrocher et est généralement écrit au début de l'opération sur cette classe.
CHDeclareMethod0(id, WCContentItemViewTemplateNewSight, SLSightDataItem){......}
CHDeclareMethod(count, return_type, class_type, name1, arg1, ....)
signifie créer une nouvelle méthode, count signifie le nombre de paramètres de cette méthode, return_type signifie le type de retour, class_type remplit le nom de classe de cette méthode, name1 signifie le nom de la méthode, arg1 représente le premier paramètre, s'il n'y a pas de paramètre, laissez-le vide, et ainsi de suite.
CHOptimizedMethod0(self, void, WCContentItemViewTemplateNewSight, onLongTouch){
CHSuper(0, className, Method);//可选
......
}
CHOptimizedMethod(count, optimization, return_type, class_type, name1, type1, arg1)
signifie accrocher la méthode d'origine (si CHSuper(0, className, Method)
signifie copier la méthode d'origine, CHSuper signifie appeler la méthode d'origine à la position actuelle), count signifie le nombre de paramètres de la méthode hook, l'optimisation se remplit généralement soi, return_type est le type de valeur de retour de la méthode, class_type remplit le nom de la classe de la classe actuelle, name1 est le nom de la méthode, arg1 est le paramètre, s'il n'y a pas de paramètres, remplissez arg, et ainsi de suite.
CHConstructor
{
@autoreleasepool
{
CHLoadLateClass(WCContentItemViewTemplateNewSight);
CHHook(0, WCContentItemViewTemplateNewSight, onLongTouch);
}
}
Il s'agit de la fonction d'entrée de CaptainHook. Toutes les classes hookées doivent être déclarées pour être chargées ici, et les méthodes de la classe doivent être déclarées hookées ici.
Ensuite, vous pouvez écrire du code dans les classes et les méthodes. Le code est trop long donc je ne le publierai pas sur github avec MMPlugin.
Ce projet comprend les fonctions de transfert de courtes vidéos, de récupération automatique des enveloppes rouges et de modification des étapes des exercices WeChat. Les fonctions de récupération automatique des enveloppes rouges et de modification des étapes des exercices WeChat peuvent être désactivées manuellement.
Remarque: Si vous utilisez des classes système, n'oubliez pas d'importer la bibliothèque de classe correspondante (telle que l'UIKIT) et les fichiers d'en-tête, sinon une erreur sera signalée lors de la compilation.
Une fois la compilation réussie, vous pouvez trouver la bibliothèque statique compilée dans le dossier des produits.
Trouvez-le dans Finder et copiez-le pour une utilisation ultérieure.
Les documents que vous devriez avoir à ce stade comprennent:
Trouvez le fichier binaire WeChat à partir de l'application WeChat d'origine et copiez-la pour une utilisation ultérieure.
Exécutez la commande suivante pour injecter mmplugin.dylib dans le fichier binaire WeChat.
LeonLei-MBP:WeChat gaoshilei$ ./yololib WeChat MMPlugin.dylib
Lors de l'exécution de cette commande, assurez-vous que Yololib, WeChat et WeChat.App sont dans le même répertoire.
Une fois terminé, copiez mmplugin.dylib et WeChat sur le wechat.app d'origine, écrasant le fichier WeChat d'origine.
Ouvrez le signataire de l'application iOS et sélectionnez divers paramètres comme indiqué ci-dessous:
Ce que j'ai choisi ici est un certificat de niveau d'entreprise.
Connectez-vous à votre téléphone mobile et exécutez la commande suivante pour vérifier si iDeviceInstaller est connecté au téléphone mobile:
LeonLei-MBP:WeChat gaoshilei$ ideviceinfo
Si de nombreuses informations sur le téléphone mobile sont imprimées, cela signifie que la connexion est réussie et que vous pouvez installer le package IPA. Exécutez la commande suivante pour installer:
LeonLei-MBP:WeChat gaoshilei$ ideviceinstaller -i WeChat.ipa
WARNING: could not locate iTunesMetadata.plist in archive !
WARNING: could not locate Payload/WeChat.app/SC_Info/WeChat.sinf in archive !
Copying ' WeChat.ipa ' to device... DONE.
Installing ' com.xxxxxxxxxxxx '
- CreatingStagingDirectory (5%)
- ExtractingPackage (15%)
- InspectingPackage (20%)
- TakingInstallLock (20%)
- PreflightingApplication (30%)
- InstallingEmbeddedProfile (30%)
- VerifyingApplication (40%)
- CreatingContainer (50%)
- InstallingApplication (60%)
- PostflightingApplication (70%)
- SandboxingApplication (80%)
- GeneratingApplicationMap (90%)
- Complete
Une fois l'installation terminée, ouvrez WeChat sur votre téléphone pour essayer les nouvelles fonctionnalités que nous avons ajoutées! Si un certain lien est bloqué, une erreur sera signalée. Veuillez consulter les rendus: