本文講述的是符號(hào)化“殘破”的棧,如果你有一個(gè)系統(tǒng)生成的crash日志限府,請(qǐng)交給Xcode自帶的symbolicatecrash腳本。
Symbolicatecrash腳本的核心也是通過(guò)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
這是我寫(xiě)的一個(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)庫(kù)中的符號(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)庫(kù)的符號(hào)文件(將該手機(jī)連接到電腦的Xcode上胰蝠,會(huì)自動(dòng)同步系統(tǒng)符號(hào)文件)
(3)該動(dòng)態(tài)庫(kù)載入到內(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)色框圈出來(lái)的部分丐怯,即為app代碼載入到內(nèi)存的基地址
紅色框圈出來(lái)的部分,即為各個(gè)動(dòng)態(tài)庫(kù)載入到內(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)庫(kù)們读跷。
介紹加載和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è)地址,可以通過(guò)分析Mach-O文件分析出來(lái)闪盔。這就容易產(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)過(guò)多次啟動(dòng)塔拳,每次都會(huì)被布局到一個(gè)新計(jì)算出來(lái)的地址鼠证。
所以僅僅憑借“一個(gè)指令在內(nèi)存中的地址”和dSYM文件,是無(wú)法進(jìn)行符號(hào)化的蝙斜,因?yàn)檫@個(gè)“地址”同時(shí)依賴于ASLR生成的offset名惩。
我理解其實(shí)只需要一個(gè)offset,配合已知的架構(gòu)孕荠、加載方式等信息娩鹉,應(yīng)該就能推測(cè)出app自身的基地址和各個(gè)庫(kù)的基地址。嘗試后也證明稚伍,各個(gè)庫(kù)的基地址-offset后的值在同個(gè)設(shè)備的多次啟動(dòng)上是一致的弯予。
但是為了圖省事,還是自己打印一下所有庫(kù)的基地址吧(′?ω?`)