手把手教你逆向微信之朋友圈小視頻轉(zhuǎn)發(fā)(上)

LeonLei的博客 歡迎來(lái)訪赤赊!

前言

此文為逆向微信二進(jìn)制文件牡属,實(shí)現(xiàn)朋友圈小視頻轉(zhuǎn)發(fā)的教程,從最開(kāi)始的匯編代碼入手到最后重簽名安裝等操作谎替,手把手教你玩轉(zhuǎn)微信偷溺!學(xué)會(huì)之后再去逆向微信其他功能易如反掌。
本篇文章由于篇幅太長(zhǎng)分成了兩篇钱贯,上篇講解的是逆向工作挫掏,也就是怎么找到相關(guān)的函數(shù)和方法實(shí)現(xiàn),下篇講解的是怎么在非越獄機(jī)重簽名安裝和越獄機(jī)tweak安裝的詳細(xì)過(guò)程秩命。
正文的第二部分還提供了微信自動(dòng)搶紅包尉共、修改微信步數(shù)的代碼褒傅,這些都可以照葫蘆畫(huà)瓢按照本文的套路一步步逆向找到,這里就不再贅述袄友。
在實(shí)踐之前樊卓,需要準(zhǔn)備好一部越獄的手機(jī),然后將下文列出的所有工具安裝好杠河。IDA跟Reveal都是破解版,IDA的正版要2000多刀浇辜,對(duì)于這么牛逼的逆向工具確實(shí)物有所值券敌,不過(guò)不是專(zhuān)門(mén)研究逆向的公司也沒(méi)必要用正版的,下個(gè)Windows的破解版就好柳洋,Mac上暫時(shí)沒(méi)找到待诅。Mac上可以用hopper代替IDA,也是一款很牛逼的逆向工具熊镣。廢話不多說(shuō)卑雁,正式開(kāi)始吧!

轉(zhuǎn)載請(qǐng)注明出處:來(lái)自LeonLei的博客http://www.gaoshilei.com

逆向微信朋友圈(上篇)

一绪囱、獲取朋友圈的小視頻

注意:本文逆向的微信的二進(jìn)制文件為6.3.28版本测蹲,如果是不同的微信版本,二進(jìn)制文件中的基地址也不相同

本文涉及到的工具

  1. cycript
  2. LLDB與debugserver(Xcode自帶)
  3. OpenSSH
  4. IDA
  5. Reveal
  6. theos
  7. CydiaSubstrate
  8. iOSOpenDev
  9. ideviceinstaller
  10. tcprelay(本地端口映射鬼吵,USB連接SSH扣甲,不映射可通過(guò)WiFi連接)
  11. dumpdecrypted
  12. class-dump
  13. iOS App Signer
  14. 編譯好的yololib

逆向環(huán)境為MacOS + iPhone5S 9.1越獄機(jī)
先用dumpdecrypted給微信砸殼(不會(huì)的請(qǐng)我寫(xiě)的看這篇教程),獲得一個(gè)WeChat.decrypted文件齿椅,先把這個(gè)文件扔到IDA中分析(60MB左右的二進(jìn)制文件琉挖,IDA差不多40分鐘才能分析完),用class-dump導(dǎo)出所有頭文件

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

我滴個(gè)親娘涣脚!一共有8000個(gè)頭文件示辈,微信果然工程量浩大!穩(wěn)定一下情緒遣蚀,理一理思路繼續(xù)搞矾麻。要取得小視頻的下載鏈接,找到播放視頻的View妙同,順藤摸瓜就能找到小視頻的URL射富。用Reveal查看小視頻的播放窗口

Reveal

可以看出來(lái)WCContentItemViewTemplateNewSigh這個(gè)對(duì)象是小視頻的播放窗口,它的subView有WCSightView粥帚,SightView胰耗、SightPlayerView,這幾個(gè)類(lèi)就是我們的切入點(diǎn)芒涡。
保存視頻到favorite的時(shí)候是長(zhǎng)按視頻彈出選項(xiàng)的柴灯,那么在WCContentItemViewTemplateNewSight這個(gè)類(lèi)里面可能有手勢(shì)相關(guān)的方法卖漫,去剛才導(dǎo)出的頭文件中找線索。

- (void)onLongTouch;
- (void)onLongPressedWCSight:(id)arg1;
- (void)onLongPressedWCSightFullScreenWindow:(id)arg1;

這幾個(gè)方法跟長(zhǎng)按手勢(shì)相關(guān)赠群,再去IDA中找到這些函數(shù)羊始,逐個(gè)查看。onLongPressedWCSight和onLongPressedWCSightFullScreenWindow都比較簡(jiǎn)單查描,onLongTouch比較長(zhǎng)突委,而且發(fā)現(xiàn)了內(nèi)部調(diào)用了方法Favorites_Add,因?yàn)殚L(zhǎng)按視頻的時(shí)候出來(lái)一個(gè)選項(xiàng)就是Favorites冬三,并且我看到這個(gè)函數(shù)調(diào)用

ADRP            X8, #selRef_sightVideoPath@PAGE
LDR             X1, [X8,#selRef_sightVideoPath@PAGEOFF]

這里拿到了小視頻的地址匀油,可以推測(cè)這個(gè)函數(shù)跟收藏有關(guān),下面打斷點(diǎn)測(cè)試勾笆。

(lldb) im li -o -f
[  0] 0x000000000003c000 /var/mobile/Containers/Bundle/Application/2F1D52EC-C57E-4F95-B715-EF04351232E8/WeChat.app/WeChat(0x000000010003c000)

可以看到WeChat的ASLR為0x3c000敌蚜,在IDA查找到這三個(gè)函數(shù)的基地址,分別下斷點(diǎn)

(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

回到微信里面長(zhǎng)按小視頻窝爪,看斷點(diǎn)觸發(fā)情況

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
……

發(fā)現(xiàn)斷點(diǎn)2先被觸發(fā)弛车,接著觸發(fā)斷點(diǎn)1,后面斷點(diǎn)2和1又各觸發(fā)了1次蒲每,斷點(diǎn)3一直很安靜纷跛。可以排除onLongPressedWCSightFullScreenWindow與收藏小視頻的聯(lián)系邀杏。小視頻的蹤影就要在剩下的兩個(gè)方法中尋找了忽舟。通過(guò)V找到C,順藤摸瓜找到M屢試不爽淮阐!用cycript注入WeChat叮阅,拿到播放小視頻的view所在的Controller。

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>"

通過(guò)響應(yīng)者鏈條找到
WCContentItemViewTemplateNewSight所屬的Controller為WCTimeLineViewController泣特。在這個(gè)類(lèi)的頭文件中并沒(méi)有發(fā)現(xiàn)有價(jià)值的線索浩姥,不過(guò)我們注意到小視頻所在的view是屬于MMTableVIewCell的(見(jiàn)上圖Reveal分析圖),這是每一個(gè)iOS最熟悉的TableView状您,cell的數(shù)據(jù)是通過(guò)UITableViewDataSource的代理方法- tableView:cellForRowAtIndexPath:賦值的勒叠,通過(guò)這個(gè)方法肯定能知道到M的影子。在IDA中找到[WCTimeLineViewController tableView:cellForRowAtIndexPath:],定位到基地址0x10128B6B0位置:

__text:000000010128B6B0     ADRP     X8, #selRef_genNormalCell_indexPath_@PAGE

這里的函數(shù)是WCTimeLineViewController中生成cell的方法膏孟,除了這個(gè)方法在這個(gè)類(lèi)中還有另外三個(gè)生成cell的方法:

- (void)genABTestTipCell:(id)arg1 indexPath:(id)arg2;
- (void)genRedHeartCell:(id)arg1 indexPath:(id)arg2;
- (void)genUploadFailCell:(id)arg1 indexPath:(id)arg2;

通過(guò)字面意思可以猜測(cè)出normal這個(gè)應(yīng)該是生成小視頻cell的方法眯分。繼續(xù)在IDA中尋找線索

__text:0000000101287CC8     ADRP     X8, #selRef_getTimelineDataItemOfIndex_@PAGE

genNormalCell:IndexPath:方法中發(fā)現(xiàn)上面這個(gè)方法,可以大膽猜想這個(gè)方法是獲取TimeLine(朋友圈)數(shù)據(jù)的方法柒桑,那小視頻的數(shù)據(jù)肯定也是通過(guò)這個(gè)方法獲取的弊决,并且IDA可以看到這個(gè)方法中調(diào)用一個(gè)叫做selRef_getTimelineDataItemOfIndex_的方法,獲取DataItem貌似就是cell的數(shù)據(jù)源啊飘诗!接下來(lái)用LLDB下斷點(diǎn)驗(yàn)證猜想与倡。
通過(guò)IDA可以找到這個(gè)方法對(duì)應(yīng)的基地址為:0x101287CE4,先打印正在運(yùn)行WeChat的ASLR偏移

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)

所以我們下斷點(diǎn)的位置是0x50000+0x101287CE4

(lldb) br s -a 0x50000+0x101287CE4
Breakpoint 1: where = WeChat`___lldb_unnamed_symbol63721$$WeChat + 252, address = 0x00000001012d7ce4

打印x0的值

(lldb) po $x0
Class name: WCDataItem, addr: 0x15f5f03b0
tid: 12393001887435993280
username: wxid_z8twcz4o18fg12
createtime: 1477360950
commentUsers: (
)
contentObj: <WCContentItem: 0x15f57d000>

得到一個(gè)WCDataItem的對(duì)象昆稿,這里x0的值就是selRef_getTimelineDataItemOfIndex_執(zhí)行完的返回值纺座,然后把x0的值改掉

(lldb) register write $x0 0
(lldb) c

此時(shí)會(huì)發(fā)現(xiàn)我們要刷新的那條小視頻內(nèi)容全部為空

小視頻內(nèi)容為空

到這里已經(jīng)找到了小視頻的源數(shù)據(jù)獲取方法,問(wèn)題是我們?cè)趺茨玫竭@個(gè)WCDataItem呢溉潭?繼續(xù)看IDA分析函數(shù)的調(diào)用情況:

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
......

selRef_getTimelineDataItemOfIndex_傳入的參數(shù)是x2净响,可以看到傳值給x2的x21是函數(shù)selRef_calcDataItemIndex_的返回值,是一個(gè)unsigned long數(shù)據(jù)類(lèi)型喳瓣。繼續(xù)分析别惦,selRef_getTimelineDataItemOfIndex_函數(shù)的調(diào)用者是上一步selRef_getService_的返回值,經(jīng)過(guò)斷點(diǎn)分析發(fā)現(xiàn)是一個(gè)WCFacade對(duì)象夫椭。整理一下selRef_getTimelineDataItemOfIndex_的調(diào)用:
調(diào)用者是selRef_getService_的返回值;參數(shù)是selRef_calcDataItemIndex_的返回值
下面把目光轉(zhuǎn)向那兩個(gè)函數(shù)氯庆,用相同的原理分析它們各自怎么實(shí)現(xiàn)調(diào)用

  1. 先看selRef_getService_
    在0x101287CB4這個(gè)位置可以發(fā)現(xiàn)蹭秋,這個(gè)函數(shù)的調(diào)用者是從通過(guò)x19 MOV的,打印x19發(fā)現(xiàn)是一個(gè)MMServiceCenter對(duì)象堤撵,往上找x19是在0x101287C88這個(gè)位置賦值的仁讨,結(jié)果很清晰x19是[MMServiceCenter defaultCenter]的返回值。
    在0x101287CA4位置可以找到傳入的參數(shù)x2实昨,往上分析可以看出來(lái)它的參數(shù)是[WCFacade class]的返回值洞豁。
  2. 接著找selRef_calcDataItemIndex_
    在0x101287C58的位置找到它的調(diào)用者x0,x0通過(guò)x22賦值荒给,繼續(xù)向上尋找丈挟,發(fā)現(xiàn)在最上面0x101287BF0的位置,x22是x0賦值的志电,一開(kāi)始的x0就是WCTimeLineViewController自身曙咽。
    在0x101287C4C位置發(fā)現(xiàn)傳入的參數(shù)來(lái)自x2,x2是通過(guò)上一步selRef_section函數(shù)的返回值x0賦值的,在0x101287C30位置可以發(fā)現(xiàn)selRef_section函數(shù)的調(diào)用者是x20賦值的挑辆,如下圖所示例朱,最終找到selRef_section的調(diào)用者是x3
    selRef_section函數(shù)的調(diào)用者

    x3就是函數(shù)WCTimeLineViewController - (void)genNormalCell:(id) indexPath:(id)的第二個(gè)參數(shù)indexPath,,所以selRef_calcDataItemIndex_的參數(shù)是[IndexPath section]鱼蝉。
    對(duì)上面的分析結(jié)果做個(gè)梳理:
    因此getTimelineDataItemOfIndex:的調(diào)用者可以通過(guò)
[[MMServiceCenter defaultCenter] getService:[WCFacade class]]

來(lái)獲得,它的參數(shù)可以通過(guò)下面的函數(shù)獲取

[WCTimeLineViewController calcDataItemIndex:[indexPath section]]

總感覺(jué)還少點(diǎn)什么洒嗤?indexPath我們還沒(méi)拿到呢!下一步就是拿到indexPath,這個(gè)就比較簡(jiǎn)單了魁亦,因?yàn)槲覀兾挥?code>[WCContentItemViewTemplateNewSight onLongTouch]中渔隶,所以可以通過(guò)[self nextResponder]依次拿到MMTableViewCell、MMTableView和WCTimeLineViewController洁奈,再通過(guò)[MMTableView indexPathForCell:MMTableViewCell]拿到indexPath派撕。
做完這些婉弹,已經(jīng)拿到WCDataItem對(duì)象,接下來(lái)的重點(diǎn)要放在WCDataItem上终吼,最終要獲取我們要的小視頻镀赌。到這個(gè)類(lèi)的頭文件中找線索,因?yàn)橐曨l是下載完成后才能播放的际跪,所以這里應(yīng)該拿到了視頻的路徑商佛,所以要注意url和path相關(guān)的屬性或方法,然后找到下面這幾個(gè)嫌疑對(duì)象

@property(retain, nonatomic) NSString *sourceUrl2; 
@property(retain, nonatomic) NSString *sourceUrl; 
- (id)descriptionForKeyPaths;
- (id)keyPaths;

回到LLDB中姆打,用斷點(diǎn)打印這些值良姆,看看有什么。

(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

并沒(méi)有什么有價(jià)值的線索幔戏,不過(guò)注意到WCDataItem里面有一個(gè)WCContentItem玛追,看來(lái)只能從這兒入手了,去看一下頭文件吧闲延!

@property(retain, nonatomic) NSString *linkUrl; 
@property(retain, nonatomic) NSString *linkUrl2; 
@property(retain, nonatomic) NSMutableArray *mediaList;

在LLDB打印出來(lái)

(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>
)

mediaList數(shù)組里面有一個(gè)WCMediaItem對(duì)象痊剖,Media一般用來(lái)表示視頻和音頻,大膽猜測(cè)就是它了垒玲!趕緊找到頭文件搜索一遍陆馁。

@property(retain, nonatomic) WCUrl *dataUrl;
- (id)pathForData;
- (id)pathForSightData;
- (id)pathForTempAttachVideoData;
- (id)videoStreamForData;

上面這些屬性和方法中pathForSightData是最有可能拿到小視頻路徑的,繼續(xù)驗(yàn)證

(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

拿到小視頻的網(wǎng)絡(luò)url和本地路徑了合愈!這里可以用iFunBox或者scp從沙盒拷貝這個(gè)文件看看是不是這個(gè)cell應(yīng)該播放的小視頻叮贩。

LeonLei-MBP:~ gaoshilei$ scp root@192.168.0.115:/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    

用QuickTime打開(kāi)發(fā)現(xiàn)果然是我們要尋找的小視頻。再驗(yàn)證一下url是否正確佛析,把上面打印的dataUrl在瀏覽器中打開(kāi)益老,發(fā)現(xiàn)也是這個(gè)小視頻。分析這個(gè)類(lèi)可以得出下面的結(jié)論:

  • dataUrl:小視頻的網(wǎng)絡(luò)url
  • pathForData:小視頻的本地路徑
  • pathForSightData:小視頻的本地路徑(不帶后綴)

至此小視頻的路徑和取得方式分析已經(jīng)完成寸莫,要實(shí)現(xiàn)轉(zhuǎn)發(fā)還要繼續(xù)分析微信的朋友圈發(fā)布杨箭。

二、實(shí)現(xiàn)轉(zhuǎn)發(fā)功能

1.“走進(jìn)死胡同”

這節(jié)是我在找小視頻轉(zhuǎn)發(fā)功能時(shí)走的彎路储狭,扒到最后并沒(méi)有找到實(shí)現(xiàn)方法互婿,不過(guò)也提供了一些逆向中常用的思路和方法,不想看的可以跳到第二節(jié)辽狈。

(1)找到小視頻拍攝完成調(diào)用的方法名稱(chēng)

打開(kāi)小視頻的拍攝界面慈参,用cycript注入,我們要找到發(fā)布小視頻的方法是哪個(gè)刮萌,然后查看當(dāng)前的窗口有哪些window(因?yàn)樾∫曨l的拍攝并不是在UIApplication的keyWindow中進(jìn)行的)

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>>"
)

發(fā)現(xiàn)當(dāng)前頁(yè)面一共有5個(gè)window驮配,其中MMUIWindow是小視頻拍攝所在的window,打印它的UI樹(shù)狀結(jié)構(gòu)

cy# [#0x127796440 recursiveDescription]

打印結(jié)果比較長(zhǎng),不貼了壮锻。找到這個(gè)按鈕是拍攝小視頻的按鈕

   |    |    |    |    |    | <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>>

然后執(zhí)行

cy# [#0x1277a9d70 setHidden:YES]

發(fā)現(xiàn)拍攝的按鈕消失了琐旁,驗(yàn)證了我的猜想。尋找按鈕的響應(yīng)事件猜绣,可以通過(guò)target來(lái)尋找

cy# [#0x1277a9d70 allTargets]
[NSSet setWithArray:@[#"<MainFrameSightViewController: 0x1269a4600>"]]]
cy# [#0x1277a9d70 allControlEvents]
193
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:193]
null

發(fā)現(xiàn)按鈕并沒(méi)有對(duì)應(yīng)的action灰殴,這就奇怪了!UIButton必須要有target和action掰邢,不然這個(gè)Button不能響應(yīng)事件牺陶。我們?cè)囋嚻渌腃ontrolEvent

cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchDown]
@["btnPress"]
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchUpOutside]
@["btnRelease"]
cy# [#0x1277a9d70 actionsForTarget:#0x1269a4600 forControlEvent:UIControlEventTouchUpInside]
@["btnRelease"]

結(jié)果發(fā)現(xiàn)這三個(gè)ContrlEvent有對(duì)應(yīng)的action,我們?cè)倏纯催@三個(gè)枚舉的值

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;

可以看出來(lái)UIControlEventTouchDown對(duì)應(yīng)1辣之,UIControlEventTouchUpInside對(duì)應(yīng)128掰伸,UIControlEventTouchUpOutside對(duì)應(yīng)64,三者相加正好193怀估!原來(lái)調(diào)用[#0x1277a9d70 allControlEvents]的時(shí)候返回的應(yīng)該是枚舉狮鸭,有多個(gè)枚舉則把它們的值相加,是不是略坑多搀?我也是這樣覺(jué)得的歧蕉!剛才我們把三種ControlEvent對(duì)應(yīng)的action都打印出來(lái)了,繼續(xù)LLDB+IDA進(jìn)行動(dòng)態(tài)分析酗昼。

(2)找到小視頻拍攝完成跳轉(zhuǎn)到發(fā)布界面的方法

因?yàn)橐业叫∫曨l發(fā)布的方法,所以對(duì)應(yīng)的btnPress函數(shù)我們并不關(guān)心梳猪,把重點(diǎn)放在btnRelease上面麻削,拍攝按鈕松開(kāi)后就會(huì)調(diào)用的方法。在IDA中找到這個(gè)方法

MainFrameSightViewController - (void)btnRelease

找到之后下個(gè)斷點(diǎn)

(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

用手機(jī)拍攝小視頻然后松開(kāi)春弥,觸發(fā)了斷點(diǎn)呛哟,說(shuō)明我們的猜想是正確的。繼續(xù)分析發(fā)現(xiàn)代碼是從上圖的右邊走的匿沛,看了一下沒(méi)有什么方法是跳轉(zhuǎn)到發(fā)布視頻的扫责,不過(guò)仔細(xì)看一下有一個(gè)block,是系統(tǒng)的延時(shí)block逃呼,位置在0x102093760鳖孤。然后我們跟著斷點(diǎn)進(jìn)去,在0x1028255A0跳轉(zhuǎn)到x16所存的地址

(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>

發(fā)現(xiàn)傳入的參數(shù)x2是一個(gè)block抡笼,我們?cè)倩仡櫼幌耫ispatch_after函數(shù)

void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);

這個(gè)函數(shù)有三個(gè)參數(shù)苏揣,分別是dispatch_time_t、dispatch_queue_t推姻、dispatch_block_t平匈,那這里打印的x2就是要傳入的block,所以我們猜測(cè)拍攝完小視頻會(huì)有一個(gè)延時(shí),然后執(zhí)行剛才傳入的block增炭,所以x2中肯定有其他方法調(diào)用忍燥,下一步就是要知道這個(gè)block的位置。

(lldb) memory read --size 8 --format x 0x16fd49f88
0x16fd49f88: 0x000000019f8fd218 0x00000000c2000000
0x16fd49f98: 0x000000010214777c 0x0000000102fb0e60
0x16fd49fa8: 0x000000015da32600 0x000000015ea1a430
0x16fd49fb8: 0x000000015cf5fee0 0x000000016fd49ff0

0x000000010214777c就是block所在的位置隙姿,當(dāng)然要減掉當(dāng)前WeChat的ASLR偏移梅垄,最終在IDA中的地址為0x10209377C,突然發(fā)現(xiàn)這就是btnRelease的子程序sub_10209377C孟辑。這個(gè)子程序非常簡(jiǎn)單哎甲,只有一個(gè)方法selRef_logicCheckState_有可能是我們的目標(biāo)。先看看這個(gè)方法是誰(shuí)調(diào)用的

(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>

發(fā)現(xiàn)還是MainFrameSightViewController這個(gè)對(duì)象調(diào)用的饲嗽,那selRef_logicCheckState_肯定也在這個(gè)類(lèi)的頭文件中炭玫,尋找一下果然發(fā)現(xiàn)了

- (void)logicCheckState:(int)arg1;

在IDA左側(cè)窗口中尋找[MainFrameSightViewController logicCheckState:],發(fā)現(xiàn)這個(gè)方法超級(jí)復(fù)雜貌虾,邏輯太多了吞加,不著急慢慢捋。
在0x102094D6C位置我們發(fā)現(xiàn)一個(gè)switch jump尽狠,思路就很清晰了衔憨,我們只要找到小視頻拍攝完成的這條線往下看就行了,LLDB來(lái)幫忙看看走的那條線袄膏。在0x102094D6C位置下個(gè)斷點(diǎn)柑肴,這個(gè)斷點(diǎn)在拍攝小視頻的時(shí)候會(huì)多次觸發(fā)抢腐,可以在拍攝之前把斷點(diǎn)dis掉,拍攝松手之前再啟用斷點(diǎn),打印此時(shí)的x8值

(lldb) p/x $x8
(unsigned long) $38 = 0x0000000102174e10

x8是一個(gè)指針扬跋,它指向的地址是0x102174e10缘厢,用這個(gè)地址減去當(dāng)前ASLR的偏移就可以找到在IDA中的基地址萝毛,發(fā)現(xiàn)是0x102094E10楷力,拍攝完成的邏輯處理這條線找到了,一直走到0x102094E24位置之后跳轉(zhuǎn)0x1020951C4锌奴,這個(gè)分支的內(nèi)容較少兽狭,里面有三個(gè)函數(shù)

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

其中selRef_finishWriterselRef_turnCancelBtnForFinishRecording需要重點(diǎn)關(guān)注,這兩個(gè)方法看上去都是小視頻錄制結(jié)束的意思鹿蜀,線索極有可能就在這兩個(gè)函數(shù)中箕慧。通過(guò)查看調(diào)用者發(fā)現(xiàn)這兩個(gè)方法都屬于MainFrameSightViewController,繼續(xù)在IDA中查看這兩個(gè)方法茴恰。在selRef_finishWriter中靠近末尾0x102094248的位置發(fā)現(xiàn)一個(gè)方法名叫做f_switchToSendingPanel销钝,下個(gè)斷點(diǎn),然后拍攝視頻琐簇,發(fā)現(xiàn)這個(gè)方法并沒(méi)有被觸發(fā)蒸健。應(yīng)該不是通過(guò)這個(gè)方法調(diào)用發(fā)布界面的座享,繼續(xù)回到selRef_finishWriter方法中;在0x1020941DC的位置調(diào)用方法selRef_stopRecording似忧,打印它的調(diào)用者發(fā)現(xiàn)這個(gè)方法屬于SightFacade渣叛,繼續(xù)在IDA中尋找這個(gè)方法的實(shí)現(xiàn)。在這個(gè)方法的0x101F9BED4位置又調(diào)用了selRef_stopRecord盯捌,同樣打印調(diào)用者發(fā)現(xiàn)這個(gè)方法屬于SightCaptureLogicF4淳衙,有點(diǎn)像剝洋蔥,繼續(xù)在尋找這個(gè)方法的實(shí)現(xiàn)饺著。在這個(gè)方法內(nèi)部0x101A98778位置又調(diào)用了selRef_finishWriting箫攀,同樣的原理找到這個(gè)方法是屬于SightMovieWriter。已經(jīng)剝了3層了幼衰,繼續(xù)往下:
SightMovieWriter - (void)finishWriting中的0x10261D004位置分了兩條線靴跛,這個(gè)位置下個(gè)斷點(diǎn),然后拍攝完小視頻觸發(fā)斷點(diǎn)渡嚣,打印x19的值

(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 }>

所以代碼不會(huì)跳轉(zhuǎn)到loc_10261D054而是走的左側(cè)梢睛,在左側(cè)的代碼中發(fā)現(xiàn)啟用了一個(gè)block,這個(gè)block是子程序sub_10261D0AC识椰,地址為0x10261D0AC绝葡,找到這個(gè)地址,結(jié)構(gòu)如下圖所示:

sub_10261D0AC

可以看出來(lái)主要分兩條線腹鹉,我們?cè)诘谝粋€(gè)方框的末尾也就是0x10261D108位置下個(gè)斷點(diǎn)藏畅,等拍攝完畢觸發(fā)斷點(diǎn)之后打印x0的值為1,這里的匯編代碼為

__text:000000010261D104                 CMP             X0, #2
__text:000000010261D108                 B.EQ            loc_10261D234

B.EQ是在上一步的結(jié)果為0才會(huì)跳轉(zhuǎn)到loc_10261D234功咒,但是這里的結(jié)果是不為0的愉阎,將x0的值改為2讓上一步的結(jié)果為0

(lldb) po $x0
1
(lldb) register write $x0 2
(lldb) po $x0
2

此時(shí)放開(kāi)斷點(diǎn),等待跳轉(zhuǎn)到小視頻發(fā)布界面航瞭,結(jié)果是一直卡在這個(gè)界面沒(méi)有任何反應(yīng)诫硕,所以猜測(cè)實(shí)現(xiàn)跳轉(zhuǎn)的邏輯應(yīng)該在右邊的那條線坦辟,繼續(xù)順著右邊的線尋找:
在右側(cè)0x10261D3AC位置發(fā)現(xiàn)調(diào)用了下面的這個(gè)方法

- (void)finishWritingWithCompletionHandler:(void (^)(void))handler;

這個(gè)方法是系統(tǒng)提供的AVAssetWriter里面的方法刊侯,在視頻寫(xiě)入完成之后要做的操作,這個(gè)里是要傳入一個(gè)block的锉走,因?yàn)橹挥幸粋€(gè)參數(shù)所以對(duì)應(yīng)的變量是x2滨彻,打印x2的值

(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

并且通過(guò)棧內(nèi)存找到block位置為0x10261D4B0(需要減去ASLR的偏移)

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

只調(diào)用了兩個(gè)方法,一個(gè)是selRef_stopAmr停止amr(一種音頻格式)挪蹭,另一個(gè)是selRef_compressAudio壓縮音頻亭饵,拍攝完成的下一步操作應(yīng)該不會(huì)放在這兩個(gè)方法里面,找了這么久也沒(méi)有頭緒梁厉,這個(gè)路看來(lái)走不通了辜羊,不要鉆牛角尖踏兜,戰(zhàn)略性撤退尋找其他入口。
逆向的樂(lè)趣就是一直尋找真相的路上八秃,能體會(huì)到成功的樂(lè)趣碱妆,也有可能方向錯(cuò)了離真相反而越來(lái)越遠(yuǎn),不要?dú)怵H調(diào)整方向繼續(xù)前進(jìn)昔驱!

2.“另辟蹊徑”

(由于微信在后臺(tái)偷偷升級(jí)了疹尾,下面的內(nèi)容都是微信6.3.30版本的ASLR,上面的分析基于6.3.28版本)

注意到在點(diǎn)擊朋友圈右上角的相機(jī)按鈕底部會(huì)彈出一個(gè)Sheet骤肛,第一個(gè)就是Sight小視頻纳本,從這里入手,用cycript查看Sight按鈕對(duì)應(yīng)的事件是哪個(gè)

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()

底部的Sheet是NewYearActionSheet腋颠,然后打印NewYearActionSheet的UI樹(shù)狀結(jié)構(gòu)圖(比較長(zhǎng)不貼了)繁成。然后找到Sight對(duì)應(yīng)的UIButton是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:"]

通過(guò)UIControl的actionsForTarget:forControlEvent:方法可以找到按鈕綁定的事件,Sight按鈕的觸發(fā)方法為OnDefaultButtonTapped:秕豫,回到IDA中在NewYearActionSheet中找到這個(gè)方法們繼續(xù)往下分析只有這個(gè)方法selRef_dismissWithClickedButtonIndex_animated朴艰,通過(guò)打印它的調(diào)用者發(fā)現(xiàn)還是NewYearActionSheet,繼續(xù)點(diǎn)進(jìn)去找到newYearActionSheet_clickedButtonAtIndex方法混移,看樣子不是NewYearActionSheet自己的祠墅,打印調(diào)用者x0發(fā)現(xiàn)它屬于類(lèi)WCTimeLineViewController。跟著斷點(diǎn)走下去在0x1012B78CC位置調(diào)用了方法#selRef_showSightWindowForMomentWithMask_byViewController_scene
通過(guò)觀察發(fā)現(xiàn)這個(gè)方法的調(diào)用者是0x1012B78AC這個(gè)位置的返回值x0歌径,這是一個(gè)類(lèi)SightFacade毁嗦,猜測(cè)這個(gè)方法在SightFacade里面,去頭文件里找一下果然發(fā)現(xiàn)這個(gè)方法

- (void)showSightWindowForMomentWithMask:(id)arg1 byViewController:(id)arg2 scene:(int)arg3;

這個(gè)方法應(yīng)該就是跳轉(zhuǎn)到小視頻界面的方法了回铛。下面分別打印它的參數(shù)

(lldb) po $x2
<UIImage: 0x14f046660>, {320, 568}
(lldb) po $x3
<WCTimeLineViewController: 0x14e214800>
(lldb) po $x4
2
(lldb) po $x0
<SightFacade: 0x14f124b40>

其中x2狗准、x3、x4分別對(duì)應(yīng)三個(gè)參數(shù)茵肃,x0是調(diào)用者腔长,跳到這個(gè)方法內(nèi)部查看怎么實(shí)現(xiàn)的。發(fā)現(xiàn)在這個(gè)方法中進(jìn)行了小視頻拍攝界面的初始化工作验残,首先初始化一個(gè)MainFrameSightViewController捞附,再創(chuàng)建一個(gè)UINavigationController將MainFrameSightViewController放進(jìn)去,接下來(lái)初始化一個(gè)MMWindowController調(diào)用

- (id)initWithViewController:(id)arg1 windowLevel:(int)arg2;

方法將UINavigationController放了進(jìn)去您没,完成小視頻拍攝界面的所有UI創(chuàng)建工作鸟召。
拍攝完成之后進(jìn)入發(fā)布界面,此時(shí)用cycript找到當(dāng)前的Controller是SightMomentEditViewController氨鹏,由此萌生一個(gè)想法欧募,跳過(guò)前面的拍攝界面直接進(jìn)入發(fā)布界面不就可以了嗎?我們自己創(chuàng)建一個(gè)SightMomentEditViewController然后放到UINavigationController里面仆抵,然后再將這個(gè)導(dǎo)航控制器放到MMWindowController里面跟继。(這里我已經(jīng)寫(xiě)好tweak進(jìn)行了驗(yàn)證种冬,具體的tweak思路編寫(xiě)在后文有)結(jié)果是的確可以彈出發(fā)布的界面,但是導(dǎo)航欄的NavgationBar遮住了原來(lái)的舔糖,整個(gè)界面是透明的碌廓,很難看,而且發(fā)布完成之后無(wú)法銷(xiāo)毀整個(gè)MMWindowController剩盒,還是停留在發(fā)布界面谷婆。我們要的結(jié)果不是這個(gè),不過(guò)確實(shí)有很大的收獲辽聊,最起碼可以直接調(diào)用發(fā)布界面了纪挎,小視頻也能正常轉(zhuǎn)發(fā)。我個(gè)人猜測(cè)跟匆,當(dāng)前界面不能被銷(xiāo)毀的原因是因?yàn)镸MWindowController新建了一個(gè)window,它跟TimeLine所在的keyWindow不是同一個(gè)异袄,SightMomentEditViewController的按鈕觸發(fā)的方法是沒(méi)有辦法銷(xiāo)毀這個(gè)window的,所以有一個(gè)大膽的猜想玛臂,我直接在當(dāng)前的WCTimeLineViewController上把SightMomentEditViewController展示出來(lái)不就可以了嗎烤蜕?

[WCTimelineVC presentViewController:editSightVC animated:YES completion:^{
}];

像這樣展示豈不妙哉?不過(guò)通過(guò)觀察SightMomentEditViewController的頭文件迹冤,結(jié)合小視頻發(fā)布時(shí)界面上的元素讽营,推測(cè)創(chuàng)建這個(gè)控制器至少需要兩個(gè)屬性,一個(gè)是小視頻的路徑泡徙,另一個(gè)是小視頻的縮略圖橱鹏,將這兩個(gè)關(guān)鍵屬性給了SightMomentEditViewController那么應(yīng)該就可以正常展示了

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:^{
}];

小視頻的發(fā)布界面可以正常顯示并且所有功能都可以正常使用,唯一的問(wèn)題是返回按鈕沒(méi)有效果堪藐,并不能銷(xiāo)毀SightMomentEditViewController莉兰。用cycript查看左側(cè)按鈕的actionEvent找到它的響應(yīng)函數(shù)是- (void)popSelf;,點(diǎn)擊左側(cè)返回觸發(fā)的是pop方法礁竞,但是這個(gè)控制器并不在navgationController里面糖荒,所以無(wú)效,我們要對(duì)這個(gè)方法進(jìn)行重寫(xiě)

- (void)popSelf
{
    [self dismissViewControllerAnimated:YES completion:^{

    }];
}

此時(shí)再點(diǎn)擊返回按鈕就可以正常退出了模捂,此外捶朵,在WCContentItemViewTemplateNewSight中發(fā)現(xiàn)了一個(gè)方法叫做- (void)sendSightToFriend;,可以直接將小視頻轉(zhuǎn)發(fā)給好友枫绅,至此小視頻轉(zhuǎn)發(fā)的功能已經(jīng)找到了泉孩。

手把手教你逆向微信之朋友圈小視頻轉(zhuǎn)發(fā)(下)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末硼端,一起剝皮案震驚了整個(gè)濱河市并淋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌珍昨,老刑警劉巖县耽,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件句喷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡兔毙,警方通過(guò)查閱死者的電腦和手機(jī)唾琼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)澎剥,“玉大人锡溯,你說(shuō)我怎么就攤上這事⊙埔Γ” “怎么了祭饭?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)叙量。 經(jīng)常有香客問(wèn)我倡蝙,道長(zhǎng),這世上最難降的妖魔是什么绞佩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任寺鸥,我火速辦了婚禮,結(jié)果婚禮上品山,老公的妹妹穿的比我還像新娘胆建。我一直安慰自己,他們只是感情好肘交,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布眼坏。 她就那樣靜靜地躺著,像睡著了一般酸些。 火紅的嫁衣襯著肌膚如雪宰译。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天魄懂,我揣著相機(jī)與錄音沿侈,去河邊找鬼。 笑死市栗,一個(gè)胖子當(dāng)著我的面吹牛缀拭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播填帽,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蛛淋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了篡腌?” 一聲冷哼從身側(cè)響起褐荷,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嘹悼,沒(méi)想到半個(gè)月后叛甫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體层宫,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年其监,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了萌腿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抖苦,死狀恐怖毁菱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锌历,我是刑警寧澤鼎俘,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站辩涝,受9級(jí)特大地震影響贸伐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜怔揩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一捉邢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧商膊,春花似錦伏伐、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至实幕,卻和暖如春吝镣,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背昆庇。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工末贾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人整吆。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓拱撵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親表蝙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拴测,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,076評(píng)論 25 707
  • 簡(jiǎn)介 本文是針對(duì)于閱讀過(guò)相關(guān)逆向朋友圈小視頻的人,如果沒(méi)有看過(guò)的話府蛇,閱讀本文應(yīng)該會(huì)一臉懵逼集索,所以建議大家可以搜一篇...
    FlyOceanFish閱讀 1,733評(píng)論 0 0
  • 傍晚練字時(shí)抄谐,女兒希望我聽(tīng)她唱一首歌。 我答應(yīng)好扰法,寫(xiě)完一個(gè)字后蛹含,停下筆,站起來(lái)塞颁,這時(shí)女兒已經(jīng)拿好她的歌本站到了我的面...
    走向陽(yáng)光的自己閱讀 689評(píng)論 4 3
  • 溝通的大致流程:關(guān)鍵點(diǎn):確認(rèn)對(duì)方是否理解你的本意浦箱。 溝通的目的是為了解決問(wèn)題,溝通的結(jié)果是要給出當(dāng)前情況下的最優(yōu)方...
    FlaminEcho閱讀 301評(píng)論 0 0