IOS動態調試匯總-傻瓜版教程

參考部落格:
//juejin.cn/post/6872764160640450574#heading-4 (斷點後續指令)
//www.jianshu.com/p/67f08a4d8cf2 (斷點相關)

基礎環境搭建

在x-code目錄中找到對於版本的DeveloperDiskImage.dmg ,路徑大致/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/9.2/DeveloperDiskImage.dmg

雙擊DeveloperDiskImage.dmg 在/Volumes/DeveloperDiskImage/usr/bin/ 下找到debugserver.

debugserver複製出來

創建一個entitlement.xml文件與debugserver保持同一目錄

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "//www.apple.com/DTDs/PropertyList-1.0.dtd"> 
<plist version="1.0"> 
  <dict> 
    <key>com.apple.springboard.debugapplications</key> 
    <true/> 
    <key>get-task-allow</key> 
    <true/> 
    <key>task_for_pid-allow</key> 
    <true/> 
    <key>run-unsigned-code</key> 
    <true/> 
  </dict> 
</plist>

用codesign對debugserver簽名, 命令如下:

codesign -s - --entitlements entitlement.xml -f debugserver 
scp -r debugserver [email protected]:/usr/bin/    #192.168.2.17 是 iPhone 設備的 ip 地址.



debugserver 拷貝到iOS中 目錄為/usr/bin/ 

pc終端命令行窗口1、2中,創建埠代理

iproxy 1998 1998
iproxy 2222 22

pc終端命令行窗口3中連接手機的ssh

#這裡前提要安裝OpenSSH,Cycript

ssh root@localhost -p 2222

#默認密碼是alpine

手機終端中輸入

ps -e |grep app

#找到對應的app
#如/var/containers/Bundle/Application/4CF47160-4687-41C9-B987-EB40B541A461/pinduoduo.app/pinduoduo

手機端開啟debugserver

debugserver -x backboard 127.0.0.1:1998 /var/containers/Bundle/Application/4CF47160-4687-41C9-B987-EB40B541A461/pinduoduo.app/pinduoduo

pc終端命令行
執行

lldb

process connect connect://127.0.0.1:1998

#如果此事介面出現報錯或者停止 按C繼續執行,可以先下好斷點再運行
image list -o -f   # 第一行即為獲取 獲取ASLR偏移地址

#[  0] 0x00000000007a0000 /private/var/containers/Bundle/Application/4CF47160-4687-41C9-B987-EB40B541A461/pinduoduo.app/pinduoduo(0x00000001007a0000)
00000000007a0000 為ASLR偏移地址

計算ptrace偏移後地址

p/x 0x0000000101319A8C + 0x00000000007a0000  # [ptrace偏移後地址] = 方法位置+ ASLR偏移地址
br s -a [ptrace偏移後地址]

設置斷點相關 (每一行代表一種寫法)

給所有名為xx的函數設置一個斷點

(lldb)breakpoint set —name xx
(lldb)br s -n xx
(lldb)b xx

在文件F指定行L設置斷點

(lldb)breakpoint set —file F —line L
(lldb)br s -f F -l L
(lldb)b F:L

給所有名為xx的C++函數設置一個斷點(希望沒有同名的C函數)

(lldb)breakpoint set —method xx
(lldb)br s -M xx

給一個OC函數[objc msgSend:]設置一個斷點

(lldb)breakpoint set —name 「[objc msgSend:]」
(lldb)b -n 「[objc msgSend:]」

給所有名為xx的OC方法設置一個斷點(希望沒有名為xx的C或者C++函數)

(lldb)breakpoint set —selector xx
(lldb)br s -S count

給所有函數名正則匹配成功的函數設置一個斷點

(lldb)breakpoint set --func-regex regular-expression
(lldb)br s -r regular-expression

給指定函數地址func_addr的位置設置一個斷點

(lldb)br set -a func_addr

斷點查看

(lldb)breakpoint list
(lldb)br l

斷點刪除

(lldb)breakpoint delete index
(lldb)br del index

index指明斷點的序號,如果為空則刪除所有斷點

斷點後續移動操作

源碼級別(和你ida彙編看到一樣的一步一步)

step-in步入進入函數內部

(lldb) thread step-in
(lldb) step
(lldb) s

step-over步入不會進入函數內部

(lldb) thread step-over
(lldb) next
(lldb) n

指令級別

step-in 在彙編介面,跳轉下一步,在有bl指令的地方,si單步進入到bl指令所跳轉的子函數內部

(lldb) thread step-inst
(lldb) si

step-over 在彙編介面,跳轉下一步,在有bl指令的地方,ni不會單步進入到bl指令所跳轉的子函數內部

(lldb) thread step-inst-over
(lldb) ni

step-out 從一個函數跳出

(lldb) thread step-out
(lldb) finish
(lldb) f

下一個斷點

c

lldb常用命令(以下需要斷點命中後操作)

1.計算表達式命令(expression、po、p)

expression可簡寫為expr

計算以及生成一個表達式

(lldb) expr (int)printf ("Print nine: %d.\n", 4 + 5) 
Print nine: 9.
(int) $0 = 15

創建一個變數並分配值

(lldb) expr int $val = 10
(lldb) expr $val
(int) $val = 10

exp列印值、修改值

(lldb) expr width
(CGFloat) $0 = 10
(lldb) expr width = 2
(CGFloat) $1 = 2
(lldb) po width
2

(lldb) p width
(CGFloat) $3 = 2

p、po與expr的關係

(lldb) expr -- person
(Person *) $0 = 0x000000010053b7f0
(lldb) p person
(Person *) $1 = 0x000000010053b7f0
(lldb) expr -o -- person
<Person: 0x10053b7f0>

(lldb) po person
<Person: 0x10053b7f0>

總結

p是expr --的簡寫,它的工作是把接收到參數在當前環境中進行編譯,然後列印出來
po是expr -o --的簡寫,它所做的操作和p相同。如果接收到的參數是一個指針,那麼它會調用對象的description方法並列印;如果接收到的參數是一個core foundation對象,那麼它會調用CFShow方法並列印。如果這兩個方法都調用失敗,那麼po列印出和p相同的內容。

使用p做進位轉換

//默認列印為10進位
(lldb) p 10
(int) $0 = 10
//轉16進位
(lldb) p/x 10
(int) $1 = 0x0000000a
//轉8進位
(lldb) p/o 10
(int) $2 = 012
//轉二進位
(lldb) p/t 10
(int) $3 = 0b00000000000000000000000000001010
//字元轉10進位數字
(lldb) p/d 'A'
(char) $4 = 65
//10進位數字轉字元
(lldb) p/c 66
(int) $5 = B\0\0\0

記憶體讀取

(lldb) x person
0x10053a6b0: 5d 22 00 00 01 80 1d 00 00 00 00 00 00 00 00 00  ]"..............
0x10053a6c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
(lldb) x/4gx person
0x10053a6b0: 0x001d80010000225d 0x0000000000000000
0x10053a6c0: 0x0000000000000000 0x0000000000000000
(lldb) x/3wx person
0x10053a6b0: 0x0000225d 0x001d8001 0x00000000
(lldb) x &width
0x7ffeefbff4f0: 00 00 00 00 00 00 00 00 b0 a6 53 00 01 00 00 00  ..........S.....
0x7ffeefbff500: 30 f5 bf ef fe 7f 00 00 01 00 00 00 00 00 00 00  0...............
(lldb) x/4gx &width
0x7ffeefbff4f0: 0x0000000000000000 0x000000010053a6b0
0x7ffeefbff500: 0x00007ffeefbff530 0x0000000000000001
(lldb) x/4go
0x7ffeefbff510: 03777735757772440
0x7ffeefbff518: 03777755321776311
0x7ffeefbff520: 00
0x7ffeefbff528: 01

x是讀取記憶體的命令,x/4gx中第一個x是讀取記憶體命令,後面的g是每次讀取8位元組,x的意思是16進位顯示結果,4表示連續列印4段。

對於g,常用的大小格式為b對應byte 1位元組,h對應half word 2位元組,w對應word 4位元組,g對應giant word 8位元組
對於x,我們還可以用o對應8機制,b對應2進位,x對應16進位,f對應浮點,d對應10進位

call方法調用

(lldb) call width
(CGFloat) $0 = 0
(lldb) call testFunction()
123456
複製程式碼
4.變數檢查(frame)
(lldb) fr v b
(Int??) b = nil
(lldb) fr v -r b
(Int??) b = nil
(lldb) fr v -R b
(Swift.Optional<Swift.Optional<Swift.Int>>) b = some {
  some = none {
    some = {
      _value = 0
    }
  }
}
(lldb) fr v -a b
(Int??) b = nil

我們常用fr v -R來查看類型的結構