利用簡單的逆向來解決GCD崩潰

我們平時在開發過程中,偶爾會遇到一些崩潰日志,看到堆棧就想放棄的,例如下面這個,很明顯的野指針崩潰,但是不知道崩潰在哪里

Thread 0 Crashed:
0   libobjc.A.dylib                     0x0000000185f5ef70 objc_msgSend + 16
1   libdispatch.dylib                   0x000000018639e1fc _dispatch_call_block_and_release + 24
2   libdispatch.dylib                   0x000000018639e1bc _dispatch_client_callout + 16
3   libdispatch.dylib                   0x00000001863a2d68 _dispatch_main_queue_callback_4CF + 1000
4   CoreFoundation                      0x00000001874c2810 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
5   CoreFoundation                      0x00000001874c03fc __CFRunLoopRun + 1660
6   CoreFoundation                      0x00000001873ee2b8 CFRunLoopRunSpecific + 444
7   GraphicsServices                    0x0000000188ea2198 GSEventRunModal + 180
8   UIKit                               0x000000018d4357fc -[UIApplication _run] + 684
9   UIKit                               0x000000018d430534 UIApplicationMain + 208
10  DemoApp                              0x00000001000591bc main (main.mm:25)
11  ???                                 0x00000001863d15b8 0x0 + 0

當然,從其他的堆棧看來,也沒有什么明顯有用的信息
這種時候,按照一般的公司開發流程和節奏的話,如果這種崩潰不多,基本上不會花很多時間來研究
但是,一旦這種崩潰很多的話,就原地爆炸了

解決之路

回到問題上來,遇到這種問題我們一般可以簡單逆向看一下,說不定有意外的驚喜,這里需要用到hopper來實現逆向

首先,我們看一下崩潰日志底部的兩個重要信息

寄存器信息
Thread 0 crashed with ARM-64 Thread State:
    pc: 0x0000000185f5ef70     fp: 0x000000016fdaac10     sp: 0x000000016fdaabf0     x0: 0x0000000135d168b0 
    x1: 0x0000000197b5f199     x2: 0x00000001706868b0     x3: 0x000000017465d4f0     x4: 0x0000000000000000 
    x5: 0x0000000000000000     x6: 0x00000001007f01fc     x7: 0x0000000000000000     x8: 0x000000010137a000 
    x9: 0x000000018def68ee    x10: 0x0000000134b40c00    x11: 0x0000008c000000ff    x12: 0x0000000134b41ae0 
   x13: 0x20000000135d5d93    x14: 0x0000000008000000    x15: 0x0000000000000000    x16: 0x00000000135d5d90 
   x17: 0x00000001873f3da8    x18: 0x0000000000000000    x19: 0x000000017068eab0    x20: 0x0000000170a4d590 
   x21: 0x00000001706868b0    x22: 0x0000000000000000    x23: 0x0000000000000014    x24: 0x00000001acdf8d20 
   x25: 0x0000000000000000    x26: 0xffffffffffffffff    x27: 0x0000000170e68f80    x28: 0x0000000002ffffff 
    lr: 0x00000001007f025c   cpsr: 0x0000000020000000 
模塊地址信息
binary Images:
       0x100054000 -        0x100fa3fff +DemoApp arm64  <593c3b8025743182ab2b8948cf9f33f7> /var/containers/Bundle/Application/29AF15E3-7F1D-4FB8-AC08-C72C9EB8521F/DemoApp.app/DemoApp
       0x185ec8000 -        0x185ec9fff  libSystem.B.dylib arm64  <1b4d75209f4a37969a9575de48d48668> /usr/lib/libSystem.B.dylib
       0x185eca000 -        0x185f1ffff  libc++.1.dylib arm64  <b2db8b1d09283b7bafe1b2933adc5dfd> /usr/lib/libc++.1.dylib
       0x185f20000 -        0x185f40fff  libc++abi.dylib arm64  <e3419bbaface31b5970c6c8d430be26d> /usr/lib/libc++abi.dylib

在這個例子里,我們只關心LR寄存器的值
lr: 0x00000001007f025c
我們再看看DemoApp的地址范圍
0x100054000 - 0x100fa3fff +DemoApp
發現這個地址剛好DemoApp的范圍內!

我們迎來了第一個驚喜
因為lr存放著當前調用的返回地址,意味著崩潰點的被調用的地方,是在我們自己的代碼里面,而不是系統api內部,這樣我們還是可以把它揪出來的。

我們可以簡單計算出絕對偏移獲得函數的地址,即 lr - 模塊基址
這里是0x79C25C = 0x00000001007f025c - 0x100054000

這里開始我們要用到Hopper

  • 打開Hopper,加載這個DemoApp的可執行文件
  • Go Address : ** 0x79C25C**

很快的第二個驚喜
在這個案例上,我們很幸運的,一下子就圈定了崩潰點的范圍,如下

        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================


                     -[ImageDisplayCell setRefDataItem:]:
000000010079c050         stp        x22, x21, [sp, #-0x30]!                     ; Objective C Implementation defined at 0x1011cabd0 (instance method), DATA XREF=0x1011cabd0
000000010079c054         stp        x20, x19, [sp, #0x10]
000000010079c058         stp        x29, x30, [sp, #0x20]
000000010079c05c         add        x29, sp, #0x20
000000010079c060         sub        sp, sp, #0x60
000000010079c064         mov        x19, x2
000000010079c068         mov        x20, x0
000000010079c06c         adrp       x8, #0x101350000
000000010079c070         ldrsw      x21, [x8, #0x640]                           ; 0x101350640
000000010079c074         ldr        x0, x20, x21
000000010079c078         cmp        x0, x19
000000010079c07c         b.eq       loc_10079c1b0

000000010079c080         adrp       x8, #0x101306000                            ; @selector(updateProgressiveImageWithData:expectedNumberOfBytes:)
000000010079c084         ldr        x1, [x8, #0x1c8]                            ; "release",@selector(release)
000000010079c088         bl         imp___stubs__objc_msgSend
000000010079c08c         adrp       x8, #0x101306000                            ; @selector(updateProgressiveImageWithData:expectedNumberOfBytes:)
000000010079c090         ldr        x1, [x8, #0x338]                            ; "retain",@selector(retain)
000000010079c094         mov        x0, x19
000000010079c098         bl         imp___stubs__objc_msgSend
000000010079c09c         str        x0, x20, x21
000000010079c0a0         adrp       x8, #0x101326000                            ; @selector(setFirstResponseItem:)
000000010079c0a4         ldr        x1, [x8, #0x520]                            ; "picTitle",@selector(picTitle)
000000010079c0a8         mov        x0, x19
000000010079c0ac         bl         imp___stubs__objc_msgSend
000000010079c0b0         mov        x21, x0
000000010079c0b4         adrp       x8, #0x10130a000                            ; @selector(s_component)
000000010079c0b8         ldr        x1, [x8, #0xd98]                            ; "titleLabel",@selector(titleLabel)
000000010079c0bc         mov        x0, x20
000000010079c0c0         bl         imp___stubs__objc_msgSend
000000010079c0c4         adrp       x8, #0x101302000                            ; @selector(tableView:canPerformAction:forRowAtIndexPath:withSender:)
000000010079c0c8         ldr        x1, [x8, #0xf60]                            ; "setText:",@selector(setText:)
000000010079c0cc         mov        x2, x21
000000010079c0d0         bl         imp___stubs__objc_msgSend
000000010079c0f0         add        x8, sp, #0x30
000000010079c0f4         stp        xzr, x8, [sp, #0x30]
000000010079c0f8         movz       w8, #0x5200
000000010079c0fc         str        w8, [sp, #0x40]
000000010079c100         orr        w8, wzr, #0x30
000000010079c104         str        w8, [sp, #0x44]
000000010079c108         adr        x8, #0x10079c1e0
000000010079c10c         nop
000000010079c110         fmov       d0, x8
000000010079c114         adr        x8, #0x10079c1f0
000000010079c118         nop
000000010079c11c         ins        v0, x8
000000010079c120         stur       q0, [sp, #0x48]
000000010079c124         str        x20, [sp, #0x58]
000000010079c128         adrp       x8, #0x101326000                            ; @selector(setFirstResponseItem:)
000000010079c12c         ldr        x1, [x8, #0x470]                            ; "picUrl",@selector(picUrl)
000000010079c130         mov        x0, x19
000000010079c134         bl         imp___stubs__objc_msgSend
000000010079c138         adrp       x8, #0x101309000                            ; @selector(customAttributes)
000000010079c13c         ldr        x1, [x8, #0x178]                            ; "urlDecodedString",@selector(urlDecodedString)
000000010079c140         bl         imp___stubs__objc_msgSend
000000010079c144         mov        x19, x0
000000010079c148         adrp       x8, #0x10133e000
000000010079c14c         ldr        x0, [x8, #0x518]                            ; objc_cls_ref_ImageLoadManager,__objc_class_ImageLoadManager_class
000000010079c150         adrp       x8, #0x101305000                            ; @selector(dataWithBytesNoCopy:length:freeWhenDone:)
000000010079c154         ldr        x1, [x8, #0x9f8]                            ; "sharedManager",@selector(sharedManager)
000000010079c158         bl         imp___stubs__objc_msgSend
000000010079c15c         adrp       x8, #0x100f50000
000000010079c160         ldr        x8, [x8, #0x6b8]                            ; __NSConcreteStackBlock_100f506b8,__NSConcreteStackBlock
000000010079c164         str        x8, sp
000000010079c168         movz       w8, #0xc200
000000010079c16c         stp        w8, wzr, [sp, #0x8]
000000010079c170         adr        x8, #0x10079c1fc
000000010079c174         nop
000000010079c178         str        x8, [sp, #0x10]
000000010079c17c         adrp       x8, #0x100f82000
000000010079c180         add        x8, x8, #0xe0                               ; 0x100f820e0
000000010079c184         stp        x8, x19, [sp, #0x18]
000000010079c188         add        x8, sp, #0x30
000000010079c18c         str        x8, [sp, #0x28]
000000010079c190         adrp       x8, #0x10130d000                            ; @selector(hideHistoryView)
000000010079c194         ldr        x1, [x8, #0x5a8]                            ; "loadImageForURLString:completionBlock:",@selector(loadImageForURLString:completionBlock:)
000000010079c198         mov        x3, sp
000000010079c19c         mov        x2, x19
000000010079c1a0         bl         imp___stubs__objc_msgSend
000000010079c1a4         add        x0, sp, #0x30
000000010079c1a8         orr        w1, wzr, #0x8
000000010079c1ac         bl         imp___stubs___Block_object_dispose

                     loc_10079c1b0:
000000010079c1b0         sub        sp, x29, #0x20                              ; CODE XREF=-[ImageDisplayCell setRefDataItem:]+44
000000010079c1b4         ldp        x29, x30, [sp, #0x20]
000000010079c1b8         ldp        x20, x19, [sp, #0x10]
000000010079c1bc         ldp        x22, x21, [sp]!, #0x30
000000010079c1c0         ret
                        ; endp
000000010079c1c4         b          -[ImageDisplayCell setRefDataItem:]+376
000000010079c1c8         mov        x19, x0                                     ; CODE XREF=-[ImageDisplayCell setRefDataItem:]+372
000000010079c1cc         add        x0, sp, #0x30
000000010079c1d0         orr        w1, wzr, #0x8
000000010079c1d4         bl         imp___stubs___Block_object_dispose
000000010079c1d8         mov        x0, x19
000000010079c1dc         bl         imp___stubs___Unwind_Resume
000000010079c1e0         dd         0x9100a000                                  ; DATA XREF=-[ImageDisplayCell setRefDataItem:]+184
000000010079c1e4         ldr        x1, [x1, #0x28]
000000010079c1e8         movz       w2, #0x83
000000010079c1ec         b          imp___stubs___Block_object_assign
000000010079c1f0         dd         0xf9401400                                  ; DATA XREF=-[ImageDisplayCell setRefDataItem:]+196
000000010079c1f4         movz       w1, #0x83
000000010079c1f8         b          imp___stubs___Block_object_dispose
000000010079c1fc         dd         0xa9bd57f6                                  ; DATA XREF=-[ImageDisplayCell setRefDataItem:]+288
000000010079c200         stp        x20, x19, [sp, #0x10]
000000010079c204         stp        x29, x30, [sp, #0x20]
000000010079c208         add        x29, sp, #0x20
000000010079c20c         mov        x19, x1
000000010079c210         mov        x20, x0
000000010079c214         cbz        x19, -[ImageDisplayCell setRefDataItem:]+552
000000010079c218         ldr        x21, [x20, #0x20]
000000010079c21c         adrp       x8, #0x101305000
000000010079c220         ldr        x1, [x8, #0xd8]
000000010079c224         mov        x0, x2
000000010079c228         bl         imp___stubs__objc_msgSend
000000010079c22c         mov        x2, x0
000000010079c230         adrp       x8, #0x101300000
000000010079c234         ldr        x1, [x8, #0x940]
000000010079c238         mov        x0, x21
000000010079c23c         bl         imp___stubs__objc_msgSend
000000010079c240         cbz        w0, -[ImageDisplayCell setRefDataItem:]+552
000000010079c244         ldr        x8, [x20, #0x28]
000000010079c248         ldr        x8, [x8, #0x8]
000000010079c24c         ldr        x0, [x8, #0x28]
000000010079c250         adrp       x8, #0x101326000
000000010079c254         ldr        x1, [x8, #0x818]
000000010079c258         bl         imp___stubs__objc_msgSend
000000010079c25c         adrp       x8, #0x101301000
000000010079c260         ldr        x1, [x8, #0x260]
000000010079c264         mov        x2, x19
000000010079c268         ldp        x29, x30, [sp, #0x20]
000000010079c26c         ldp        x20, x19, [sp, #0x10]
000000010079c270         ldp        x22, x21, [sp]!, #0x30
000000010079c274         b          imp___stubs__objc_msgSend
000000010079c278         ldp        x29, x30, [sp, #0x20]                       ; CODE XREF=-[ImageDisplayCell setRefDataItem:]+452, -[ImageDisplayCell setRefDataItem:]+496
000000010079c27c         ldp        x20, x19, [sp, #0x10]
000000010079c280         ldp        x22, x21, [sp]!, #0x30
000000010079c284         ret

到這里基本上對比源碼,我們就已經可以找到原因了,因為很明確的崩潰函數是在-[ImageDisplayCell setRefDataItem:]

- (void)setRefDataItem:(PicBRefDataItem *)refDataItem
{
    if (_refDataItem != refDataItem)
    {
        [_refDataItem release];
        _refDataItem = [refDataItem retain];
        
        NSString *title = [refDataItem picTitle];
        self.titleLabel.text = title;
        
        __weak typeof(self) weakSelf = self;
        
        NSString *requestURL = [refDataItem.picUrl urlDecodedString];
        [[ImageLoadManager sharedManager] loadImageForURLString:requestURL
                                                     completionBlock:^(UIImage *image, NSURL *imageURL, NSData *data, NSError *error, BOOL isCache) {
                                                         
                                                         if (image && [requestURL isEqualToString:[imageURL absoluteString]])
                                                         {
                                                             weakSelf.thumbnailImageView.image = image;
                                                         }
                                                     }];
    }
}

但是我們還是堅持一下,進一步看看是否可以精確定位到問題出現在哪一步

即使匯編不大好,也可以很容易地和源碼對應起來,而我們算出來的地址,就在loc_10079c1b0這個塊里面,對應的就是源碼的block,我們看一下崩潰點前后的一小段匯編

000000010079c240         cbz        w0, -[ImageDisplayCell setRefDataItem:]+552
000000010079c244         ldr        x8, [x20, #0x28]
000000010079c248         ldr        x8, [x8, #0x8]
000000010079c24c         ldr        x0, [x8, #0x28]
000000010079c250         adrp       x8, #0x101326000
000000010079c254         ldr        x1, [x8, #0x818]
000000010079c258         bl         imp___stubs__objc_msgSend
000000010079c25c         adrp       x8, #0x101301000
000000010079c260         ldr        x1, [x8, #0x260]
000000010079c264         mov        x2, x19

在崩潰點000000010079c25c其實已經掛掉了,前面的一個objc_msgSend,就是傳說中的野指針崩潰,我們就要定位這個崩潰的方法,看上面一段

000000010079c250         adrp       x8, #0x101326000
000000010079c254         ldr        x1, [x8, #0x818]

adrp作為常量加載跳轉,在這個場景下,x1存的是selector,總結起來,就是說,objc_msgSend 調用的方法入口地址是
0x101326000 + 0x818

我們不妨Go Address:0x101326818 看一下

0000000101326818         dq         0x100cf2a9b                                 ; 
@selector(thumbnailImageView), "thumbnailImageView", DATA XREF=-[ImageDisplayCell initialized]+276, -[ImageDisplayCell layoutSubviews]+156, -[ImageDisplayCell prepareForReuse]+80

到這里,基本可以精確定位到案發現場了,對應到源碼,從上下文來看,就是weakSelf野指針了

        __weak typeof(self) weakSelf = self;                                        
       weakSelf.thumbnailImageView.image = image;

發現原因很簡單,就是在MRC上__block相當于assign,在一個異步block里面,有可能已經野指針了,修復方法很多,最簡單的修復就是用weak

這是一個很容易犯的錯誤,但是當代碼量到了一定的規模,收集上來的崩潰日志沒有明顯提示方法入口的時候,這種方法還是實用的,習慣之后,也很容易上手。

PS: 簡單科普一下LR這個寄存器

R14稱為子程序鏈接寄存器LR(Link Register),當執行子程序調用指令(BL)時,R14可得到R15(程序計數器PC)的備
份.在每一種運行模式下,都可用R14保存子程序的返回地址,當用BL或BLX指令調用子程序時,將PC的當前值復制給
R14,執行完子程序后,又將R14的值復制回PC,即可完成子程序的調用返回。以上的描述可用指令完成。

參考文檔

崩潰分析匯編基礎 by Vedon_fu

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容