天天看點

用debugserver + lldb代替gdb進行動态調試

因為Apple已經棄gdb投lldb,是以随着我動态調試的次數越來越頻繁,gdb上一個接一個的bug經常會讓人很惱火。既然蘋果打算建立自己的調試器王國,也投入了财力精力,那我們幹脆也上手lldb玩玩,看看lldb是不是比gdb要更好用(以下操作在iPhone 5,iOS 8.1上測試,方法同樣适用于arm64。更多内容請參照iphonedevwiki)

一、配置debugserver

1. 在iOS中安裝debugserver

debugserver運作在iOS上,顧名思義,它作為服務端,實際執行LLDB(作為用戶端)傳過來的指令,再把執行結果回報給LLDB,顯示給使用者,即所謂的“遠端調試”。在預設情況下,iOS上并沒有安裝debugserver,隻有在裝置連接配接過一次Xcode,并在Window→Devices菜單中添加此裝置後,debugserver才會被Xcode安裝到iOS的“/Developer/usr/bin/”目錄下。

2. 幫debugserver減肥

對照下表,記下裝置的ARM資訊。

   Name    ARM
iPhone 4s armv7
iPhone 5 armv7s
iPhone 5c armv7s
iPhone 5s arm64
iPhone 6 Plus arm64
iPhone 6 arm64
iPad 2 armv7
iPad mini armv7
The New iPad armv7
iPad with Retina display armv7s
iPad Air arm64
iPad Air 2 arm64
iPad mini with Retina display arm64
iPad mini 3 arm64
iPod touch 5 armv7

我的iPhone 5對應的ARM是armv7s。将未經處理的debugserver從 iOS拷貝到OSX中的“/Users/snakeninny/”目錄下:

  1. snakeninnysiMac:~ snakeninny$ scp [email protected]:/Developer/usr/bin/debugserver ~/debugserver

複制代碼 然後幫它減肥:

  1. snakeninnysiMac:~ snakeninny$ lipo -thin armv7s ~/debugserver -output ~/debugserver

複制代碼 注意把這裡的“armv7s”換成你的裝置所對應的ARM。

3. 給debugserver添加task_for_pid權限

下載下傳 http://iosre.com/ent.xml到OSX的“/Users/snakeninny/”目錄,然後運作:

  1. snakeninnysiMac:~ snakeninny$ /opt/theos/bin/ldid -Sent.xml debugserver

複制代碼 注意,此處的 ldid來 自joedj,且“-S”選項與“ent.xml”之間是沒有空格的。

正常情況下,上面這條指令會在5秒内執行完畢。如果ldid卡住了,執行逾時,就換一種方案:下載下傳http://iosre.com/ent.plist到“/Users/snakeninny/”,然後運作:

  1. snakeninnysiMac:~ snakeninny$ codesign -s - --entitlements ent.plist -f debugserver

複制代碼

4. 将經過處理的debugserver拷回 iOS

将經過處理的debugserver拷回 iOS,并添加執行權限,指令如下:

  1. snakeninnysiMac:~ snakeninny$ scp ~/debugserver [email protected]:/usr/bin/debugserver
  2. snakeninnysiMac:~ snakeninny$ ssh [email protected]
  3. FunMaker-5:~ root# chmod +x /usr/bin/debugserver

複制代碼 這裡之是以把處理過的debugserver存放在iOS的“/usr/bin/”下,而沒有覆寫“/Developer/usr/bin/”下的原版debugserver,一是因為原版debugserver是不可寫的,無法覆寫;二是因為“/usr/bin/”下的指令無須輸入全路徑就可以執行,即在任意目錄下運作“debugserver”都可啟動處理過的debugserver。

二、在iOS上用debugserver來attach程序

debugserver + lldb調試方法跟gdb最大的不同,在于前者是用OSX中的lldb遠端連接配接debugserver,由debugserver作為lldb和iOS的中轉,在執行指令和傳回結果;而後者是gdb直接運作在iOS上。但對于一般的開發者來說,這個差別跟我們沒關系,了解一下就好~

在iOS上運作下面的指令來attach程序,其中1234是我們指定的端口号:

  1. debugserver *:1234 -a "SpringBoard"

複制代碼 成功後會顯示:

三、在OSX上用lldb遠端調試

首先在Terminal中運作lldb,然後輸入以下指令:

  1. process connect connect://iOSIP:1234

複制代碼 注意,這條指令執行耗時比較長,很多讀者可能會以為 iOS/OSX死掉了,其實沒有,耐心等一會,看看 @iOS應用逆向工程有沒有重新整理微網誌,或在論壇裡逛逛吧~

執行成功後會顯示:

四、擷取ASLR的offset

首先在lldb裡輸入"c"并回車,讓程序繼續執行;lldb有一個gdb沒有的優點,就是可以在程序運作的過程中執行一些指令,這樣就可以有效避免SpringBoard這樣的程序在暫停過久後被WatchDog給kill掉。在lldb裡輸入

  1. image list -o -f

複制代碼 顯示如下圖檔:

其中第一列[X]是image的序号,不用管;第二列是ASLR的offset(也就是對應image的虛拟記憶體slide);第三列是image的全路徑和slide之後的基位址,也不用管~是以第二列就是我們需要的資訊。

五、在記憶體位址上下斷點

假如我們在SpringBoard這個image的0xb446(在_menuButtonDown:中)處下斷點,則此位址在記憶體中的實際位置是0xb446 + 0x9a000 = 0xa5446,在lldb中對應的指令是:

  1. br s -a 0xA5446

複制代碼 執行成功後顯示:

值得注意的是,lldb指令裡如果涉及到加法操作,必須要加上單引号,即

  1. br s -a '0x0009a000 + 0xb446'

複制代碼 感謝@0xBBC 的提醒

六、更改寄存器的值 按下home鍵,觸發斷點,顯示如圖:

可以看到,lldb把包括斷點在内的4條指令顯示了出來,友善我們調試。這裡,我們将r0的值設為0,讓其跳轉到0xa5470(0xb470 + 0x9a000)處。更改r0值的lldb指令是:

  1. register write r0 0

複制代碼 接着”ni“兩次,我們就可以看到程式執行到了0xa5470處,如圖:

七、用lldb啟動一個App

  1. debugserver -x backboard *:1234 /path/to/app/executable

複制代碼 如

  1. debugserver -x backboard *:1234 /Applications/MobileNotes.app/MobileNotes

複制代碼 此指令會啟動記事本,并斷在dyld的第一條指令上,如圖所示:

接下來,在lldb中持續輸入“ni”,直到出現“error: invalid thread”的字樣,如圖所示:

稍等片刻,lldb即會停在程式的第一條指令上,如圖所示:

此時我們即已處在程序内部,可以開始一窺究竟啦~ 相較attach的半路出家,這種方式更有助于我們從頭調試一個程式,可以觀察到一些變量的初始化過程。

八、更多lldb指令 經過上面的操作,我們可以看到,lldb還是比較友善的,用慣了gdb而對它不熟悉的朋友可以通過lldb與gdb指令對照表來熟悉lldb的指令。其實有了上面的幾個操作,我們就可以開始簡單動态分析程式了,相信能把上面六步走通的朋友,已經具備了舉一反三的能力,其他需要用到的功能都可以Google到,當然更歡迎你到論壇裡發帖提問或分享。好了,debugserver + lldb的簡單介紹到此結束,接下來趕緊打開Terminal,hack起來吧~!