Für das Update 2020.1.14 ist keine Jailbreak-Installation erforderlich
Dieser Artikel ist ein Tutorial zum Umkehren der WeChat-Binärdatei, um die Weiterleitung kurzer Videos in Moments zu realisieren. Vom ersten Assemblercode bis zur endgültigen Neusignierungsinstallation und anderen Vorgängen erfahren Sie Schritt für Schritt, wie Sie mit WeChat spielen. Nachdem Sie es gelernt haben, wird es einfach sein, andere WeChat-Funktionen zurückzuentwickeln.
Dieser Artikel ist aufgrund seiner Länge in zwei Teile unterteilt. Im ersten Teil wird die umgekehrte Arbeit erläutert, d Optimieren Sie die Installation auf Maschinen mit Jailbreak.
Der zweite Teil des Textes enthält auch den Code für WeChat, um automatisch rote Umschläge zu erfassen und die Anzahl der WeChat-Schritte zu ändern. Diese finden Sie Schritt für Schritt, indem Sie der Routine dieses Artikels folgen und werden hier nicht wiederholt.
Vor dem Üben müssen Sie ein Telefon mit Jailbreak vorbereiten und alle unten aufgeführten Tools installieren. Bei IDA und Reveal handelt es sich um Crack-Versionen, die mehr als 2.000 Dollar kosten. Wenn Sie jedoch kein auf Reverse Engineering spezialisiertes Unternehmen sind, ist es das Geld wert Ich muss die Originalversion von Windows verwenden. Ich kann sie noch nicht auf dem Mac finden. Sie können Hopper verwenden, um IDA auf dem Mac zu ersetzen, was auch ein sehr leistungsfähiges Reverse-Engineering-Tool ist. Fangen wir ohne weitere Umschweife an!
Hinweis: Die in diesem Artikel umgekehrte WeChat-Binärdatei ist Version 6.3.28. Wenn es sich um eine andere WeChat-Version handelt, ist auch die Basisadresse in der Binärdatei unterschiedlich.
Umgekehrte Umgebung für MacOS + iPhone5S 9.1-Maschine mit Jailbreak <br> Verwenden Sie zuerst dumpdecrypted, um die Shell von WeChat zu zerschlagen (wenn Sie nicht wissen, wie, lesen Sie bitte dieses von mir geschriebene Tutorial), holen Sie sich eine WeChat.decrypted-Datei und werfen Sie diese Datei zunächst zur Analyse in IDA (eine Binärdatei von etwa 60 MB). Die Analyse durch IDA End dauert etwa 40 Minuten. Verwenden Sie Class-Dump, um alle Header-Dateien zu exportieren
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
Ich habe eine Schwiegermutter! Es gibt insgesamt 8.000 Header-Dateien und WeChat hat wirklich eine Menge Arbeit! Beruhige deine Gefühle, sammle deine Gedanken und mache weiter. Um den Download-Link eines kurzen Videos zu erhalten, suchen Sie die Ansicht, in der das Video abgespielt wird, und folgen Sie den Hinweisen, um die URL des kurzen Videos zu finden. Verwenden Sie Reveal, um das Wiedergabefenster des kurzen Videos anzuzeigen. Sie können sehen, dass das WCContentItemViewTemplateNewSigh-Objekt das Wiedergabefenster des kurzen Videos ist. Diese Klassen sind unser Einstiegspunkt. Wenn Sie ein Video als Favorit speichern, drücken Sie lange auf das Video, um die Option anzuzeigen. In der gerade exportierten Header-Datei finden Sie möglicherweise gestenbezogene Methoden.
- (void)onLongTouch;
- (void)onLongPressedWCSight:(id)arg1;
- (void)onLongPressedWCSightFullScreenWindow:(id)arg1;
Diese Methoden beziehen sich auf die Geste des langen Drückens. Suchen Sie diese Funktionen in IDA und zeigen Sie sie einzeln an. onLongPressedWCSight und onLongPressedWCSightFullScreenWindow sind relativ einfach, onLongTouch ist relativ lang, und ich habe festgestellt, dass die Methode Favorites_Add intern aufgerufen wird, denn wenn Sie lange auf das Video drücken, wird als Option „Favoriten“ angezeigt, und ich habe diesen Funktionsaufruf gesehen
ADRP X8, #selRef_sightVideoPath@PAGE
LDR X1, [X8,#selRef_sightVideoPath@PAGEOFF]
Ich habe die Adresse des kurzen Videos hier erhalten. Es kann spekuliert werden, dass diese Funktion mit der Sammlung zusammenhängt. Lassen Sie uns den Punkt aufschlüsseln und ihn testen.
(lldb) im li -o -f
[ 0] 0x000000000003c000 /var/mobile/Containers/Bundle/Application/2F1D52EC-C57E-4F95-B715-EF04351232E8/WeChat.app/WeChat(0x000000010003c000)
Sie können sehen, dass die ASLR von WeChat 0x3c000 ist. Suchen Sie die Basisadressen dieser drei Funktionen in IDA und legen Sie jeweils Haltepunkte fest.
(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
Gehen Sie zurück zu WeChat und drücken Sie lange auf das kurze Video, um zu sehen, wie der Haltepunkt ausgelöst wird.
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
……
Es wurde festgestellt, dass zuerst Haltepunkt 2 ausgelöst wurde, dann Haltepunkt 1 und dann jeweils einmal Haltepunkt 3 ausgelöst wurde. Sie können die Verbindung zwischen onLongPressedWCSightFullScreenWindow und der Sammlung von Kurzvideos ausschließen. Die Spuren von Kurzvideos müssen in den verbleibenden beiden Methoden gefunden werden. Finden Sie C bis V und finden Sie M, indem Sie den Hinweisen folgen, was sich bewährt hat! Verwenden Sie cycript, um WeChat zu injizieren und den Controller zu erhalten, in dem sich die Ansicht befindet, die das kurze Video abspielt.
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>"
Suchen Sie den Controller, zu dem WCContentItemViewTemplateNewSight gehört, über die Responder-Kette und es ist WCTimeLineViewController. In der Header-Datei dieser Klasse wurden keine wertvollen Hinweise gefunden, aber wir haben festgestellt, dass die Ansicht, in der sich das kurze Video befindet, zu MMTableVIewCell gehört (siehe das Reveal-Analysediagramm oben), der für jedes iOS am häufigsten verwendeten TableView- und Zellendaten. Es wird über die Proxy-Methode von UITableViewDataSource zugewiesen - tableView:cellForRowAtIndexPath:
Mit dieser Methode können Sie den Schatten von M definitiv kennen. Suchen Sie in IDA nach [WCTimeLineViewController tableView:cellForRowAtIndexPath:]
und suchen Sie die Basisadresse 0x10128B6B0:
__text:000000010128B6B0 ADRP X8, #selRef_genNormalCell_indexPath_@PAGE
Die Funktion hier ist die Methode zum Generieren von Zellen in WCTimeLineViewController. Zusätzlich zu dieser Methode gibt es drei weitere Methoden zum Generieren von Zellen in dieser Klasse:
- (void)genABTestTipCell:(id)arg1 indexPath:(id)arg2;
- (void)genRedHeartCell:(id)arg1 indexPath:(id)arg2;
- (void)genUploadFailCell:(id)arg1 indexPath:(id)arg2;
Aus der wörtlichen Bedeutung können wir erraten, dass „Normal“ eine Methode zum Erzeugen kleiner Videozellen sein sollte. Suchen Sie weiterhin nach Hinweisen in IDA
__text:0000000101287CC8 ADRP X8, #selRef_getTimelineDataItemOfIndex_@PAGE
Die obige Methode befindet sich in genNormalCell:IndexPath:
: Sie können davon ausgehen, dass es sich bei dieser Methode um eine Methode zum Abrufen von TimeLine-Daten (Momenten) handelt. Die Daten des kurzen Videos müssen ebenfalls über diese Methode abgerufen werden Rufen Sie in dieser Methode eine Methode namens selRef_getTimelineDataItemOfIndex_
auf, die das DataItem erhält, das die Datenquelle der Zelle zu sein scheint! Als nächstes verwenden Sie LLDB, um Haltepunkte festzulegen, um die Vermutung zu überprüfen. Sie können die dieser Methode entsprechende Basisadresse über IDA finden: 0x101287CE4 Geben Sie zunächst den ASLR-Offset für die Ausführung von WeChat aus.
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)
Der Standort unseres Haltepunkts ist also 0x50000+0x101287CE4
(lldb) br s -a 0x50000+0x101287CE4
Breakpoint 1: where = WeChat`___lldb_unnamed_symbol63721$$WeChat + 252, address = 0x00000001012d7ce4
Geben Sie den Wert von x0 aus
(lldb) po $x0
Class name: WCDataItem, addr: 0x15f5f03b0
tid: 12393001887435993280
username: wxid_z8twcz4o18fg12
createtime: 1477360950
commentUsers: (
)
contentObj: <WCContentItem: 0x15f57d000>
Holen Sie sich hier ein WCDataItem-Objekt, das der Rückgabewert nach der Ausführung von selRef_getTimelineDataItemOfIndex_
ist, und ändern Sie dann den Wert von x0.
(lldb) register write $x0 0
(lldb) c
Zu diesem Zeitpunkt werden Sie feststellen, dass der Inhalt des kleinen Videos, das wir aktualisieren möchten, vollständig leer ist.
An diesem Punkt haben wir die Methode gefunden, um die Quelldaten des kurzen Videos zu erhalten. Die Frage ist, wie wir dieses WCDataItem erhalten. Schauen Sie sich weiterhin den Aufrufstatus der IDA-Analysefunktion an:
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
......
Der an selRef_getTimelineDataItemOfIndex_
übergebene Parameter ist x2. Sie können sehen, dass x21 der Rückgabewert der Funktion selRef_calcDataItemIndex_
ist, bei der es sich um einen vorzeichenlosen langen Datentyp handelt. In Fortsetzung der Analyse ist der Aufrufer selRef_getTimelineDataItemOfIndex_
der Rückgabewert von selRef_getService_
im vorherigen Schritt. Nach der Haltepunktanalyse wird festgestellt, dass es sich um ein WCFacade
-Objekt handelt. Sortieren wir die Aufrufe von selRef_getTimelineDataItemOfIndex_
:
Der Aufrufer ist der Rückgabewert von selRef_getService_
; der Parameter ist der Rückgabewert von selRef_calcDataItemIndex_
<br> Wenden wir uns nun diesen beiden Funktionen zu und analysieren nach demselben Prinzip, wie sie den Aufruf implementieren.
selRef_getService_
an:MMServiceCenter
zugewiesen ist ist die Rückgabe des [MMServiceCenter defaultCenter]
.[WCFacade class]
ist.selRef_calcDataItemIndex_
:WCTimeLineViewController
den Aufrufer x0 an der Position 0x101287C58.selRef_section
0x101287C4C wird festgestellt, dass selRef_section
eingehende Parameter von selRef_section
stammt oder x3 istWCTimeLineViewController - (void)genNormalCell:(id) indexPath:(id)
, daher ist der Parameter von selRef_calcDataItemIndex_
[IndexPath section]
.getTimelineDataItemOfIndex:
kann also passieren [[MMServiceCenter defaultCenter] getService:[WCFacade class]]
Um ihn zu erhalten, können seine Parameter über die folgende Funktion abgerufen werden
[WCTimeLineViewController calcDataItemIndex:[indexPath section]]
Haben Sie immer das Gefühl, dass etwas fehlt? Wir haben den indexPath noch nicht erhalten! Der nächste Schritt besteht darin, den indexPath abzurufen, was relativ einfach ist, da wir uns in [WCContentItemViewTemplateNewSight onLongTouch]
befinden, sodass wir MMTableViewCell, MMTableView und WCTimeLineViewController nacheinander über [self nextResponder]
abrufen und dann den indexPath über [MMTableView indexPathForCell:MMTableViewCell]
.
Danach haben wir das WCDataItem-Objekt erhalten. Der nächste Fokus sollte auf WCDataItem liegen und schließlich das gewünschte kurze Video erhalten. Suchen Sie in der Header-Datei dieser Klasse nach Hinweisen. Da das Video erst nach dem Herunterladen abgespielt werden kann, sollte der Pfad des Videos hier abgerufen werden. Achten Sie daher auf die Attribute oder Methoden im Zusammenhang mit URL und Pfad und finden Sie dann Folgendes Verdächtige.
@property(retain, nonatomic) NSString *sourceUrl2;
@property(retain, nonatomic) NSString *sourceUrl;
- (id)descriptionForKeyPaths;
- (id)keyPaths;
Gehen Sie zurück zu LLDB und drucken Sie diese Werte mit Haltepunkten aus, um zu sehen, was dort ist.
(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
Es gibt keine wertvollen Hinweise, aber mir ist aufgefallen, dass es in WCDataItem ein WCContentItem gibt. Der einzige Weg, damit anzufangen, ist hier.
@property(retain, nonatomic) NSString *linkUrl;
@property(retain, nonatomic) NSString *linkUrl2;
@property(retain, nonatomic) NSMutableArray *mediaList;
Drucken Sie es in LLDB aus
(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>
)
Es gibt ein WCMediaItem-Objekt im mediaList-Array, das im Allgemeinen zur Darstellung von Video und Audio verwendet wird. Finden Sie schnell die Header-Datei und durchsuchen Sie sie.
@property(retain, nonatomic) WCUrl *dataUrl;
- (id)pathForData;
- (id)pathForSightData;
- (id)pathForTempAttachVideoData;
- (id)videoStreamForData;
Unter den oben genannten Attributen und Methoden ist pathForSightData
am wahrscheinlichsten, um den kurzen Videopfad zu erhalten.
(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
Habe die Netzwerk-URL und den lokalen Pfad des kurzen Videos erhalten! Hier können Sie mit iFunBox oder scp diese Datei aus der Sandbox kopieren, um zu sehen, ob es sich um ein kurzes Video handelt, das diese Zelle abspielen soll.
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
Öffnen Sie es mit QuickTime und stellen Sie fest, dass es sich tatsächlich um das kurze Video handelt, nach dem wir suchen. Überprüfen Sie erneut, ob die URL korrekt ist. Öffnen Sie die oben gedruckte dataUrl im Browser und stellen Sie fest, dass es sich auch um dieses kurze Video handelt. Bei der Analyse dieser Klasse können wir folgende Schlussfolgerungen ziehen:
Zu diesem Zeitpunkt ist die Analyse des Pfads und der Erfassungsmethode des Kurzvideos abgeschlossen. Um eine Weiterleitung zu erreichen, müssen wir die Veröffentlichung von WeChat Moments weiter analysieren.
Dieser Abschnitt ist ein Umweg, den ich gemacht habe, als ich nach der Kurzvideo-Weiterleitungsfunktion gesucht habe. Ich habe jedoch keine Möglichkeit gefunden, sie zu implementieren. Wenn Sie dies nicht tun, finden Sie darin auch einige häufig verwendete Ideen und Methoden. Wenn Sie es nicht lesen möchten, können Sie zum zweiten Abschnitt springen.
Öffnen Sie die Aufnahmeschnittstelle des Kurzvideos und fügen Sie es mit Cycript ein. Wir müssen herausfinden, welche Methode zum Veröffentlichen des Kurzvideos verwendet wird, und dann überprüfen, welche Fenster sich im aktuellen Fenster befinden (da die Aufnahme des Kurzvideos nicht erfolgt). erfolgt im keyWindow von 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>>"
)
Es wurde festgestellt, dass die aktuelle Seite insgesamt 5 Fenster hat, darunter MMUIWindow, das Fenster, in dem das kurze Video aufgenommen wurde. Drucken Sie die UI-Baumstruktur aus.
cy# [#0x127796440 recursiveDescription]
Das gedruckte Ergebnis ist ziemlich lang, daher werde ich es nicht veröffentlichen. Finden Sie diese Schaltfläche, mit der Sie kurze Videos aufnehmen können
| | | | | | <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>>
und dann ausführen
cy# [#0x1277a9d70 setHidden:YES]
Ich stellte fest, dass der Aufnahmeknopf verschwunden war, was meinen Verdacht bestätigte. Um das Antwortereignis der Schaltfläche zu finden, können Sie es über das Ziel finden
cy# [#0x1277a9d70 allTargets]
[NSSet setWithArray:@[#"<MainFrameSightViewController: 0x1269a4600>"]]]
cy# [#0x1277a9d70 allControlEvents]
193
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:193]
null
Ich habe festgestellt, dass die Schaltfläche keine entsprechende Aktion hat, was seltsam ist! UIButton muss ein Ziel und eine Aktion haben, sonst kann der Button nicht auf Ereignisse reagieren. Probieren wir andere ControlEvents aus
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchDown]
@["btnPress"]
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchUpOutside]
@["btnRelease"]
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchUpInside]
@["btnRelease"]
Es stellt sich heraus, dass diese drei ControlEvents entsprechende Aktionen haben. Schauen wir uns die Werte dieser drei Aufzählungen an.
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;
Es ist ersichtlich, dass UIControlEventTouchDown 1 entspricht, UIControlEventTouchUpInside 128 entspricht, UIControlEventTouchUpOutside 64 entspricht und die Summe der drei genau 193 beträgt! Es stellt sich heraus, dass beim Aufruf von [#0x1277a9d70 allControlEvents]
der zurückgegebene Wert eine Aufzählung sein sollte. Wenn es mehrere Aufzählungen gibt, ist das nicht etwas verwirrend. Mir geht es genauso! Gerade haben wir die den drei ControlEvents entsprechenden Aktionen ausgedruckt und mit LLDB+IDA zur dynamischen Analyse fortgefahren.
Da wir einen Weg finden müssen, kurze Videos zu veröffentlichen, ist uns die entsprechende btnPress
-Funktion egal. Wir konzentrieren uns auf btnRelease
, eine Methode, die nach dem Loslassen der Aufnahmetaste aufgerufen wird. Suchen Sie diese Methode in IDA und legen Sie den nächsten Haltepunkt fest, nachdem Sie sie gefunden haben
(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
Nehmen Sie mit Ihrem Mobiltelefon ein kurzes Video auf und lassen Sie es dann los, wodurch der Haltepunkt ausgelöst wird, der zeigt, dass unsere Vermutung richtig ist. Als wir die Analyse fortsetzten, stellten wir fest, dass der Code von der rechten Seite des Bildes oben stammt. Nach dem Betrachten gibt es keine Möglichkeit, zur Videoversion zu springen. Wenn Sie jedoch genau hinsehen, gibt es einen Block Der Verzögerungsblock des Systems liegt bei 0x102093760. Dann folgen wir dem Haltepunkt und springen zu der in x16 gespeicherten Adresse bei 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>
Es wurde festgestellt, dass der übergebene Parameter x2 ein Block ist. Sehen wir uns die Funktion „dispatch_after“ noch einmal an.
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
Diese Funktion verfügt über drei Parameter, nämlich „dispatch_time_t“, „dispatch_queue_t“ und „dispatch_block_t“. also muss x2 sein. Wenn es andere Methodenaufrufe gibt, besteht der nächste Schritt darin, die Position dieses Blocks zu kennen.
(lldb) memory read --size 8 --format x 0x16fd49f88
0x16fd49f88: 0x000000019f8fd218 0x00000000c2000000
0x16fd49f98: 0x000000010214777c 0x0000000102fb0e60
0x16fd49fa8: 0x000000015da32600 0x000000015ea1a430
0x16fd49fb8: 0x000000015cf5fee0 0x000000016fd49ff0
0x000000010214777c ist der Speicherort des Blocks btnRelease
Natürlich muss der aktuelle ASLR-Offset in IDA 0x10209377C sein. Diese Unterroutine ist sehr einfach und verfügt nur über eine Methode selRef_logicCheckState_
, die möglicherweise unser Ziel ist. Schauen wir uns zunächst an, wer diese Methode aufgerufen hat
(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>
Ich habe festgestellt, dass es immer noch vom MainFrameSightViewController-Objekt aufgerufen wurde, daher muss sich selRef_logicCheckState_
auch in der Header-Datei dieser Klasse befinden, die ich gesucht und gefunden habe.
- (void)logicCheckState:(int)arg1;
Suchen Sie im linken Fenster von IDA nach [MainFrameSightViewControllerlogicCheckState:] und stellen Sie fest, dass diese Methode sehr kompliziert ist und zu viel Logik enthält. Machen Sie sich also keine Sorgen und gehen Sie sie langsam durch. Wir haben einen Schaltersprung bei 0x102094D6C gefunden und die Idee war sehr klar. Wir müssen nur die Zeile finden, in der das kurze Video gedreht wurde, und LLDB wird uns helfen, die Zeile zu betrachten. Legen Sie einen Haltepunkt bei 0x102094D6C fest. Dieser Haltepunkt wird beim Aufnehmen kurzer Videos mehrmals ausgelöst. Sie können den Haltepunkt vor der Aufnahme deaktivieren, den Haltepunkt vor dem Loslassen aktivieren und zu diesem Zeitpunkt den x8-Wert drucken.
(lldb) p/x $x8
(unsigned long) $38 = 0x0000000102174e10
x8 ist ein Zeiger und die Adresse, auf die er zeigt, ist 0x102174e10. Verwenden Sie diese Adresse abzüglich des aktuellen ASLR-Offsets. Es wird festgestellt, dass die logische Verarbeitungszeile 0x102094E10 ist Springen Sie nach der Position 0x102094E24 zu 0x1020951C4. Dieser Zweig hat weniger Inhalt und enthält drei Funktionen.
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
Darunter sind selRef_finishWriter
und selRef_turnCancelBtnForFinishRecording
. Diese beiden Methoden scheinen das Ende der kurzen Videoaufzeichnung zu bedeuten. Bei der Betrachtung des Aufrufers haben wir festgestellt, dass diese beiden Methoden zu MainFrameSightViewController gehören. Überprüfen Sie diese beiden Methoden weiterhin in IDA. Ich habe am Ende von selRef_finishWriter
bei 0x102094248 eine Methode namens f_switchToSendingPanel
gefunden, einen Haltepunkt festgelegt und dann das Video gedreht. Dabei habe ich festgestellt, dass diese Methode nicht ausgelöst wurde. Die Veröffentlichungsschnittstelle sollte nicht über diese Methode aufgerufen werden. Kehren Sie daher zur Methode selRef_finishWriter
zurück. Rufen Sie die Methode selRef_stopRecording
am Speicherort 0x1020941DC auf. Der Aufrufer, der sie ausgibt, stellt fest, dass diese Methode zu SightFacade
gehört, und sucht weiterhin nach der Methode Implementierung dieser Methode in IDA. selRef_stopRecord
wird erneut an der Position 0x101F9BED4 dieser Methode aufgerufen. Der Aufrufer stellt außerdem fest, dass diese Methode zu SightCaptureLogicF4 gehört. Es ist ein bisschen wie das Schälen einer Zwiebel und sucht weiterhin nach der Implementierung dieser Methode. selRef_finishWriting
wird innerhalb dieser Methode erneut an Position 0x101A98778 aufgerufen. Nach dem gleichen Prinzip wird festgestellt, dass diese Methode zu SightMovieWriter gehört. Drei Schichten wurden abgezogen, weiter geht es:
Zwei Zeilen werden an der Position 0x10261D004 in SightMovieWriter - (void)finishWriting
An dieser Position wird ein Haltepunkt gesetzt, und dann wird der Haltepunkt nach der Aufnahme des kurzen Videos ausgelöst und der Wert von x19 gedruckt.
(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 }>
Daher springt der Code nicht zu loc_10261D054, sondern nach links. Es wird festgestellt, dass dieser Block das Unterprogramm sub_10261D0AC ist und die Adresse 0x10261D0AC lautet wie unten gezeigt:
Es ist ersichtlich, dass es hauptsächlich in zwei Zeilen unterteilt ist, nämlich 0x10261D108. Nach dem Auslösen des Haltepunkts wird der Wert von x0 als 1 gedruckt Ist
__text:000000010261D104 CMP X0, #2
__text:000000010261D108 B.EQ loc_10261D234
B.EQ springt nur dann zu loc_10261D234, wenn das Ergebnis des vorherigen Schritts 0 ist, das Ergebnis hier jedoch nicht 0 ist. Ändern Sie den Wert von x0 in 2, sodass das Ergebnis des vorherigen Schritts 0 ist.
(lldb) po $x0
1
(lldb) register write $x0 2
(lldb) po $x0
2
Lassen Sie zu diesem Zeitpunkt den Haltepunkt los und warten Sie, bis Sie zur kurzen Video-Veröffentlichungsoberfläche springen. Das Ergebnis ist, dass es in dieser Schnittstelle hängen bleibt und keine Reaktion erfolgt. Ich denke also, dass die Logik zum Erkennen des Sprungs in der rechten Zeile liegen sollte. und suchen Sie weiter entlang der rechten Zeile: Es wurde festgestellt, dass die folgende Methode rechts bei 0x10261D3AC aufgerufen wurde
- (void)finishWritingWithCompletionHandler:(void (^)(void))handler;
Diese Methode ist eine vom System bereitgestellte Methode, die nach Abschluss des Videoschreibens ausgeführt werden muss. Da es nur einen Parameter gibt, ist die entsprechende Variable x2 und der Wert von x2 wird gedruckt.
(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
Und finden Sie die Blockposition 0x10261D4B0 über den Stapelspeicher (der ASLR-Offset muss subtrahiert werden).
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
Es werden nur zwei Methoden aufgerufen, eine ist selRef_stopAmr
zum Stoppen von AMR (ein Audioformat) und die andere ist selRef_compressAudio
zum Komprimieren von Audio. Der nächste Vorgang nach der Aufnahme sollte nicht so lange in diesen beiden Methoden platziert werden Und ich habe immer noch keine Ahnung, dass diese Straße tot zu sein scheint, geraten Sie nicht in Schwierigkeiten, ziehen Sie sich strategisch zurück und suchen Sie nach anderen Eingängen.
Der Spaß daran, den umgekehrten Weg zu gehen, besteht darin, dass man auf dem Weg zur Wahrheit die Freude am Erfolg erleben kann. Man kann auch in die falsche Richtung gehen und sich immer weiter von der Wahrheit entfernen. Lassen Sie sich nicht entmutigen, passen Sie die Richtung an und mach weiter so!
(Da WeChat im Hintergrund heimlich aktualisiert wurde, ist der folgende Inhalt die ASLR von WeChat Version 6.3.30, und die obige Analyse basiert auf Version 6.3.28.)
Beachten Sie, dass beim Klicken auf die Kameraschaltfläche in der oberen rechten Ecke des Freundeskreises unten ein Blatt angezeigt wird. Das erste ist das kurze Video „Starten Sie hier“ und verwenden Sie cycript, um zu sehen, welchem Ereignis die Schaltfläche „Sicht“ entspricht Zu.
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()
Das Blatt unten ist NewYearActionSheet. Drucken Sie dann das UI-Baumstrukturdiagramm von NewYearActionSheet aus (es ist zu lang, daher werde ich es nicht veröffentlichen). Stellen Sie dann fest, dass der UIButton, der Sight entspricht, 0x14f36d470 ist
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:"]
Das an die Schaltfläche gebundene Ereignis kann über die selRef_dismissWithClickedButtonIndex_animated
actionsForTarget:forControlEvent:
von OnDefaultButtonTapped:
gefunden werden. Gehen Sie zurück zu IDA und suchen Sie diese Methode in NewYearActionSheet. Beim Drucken des Aufrufers stellen wir fest, dass es sich immer noch um NewYearActionSheet handelt, um die Methode newYearActionSheet_clickedButtonAtIndex
zu finden. Beim Drucken des Aufrufers x0 stellen wir fest, dass er zur Klasse WCTimeLineViewController gehört. Folgen Sie dem Haltepunkt und rufen Sie die Methode #selRef_showSightWindowForMomentWithMask_byViewController_scene
an der Position 0x1012B78CC auf. Durch Beobachtung haben wir herausgefunden, dass der Rückgabewert x0 an der Position 0x1012B78AC ist Ich habe die Header-Datei durchsucht, um diese Methode zu finden
- (void)showSightWindowForMomentWithMask:(id)arg1 byViewController:(id)arg2 scene:(int)arg3;
Diese Methode sollte die Methode sein, um zur kleinen Videoschnittstelle zu springen. Drucken Sie die Parameter unten separat aus
(lldb) po $x2
<UIImage: 0x14f046660>, {320, 568}
(lldb) po $x3
<WCTimeLineViewController: 0x14e214800>
(lldb) po $x4
2
(lldb) po $x0
<SightFacade: 0x14f124b40>
Unter diesen entsprechen x2, x3 und x4 den drei Parametern x0. Springen Sie in das Innere dieser Methode, um zu sehen, wie sie implementiert wird. Es wurde festgestellt, dass die Schnittstelle für kurze Videoaufnahmen in dieser Methode initialisiert wird. Zuerst wird ein MainFrameSightViewController initialisiert, dann wird ein UINavigationController erstellt, um den MainFrameSightViewController darin einzufügen, und dann wird ein MMWindowController zum Aufrufen initialisiert
- (id)initWithViewController:(id)arg1 windowLevel:(int)arg2;
Die Methode fügt den UINavigationController ein und schließt alle UI-Erstellungsarbeiten für die Kurzvideoaufnahmeschnittstelle ab. Nachdem die Aufnahme abgeschlossen ist, rufen Sie die Veröffentlichungsschnittstelle auf. Stellen Sie zu diesem Zeitpunkt fest, dass der aktuelle Controller SightMomentViewController ist ? Wir erstellen selbst einen SightMomentEditViewController, fügen ihn in den UINavigationController ein und fügen diesen Navigationscontroller dann in den MMWindowController ein. **(Ich habe die Optimierung hier zur Überprüfung geschrieben, und die spezifischen Optimierungsideen werden später geschrieben)** Das Ergebnis ist, dass die Veröffentlichungsoberfläche tatsächlich angezeigt werden kann, aber die NavgationBar der Navigationsleiste deckt das Original und das Ganze ab Die Schnittstelle ist transparent, sie ist hässlich und nach Abschluss der Veröffentlichung kann der gesamte MMWindowController nicht zerstört werden und verbleibt weiterhin in der Veröffentlichungsschnittstelle. Dies ist nicht das gewünschte Ergebnis, aber wir gewinnen viel. Zumindest können wir die Veröffentlichungsschnittstelle direkt aufrufen und kleine Videos können normal weitergeleitet werden. Meine persönliche Vermutung ist, dass der Grund, warum die aktuelle Schnittstelle nicht zerstört werden kann, darin liegt, dass MMWindowController ein neues Fenster erstellt hat, das nicht mit dem keyWindow identisch ist, in dem sich TimeLine befindet. Die Schaltflächenauslösermethode von SightMomentEditViewController kann dieses Fenster nicht zerstören, daher habe ich ein Mutige Vermutung: Kann ich SightMomentEditViewController nicht einfach direkt auf dem aktuellen WCTimeLineViewController anzeigen?
[WCTimelineVC presentViewController:editSightVC animated:YES completion:^{
}];
Wäre es nicht schön, es so darzustellen? Durch Beobachtung der Header-Datei von SightMomentEditViewController in Kombination mit den Elementen auf der Schnittstelle bei der Veröffentlichung des Kurzvideos wird jedoch spekuliert, dass für die Erstellung dieses Controllers mindestens zwei Attribute erforderlich sind: eines ist der Pfad des Kurzvideos und das andere ist die Miniaturansicht des kurzen Videos. Wenn das Attribut an SightMomentEditViewController übergeben wird, sollte es normal angezeigt werden.
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:^{
}];
Die kurze Video-Publishing-Oberfläche kann normal angezeigt werden und alle Funktionen können normal verwendet werden. Das einzige Problem besteht darin, dass die Zurück-Schaltfläche keine Wirkung hat und der SightMomentEditViewController nicht zerstört werden kann. Überprüfen Sie mit cycript das Aktionsereignis der linken Schaltfläche und stellen Sie fest, dass die Antwortfunktion - (void)popSelf;
Durch Klicken auf die linke Schaltfläche wird die Pop-Methode ausgelöst, dieser Controller befindet sich jedoch nicht im NavigationController und ist daher ungültig Ich muss diese Methode wiederholen
- (void)popSelf
{
[self dismissViewControllerAnimated:YES completion:^{
}];
}
Klicken Sie zu diesem Zeitpunkt auf die Schaltfläche „Zurück“, um den Vorgang normal zu beenden. Außerdem wurde in WCContentItemViewTemplateNewSight eine Methode namens - (void)sendSightToFriend;
gefunden, mit der kurze Videos direkt an Freunde weitergeleitet werden können .
Die Weiterleitung von Kurzvideos unterstützt vier Funktionen: Weiterleiten an Momente, Weiterleiten an Freunde, Speichern im lokalen Fotoalbum und Kopieren des Kurzvideo-Links in die Zwischenablage. Wenn das Kurzvideo nicht heruntergeladen wurde, wird durch langes Drücken nur der URL-Link des Kurzvideos angezeigt.
Hier müssen wir zwei Klassen einbinden, nämlich WCContentItemViewTemplateNewSight und SightMomentEditViewController, und dann das Menü-Popup-Menü hinzufügen. Der spezifische Code lautet wie folgt:
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];
Ich habe die spezifische Tweak-Datei auf Github WCSightRetweet abgelegt
@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"//安装完成杀掉的进程
Die Steuerdatei muss nicht geändert werden. Führen Sie dann den Befehl make package install
aus, um WeChat zu installieren, und öffnen Sie WeChat erneut. Die Funktion zum Weiterleiten kurzer Videos wurde hinzugefügt.
Installieren Sie macports (der Installationsvorgang erfordert eine VPN-Verbindung, andernfalls ist die Installation nicht erfolgreich)
Öffnen Sie nach der Installation von MacPorts das Terminal und geben Sie sudo port -v selfupdate
ein, um MacPorts auf die neueste Version zu aktualisieren, was lange dauern kann.
Installieren Sie nach der Aktualisierung von MacPorts die DPKG-Datei und geben Sie im Terminal sudo port -f install dpkg
ein.
Laden Sie iOSOpendev herunter und installieren Sie es. Wenn die Installation fehlschlägt, können Sie mit Command + L
nach Problemen während der Installation suchen.
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";
}
Hier ist eine Lösung: Laden Sie den Ordner „Specifications“ in iOSOpenDevInstallSolve herunter
5. Um das Installationsfehlerproblem zu beheben, öffnen Sie den in Schritt 4 heruntergeladenen Ordner „Spezifikationen“. Darin sollten sich 8 Dateien befinden. Wenn Sie mehrere xcodes installiert haben, fügen Sie diese bitte in die entsprechenden xcodes ein.
(1) Die vier Dateien, die mit iPhoneOS beginnen, werden im Ordner /Application/Xcode/Content/Developer/Platforms/IphoneOS.platform/Developer/Library/Xcode/Specifications abgelegt (falls nicht, erstellen Sie bitte selbst einen Ordner „Specifications“).
(2) Die anderen vier Dateien, beginnend mit iPhone Simulator, werden im Ordner /Application/Xcode/Content/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Xcode/Specifications abgelegt (falls nicht, erstellen Sie bitte ebenfalls eine).
(3) Erstellen Sie einen Ordner „usr“ unter dem Ordner „/Application/Xcode/Content/Developer/Platforms/iPhoneSimulator.platform/Developer/“ und erstellen Sie einen Ordner mit dem Namen „bin“ unter dem Ordner „usr“.
Hinweis: Manchmal wird eine Meldung angezeigt, dass die Installation fehlgeschlagen ist. Wenn im Optionsmenü des Projekts iOSOpenDev angezeigt wird, ist die Installation erfolgreich.
Um das ipa-Paket zu installieren, können Sie auch Tools wie itool verwenden, ideviceinstaller kann jedoch den Installationsprozess sehen, was es uns erleichtert, die Fehlerursache zu finden.
Befehl ausführen
brew install ideviceinstaller
Wenn Sie die Meldung erhalten, dass der Befehl brew nicht gefunden werden kann, bedeutet dies, dass Homebrew nicht auf Ihrem Mac installiert wurde.
Häufige Fehlermeldungen:
ERROR: Could not connect to lockdownd, error code -5
Installieren Sie zu diesem Zeitpunkt einfach libimobiledevice neu (da ideviceinstaller auf vielen anderen Plug-Ins basiert).
Führen Sie den folgenden Befehl aus:
$ brew uninstall libimobiledevice
$ brew install --HEAD libimobiledevice
Laden Sie das iOS App Signer-Tool zum erneuten Signieren herunter* (sparen Sie viele Befehlszeilenvorgänge und signieren Sie mit einem Klick erneut!)*
(3) Laden Sie die gehackte WeChat-Anwendung herunter
Da das AppStore-Paket verschlüsselt (geshellt) ist und nicht erneut signiert werden kann, müssen Sie ein geshelltes Paket verwenden. Sie können dumpdecrypted verwenden, um die Shell selbst zu sichern, oder Sie können direkt den PP-Assistenten oder den itool-Assistenten verwenden, um die Version mit Jailbreak herunterzuladen der geshellten WeChat-Anwendung.
(4) Installieren Sie Yololib
Yololib kann Dylib in die WeChat-Binärdatei einfügen, sodass Ihr Hook wirksam werden kann. Kompilieren Sie Yololib und erhalten Sie es
#####(1) Im vorherigen Schritt wurde eine statische Bibliothek installiert. Öffnen Sie nun ein neues Projekt in Xcode. Hier müssen Sie das CaptainHook Tweak-Projekt auswählen. Es gibt nur eine neu erstellte .mm-Datei. Wir müssen nur alle Hook-Methoden in diese Datei schreiben.
Da Maschinen ohne Jailbreak keine Tweak-Plug-Ins installieren können, um Originalanwendungen wie Maschinen mit Jailbreak einzubinden, verwendet CaptainHook den Runtime-Mechanismus, um Funktionen wie Klassendefinition und Methodenersetzung mithilfe von Makrobefehlen zu implementieren. Hier finden Sie eine kurze Einführung in die Verwendung:
CHDeclareClass(WCContentItemViewTemplateNewSight);
CHDeclareClass(ClassName)
gibt an, welche Klasse eingehängt werden soll, und wird normalerweise zu Beginn der Operation für diese Klasse geschrieben.
CHDeclareMethod0(id, WCContentItemViewTemplateNewSight, SLSightDataItem){......}
CHDeclareMethod(count, return_type, class_type, name1, arg1, ....)
bedeutet das Erstellen einer neuen Methode, count bedeutet die Anzahl der Parameter dieser Methode, return_type bedeutet den Rückgabetyp, class_type gibt den Klassennamen dieser Methode ein, name1 bedeutet den Methodennamen, arg1 stellt den ersten Parameter dar. Wenn kein Parameter vorhanden ist, lassen Sie ihn leer und so weiter.
CHOptimizedMethod0(self, void, WCContentItemViewTemplateNewSight, onLongTouch){
CHSuper(0, className, Method);//可选
......
}
CHOptimizedMethod(count, optimization, return_type, class_type, name1, type1, arg1)
bedeutet das Einbinden der ursprünglichen Methode (wenn CHSuper(0, className, Method)
bedeutet, die ursprüngliche Methode zu kopieren, CHSuper bedeutet, die ursprüngliche Methode an der aktuellen Position aufzurufen), count bedeutet die Anzahl der Hook-Methodenparameter, die Optimierung füllt im Allgemeinen self aus, return_type ist der Rückgabewerttyp der Methode, class_type füllt den Klassennamen von aus die aktuelle Klasse, name1 ist der Methodenname, arg1 ist der Parameter, wenn keine Parameter vorhanden sind, füllen Sie arg aus und so weiter.
CHConstructor
{
@autoreleasepool
{
CHLoadLateClass(WCContentItemViewTemplateNewSight);
CHHook(0, WCContentItemViewTemplateNewSight, onLongTouch);
}
}
Dies ist die Einstiegsfunktion von CaptainHook. Alle eingehängten Klassen müssen hier zum Laden deklariert werden, und die Methoden in der Klasse müssen hier als eingehängt deklariert werden.
Dann können Sie Code in die Klassen und Methoden schreiben. Der Code ist zu lang, daher werde ich ihn nicht mit MMPlugin veröffentlichen.
Dieses Projekt umfasst die Funktionen zum Weiterleiten kurzer Videos, zum automatischen Erfassen roter Umschläge und zum Ändern von WeChat-Übungsschritten. Die Funktionen zum automatischen Erfassen roter Umschläge und zum Ändern von WeChat-Übungsschritten können manuell deaktiviert werden.
Hinweis: Wenn Sie Systemklassen verwenden, denken Sie daran, die entsprechende Klassenbibliothek (z. B. UIKIT) und Header -Dateien zu importieren. Andernfalls wird während der Kompilierung ein Fehler gemeldet.
Nach einer erfolgreichen Zusammenstellung finden Sie die kompilierte statische Bibliothek im Produktordner.
Finden Sie es in Finder und kopieren Sie es für die spätere Verwendung.
Zu den Materialien, die Sie zu diesem Zeitpunkt haben sollten, gehören:
Suchen Sie die WeChat -Binärdatei aus der ursprünglichen WeChat -App und kopieren Sie sie für die spätere Verwendung.
Führen Sie den folgenden Befehl aus, um mmplugin.dylib in die WeChat -Binärdatei zu injizieren.
LeonLei-MBP:WeChat gaoshilei$ ./yololib WeChat MMPlugin.dylib
Stellen Sie bei der Ausführung dieses Befehls sicher, dass Yololib, Wechat und Wechat.app im selben Verzeichnis sind.
Nach Abschluss kopieren Sie Mmplugin.dylib und WeChat in die ursprüngliche Wechat.app, wobei die ursprüngliche Wechat -Datei überschreiben.
Öffnen Sie den iOS -App -Signator und wählen Sie verschiedene Parameter aus, wie unten gezeigt:
Was ich hier ausgewählt habe, ist ein Zertifikat auf Unternehmensebene.
Stellen Sie eine Verbindung zu Ihrem Mobiltelefon her und führen Sie den folgenden Befehl aus, um zu überprüfen, ob iDeviceInstaller mit dem Mobiltelefon verbunden ist:
LeonLei-MBP:WeChat gaoshilei$ ideviceinfo
Wenn viele Mobiltelefoninformationen ausgedruckt sind, bedeutet dies, dass die Verbindung erfolgreich ist und Sie das IPA -Paket installieren können. Führen Sie den folgenden Befehl aus, um zu installieren:
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
Nach Abschluss der Installation öffnen Sie WeChat auf Ihrem Telefon, um die neuen Funktionen zu probieren, die wir hinzugefügt haben! Wenn ein bestimmter Link steckt, wird ein Fehler gemeldet. Bitte beachten Sie die Renderings: