前言
本文翻譯自Custom LLDB Commands in Practice
翻譯的不對的地方還請多多包涵指正,謝謝~
自定義LLDB命令實戰(zhàn)

歡迎來到一篇來源于我們新書《Apple調(diào)試進階&逆向工程》靈感的特別版文章~本文將展示書中的一些非常棒的工具做成的最終產(chǎn)品結(jié)果。
本文你將使用一些自定義的LLDB調(diào)試命令和腳本來探索SpringBoard應用,在理解這些腳本上會給一些幫助。
什么,你說SpringBoard是啥?嗯,它是iOS的主屏幕應用,負責啟動iOS應用,Siri解釋你說的語言,查看通知和小工具欄等等。
通過本篇教程,你將探索在SpringBoard背后的一些邏輯,利用調(diào)試腳本來為你做繁重的工作。
開始吧
在開始探索之旅前你需要有一點需要注意的配置工作。
這里下載啟動包。這包含了一個你將要安裝到電腦的LLDB命令和腳本的文件夾。
LLDB通過加載在電腦上搜索幾個預定義的位置的內(nèi)容。其中一個地址就在~/.lldbinit。(若沒發(fā)現(xiàn)該隱藏文件,就在~/目錄下主動創(chuàng)建)
使用你最喜歡的文本編輯器并打開這個文件。為了這個特定的例子,我僅僅使用了一個簡單的終端文本編輯器nano,但你可以自由地使用覺得順手的。
-
使用
Finder,打開你剛下載的包含lldb_commands的文件夾。保持Finder開著直到你將一個文件拖到終端窗口。在終端內(nèi),使用nano或者你的文本編輯器打開~/.lldbinitnano ~/.lldbinit然后按下回車鍵。
-
在nano或者你的文本編輯器,如下編輯
command script import保證在文本后面有一個空格,因為你將從
Finder窗口把內(nèi)容的地址加進來。 通過
Finder窗口,打開lldb_commands文件夾,然后找到名叫dslldb.py的文件。把它拖到終端窗口中。(譯者注:這步的操作其實很簡單就是將文件的地址放在~/.lldbinit文件內(nèi)容command script import的后面)保存
~/.lldbinit文件并關閉編輯器。對于nano來說,你需要按下Ctrl + O來保存,按下Ctrl + X退出。
整體總結(jié)下,下面就是你需要該做的樣子:

dslldb.py文件將搜索所有在同一個文件夾的所有Python腳本并在LLDB啟動的時候?qū)⑺鼈兗虞d到LLDB。另外,它會找到所有的.txt文件并加載文件內(nèi)的命令。我們在書內(nèi)對這個過程深入介紹了,但現(xiàn)在就讓我們享受用這些命令能做的事情吧。
測試命令可用性
在新的終端窗口內(nèi),輸入如下命令:
lldb
這樣會在終端內(nèi)啟動一個空的LLDB會話,現(xiàn)在輸入如下命令:
help search
這將參考幫助文檔顯示你新添加的叫search的命令。假設一切都進行得很好,你會獲得關于該命令的幫助文本。
如果你得到以下信息:
(lldb) help search
error: 'search' is not a known command.
Try 'help' to see a current list of commands.
Try 'apropos search' for a list of related commands.
Try 'type lookup search' for information on types, methods, functions, modules, etc.
這意味著LLDB命令沒有被成功加載,保證你LLDB命令的文件夾路徑中沒有空格且不存在引號。
若一切正常,你可以訪問以下一些命令:
search:根據(jù)某一個特定類遍歷所有在對上的指針。而且能夠通過特定的模塊(比如 UIKit)或者一些條件來過濾對象。
lookup:執(zhí)行正則搜索查找類,函數(shù),或者方法。
msl:為一個特定的指針的最后一次創(chuàng)建或者開辟獲取堆棧追蹤信息。
methods:Dump所有
NSOjbect子類的方法(僅iOS)。ivars:Dump所有
NSOjbect子類實例對象所有的實例變量。
這些只是LLDB能做的一小部分,你可以使用SpringBoard探索更多神奇的命令。
注:如想了解我最新最棒的命令,可檢出這個地址。無論何時我需要一個命令,都會創(chuàng)建它并將它放到那個倉庫里。你可能會發(fā)現(xiàn)一些令人驚奇的東西~
你不需要終端了,可以自由的關掉它并打開我們的XCode~
玩轉(zhuǎn)SprintBoard
我經(jīng)常想看看開發(fā)人員在產(chǎn)出的過程中如何做到的。通過探索別人已經(jīng)做好的,我可以學到它們的實現(xiàn)方式并且自己寫出更好的代碼。
遺憾的是,Apple不會開放任何它們自己程序的開源代碼,因此需要通過其他方式學習Apple是如何設計程序的。iOS模擬器提供了iOS程序的幾個功能示例,我使用LLDB通過它們來偵查出這些程序的實現(xiàn)方式。
很多人似乎認為普通的調(diào)試和逆向工程應用使用了不同的調(diào)試技巧。我非常贊同這個觀點。逆向工程某人的應用能極大挑戰(zhàn)你的調(diào)試技巧,這就是我為什么一直鼓勵大家通過逆向工程來調(diào)試。如果你能夠快速的找到你感興趣的東西而不用去讀源碼的話,想象一下當你分配一個任務找到你程序的一個bug的時候會多么的快~
嵌入到SprintBoard
使用LLDB使連接任何一個電腦上的應用成為可能(只要你禁用了Rootless機制)。幸運的是,你沒必要禁用Rootless機制就可以把LLDB命令嵌入到iOS模擬器應用上。
這意味著你可以使用Xcode附加到SpringBoard并使用所有你順手的命令。
打開任何一個Xcode工程---是的,任何一個。你并不是要編譯這個應用,而是使用已經(jīng)存在的窗口探索SpringBoard程序。
打開模擬器,跳到Xcode。在Debug按鈕里,點擊 Attach to Process,選擇 SprintBoard。

給LLDB和Xcode一些時間讓其附加到SpringBoard。成功后,你會看到一個暫停按鈕出現(xiàn)在Xcode LLDB 工作面板上。通過點擊暫停按鈕來暫停這個進程。
你可能需要通過點擊Command + Shift + Y觸發(fā)顯示該面板。一旦SpringBoard程序暫停后,在LLDB里輸入:
(lldb) dclass
這會dump所有在SpringBoard中可用的單個Objective-C類。如你所見,有很多類...
我知道很多人現(xiàn)在只關心Swift,對探索Objective-C類不感興趣,但是Swift很大程度依賴于Objective-C并且在寫書時,SpringBoard進程并沒有任何Swift類。你可通過輸入
dclass -f SwiftObject搜索純Swift類,或者輸入dclass -r \\.通過查找類名中的點好搜索繼承于NSObject的Swift子類。如果你希望學習背后的邏輯,請看書中21章:“Script Bridging with SBValue & Language Contexts”
為什么不過濾一下 dclass 命令的結(jié)果呢?Dump 所有 UIView子類是可行的:
(lldb) dclass -f UIView -m SpringBoard
這將限制你的搜索SpringBoard的可執(zhí)行程序中UIView的子類。
仍然有很多 UIViews。過濾search的結(jié)果僅展示類名中包含大小寫不敏感的“image”詞匯。
(lldb) dclass -f UIView -m SpringBoard -r (?i)image
這將僅展示類名中包含大小寫不敏感的“image”詞匯的類名,且這些類都是在SpringBoard的可執(zhí)行文件內(nèi)實現(xiàn)的,且是UIView的子類。是不是很贊?
這個奇怪的(?i)是啥?在《Advanced Apple Debugging & Reverse Engineering》中,你將學習Objective-C和Swift的函數(shù)簽名且怎樣執(zhí)行更智能的正則來搜索你感性的代碼。
你會得到以下類信息:
Dumping all classes in SpringBoard, with filter: UIView
************************************************************
SBDeckSwitcherIconImageContainerView
SBSwitcherSnapshotImageView
SBIconImageView
SBStarkIconImageView
SBLiveIconImageView
SBClockApplicationIconImageView
SBFolderIconImageView
SBIconImageCrossfadeView
SBIconImageFolderCrossfadeView
SBIconImageAppCrossfadeView
SBIconImageAppLowQualityCrossfadeView
SBDarkeningImageView
SBCornerAnimatingImageView
SBAutoPurgingImageView
SBImageAlertView
從輸出來看,讓我們來看看SBIconImageView類。Dump SBIconImageView 類所有方法和屬性:
(lldb) methods SBIconImageView
你會得到一些相似的以下片段:
<SBIconImageView: 0x10b472258>:
in SBIconImageView:
Class Methods:
+ (id) viewMap; (0x10aef017d)
+ (unsigned long) viewMap:(id)arg1 maxRecycledViewsOfClass:(Class)arg2; (0x10aef023c)
+ (id) windowForRecycledViewsInViewMap:(id)arg1; (0x10aef0249)
+ (void) recycleIconImageView:(id)arg1; (0x10aef02a0)
+ (id) dequeueRecycledIconImageViewOfClass:(Class)arg1; (0x10aef0312)
+ (double) cornerRadius; (0x10aeef0e1)
這不僅會 Dump 下類方法和實例方法,且能Dump類屬性并輸出代碼所在的內(nèi)存地址。
如你希望在代碼中私下里使用SBIconImageView類,可以使用dclass為該類創(chuàng)建一個Objective-C頭文件。在LLDB中輸入以下命令:
(lldb) dclass -p SBIconImageView
該命令會生成一個頭文件,你可以將它拉入到你的App工程內(nèi)并使用它。

為了能夠在代碼中調(diào)用
SBIconImageView類,你需要加在合適的實現(xiàn)了該類的動態(tài)鏈接庫。在書中,這些內(nèi)容在第十五章寫明了-- “Hooking & Executing Code with dlopen & dlsym”
讓我們回到SBIconImageView類,搜索到所有正在內(nèi)存中的該類對象是不是很棒?OK,使用 search 命令,你可以動態(tài)地搜到在堆棧中所有該類的實例。在LLDB中,輸入:
(lldb) search SBIconImageView
你會看到類似以下片段的輸出:
(lldb) search SBIconImageView
<__NSArrayM 0x618000858270>(
<SBIconImageView: 0x7ff6ad7492f0; frame = (-1 -1; 62 62); userInteractionEnabled = NO; layer = <CALayer: 0x6100002226a0>>,
<SBIconImageView: 0x7ff6b0a78e30; frame = (-1 -1; 62 62); userInteractionEnabled = NO; layer = <CALayer: 0x608000225520>>,
<SBIconImageView: 0x7ff6ad743d90; frame = (-1 -1; 62 62); userInteractionEnabled = NO; layer = <CALayer: 0x610000221700>>,
棒極了,但這些實例在哪兒?你可以很遍歷所有這些實例且能使用search命令對他們執(zhí)行自定義的動作。輸入:
(lldb) search SBIconImageView -p '[obj setHidden:YES]'
回到模擬器,你會看到你剛剛做的事情:

你能準確猜出SBIconImageView類做什么的嗎?!通過顯示所有的SBIconImageView實例來撤銷你剛剛做的事情。
(lldb) search SBIconImageView -p '[obj setHidden:NO]'
搜索命令很不錯,但它返回的是所有屏幕應用的結(jié)果---太多了。如果你只想要找到代表Messages(信息)應用的SBIconImageView實例呢?
使用methods命令,你可以搜索你感興趣的能幫你唯一標識特定SBIconImageView實例的代碼。
例如SBIconImageView類有一個屬性叫icon,它持有一個叫SBApplicationIcon的類(不同的SDK可能類不一樣)。Dump 所有這個類的方法:
(lldb) methods SBApplicationIcon
這個方法內(nèi)部有一個叫displayName的屬性。你可以使用這個知識點并通過displayName來快速找到特定的SBApplicationIcon實例!
在LLDB輸入:
(lldb) search SBIconImageView -c '[[[obj icon] displayName] containsString:@"Messages"]'
這將返回一個displayName包含"Messages"的SBApplicationIcon實例。你會得到如下類似結(jié)果:
<__NSArrayM 0x618000e5dac0>(
<SBIconImageView: 0x7fb7b567e020; frame = (-1 -1; 62 62); userInteractionEnabled = NO; layer = <CALayer: 0x61000023a660>>
)
拷貝SBApplicationIcon實例指針。我的例子中,它是0x7fb7b567e020,但你的地址應該是不一樣的。通過tv命令來隱藏這個界面:
(lldb) tv 0x7fb7b567e020
Message應用的圖片現(xiàn)在應該消失了:

現(xiàn)在你發(fā)現(xiàn)它啦~ 找到這個實例所有的屬性值:
(lldb) ivars 0x7fb7b567e020
ivars命令和methods一樣也是從已經(jīng)編譯成iOS可執(zhí)行文件的代碼構(gòu)建出來的。你只是在調(diào)試的過程中以應用的方法使用此代碼。你可以在書中第七章“Image”學到找到這些代碼的方法。
我們來最后一個命令 --- lookup --- 你會再書中第22章“SB Examples, Improved Lookup”創(chuàng)建的。該命令會根據(jù)正則表達式搜索所有可執(zhí)行文件的代碼。
在LLDB中輸入:
(lldb) lookup Test
這樣你會看到很多代碼,實際上可以使用--summary選項來簡化:
(lldb) lookup Test -s
你會看到如下類似片段:
1 hits in: AssistantServices
39 hits in: ChatKit
9 hits in: FrontBoard
5 hits in: VideoToolbox
28 hits in: CoreData
7 hits in: MPUFoundation
5 hits in: CoreDuet
2 hits in: BaseBoardUI
7 hits in: MediaServices
5 hits in: PassKitCore
11 hits in: MusicLibrary
16 hits in: Foundation
6 hits in: Sharing
2 hits in: libsqlite3.dylib
8 hits in: PhotoLibrary
如果希望只搜索BaseBoardUI模塊的信息呢?可以基于一個模塊來使用lookup命令過濾搜索結(jié)果。
(lldb) lookup Test -m BaseBoardUI
****************************************************
2 hits in: BaseBoardUI
****************************************************
-[UIView(BaseBoardUI) bs_isHitTestingDisabled]
-[UIView(BaseBoardUI) bs_setHitTestingDisabled:]
這意味著我可以對在SpringBoard應用內(nèi)的所有 UIView 使用這些代碼!例如,我可以輸入 po [[UIApp keyWindow] bs_isHitTestingDisabled] 命令打印出屬性值。
不在輸入內(nèi)的是基于SpringBoard應用的所有代碼。這也可以理解,因為可執(zhí)行代碼被剝離了(譯者注:應用自定義代碼和引用的模塊,框架剝離開),你看不到調(diào)試的符號信息。對于Framework則不同,因為它們需要記錄這些信息,當加載時,它能知道準確的地址。
不能使用lookup命令來搜索可執(zhí)行文件實在是讓我有些難過...
等下你猜?你可以的
輸入一下命令:
(lldb) lookup Test -X
這個命令將利用Objective-C的運行時來執(zhí)行正則搜索,而不是使用DWARF調(diào)試信息。
如你所見,有很多在最終的SpringBoard應用的測試代碼。試試以下命令:
(lldb) po [[SBTestDataProvider sharedInstance] publish]
一旦你通過點擊繼續(xù)按鈕或者在LLDB中輸入continue命令恢復應用后,你會看到一個彈窗~

是的!通知哦~
那么這是不是一篇很有意思的調(diào)試課呢~
何去何從
你也看到了,自定義調(diào)試命令有強大的能力?!禔dvanced Apple Debugging & Reverse Engineering》書能讓你的調(diào)試能力有很大的飛躍。
如果你喜歡本篇文章,可以購買書籍《Advanced Apple Debugging & Reverse Engineering》。
以下是書的一部分內(nèi)容介紹:

- 開始:關于LLDB及其大量的命令和選項
- Python能力:使用LLDB的Python模塊創(chuàng)建強大自定義的調(diào)試命令從而窺探和提高現(xiàn)有程序
- 理解匯編:真正理解匯編層面代碼是如何工作的,以及怎樣在內(nèi)存中探索這些代碼
- Ptrace和Friends:學習如何利用ptrace,dlopen和dlsym來hook C和Swift函數(shù)探索沒有源碼的代碼
- 腳本橋接:擴展調(diào)試器讓它幾乎做任何你想做的事,學習如何在腳本中傳遞可選參數(shù)或參數(shù)
- DTrace:使用DTrace深入探索函數(shù)獲取大量的進程信息
- 等等。。。

