Xcode 为什么可以调试APP?
平时开发中当我们给代码打断点,调试程序(lldb),这一切都离不开一个媒介debugserver,它负责将lldb指令给到app,然后app将结果通过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权限,通过Xcode安装的debugserver,只能调试自己的app,要想逆向别人的app,这种肯定是行不通的。前面说到,不能调试别人app的原因是权限不够,要想在没有源码的情况下调试别人的app,就需要修改debugserver权限。
更改debugserver 权限
认识debugserver
放在/Developer/usr/bin/debugserver是没有调试其他app的权限的,现在的做法是先把iPhone上的debugserver放到电脑上修改好权限再放回手机上,已达到可以调试其他app的目的。
拖到MachOView中可以看到这是个胖二进制文件

给debugserver瘦身
由于debugserver是个胖二进制文件,我的越狱手机是iPhone6plus、iOS9.0.1是arm64架构的,我们只要流行arm64即可使用lipo命令
1 |
|

为了方便将debugserver_arm64改为debugserver,下面的提到的debugserver指的都是debugserver_arm64。
给debugserver增加 task_for_pid权限
查看debugserver原来的权限
通过ldid查看原来的命令
1 | ➜ debugserver ldid -e ./debugserver |
将旧的权限输出到entitlement.plist文件中
1 | ➜ debugserver ldid -e ./debugserver > ./entitlement.plist |
在entitlement.plist增加一个task_for_pid-allow

重签名debugserver
将改好的entitlement.plist重签debugserver
1 | ➜ debugserver ldid -Sentitlement.plist debugserver |
再次查看debugserver权限确认是否添加成功
1 | ➜ debugserver ldid -e ./debugserver | grep task_for_pid-allow |
说明成功添加
将debugserver 放回到手机
前面已经赋予了debugserver可以调试其他app的权限了,接下来放回到手机上使用。
此时我们把修改后的debugserver放到手机的/usr/bin/目录下,原因有2个
- 手机上原来的
debugserver存放的目录/Developer/usr/bin/debugserver是只读的; - 放到
/usr/bin/下可以在手机上直接敲debugserver方便使用
顺便增加执行权限
1 | iPhone:~ root# chmod +x /usr/bin/debugserver |
1 | iPhone:~ root# ls -l /usr/bin/debugserver |
用debugserver启动或附加进程
启动进程
1 | debugserver -x backboard IP:port /path/to/executable |
debugserver会启动executable,并开启port端口, 等待来自IP的LLDB接入。
先实现一个打开系统的app,iPhone系统应用都放在/Applications下面

已打开系统的计算器为例
1 | iPhone:~ root# debugserver -x backboard *:1234 /Applications/Calculator.app/Calculator |
上面的代码会启动Calculator.app,并开启1234端 口,等待任意IP地址的LLDB接入。
附加进程
1 | debugserver IP:port -a "ProcessName" |
debugserver会附加ProcessName,并开启port端 口,等待来自IP的LLDB接入。
已打开手机上的新浪微博为例子
用户安装的app放在/var/mobile/Containers/Bundle/Application下
先点击新浪微博
1 | iPhone:~ root# debugserver *:1234 -a "Weibo" |
-a是附加的意思
lldb连接debugserver
现在手机端的服务已经开启。
先电脑端输入lldb指令
1 | ➜ ~ lldb |
连接服务
1 | process connect connect://192.168.1.102:1234 |
ip就是手机的ip地址1234就是端口
稍微等一会(1到2分钟吧)就会出现下面的东西
1 | Process 4348 stopped |
此时的app是无法交互的我们输入c继续程序
1 | (lldb) c |
会发现微博可以交互了。
一个简单的demo
连接手机
1 | ssh root@10.9.24.154 |
你也可以使用usb连接
开启手机服务
1 | debugserver *:1234 -a 818 |
mac端输入lldb指令连接手机
1 | ➜ ~ lldb |
计算-[ViewController btnClick]:的真实函数地址
查看ASLR
什么是ASLR
全称ASLR (Address Space Layout Randomization)每次进程启动时,同一进程的所有模块在虚拟内存中的起始 地址都会产生随机偏移。就拿我们的例子来说,在虚拟内存的起始地址是0x0

我们先不考虑随机地址偏移,也就是说明了这个macho加载内存的时候_TEXT段的起始地址应该是0x100000000,但是如果有了偏移量,那么我们加上偏移量就能计算出内存中的真实地址了。
1 | (lldb) image list -o -f |
- 第一列[X]是模块的序
- 第二列是模块在虚拟内存中的起始地址因ASLR 而产生的随机偏移(以下简称ASLR偏移);
- 第三列 是模块的全路径,括号里是偏移之后的起始地址。
可以看到偏移量为0x40000
hopper查看再Macho中的值

所以真实的函数地址是 = 0x00000000000b0000 + 0x0000000100006738 == 0x1000B6738;
在-[ViewController btnClick]:设置断点
1 | (lldb) breakpoint set -a 0x1000B6738 |
输入c继续程序
点击按钮
1 | (lldb) breakpoint set -a 0x1000B6738 |
可以看到已经断到了方法处,可以和hopper的内容对比一看是一样的。
简单使用lldb指令
1 | (lldb) ni |
打印寄存器的值
1 |
|
打印层级
1 | (lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription] |
给按钮加一个黄色背景
1 | (lldb) po [0x14c641240 setBackgroundColor:[UIColor yellowColor] ] |

(完)