LLDB 动态调试
LLDB的Xcode默认的调试器,它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。平时用Xcode运行程序,实际走的都是LLDB。熟练使用LLDB,可以让debug事半功倍。
LLDB 用法
指令的格式
1 | <command>: 命令 |
比如给test函数设置断点
1 | breakpoint set -n test |
常用LLDB命令
help命令
1 | help + <command> |
执行表达式
1 | expression <cmd-options>--<expr> |
注:
expression
、expression —
和指令p
、call
的效果一样,执行代码并打印对象和内存地址
expression、-O —
和指令po
效果一样,相当于%@ 方式打印出object.description
po $寄存器
可直接打印寄存器地址指向的内容
x/<类型> 地址 可读取内存内容 eg:
x/s $x1
可读取x1寄存器的字符串
流程控制
thread backtrack
(简写bt
) 打印线程的堆栈信息thread return <expr>
让函数直接返回某个值,不会执行断点后面的代码frame variable [<variable-name>]
打印当前栈帧的变量thread continue
、c
: 程序继续执行thread step-over
、next
、n
: 单步运行,把子函数当做整体一步执行thread step-in
、step
、s
: 单步运行,遇到子函数会进入子函数thread step-out
、finish
:直接执行完当前函数所有代码,返回上一个函数si
、ni
和s
、n
类似 (指令级别的单步运行)thread step-inst-over
简写:nexti、nithread step-inset
简写:stepi、sis
、n
是源码级别si
、ni
是汇编指令级别
断点
1 | breakpoint set 缩写 br s |
断点命令
1 | breakpoint command add 断点编号 # 给断点预习设置需要执行的命令,到触发断点时,就会按顺序执行 |
内存断点
在内存数据发生改变的时候触发,开发中可以设置内存断点,然后使用bt
查看调用栈
1 | watchpoint set variable 变量 |
模块查找
1 | image lookup # 模块查找 |
寄存器
memory read
读取所有寄存器的值
1 | memory read/[数量][格式][字节数] [内存地址] |
memory write 寄存器 值
给某个寄存器写入值
1 | memory write [内存地址] [数值] |
$+寄存器可直接访问对象
po $x0 打印方法调用者
x/s $x1 打印方法名
po $x2 打印参数 (以此类推,x3,x4也可能是参数)
如果是非arm64, 寄存器就是r0,r1,r2
小技巧
- 敲Enter,会自动执行上次的命令
- 绝大多数命令都可以使用缩写
动态调试其他App
给debugserver增加权限
- 默认情况下,/Developer/usr/bin/debugserver缺少一定的权限,只能调试Xcode安装的APP,无法调试其他APP(比如来自APP Store的APP)
- 如果希望调试其他APP,需要对debugserver重新签名,签上2个调试相关的权限
- get-task-allow
- task_for_pid-allow
- 将重签名的debugserver 放入 /usr/bin 目录
签名方法:
- 通过ldid签名
1 | ldid -e debugserver > debugserver.entitlements # 将debugserver权限导出 |
- 通过codesign签名
1 | 查看权限信息 |
让debugserver附加到某个APP进程
1 | 方式一 将debugserver附加在一个已启动的进程上 |
LLDB连接debugserver
- 启动LLDB
1 | lldb |
- 连接debugserver服务
1 | (lldb) process connect connect: //手机IP地址: debugserver服务端口号 |
- 使用LLDB的c命令让程序先继续执行
1 | (lldb) c |
- 接下来就可以用LLDB命令调试APP了
- 退出连接
1 | (lldb) process detach |
断点调试
因调试第三方程序时不能使用函数名打断点,只能使用函数地址打断点
1 | 因ASLR原因,需要先获取偏移地址 |
遇到的问题
lldb连接debugserver时报错 error: rejecting incoming connection from ::ffff:127.0.0.1 (expecting ::1)
1 | sirde-iPhone:~ root# debugserver_arm64 *:10011 -a neteasemusic |
解决办法
使用ip地址替换 * 和localhost
手机端:
1 | sirde-iPhone:~ root# debugserver 127.0.0.1:10011 -a neteasemusic |
电脑端:
1 | (lldb) process connect connect://127.0.0.1:10011 |
mac lldb提示ImportError: cannot import name _remove_dead_weakref
1 | ➜ ~ lldb |
查看是否安装了python和python@2
1 | brew list |
remove 多余的 python@2
1 | brew remove python@2 --ignore-dependencies |
debugserver 在 iOS 12遇到的坑
因iOS12为不完美越狱,开发中总会遇到各种困难 kill:9,可通过如下方法解决
从/Developer/usr/bin/debugserver取出debugserver (或使用其他工具复制到电脑)
1
2cp /Developer/usr/bin/debugserver /var/root/
scp root@ip:/var/root/debugserver ./对debugserver进行瘦身,抽取arm64架构二进制文件
1
lipo -thin arm64 debugserver -output debugserver_arm64
复制debugserver_arm64到手机usr/bin目录 (可使用其他工具复制)
1
scp debugserver_arm64 root@ip:/usr/bin/debugserver_arm64
用ldid对debugserver进行签名 (也可在在电脑中签名后复制到手机)
1
2// uncOver越狱会提供debugserver.xml在/usr/share/entitlements/debugserver.xml
ldid -S/usr/share/entitlements/debugserver.xml /usr/bin/debugserver_arm64使用inject命令 (貌似是添加信任)
1
inject /usr/bin/debugserver_arm64