詳解沒有dSYM文件 如何解析iOS崩潰日志


  Xcode支持崩潰日志自動符號化,前提是本地有當時Build/Archive生成的dSYM文件,iOS崩潰日志符號化后,可以幫助開發者更好的定位問題,但如果dSYM文件丟失或拿到的崩潰日志不是標准的crash log,如何定位crash呢,筆者結過嘗試發現一樣可以定位到具體函數。

  在無dSYM文件情況下,之所以無法解析出崩潰地址對應的函數名,是因為Xcode在導出ipa時會去除Symbol Table(符號表)的非系統符號部分。這時address無法對應函數名,所以無法確定是在哪個函數或block中出了問題。因此解析日志的關鍵是要恢復符號表,國內已有大神做過研究 楊君的小黑屋,本文基於此完成解析目標。

我們以測試程序CrashTest的崩潰為例,介紹一下具體解析步驟

如圖,

 

 

我們拿到了崩潰日志,這是一個arm64架構的崩潰日志,從最后的backtrace我們知道程序在訪問數組元素時異常終止,但由於本地沒有對應的dSYM文件,Xcode沒有將紅框內3,4兩行符號化為具體的函數名,本文的工作就是要將這2行符號化

開始之前,先解釋一下這幾行地址的含義

:左邊這一列是崩潰時的調用棧地址(虛擬內存地址)

基址:基址指向的地址是CrashTest這個模塊加載到內存中的起始地址

偏移:左邊棧地址 = 基址 + 偏移 (注意是10進制的)

 

什么?你拿到的崩潰日志不是這樣的! (老司機請繞過)

像這樣,只有一堆地址

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Triggered by Thread:  0

Last Exception Backtrace:
(0x186e9de48 0x1975dc0e4 0x186d83a54 0x10000c00c 0x10000bf7c 0x18b6810f8 0x18b66a22c 0x18b680a94 0x18b680720 0x18b679c74 0x18b64d38c 0x18b8ec1b4 0x18b64b8f4 0x186e560e8 0x186e5538c 0x186e5343c 0x186d811f4 0x18ff0f5a4 0x18b6b2784 0x10000c574 0x197c4aa08)

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib            0x0000000197d63270 0x197d48000 + 111216
1   libsystem_pthread.dylib           0x0000000197e01224 0x197dfc000 + 21028
2   libsystem_c.dylib                 0x0000000197cdab14 0x197c78000 + 404244
3   libc++abi.dylib                   0x0000000196dad414 0x196dac000 + 5140
4   libc++abi.dylib                   0x0000000196dccb88 0x196dac000 + 134024
5   libobjc.A.dylib                   0x00000001975dc3bc 0x1975d4000 + 33724
6   libc++abi.dylib                   0x0000000196dc9bb0 0x196dac000 + 121776
7   libc++abi.dylib                   0x0000000196dc9738 0x196dac000 + 120632
8   libobjc.A.dylib                   0x00000001975dc290 0x1975d4000 + 33424
9   CoreFoundation                    0x0000000186d812a0 0x186d78000 + 37536
10  GraphicsServices                  0x000000018ff0f5a0 0x18ff04000 + 46496
11  UIKit                             0x000000018b6b2780 0x18b63c000 + 485248
12  CrashTest                         0x000000010000c570 0x100004000 + 34160
13  libdyld.dylib                     0x0000000197c4aa04 0x197c48000 + 10756

 沒有關系,其實和上面是一樣的,我們來找找基址和偏移

往下找到 Binary Images段,這里顯示的就是崩潰程序當時加載的所有庫的快照

Binary Images:
0x100004000 - 0x10000ffff CrashTest arm64  <5fc8820b297631d087e5e665b261ed0c> /var/mobile/Containers/Bundle/Application/D8F09771-5B65-4403-A19C-CE77DAF32623/CrashTest.app/CrashTest // 這里第一行便是我們要找的
0x120070000 - 0x120097fff dyld arm64  <f958ba064181388a9658f927da42e9e7> /usr/lib/dyld
0x185678000 - 0x18580bfff AVFoundation arm64  <0c542593e3613f82b7e860cb5beeeed6> /System/Library/Frameworks/AVFoundation.framework/AVFoundation
0x18580c000 - 0x185870fff libAVFAudio.dylib arm64  <c9d296cb28c73570aaf8355b05f1adee> /System/Library/Frameworks/AVFoundation.framework/libAVFAudio.dylib
0x1858b4000 - 0x1858b4fff Accelerate arm64  <e9ba7838f51634a7b59ed392be50e86f> /System/Library/Frameworks/Accelerate.framework/Accelerate
0x1858cc000 - 0x185aebfff vImage arm64  <da44067fc79931c7aef1b7e88bf82a83> /System/Library/Frameworks/Accelerate.framework/Frameworks/vImage.framework/vImage
0x185aec000 - 0x185b93fff libBLAS.dylib arm64  <e5276e7784ef34a4baca480264978ea0> /System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/libBLAS.dylib

 然后找到我們的應用 CrashTest

0x100004000 - 0x10000ffff CrashTest arm64  <5fc8820b297631d087e5e665b261ed0c> /var/mobile/Containers/Bundle/Application/D8F09771-5B65-4403-A19C-CE77DAF32623/CrashTest.app/CrashTest

 我們看到行首有個地址區間 0x100004000 - 0x10000ffff , 這便是崩潰程序的內存區,起始地址(基址)為 0x100004000,OK基址找到了。

2020.06.08 補充: 其實不看images段的輸出, 也能知道基址. 

// 看崩潰棧里其實已經有了, 下面這3個地址很有意思
CrashTest                         0x000000010000c570 0x100004000 + 34160
// 0x100004000(基址) + 34160(十進制) = 0x000000010000c570
// 有的棧打印,類似如下的
CrashTest                         0x000000010000c570 CrashTest + 34160
// 其實也可以算出基址: 0x000000010000c570 - 0x8570(十進制的34160) = 0x100004000

 

然后我們再看看崩潰時的棧

Last Exception Backtrace:
(0x186e9de48 0x1975dc0e4 0x186d83a54 0x10000c00c 0x10000bf7c 0x18b6810f8 0x18b66a22c 0x18b680a94 0x18b680720 0x18b679c74 0x18b64d38c 0x18b8ec1b4 0x18b64b8f4 0x186e560e8 0x186e5538c 0x186e5343c 0x186d811f4 0x18ff0f5a4 0x18b6b2784 0x10000c574 0x197c4aa08)

 其中位於地址區間 0x100004000 - 0x10000ffff 的,有2個 0x10000c00c 0x10000bf7c,這就是崩潰程序的調用棧,其余地址為系統庫函數調用棧。

 

OK開始動手

 

1. 下載符號恢復 工具 

修改權限

chmod a+x restore-symbol 

 

2. 恢復符號

我們拿到的崩潰日志來自arm64機器,所以先將二進制文件 CrashTest.app/CrashTest 瘦身 (必須正確選擇目標CPU架構類型,否則解析出來也是錯的)

lipo -thin arm64 CrashTest -output CrashTest-arm64

 接着用工具恢復符號表

./restore-symbol -o CrashTest-symbol CrashTest-arm64

 現在我們得到了一個恢復了符號表的二時制文件 CrashTest-symbol

3. 使用蘋果自帶命令行工具atos,將崩潰地址解析成具體函數

atos -arch arm64 -o CrashTest-symbol -l 0x100030000 0x100034340 0x1000342b0
# 簡單解釋一下這個命令,atos -arch CPU架構 -o 進制文件 -l 起始地址 ...一系列內存地址
# -l 后面跟的是模塊的起始地址,再后面可以羅列很多地址,該命令會依次解析出具體函數

得到如下輸出

-[ViewController getChild:] (in CrashTest-symbol) + 64
-[ViewController crashOnFunc:] (in CrashTest-symbol) + 44

至此,完成了解析。

本篇涉及的崩潰是普通函數中的崩潰,如果崩潰發生在block中,則需要借住反編譯工具,請參考 恢復二進制文件中的block符號表

 PS:再多說一點,上面的解析輸出我們看到在函數后面有一個 + 64, 這個+ 64、+ 44是什么意思呢,我開始也不太明白,仔細觀察Hopper/IDA解析出來的的匯編代碼,才明白原來這也是個偏移,是指調用地址相對於函數的起始地址的偏移,並非.m文件中的代碼行數。

PS2: 當然還有更直接的方法,用Hopper打開二進制文件,然后根據崩潰偏移量,直接定位到匯編代碼😂😂😂,看看是在什么操作崩潰的。當然這需要你學習一下ARM匯編語言,分享ASM文檔可直接搜索ARM匯編指令解釋

附件:

本篇使用的DEMO

參考 & 感謝

  楊君的小黑屋 http://blog.imjun.net/posts/restore-symbol-of-iOS-app/

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM