Xcode 為什么可以調(diào)試APP?
平時開發(fā)中當我們給代碼打斷點,調(diào)試程序(lldb),這一切都離不開一個媒介debugserver
剑刑,它負責將lldb
指令給到app
,然后app
將結(jié)果通過debugserver
傳給lldb
debugserver
一開始是在Xcode
中的双肤,一旦手機連接Xcode
信任后施掏,debugserver
便會安裝到手機上
Xcode
目錄:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/9.1/DeveloperDiskImage.dmg/usr/bin/debugserver
;iPhone
目錄:/Developer/usr/bin/debugserver
但是缺少task_for_pid
權(quán)限茅糜,通過Xcode
安裝的debugserver
七芭,只能調(diào)試自己的app,要想逆向別人的app,這種肯定是行不通的蔑赘。前面說到狸驳,不能調(diào)試別人app的原因是權(quán)限不夠预明,要想在沒有源碼的情況下調(diào)試別人的app,就需要修改debugserver
權(quán)限耙箍。
更改debugserver
權(quán)限
認識debugserver
放在/Developer/usr/bin/debugserver
是沒有調(diào)試其他app的權(quán)限的撰糠,現(xiàn)在的做法是先把iPhone
上的debugserver
放到電腦上修改好權(quán)限再放回手機上,已達到可以調(diào)試其他app的目的辩昆。
拖到MachOView
中可以看到這是個胖二進制文件
給debugserver瘦身
由于debugserver
是個胖二進制文件阅酪,我的越獄手機是iPhone6plus
、iOS9.0.1
是arm64
架構(gòu)的卤材,我們只要流行arm64
即可使用lipo
命令
lipo -thin arm64 ./debugserver -o ./debugserver_arm64
為了方便將debugserver_arm64
改為debugserver
遮斥,下面的提到的debugserver
指的都是debugserver_arm64
。
給debugserver增加 task_for_pid權(quán)限
查看debugserver
原來的權(quán)限
通過ldid
查看原來的命令
? debugserver ldid -e ./debugserver
<?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.diagnosticd.diagnostic</key>
<true/>
<key>com.apple.frontboard.debugapplications</key>
<true/>
<key>com.apple.frontboard.launchapplications</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.springboard.debugapplications</key>
<true/>
<key>run-unsigned-code</key>
<true/>
<key>seatbelt-profiles</key>
<array>
<string>debugserver</string>
</array>
</dict>
</plist>
將舊的權(quán)限輸出到entitlement.plist
文件中
? debugserver ldid -e ./debugserver > ./entitlement.plist
在entitlement.plist
增加一個task_for_pid-allow
重簽名debugserver
將改好的entitlement.plist
重簽debugserver
? debugserver ldid -Sentitlement.plist debugserver
再次查看debugserver
權(quán)限確認是否添加成功
? debugserver ldid -e ./debugserver | grep task_for_pid-allow
<key>task_for_pid-allow</key>
? debugserver
說明成功添加
將debugserver
放回到手機
前面已經(jīng)賦予了debugserver
可以調(diào)試其他app
的權(quán)限了扇丛,接下來放回到手機上使用术吗。
此時我們把修改后的debugserver
放到手機的/usr/bin/
目錄下,原因有2個
- 手機上原來的
debugserver
存放的目錄/Developer/usr/bin/debugserver
是只讀的帆精; - 放到
/usr/bin/
下可以在手機上直接敲debugserver
方便使用
順便增加執(zhí)行權(quán)限
iPhone:~ root# chmod +x /usr/bin/debugserver
iPhone:~ root# ls -l /usr/bin/debugserver
-rwxr-xr-x 1 root wheel 4646672 Dec 9 15:49 /usr/bin/debugserver
iPhone:~ root#
用debugserver
啟動或附加進程
啟動進程
debugserver -x backboard IP:port /path/to/executable
debugserver會啟動executable较屿,并開啟port端口, 等待來自IP的LLDB接入卓练。
先實現(xiàn)一個打開系統(tǒng)的app,iPhone系統(tǒng)應用都放在/Applications
下面
已打開系統(tǒng)的計算器為例
iPhone:~ root# debugserver -x backboard *:1234 /Applications/Calculator.app/Calculator
上面的代碼會啟動Calculator.app
隘蝎,并開啟1234端 口,等待任意IP地址的LLDB接入襟企。
附加進程
debugserver IP:port -a "ProcessName"
debugserver
會附加ProcessName
嘱么,并開啟port端 口,等待來自IP的LLDB接入顽悼。
已打開手機上的新浪微博為例子
用戶安裝的app放在/var/mobile/Containers/Bundle/Application
下
先點擊新浪微博
iPhone:~ root# debugserver *:1234 -a "Weibo"
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-340.3.51.1
for arm64.
Attaching to process Weibo...
Listening to port 1234 for a connection from *...
-a
是附加的意思
lldb
連接debugserver
現(xiàn)在手機端的服務已經(jīng)開啟曼振。
先電腦端輸入lldb
指令
? ~ lldb
(lldb)
連接服務
process connect connect://192.168.1.102:1234
ip
就是手機的ip地址
1234
就是端口
稍微等一會(1到2分鐘吧)就會出現(xiàn)下面的東西
Process 4348 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x000000019b3c4c30 libsystem_kernel.dylib`mach_msg_trap + 8
libsystem_kernel.dylib`mach_msg_trap:
-> 0x19b3c4c30 <+8>: ret
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x19b3c4c34 <+0>: mov x16, #-0x20
0x19b3c4c38 <+4>: svc #0x80
0x19b3c4c3c <+8>: ret
Target 0: (Weibo) stopped.
(lldb)
此時的app是無法交互的我們輸入c
繼續(xù)程序
(lldb) c
Process 4348 resuming
(lldb)
會發(fā)現(xiàn)微博可以交互了。
一個簡單的demo
連接手機
ssh root@10.9.24.154
你也可以使用usb連接
開啟手機服務
debugserver *:1234 -a 818
mac端輸入lldb
指令連接手機
? ~ lldb
(lldb) process connect connect://10.9.24.154:1234
Process 818 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x000000019811cc30 libsystem_kernel.dylib`mach_msg_trap + 8
libsystem_kernel.dylib`mach_msg_trap:
-> 0x19811cc30 <+8>: ret
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x19811cc34 <+0>: mov x16, #-0x20
0x19811cc38 <+4>: svc #0x80
0x19811cc3c <+8>: ret
Target 0: (Debugserver) stopped.
計算-[ViewController btnClick]:
的真實函數(shù)地址
查看ASLR
什么是ASLR
全稱ASLR (Address Space Layout Randomization)
每次進程啟動時蔚龙,同一進程的所有模塊在虛擬內(nèi)存中的起始 地址都會產(chǎn)生隨機偏移冰评。就拿我們的例子來說,在虛擬內(nèi)存的起始地址是0x0
我們先不考慮隨機地址偏移木羹,也就是說明了這個macho
加載內(nèi)存的時候_TEXT
段的起始地址應該是0x100000000
,但是如果有了偏移量甲雅,那么我們加上偏移量就能計算出內(nèi)存中的真實地址了。
(lldb) image list -o -f
[ 0] 0x00000000000dc000 /Users/fangshufeng/Library/Developer/Xcode/iOS DeviceSupport/9.0.1 (13A404)/Symbols/usr/lib/dyld
[ 1] 0x00000000000b0000 /var/mobile/Containers/Bundle/Application/FA2608EB-E892-4C24-9272-F40908101AA0/Debugserver.app/Debugserver(0x00000001000b0000)
[ 2] 0x00000001000c4000 /Library/MobileSubstrate/MobileSubstrate.dylib(0x00000001000c4000)
[...]
- 第一列[X]是模塊的序
- 第二列是模塊在虛擬內(nèi)存中的起始地址因ASLR 而產(chǎn)生的隨機偏移(以下簡稱ASLR偏移)坑填;
- 第三列 是模塊的全路徑抛人,括號里是偏移之后的起始地址。
可以看到偏移量為0x40000
hopper查看再Macho中的值
所以真實的函數(shù)地址是 = 0x00000000000b0000
+ 0x0000000100006738
== 0x1000B6738
;
在-[ViewController btnClick]:
設置斷點
(lldb) breakpoint set -a 0x1000B6738
Breakpoint 3: where = Debugserver`-[ViewController btnClick], address = 0x00000001000b6738
(lldb)
輸入c
繼續(xù)程序
點擊按鈕
(lldb) breakpoint set -a 0x1000B6738
Breakpoint 3: where = Debugserver`-[ViewController btnClick], address = 0x00000001000b6738
(lldb) c
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
frame #0: 0x00000001000b6738 Debugserver`-[ViewController btnClick]
Debugserver`-[ViewController btnClick]:
-> 0x1000b6738 <+0>: sub sp, sp, #0x20 ; =0x20
0x1000b673c <+4>: mov w8, #0x14
0x1000b6740 <+8>: mov w9, #0xa
0x1000b6744 <+12>: str x0, [sp, #0x18]
Target 0: (Debugserver) stopped.
(lldb)
可以看到已經(jīng)斷到了方法處脐瑰,可以和hopper
的內(nèi)容對比一看是一樣的妖枚。
簡單使用lldb
指令
(lldb) ni
Process 1083 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x000000018818a3e4 UIKit`<redacted> + 100
UIKit`<redacted>:
-> 0x18818a3e4 <+100>: cmp x22, #0x0 ; =0x0
0x18818a3e8 <+104>: cset w0, ne
0x18818a3ec <+108>: ldp x29, x30, [sp, #0x20]
0x18818a3f0 <+112>: ldp x20, x19, [sp, #0x10]
Target 0: (Debugserver) stopped.
(lldb) ni
Process 1083 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x000000018818a3e8 UIKit`<redacted> + 104
UIKit`<redacted>:
-> 0x18818a3e8 <+104>: cset w0, ne
0x18818a3ec <+108>: ldp x29, x30, [sp, #0x20]
0x18818a3f0 <+112>: ldp x20, x19, [sp, #0x10]
0x18818a3f4 <+116>: ldp x22, x21, [sp], #0x30
Target 0: (Debugserver) stopped.
(lldb)
打印寄存器的值
(lldb) po $w8
20
(lldb) p $w9
(unsigned int) $1 = 10
(lldb)
打印層級
(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
<UIWindow: 0x14c63e650; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x14c637830>; layer = <UIWindowLayer: 0x14c63d3c0>>
| <UIView: 0x14c54fe20; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x14c54f000>>
| | <_UILayoutGuide: 0x14c550280; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x14c54e040>>
| | <_UILayoutGuide: 0x14c550c30; frame = (0 736; 0 0); hidden = YES; layer = <CALayer: 0x14c63c690>>
| | <UIButton: 0x14c641240; frame = (100 100; 100 100); opaque = NO; layer = <CALayer: 0x14c6411f0>>
| | | <UIButtonLabel: 0x14c63a3c0; frame = (23 39.3333; 54 21.6667); text = '點我啊'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x14c633470>>
| | | | <_UILabelContentLayer: 0x14c651170> (layer)
(lldb)
給按鈕加一個黃色背景
(lldb) po [0x14c641240 setBackgroundColor:[UIColor yellowColor] ]
0x0000000000000001
(lldb) c
Process 1083 resuming
(lldb)
(完)