1. 程式人生 > IOS開發 >iOS逆向學習之八(動態除錯)

iOS逆向學習之八(動態除錯)

什麼是動態除錯?

動態除錯就是在我們的程式執行之時,通過下斷點、列印等一系列方式檢視引數、返回值、函式呼叫流程等等。不僅是在iOS開放中需要動態除錯,在任何語言的開發過程中都需要用到動態除錯

Xcode如何進行動態除錯?

Xcode編譯器和偵錯程式

  • Xcode最早使用的是GUN開發的GCC編譯器,但是從xcode5之後開始使用自研的LLVM編譯器,可以點選檢視GCCLLVM的介紹
  • Xcode偵錯程式早期也是使用的GUN開發的GDB偵錯程式,之後也替換成了自研的LLDB偵錯程式,可以點選檢視GDBLLDB的介紹。

Xcode除錯App的流程

  • 首先在Xcode中會自帶一個叫做==debugserver==的工具,存放在/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/De viceSupport/9.1/DeveloperDiskImage.dmg/usr/bin/debugserver
    目錄下,當我們使用Xcode在iPhone上執行我們的程式時,Xcode會將==debugserver==安裝到我們的iPhone上,具體安裝路徑為/Developer/usr/bin/debugserver
  • Xcode連線上手機後,通過自帶的==LLDB==編譯器向iPhone上的==debugserver==傳輸指令,==debugserver==接收到指令之後將指令執行到App中,App執行指令之後將結果返回為==debugserver==,然後==debugserver==會將資訊反饋給==LLDB==,最後==LLDB==會將資訊列印到Xcode上。
  • 但是Xcode這種除錯方式有很大侷限性,因為只能除錯通過Xcod安裝的App

如何不通過Xcode動態除錯任意App?

可以使用終端取代Xcode來對App進行動態除錯

debugserver環境搭建

方法一、通過ldid進行簽名

  • 獲取到iPhone下/Developer/usr/bin/debugserver目錄中的debugserver工具,複製到Mac上
  • 由於通過Xcode安裝的==debugserver==許可權不足,只能除錯Xocde安裝的App,所以我們要給==debugserver==增加更多的許可權。
  • 通過ldid -e指令匯出==debugserver==的許可權資訊
ldid -e debugserver > debugserver.entitlements
複製程式碼
  • 在debugserver.entitlements中增加以下兩個許可權
    • get-task-allow
    • task_for_pid-allow

  • 通過ldid對==debugserver==進行重新簽名
ldid -Sdebugserver.entitlements debugserver
複製程式碼
  • 由於/Developer/usr/bin/目錄是隻讀的,所以我們將重新簽名過的==debugserver==放在/usr/bin/下,然後對==debugserver==增加執行許可權,就可以在終端使用==debugserver==了
chmod +x /usr/bin/debugserver
複製程式碼

方法二、通過codesign對debugserver進行簽名

#檢視許可權基本資訊
codesign -d --entitlements - debugserver

#簽名許可權
codesign -f -s - --entitlements debugserver.entitlements debugserver

#也可以簡寫為
codesign -fs - --entitlements debugserver.entitlements debugserver
複製程式碼

讓debugserver附加到某個程序

debugserver *:埠號 -a 程序
複製程式碼

*:埠號:表示使用iPhone上的某個埠啟動debugserver服務(注意:不能使用保留埠號)
-a 程序:指定程序id或者程序名稱

使用debugserver啟動App

debugserver -x auto *:埠號 App可執行檔案路徑
複製程式碼

在Mac上啟動LLDB,遠端連線iPhone上的debugserver

在之前的學習中,我們知道可以使用iPhone的ip地址來連線手機,但是這樣需要保證手機和電腦在同一個wifi下,並且使用這種方式傳輸資料十分緩慢。所以,通常的做法是通過usb連線iPhone,將iPhone上的某個埠對映到Mac上的某個埠,然後然LLDB和Mac上的埠通訊即可

debugserver attaching

  • 通過以下指令對iPhone的10089進行對映
python ./usbmuxd/tcprelay.py -t 22:10088 9999:10089
複製程式碼

此處的10089埠可以任意定義,只要不使用保留埠號即可。使用10088埠對映22埠,是為了和iPhone進行SSH通訊

  • 對映成功之後,使用9999埠啟動==debugserver==服務。讓==debugserver==附加到騰訊視訊App程序,如下:
debugserver *:9999 -a live4iphone
複製程式碼
  • 如果出現以下效果,表明==debugserver==已經成功attach到了騰訊視訊App上

在Mac上啟動LLDB,遠端連線iPhone上的debugserver服務

  • 首先在Mac上啟動LLDB
➜  ~ lldb
(lldb)
複製程式碼
  • 通過Mac的10089埠連線==debugserver==服務
process connect connect://localhost:10089
複製程式碼

  • 由於連線上==debugserver==服務之後,程式是預設在斷點狀態,使用LLDB的c命令讓程式繼續執行
(lldb) c
Process 635 resuming
複製程式碼

常用的LLDB指令

LLDB指令的基本格式

 <command> [<subcommand> [<subcommand>...]] <action> [-options [option- value]] [argument [argument...]]
複製程式碼

對應著

命令 子命令 命令操作 命令選項 命令引數
複製程式碼

例如給test這個函式設定斷點:

breakpoint set -n test
複製程式碼

help指令

help指令可以幫助我們快速查詢LLDB指令的使用方法

help breakpoint
help breakpoint set
複製程式碼

expression -- 指令

expression指令被用來執行一個表示式

expression self.view.backgroundColor = [UIColor redColor]
//或者
expression -- self.view.backgroundColor = [UIColor redColor]
複製程式碼
  • expression、expression --和指令print、p、call效果等同

  • expression -O -- 和指令po效果等同

expression後的 -- 表示命令選項結束符,表示所有的命令選項已經設定完畢,如果沒有命令選項,--可以省略。如果expression之後有命令選項,則--不能省略。

thread backtrace

==thread backtrace==指令的作用是列印執行緒的堆疊資訊,效果和 ==bt== 的效果相同。

(lldb) thread backtrace
* thread #1,queue = 'com.apple.main-thread',stop reason = breakpoint 1.1
  * frame #0: 0x0000000102d4d61d TestFont`-[ViewController touchesBegan:withEvent:](self=0x00007fd2f86066d0,_cmd="touchesBegan:withEvent:",touches=1 element,event=0x00006000036d4ab0) at ViewController.m:26
    frame #1: 0x0000000106f6f8e8 UIKitCore`forwardTouchMethod + 353
    ......
(lldb) bt
* thread #1,event=0x00006000036d4ab0) at ViewController.m:26
    frame #1: 0x0000000106f6f8e8 UIKitCore`forwardTouchMethod + 353
    ......
複製程式碼

thread return []

讓函式返回某個值,不會執行之後的程式碼。如果函式有返回值,在後面跟上返回值,如果函式沒有返回值,就直接使用thread return即可

frame variable []

列印當前棧幀的變數

thread相關指令

以下指令從左到右依次表示:指令全稱、指令簡稱、極簡指令

  • thread continue、continue、c :讓程式跳過斷點繼續執行
  • thread step-over、next、n :單步執行,將子函式當做整體一步執行
  • thread step-in、step、s :單步執行,遇到子函式會進入子函式
  • thread step-out、finish :直接執行完當前函式的所有程式碼,返回上一個函式
  • thread step-inst-over、nexti、ni
  • thread step-inst、stepi、si

si、ni和s、n指令類似,但是s、n是原始碼級別,si、ni是彙編指令級別。每一句OC程式碼會有一條或多條彙編指令構成,s、n指令表示一步一步執行每一句OC程式碼,而si、ni表示一步一步執行彙編指令。

breakpoint相關指令

breakpoint set

設定斷點

  • breakpoint set -a 函式地址
  • breakpoint set -n 函式名
    • breakpoint set -n test
    • breakpoint set -n touchesBegan:withEvent:
    • breakpoint set -n "-[ViewController touchesBegan:withEvent:]"
  • breakpoint set -r 正則表示式

此處跟上正則表示式,會將所有匹配到的方法都加上斷點

  • breakpoint set -s 動態庫 -n 函式名
    將指定動態庫的指定函式打上斷點

breakpoint list

列出所有的斷點,每個斷點都有單獨的編號

breakpoint disable 斷點編號

禁用斷點

breakpoint enable 斷點編號

啟用斷點

breakpoint delete 斷點編號

刪除斷點

breakpoint command add 斷點編號

給指定斷點編號的斷點預先設定需要執行的命令,到觸發斷點時,就會按順序執行預先設定的命令

breakpoint command list 斷點編號

檢視某個編號的斷點所有預先設定的命令

breakpoint command delete 斷點編號

刪除指定編號斷點的所有預設命令

記憶體斷點watchpoint

給指定的記憶體下斷點,當記憶體中的資料發生改變時會觸發

watchpoint set variable 變數

對指定的變數設定記憶體斷點,當變數值改變的時候會觸發

watchpoint set variable self->_age
複製程式碼

注意:此處不能使用self.age

watchpoint set expression 記憶體地址

對指定記憶體地址設定斷點,作用和watchpoint set variable相同

watchpoint list

列出所有的記憶體斷點

watchpoint disable 斷點編號

禁用記憶體斷點

watchpoint enable 斷點編號

啟用記憶體斷點

watchpoint delete 斷點編號

刪除記憶體斷點

watchpoint command add 斷點編號

給指定斷點編號的記憶體斷點預先設定需要執行的命令,到觸發記憶體斷點時,就會按順序執行預先設定的命令

watchpoint command list 斷點編號

檢視某個編號的記憶體斷點所有預先設定的命令

watchpoint command delete 斷點編號

刪除指定編號記憶體斷點的所有預設命令

image模組查詢指令

image lookup

模組查詢指令

  • image lookup -t 型別
    查詢某個型別的資訊

  • image lookup -a 記憶體地址
    根據記憶體地址查詢在模組中的位置

  • image lookup -n 符號或函式名
    查詢某個符號或者函式的位置

image list

列出所有所載入的模組資訊

  • image list -o -f
    打印出模組的偏移地址、全路徑

LLDB小技巧

  • 每次敲Enter鍵,都會自動執行上次的命令
  • 絕大部分的指令都可以使用縮寫
(lldb) breakpoint list
(lldb) br li
(lldb) br l

(lldb) breakpoint set -n test
(lldb) br s -n test
複製程式碼