debugserver+lldb 动态调试 iOS App

3,605 阅读3分钟

Xcode 动态调试原理

图片.png

  • debugserver依附在 iOS 的 APP 进程上,时刻监听 APP 的运行状态,并有控制 APP 执行的能力;
  • lldb在PC端和debugserver建立连接并控制App行为
  • 在真机调试的时候,Xcode将debugserver加入到APP中,通过lldb来调试APP

debugserver 权限修改

Xcode 的 lldb 默认只能调试通过 Xcode 安装的 App 需要给 debugserver 授权才能调试任何 App

  1. 从 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/De viceSupport/14.5/DeveloperDiskImage.dmg/usr/bin/debugserver 获取 debugserver 程序
  2. 生成授权entitlements文件
ldid -e debugserver > debugserver.entitlements
  1. 删除 entitlements 其他权限,修改为如下四个权限
<?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.springboard.debugapplications</key>
	<true/>
	<key>run-unsigned-code</key>
	<true/>
	<key>get-task-allow</key>
	<true/>
	<key>task_for_pid-allow</key>
	<true/>
</dict>
</plist>
  1. 重新授权 debugserver 程序
ldid -Sdebugserver.entitlements debugserver
  1. 移动 debugserver 程序到 iOS 设备上
scp debugserver root@10.100.170.85:/usr/bin/

启动 debugserver 和 lldb 服务

打开 App 进程, 如设置App(Preferences), 将 debugserver 附加到 iOS 进程上:

ssh root@10.100.170.85 
/usr/bin/debugserver localhost:12345 –a Preferences

通过 USB 连接 iOS 设备, 新建 Terminal 窗口使用 iproxy(brew install mutmuxd) 映射端口

iproxy 12345 12345

新建第三个 Terminal 窗口运行 lldb,连接 debugserver

lldb
process connect connect://localhost:12345
process interrupt // 暂停程序

图片.png 这样就连接上了,可使用lldb来调试越狱设备上的应用。

LLDB指令

查看指令帮助

help image list
help br
help breakpoint set

打印

// 查看函数调用堆栈信息
bt
// 线程列表
thread list 

// 打印当前的栈帧变量, 可以带参数表示打印具体变量名
frame variable

// 查看当前执行代码信息
frame info

// 打印 OC 对象
po self 
// 查看类的方法信息
po [className _shortMethodDescription]

打印和修改界面

// 打印所有界面层次
po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
po [(UITableView*)0x13e051800 setBackgroundColor:[UIColor yellowColor]]
// 需要手动刷新下界面
e (void)[CATransaction flush]

进程和动态库查询

// 查看 App 进程加载的二进制和动态库列表和偏移地址
image list -o -f
image list -o -f | grep Demo

// 在所有模块中查找指定符号或者地址
image lookup
image lookup -t 类型 : 查找某个类型的信息
image lookup -a 内存地址 : 根据内存地址查找在模块中的位置
image lookup -n 符号名 : 查找某个符号或者函数的位置

内存操作

memory read 0x1bce000f8 // 读取内存值
memory write 0x1bce000f8 5 // 修改内存值

寄存器

// 显示所有寄存器的值
register read
register read x0
register write
po $x1 打印方法调用者
x/s $x1:打印方法名
po $x2:打印参数(以此类推,x3、x4也可能是参数)

断点

  • 函数断点
breakpoint set --name tableView:didSelectRowAtIndexPath:
br s -n tableView:didSelectRowAtIndexPath:
br set -n "-[PSUIPrefsListController tableView:didSelectRowAtIndexPath:]"
// 设置内存断点
br set -a 0x00000001008ca7e4

断点结果

Breakpoint 1: 261 locations. (1:表示断点序号, 261: 生成了 261 个断点)
Breakpoint 2: where = PreferencesUI`-[PSUIPrefsListController 
tableView:didSelectRowAtIndexPath:], address = 0x00000001d991d36c
  • 断点流程

    • c(continue): 继续运行
    • n(next): 单步执行
    • s(step in): 进入函数内部执行
    • finish: 执行到函数尾部
  • 断点操作

br list // 列出所有断点
br delete 2 // 通过序号删除断点
br delete // 删除所有断点
br disable // 禁用断点
br enable // 启用断点

watchpoint 监听内存值变化

在指定内存存储的数据发生改变的时候触发
watchpoint set variable 变量
watchpoint set variable self->age
watchpoint set expression 内存地址
watchpoint set expression &(self->_age)
watchpoint list