美文网首页
利用简单的逆向来解决GCD崩溃

利用简单的逆向来解决GCD崩溃

作者: kakukeme | 来源:发表于2018-05-03 20:25 被阅读243次

    我们平时在开发过程中,偶尔会遇到一些崩溃日志,看到堆栈就想放弃的,例如下面这个,很明显的野指针崩溃,但是不知道崩溃在哪里

    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

    转载链接
    有兴趣一起研究 群号:112365317

    相关文章

      网友评论

          本文标题:利用简单的逆向来解决GCD崩溃

          本文链接:https://www.haomeiwen.com/subject/wailrftx.html