自定義LLDB命令實戰(zhàn)

前言

本文翻譯自Custom LLDB Commands in Practice
翻譯的不對的地方還請多多包涵指正,謝謝~

自定義LLDB命令實戰(zhàn)

LLDB: 擁有此技能, 還有什么不能!

歡迎來到一篇來源于我們新書《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,但你可以自由地使用覺得順手的。

  1. 使用Finder,打開你剛下載的包含lldb_commands的文件夾。保持Finder開著直到你將一個文件拖到終端窗口。在終端內(nèi),使用nano或者你的文本編輯器打開~/.lldbinit

    nano ~/.lldbinit
    

    然后按下回車鍵。


  2. 在nano或者你的文本編輯器,如下編輯

    command script import 
    

    保證在文本后面有一個空格,因為你將從Finder窗口把內(nèi)容的地址加進來。

  3. 通過Finder窗口,打開lldb_commands文件夾,然后找到名叫dslldb.py的文件。把它拖到終端窗口中。(譯者注:這步的操作其實很簡單就是將文件的地址放在~/.lldbinit文件內(nèi)容command script import的后面)

  4. 保存~/.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ù)獲取大量的進程信息
  • 等等。。。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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

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