本文基于重簽名后的IPA包安裝在越獄手機上運行的包實現(xiàn)動態(tài)調(diào)試。動態(tài)調(diào)試借助于ldid、debugserver、lldb 來實現(xiàn)。
- ldid: 調(diào)試工具需要自行安裝至Mac端;
-
debugserver: 存于手機系統(tǒng)中,但是原本手機上的debugserver不具備get-task-allow和task_for_pid-allow權限,需要賦予權限后重新簽名將其通過SSH傳輸至手機上; - LLDB(Low level Debuger): 輕量級調(diào)試 位于Mac。
一、準備工作
1.ldid
如果Mac上未安裝 ldid,則直接執(zhí)行指令:
$ brew install ldid
2. debugserver
a. 獲取
通過一下路徑可獲取到 debugserver,將其復制出至空文件夾中:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/13.4/DeveloperDiskImage.dmg/usr/bin/debugserver
PS:真機調(diào)試包的版本號需與手機系統(tǒng)的版本號對應。
b. 賦予權限 & 重簽名
導出 entitlements 文件
$ ldid -e debugserver > debugserver.entitlements
通過文本編輯打開 debugserver.entitlements(debugserver.entitlements 無法直接打開,因為文件內(nèi)包含兩份配置 arm64、arm64v),將其內(nèi)文本替換為一下內(nèi)容(entitlements 中添加 get-task-allow 和 task_for_pid-allow 權限):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.backboardd.debugapplications</key>
<true/>
<key>com.apple.backboardd.launchapplications</key>
<true/>
<key>com.apple.frontboard.debugapplications</key>
<true/>
<key>com.apple.frontboard.launchapplications</key>
<true/>
<key>com.apple.springboard.debugapplications</key>
<true/>
<key>com.apple.system-task-ports</key>
<true/>
<key>get-task-allow</key>
<true/>
<key>platform-application</key>
<true/>
<key>run-unsigned-code</key>
<true/>
<key>task_for_pid-allow</key>
<true/>
</dict>
</plist>
重簽名
# 查看權限信息
$ codesign -d --entitlements - debugserver
# 簽名權限
$ codesign -f -s - --entitlements debugserver.entitlements debugserver
# 或著簡寫為
$ codesign -fs- --entitlements debugserver.entitlements debugserver
c. 將 debugserver 傳輸至手機上
將 debugserver 傳輸至 iPhone 上并放置于 /usr/bin/, 并對其附加權限
# iphone-ip 為手機IP地址
$ scp -r /xxx/debugserver root@iphone-ip:/usr/bin
# 例如 scp -r /Desktop/xxx/debugserver root@10.22.48.5:/usr/bin
# 輸入 SSH 秘鑰
$ alpine
# 開啟SSH, iphone-ip 為手機IP地址
$ ssh root@iphone-ip
# 輸入 SSH 秘鑰
$ alpine
# 增加權限
$ chmod +x /usr/bin/debugserver
二、debugserver 與 App 建立交互
新建終端窗口,將debugserver 與 App 建立交互
# 開啟 SSH
$ ssh root@ip
# 輸入SSH 密碼,下面為默認密碼
$ alpine
# 啟動應用后 可通過Mac端的控制臺獲取到進程ID/進程名
# ProcessID 為進程名/進程ID
$ debugserver 127.0.0.1:10011 -a <ProcessName/ProcessID>
三、debugserver 與 LLDB 建立交互
新建終端窗體,啟用端口 10011
# 新建終端窗口
$ iproxy 10011 10011
# 如果需要關閉端口
# 查看端口信息
$ lsof -i:10011
# kill 對應的PID
$ kill -9 <PID>
新建終端窗口,將 debugserver 與 LLDB 建立交互
$ lldb
$ process connect connect://127.0.0.1:10011
# 或 process connect connect:localhost:10011
# 關聯(lián)成功后,進程默認暫停,需要執(zhí)行一下命令讓程序繼續(xù)運行
$ c
四、指令響應歸納
App增加了 ptrace,動態(tài)調(diào)試建立關聯(lián)報錯,打印信息如下:
debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-900.3.106
for arm64.
Attaching to process 6044...
Segmentation fault: 11
未做防護建立關聯(lián)正確信息,打印信息如下:
debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-900.3.98
for arm64.
Attaching to process FYPrototypeDemo...
Listening to port 10011 for a connection from localhost...
Waiting for debugger instructions for process 0.
debugserver 與 LLDB 建立交互,打印信息如下:
Process 12370 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x00000001bf85b5f4 libsystem_kernel.dylib`mach_msg_trap + 8
libsystem_kernel.dylib`mach_msg_trap:
-> 0x1bf85b5f4 <+8>: ret
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x1bf85b5f8 <+0>: mov x16, #-0x20
0x1bf85b5fc <+4>: svc #0x80
0x1bf85b600 <+8>: ret
Target 0: (FYPrototypeDemo) stopped.
五、lldb 指令
1. breakpoint
查看斷點列表
$ breakpoint list
禁用斷點
# 禁用所有
$ breakpoint disable
# 禁用第一個斷點
$ breakpoint disable 1.1
啟用斷點
# 啟用所有斷點
$ breakpoint enable
# 啟用第一個斷點
$ breakpoint enable 1.1
刪除斷點
# 刪除所有斷點
$ breakpoint delete
# 刪除第1組斷點, 刪除只能刪除一組,不能單個刪除
$ breakpoint delete 1
下斷點
# 通過函數(shù)名下斷點
$ breakpoint set --name <methodName>
# 或者
$ breakpoint set -n <methodName>
# 連續(xù)下多個斷點
$ breakpoint set -n <methodName> -n <methodName>
# 例如 breakpoint set -n "-[ViewController save:]" -n "-[ViewController pause:]" -n "-[ViewController continues:]"
設置 selector 斷點
$ breakpoint set --selector <selector>
# 例如 breakpoint set --selector touchesBegan:withEvent:
設置帶有相同字符串的方法斷點
$ breakpoint set -r Game:
給某一個文件下的帶有相同字符串的方法下斷點:
$ breakpoint set --file <filename> -r <method>
# 例如 breakpoint set --file ViewController.m -r Game
注意:
簡寫:breakpoint->b
打印列表需要寫全:breakpoint list 或者 break list
2. bt、frame 命令
查看函數(shù)相關信息,使用p、down追蹤函數(shù)的調(diào)用和被調(diào)用關系
# 使用bt命令查看函數(shù)調(diào)用堆棧
$ frame select
查找方法的調(diào)用者及方法名稱
$ frame variable
3. methods、pviews 命令
methods打印當前對象的屬性和方法
$ methods self
4. 命令大全
LLDB指令
LLDB指令的格式是:
<command> [<subcommand> [<subcommand>...]] <action> [-options [option- value]] [argument [argument...]],其中,command代表命令,subcommand代表子命令,action代表命令的動作,- options 代表命令的選項,argument代表命令的參數(shù),[]中的可以省略。
help命令,用于查看指令的用法,例如:help breakpoint、help breakpoint set
expression命令,執(zhí)行一個表達式,例如:expression self.view.backgroundColor = [UIColor redColor],expression與print、p、call的效果一樣
thread backtrace命令,打印線程的堆棧信息,與bt命令效果一樣
thread return []命令,讓函數(shù)直接返回某個值,不會執(zhí)行斷點后面的代碼了,例如:thread return 3,返回了3
frame variable []命令,打印當前棧幀的變量
thread continue、continue、c命令,讓程序繼續(xù)運行
thread step-over、next、n命令,單步執(zhí)行,把子函數(shù)當做一個整體,不會進入子函數(shù)
thread step-in、step、s命令,單步執(zhí)行,遇到子函數(shù)會進入子函數(shù)內(nèi)部
breakpoint set命令,設置斷點,參數(shù)主要有以下幾種:
breakpoint set -a 函數(shù)地址
breakpoint set -n 函數(shù)名
breakpoint set -r 正則表達式
breakpoint set -s 動態(tài)庫 -n 函數(shù)名
breakpoint list命令,列出所有的斷點,每個斷點都有自己的編號
breakpoint delete 斷點編號命令,刪除某個斷點
breakpoint command add 斷點編號命令,給斷點預先設置需要執(zhí)行的命令,到觸發(fā)斷點時,就會按順序執(zhí)行
breakpoint command list 斷點編號命令,查看某個斷點的預設命令
watchpoint內(nèi)存斷點,就是當內(nèi)存數(shù)據(jù)改變時,觸發(fā)此斷點,以便確認是誰修改了內(nèi)存,子命令主要有以下幾種:
watchpoint set variable 變量,例如:watchpoint set variable self->_age,當age變量改變時,斷點就會觸發(fā),以便找到修改age內(nèi)存的代碼
watchpoint set expression 地址,例如:watchpoint set expression &self->_age
watchpoint list,列出所有的內(nèi)存斷點
watchpoint delete 斷點編號,刪除此內(nèi)存斷點
watchpoint command add 斷點編號,給此內(nèi)存斷點,增加預設命令
image lookup,尋找模塊信息,如果你想找某個類型、某個方法、某個地址在模塊中的什么位置,就可以用這個命令,主要參數(shù)如下:
image lookup -t 類型,查找某個類型的信息,例如image lookup -t NSInterger
image lookup -a 地址,看看某個內(nèi)存地址在模塊中的位置
image lookup -n 符號或者函數(shù)名,查找某個符號或者函數(shù)的位置
image list,列出所加載的模塊信息
一些小技巧:敲Enter會自動執(zhí)行上次的命令、絕大部分命令可以使用縮寫