如何符號(hào)化Objective-C調(diào)用棧

本文講述的是符號(hào)化“殘破”的棧,如果你有一個(gè)系統(tǒng)生成的crash日志,請(qǐng)交給Xcode自帶的symbolicatecrash腳本。

Symbolicatecrash腳本的核心也是通過atos功能逐行符號(hào)化,但人家封裝好了,比自己手動(dòng)一行一行做快很多。

示例棧:

    0   XSQSymbolicateDemo                  0x00000001000ba530 XSQSymbolicateDemo + 25904
    1   XSQSymbolicateDemo                  0x00000001000ba4f0 XSQSymbolicateDemo + 25840
    2   XSQSymbolicateDemo                  0x00000001000ba4bc XSQSymbolicateDemo + 25788
    3   XSQSymbolicateDemo                  0x00000001000ba478 XSQSymbolicateDemo + 25720
    4   UIKit                               0x00000001966870ec <redacted> + 96
    5   UIKit                               0x000000019668706c <redacted> + 80
    6   UIKit                               0x00000001966715e0 <redacted> + 440
    7   UIKit                               0x0000000196686950 <redacted> + 576
    8   UIKit                               0x000000019668646c <redacted> + 2480
    9   UIKit                               0x0000000196681804 <redacted> + 3192
    10  UIKit                               0x0000000196652418 <redacted> + 340
    11  UIKit                               0x0000000196e4bf64 <redacted> + 2400

這是我寫的一個(gè)demo app,并且在編譯后期濾去了符號(hào)表,所以僅能看到一些奇怪的地址。

如何符號(hào)化第三方app內(nèi)的符號(hào)

以第一行:

0   XSQSymbolicateDemo                  0x00000001000ba530 XSQSymbolicateDemo + 25904

為例

需要條件:

(1)atos工具(Xcode安裝時(shí)一般會(huì)自帶)

(2)確認(rèn)app運(yùn)行的架構(gòu)(armv7、arm64)

(3)app對(duì)應(yīng)的dSYM文件(出包時(shí)獲得)

(4)app代碼載入到內(nèi)存的基地址(后文詳細(xì)介紹)

方法:

在命令行中輸入:

xcrun atos -arch arm64 -o ./XSQSymbolicateDemo.app.dSYM/Contents/Resources/DWARF/XSQSymbolicateDemo  -l  0x1000b4000 0x00000001000ba530

即可得到符號(hào)化后的結(jié)果:

-[ViewController helloWorld2] (in XSQSymbolicateDemo) (ViewController.m:100)
如何符號(hào)化系統(tǒng)動(dòng)態(tài)庫中的符號(hào)

以這一行為例:

4   UIKit                               0x00000001966870ec <redacted> + 96

需要條件:

(1)atos工具(Xcode安裝時(shí)一般會(huì)自帶)

(2)確認(rèn)app運(yùn)行的架構(gòu)(armv7、arm64)

(2)該OS版本、該動(dòng)態(tài)庫的符號(hào)文件(將該手機(jī)連接到電腦的Xcode上,會(huì)自動(dòng)同步系統(tǒng)符號(hào)文件)

(3)該動(dòng)態(tài)庫載入到內(nèi)存的基地址(后文詳細(xì)介紹)

方法:

在命令行中輸入:

xcrun atos -arch arm64 -o ~/Library/Developer/Xcode/iOS\ DeviceSupport/10.3.1\ \(14E304\)/Symbols/System/Library/Frameworks/UIKit.framework/UIKit -l  0x196642000 0x00000001966870ec
-[UIApplication sendAction:to:from:forEvent:] (in UIKit) + 96

即可得到符號(hào)化后的結(jié)果:

-[UIApplication sendAction:to:from:forEvent:] (in UIKit) + 96
如何獲取基地址

注意:基地址在進(jìn)程每次啟動(dòng)時(shí)決定,所以重啟進(jìn)程后,符號(hào)化時(shí)必須使用當(dāng)次啟動(dòng)的基地址

方案一:從iOS生成的crash日志中獲取

在iOS系統(tǒng)生成的crash日志中的下半部分,有這樣的一些信息:

藍(lán)色框圈出來的部分,即為app代碼載入到內(nèi)存的基地址

紅色框圈出來的部分,即為各個(gè)動(dòng)態(tài)庫載入到內(nèi)存的基地址

方案二:在app運(yùn)行時(shí)打印

可以在app中調(diào)用如下代碼獲取各個(gè)image的基地址:

void printAllImage()
{
    for (int i = 0; i < _dyld_image_count(); i++) {
        char *image_name = (char *)_dyld_get_image_name(i);
        const struct mach_header *mh = _dyld_get_image_header(i);
        intptr_t vmaddr_slide = _dyld_get_image_vmaddr_slide(i);
        
        NSLog(@"Image name %s at address 0x%llx and ASLR slide 0x%lx.\n",
               image_name, (mach_vm_address_t)mh, vmaddr_slide);
    }
}

得到如下輸出:

Image name /var/containers/Bundle/Application/351121C8-CFE4-49AD-ACC0-A70C5BF1C4A6/XSQSymbolicateDemo.app/XSQSymbolicateDemo at address 0x1000b4000 and ASLR slide 0xb4000.
 Image name /System/Library/Frameworks/Foundation.framework/Foundation at address 0x190f0c000 and ASLR slide 0xeedc000.
 Image name /usr/lib/libarchive.2.dylib at address 0x190ee0000 and ASLR slide 0xeedc000.
 Image name /usr/lib/libbz2.1.0.dylib at address 0x190e9e000 and ASLR slide 0xeedc000.
 Image name /usr/lib/libSystem.B.dylib at address 0x18ef04000 and ASLR slide 0xeedc000.
 Image name /usr/lib/system/libcache.dylib at address 0x18f35a000 and ASLR slide 0xeedc000.
......

可以看到第一行代表的是app自身,之后的每一行是app載入的動(dòng)態(tài)庫們。

介紹加載和ASLR

大致理解:

在進(jìn)程啟動(dòng)的時(shí)候,內(nèi)核加載器或者dyld會(huì)將指令加載到內(nèi)存中。

ASLR全名Address Space Layout Randomization,地址空間布局隨機(jī)化,用于防范惡意程序?qū)σ阎刂愤M(jìn)行攻擊

在ASLR引入之前,由于加載的規(guī)則是固定的,所以理論上,一個(gè)進(jìn)程不管重啟多少次,每條指令對(duì)應(yīng)的內(nèi)存中的地址都是一樣的。而每條指令對(duì)應(yīng)到內(nèi)存中的哪個(gè)地址,可以通過分析Mach-O文件分析出來。這就容易產(chǎn)生安全漏洞。

ASLR引入后,在進(jìn)程啟動(dòng)前期的加載階段,會(huì)生成一個(gè)隨機(jī)數(shù)offset,讓加載形成的內(nèi)存整體偏移一個(gè)offset。

這樣一個(gè)進(jìn)程多次啟動(dòng),每次行程的內(nèi)存空間布局都不完全一致。同一個(gè)指令,經(jīng)過多次啟動(dòng),每次都會(huì)被布局到一個(gè)新計(jì)算出來的地址。

所以僅僅憑借“一個(gè)指令在內(nèi)存中的地址”和dSYM文件,是無法進(jìn)行符號(hào)化的,因?yàn)檫@個(gè)“地址”同時(shí)依賴于ASLR生成的offset。

我理解其實(shí)只需要一個(gè)offset,配合已知的架構(gòu)、加載方式等信息,應(yīng)該就能推測出app自身的基地址和各個(gè)庫的基地址。嘗試后也證明,各個(gè)庫的基地址-offset后的值在同個(gè)設(shè)備的多次啟動(dòng)上是一致的。
但是為了圖省事,還是自己打印一下所有庫的基地址吧(′?ω?`)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評(píng)論 25 709
  • 一、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運(yùn)行的地址不確定 關(guān)于...
    SeanCST閱讀 8,107評(píng)論 0 27
  • 村上春樹堅(jiān)持每天跑步,近30年,出了一本書,告訴大家他跑步的時(shí)候在想什么,我就不行。 從三月份到現(xiàn)在,斷斷續(xù)續(xù)的。...
    月光海兒閱讀 694評(píng)論 0 1
  • 樓上的大叔開門大喊,誰家的死孩子放的鞭炮,在外頭不放,在樓道里放什么鞭炮。我們聽見不好趕緊撒腿就跑。一口氣我們跑到...
    陶醉海的湛藍(lán)閱讀 242評(píng)論 0 0
  • %%百分號(hào)標(biāo)記%c字符及其ASCII碼%s字符串%d有符號(hào)整數(shù)(十進(jìn)制)%u無符號(hào)整數(shù)(十進(jìn)制)%o無符號(hào)整數(shù)(八...
    Nothing_lu閱讀 635評(píng)論 0 1

友情鏈接更多精彩內(nèi)容