相对于静态分析,静态分析只能分析静态的函数内部执行。想要动态获取程序在运行时的参数传递,执行流程及寄存器内存等信息,就需要使用动态调试的方法。
一、LLDB简介
LLDB全称 " Low Level Debugger ", 是由苹果出品,默认内置于Xcode中的动态调试工具,不但通吃C、C++、Objective-C ,还全盘支持OSX、iOS、以及iOS模拟器。
LLDB 既可以在本地调试 Mac 应用程序,也可以远程调试 iPhone 应用程序。
二、LLDB功能概括
- 在指定的条件下启动程序;
- 在指定的条件下停止程序;
- 在程序停止的时候检查程序内部发生的事;
- 在程序停止的时候对程序进行改动,观察程序执行过程有什么变化;
三、debugserver工具
需要有一点说明,LLDB是运行在OSX中的,要想调试iOS,还需要另一个工具的配合,它就是debugserver。
当使用 Xcode 调试手机 APP 时,Xcode 会将 debugserver 文件 复制到手机中,以便在手机上启动一个服务,等待 Xcode 进行远程调试。
debugserver 的作用就是它作为服务端,实际执行LLDB(客户端)传过来的命令,再把结果反馈给LLDB,然后显示给用户。
iOS设备上并没有安装debugserver,只有设备链接过一次xcode,并在Window ---- device菜单中添加此设备后,debugserver就会出现iOS设备/Developer/user/bin/目录下。
四、准备工作
1. 获取 debugserver 文件
通过爱思助手可以导出 debugserver 文件:/developer/usr/bin/debugserver
2. 签名权限 task_for_pid 添加
默认通过Xcode安装的debugserver只能调试我们自己的APP,因为缺少task_for_pic权限(这个权限和读取进程id,端口等相关的权限),所以我们可以手动给debugserver添加权限。
新建 entitlements.plist 文件,在其中写入如下内容。
<?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>
把 debugserver 和 entitlements.plist 放到同级目录,使用 codesign 对 debugserver 重新签名
codesign -s - --entitlements entitlements.plist -f debugserver
3. 通过爱思助手上传回去
通过爱思助手上传到手机 /usr/bin/debugserver
给 debugserver 添加 执行权限
chmod +x /usr/bin/debugserver
五、进行调试
转发端口
2222 端口用于 SSH
1234 端口用于 lldb 连接
iproxy 2222 22
iproxy 1234 1234
连接 SSH
ssh -p 2222 root@127.0.0.1
找到进程 ID
执行 debugserver,附加 snapchat 进程
debugserver *:1234 -a 1391
打开电脑终端,输入 lldb 进行连接
lldb
process connect connect://127.0.0.1:1234
六、lldb 命令
LLDB的所有命令在 LLVM官网 或者 Apple官网 都可以查询到。笔者会在这篇文章中列举一些比较常用的命令。
1、断点设置
命令名称 | 命令参样例 |
---|---|
使用名称设置断点 | breakpoint set --name main |
使用内存地址设置断点 | breakpoint -a 0xXXXXXXXX |
删除断点 | breakpoint delete 1 |
使断点失效/生效 | breakpoint disable/enable 2 |
查看所有断点 | breakpoint list |
OC中所有命名中包含为Test4的方法设置断点 | breakpoint set -r Test4 |
# 给所有名为xx的函数设置一个断点
breakpoint set —name xx
br s -n xx
b xx
# 在文件F指定行L设置断点
breakpoint set —file F —line L
br s -f F -l L
b F:L
# 当执行到该线程时,调试器才会断下来。
# 一段代码可能开了多线程,要在不同线程之间切来切去。这时就可以使用线程断点了
breakPoint set –f 文件名 –l 行号 –t 线程id
# 条件断点
breakpoint set -f viewController.m -l 362 -c "width > 68"
# 给所有名为xx的C++函数设置一个断点(希望没有同名的C函数)
breakpoint set —method xxx
br s -M xxx
# 给一个OC函数[objc msgSend:]设置一个断点
breakpoint set —name "-[objc msgSend:]"
b -n "-[objc msgSend:]"
# 给所有名为xx的OC方法设置一个断点(希望没有名为xx的C或者C++函数)
breakpoint set —selector xx
br s -S count
# 给所有函数名正则匹配成功的函数设置一个断点(留个正则熟练的同学尝试)
breakpoint set --func-regex regular-expression
br s -r regular-expression
# 给指定函数地址func_addr的位置设置一个断点
br set -a xxx
# 一次性断点
# -- 是命令选项结束符,如果没有选项,可以省略
breakpoint set --one-shot true --name "-[objc msgSend:]"
# 断点列表
breakpoint list
br l
# 断点删除
breakpoint delete xxx # index指明断点的序号,如果为空则删除所有断点
breakpoint delete xxx -f # 强制删除(不需要确认)
br del index
# 断点触发时自动执行下去
--auto-continue # 参数
(lldb) b CCCryptorCreate
Breakpoint 1: where = libcommonCrypto.dylib`CCCryptorCreate, address = 0x000000011047e1b7
(lldb) breakpoint modify --auto-continue true 1
(lldb) br list
Current breakpoints:
1: name = 'CCCryptorCreate', locations = 1, resolved = 1, hit count = 0 Options: enabled auto-continue
1.1: where = libcommonCrypto.dylib`CCCryptorCreate, address = 0x000000011047e1b7, resolved, hit count = 0
(lldb) breakpoint command add -s python 1
Enter your Python command(s). Type 'DONE' to end.
print "Hit this breakpoint!"
DONE
# 跳过次数
-i <count> (--ignore -count <count>)
breakpoint set -F viewDidLoad -i 3
-o <boolean>只断住一次
breakpoint set -F viewDidLoad: -o yes
2、断点命令
命令名称 | 命令参样例 |
---|---|
给某一个断点增加命令 | breakpoint command add 1 |
查看所有断点命令 | breakpoint command list |
删除断点命令 | breakpoint command delete 1 |
使某个断点命令生效/失效 | breakpoint command enable/disable |
给某一个断点增加命令 | breakpoint command delete |
3、查看堆栈,流程控制
查看当前所有堆栈 | bt |
---|---|
返回上一步堆栈 | up |
查看某一条堆栈 | frame select 1 |
查看当前堆栈的参数 | frame variable |
堆栈回滚到上一条 | thread return |
程序继续执行 | c |
单步下一步 | n |
进入下一个函数(方法) | s |
汇编级别的单步下一步 | ni |
汇编级别的进入下一个函数(方法) | si |
4、内存断点
命令名称 | 命令参样例 | 长长的标题3 | title 4 |
---|---|---|---|
直接观察一个变量 | watchpoint set variable global_var | ||
直接观察一个变量的地址 | watchpoint set expression -- 0xxxxxx | column 3 | |
删除断点 | watchpoint delete 1 | ||
使断点失效/生效 | watchpoint disable/enable 2 | ||
查看所有断点 | watchpoint list |
5、 库文件image
命令名称 | 命令参样例 |
---|---|
查看工程中使用的库(包括MachO自己) | image list |
查找可执行文件或共享库的原始地址 | image lookup --address 0x0000000100000de0 |
输出NSURL的成员变量及属性信息 | image lookup --type NSURL |
导出可执行文件和共享库的所有符号表 | image dump symtab |
6. HOOK每个断点
命令名称 | 命令参样例 |
---|---|
增加一个HOOK | target stop-hook add -o "frame variable" |
直接所有HOOK | target stop-hook list |
删除HOOK | target stop-hook disable 1 |
使HOOK失效/生效 | target stop-hook disable/enable 2 |