写在前面
日常开发中经常会用到LLDB调试 ,可能用的最多的命令就是po ,而在逆向领域中根本不可能让你在代码中下断点调试,于是乎LLDB 就成了非常重要的手段
一、LLDB
LLDB (Low Lever Debug)的缩写,是默认内置于XCode的动态调试工具,它与LLVM编译器一起,存在于主窗口底部的控制台中,能够带给我们更丰富的流程控制和数据检测的调试功能
标准的LLDB提供了一组广泛的命令,旨在与老版本的GDB命令兼容。除了使用标准配置外,还可以很容易的自定义LLDB以满足实际需要
二、LLDB命令
1. LLDB语法
<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument[argument...]]
<command> (命令)和<subcommand> (子命令):LLDB调试命令的名称<action> :执行命令的操作<options> :命令选项<arguement> :命令的参数[] :表示命令是可选的,可以有也可以没有
比如breakpoint set -n test
command :breakpoint表示断点命令action :set表示设置断点option :-n表示根据方法name设置断点arguement :test表示方法名为test
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:834688868,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!
如果你正在面试,或者正准备跳槽不妨动动小手,添加一下咱们的交流群:834688868来获取一份详细的大厂面试资料 为你的跳槽加薪多一份保障
2. LLDB初级使用
LLDB命令都是在进入LLDB状态(运行状态下点击“暂停符号”)才能使用生效的 [图片上传失败…(image-6ff20f-1634645644917)]
- 查看断点列表
where 是断点所在处address 是断点地址option disable 断点被禁用
(lldb) breakpoint list
// 设置c函数的断点
(lldb) breakpoint set -n cMethod
// 设置oc函数的断点
(lldb) breakpoint set -n "[ViewController ocMethod1]"
复制代码
(lldb) breakpoint set -n "[ViewController ocMethod1]" -n "[ViewController ocMethod2]" -n "[ViewController ocMethod3]"
// 禁用
(lldb) breakpoint disable 1
// 启用
(lldb) breakpoint enable 1
// 禁用
(lldb) breakpoint disable 1.1
// 启用
(lldb) breakpoint enable 1.1
复制代码
(lldb) breakpoint delete
复制代码
- "删除"某一组断点(不能删除单个断点,只能禁用单个断点)
(lldb) breakpoint delete 1.1
复制代码
(lldb) breakpoint set --selector touchesBegan:withEvent:
复制代码
(lldb) breakpoint set --file ViewController.m --selector touchesBegan:withEvent:
复制代码
(lldb) breakpoint set -r ocMethod
复制代码
(lldb) c
复制代码
// 查看LLDB指令帮助
(lldb) breakpoint help
// 查看LLDB的breakpoint帮助
(lldb) breakpoint help
复制代码
3. LLDB初级使用小结
- 手动打下的断点也会出现在
LLDB 的断点列表中,但LLDB 的断点不会出现在Xcode的断点列表处 - 手动打下的断点可以通过单击/长按来禁用/取消断点,也可以通过
LLDB 命令来操作,但LLDB 下的断点只能通过LLDB 来操作 - 删除的断点虽然不在列表中,但是新增断点会从先前的断点之后排序
命令 | 意义 | 简写 |
---|
breakpoint list | 查看断点列表 | breakpoint l | breakpoint set -n cMethod | 设置单个断点 | b -n cMethod | breakpoint set -n “[ViewController ocMethod1]” | | | -n “[ViewController ocMethod2]” | 设置一组断点 | b -n “[ViewController ocMethod1]” | -n “[ViewController ocMethod2]” | | | breakpoint set --selector touchesBegan:withEvent: | 设置某一个方法的断点 | b -selector touchesBegan:withEvent: | breakpoint set --file ViewController.m --selector touchesBegan:withEvent: | 设置某文件下某一个方法的断点 | b -f ViewController.m --selector touchesBegan:withEvent: | breakpoint set -r ocMethod | 设置所有匹配方法名的断点 | b -r ocMethod | breakpoint enable 1 | 启用某一组断点 | breakpoint en 1 | breakpoint disable 1 | 禁用某一组断点 | breakpoint dis 1 | breakpoint enable 1.1 | 启用某一个断点 | breakpoint en 1.1 | breakpoint disable 1.1 | 禁用某一个断点 | breakpoint dis 1.1 | breakpoint delete | 删除所有断点 | breakpoint d | continue | 退出LLDB模式 | c | next | 单步运行,将子函数当做整体一起执行 | n | stpe | 单步运行,将子函数当做整体一起执行 | s |
关于简写:
b :breakpoint set l :list -n :--name -f :--file dis :disable en :enable
三、LLDB执行代码
前文中提到关于开发中最常用的po 指令,在LLDB 中并不是print -out 的意思,其实是expression --object-
p :作为expression 的缩写,意指表达执行的意思o :则是object 的缩写(通过help expression 查看指令)po :执行对象——输出该对象的信息
既然知道了p指令有执行代码的作用,接下来就来玩一下
四、函数调用栈
接下来在以下代码中进行调试,点击屏幕依次调用test 、test1 、test2 ,在test2 处打下断点
- (void)testWithStr:(NSString *)str {
NSLog(@"test");
[self test1WithStr:str];
}
- (void)test1WithStr:(NSString *)str {
NSLog(@"test1");
[self test2WithStr:str];
}
- (void)test2WithStr:(NSString *)str {
NSLog(@"test2");
NSLog(@"%@", str);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self testWithStr:@"test"];
}
复制代码
bt ——查看函数调用栈,能够看到函数调用的顺序
up/down ——上下挪动函数调用
frame select 数字 ——跳转函数调用(0表示当前方法,2表示2个方法前)
frame variable ——查看当前函数属性:内存地址、方法名、参数
thread return ——结束当前函数调用,跳转回上一个调用栈的下一步(当前是跳转回第30行代码,准备执行31行代码)
由此,可以尝试在调用中修改参数的值从而完成新值打印
up修改已经调用过的函数参数
- 使用
up 指令回到上一步,使用p str = @"F"; 进行修改
提前修改函数参数
- 过掉断点,新值被打印
总结:已经执行过的代码再怎么回滚也不能随意修改值,应当提前打下断点进行修改
五、内存断点
内存断点,顾名思义就是给内存下的断点
通过watchpoint set variable self->_p->_name 就可以给对应的属性内存下断点
- addr:内存地址
- size:所占内存字节
- state:当前是否可用
修改内存地址的值就会来到断点处(此时相当于KVO机制)同时也能看到函数调用栈
- 内存断点也可以通过
watchpoint list 来查看内存断电列表 - 也可以通过
watchpoint delete 进行删除
六、断点添加命令
类似于给程序添加脚本命令,断点执行也可以添加命令
- 通过
breakpoint command add 1 就可以给指定断点添加命令 - 通过
DONE 结束断点命令添加
断点到来时就会执行先前的命令了
七、target stop-hook
target stop-hook 是一个给所有断点添加命令的指令——在每次stop的时候去执行一些命令(只对breakpoint、watchpoint有效)
只要target stop-hook add 操作一番,任何断点都会触发添加的命令
单条命令可以简写成target stop-hook add -o "frame variable" (-o 表示单条命令)
同样的,也可以使用target stop-hook list 查看列表
target stop-hook disable 进行禁用
target stop-hook delete /undisplay 1 进行禁用
接下来介绍一个高阶的操作
复制代码
LLDB启动的时候就会去加载一个叫做.lldbinit 的文件,在/Users/用户名 目录下 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u9jXukQO-1634648238703)(https://upload-images.jianshu.io/upload_images/23620676-f2ca245c88fbd2c4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
如果没有的话可以通过vi .lldbinit 创建,然后在文件中添加命令target stop-hook add -o "frame variable"
重新启动运行Xcode.lldbinit 文件中的命令就会被加载,是个很方便的调试手段!
八、ASLR
ASLR (Address space layout randomization)全称叫做地址空间配置随机加载 ,是一种防范内存损坏漏洞被利用的计算机安全技术,在各大平台都有应用
ASLR 通过随机放置进程关键数据区域的地址空间来防止攻击者能可靠地跳转到内存的特定位置来利用函数
说得简单易懂些就是物理地址 = ASLR + 虚拟地址
接下来通过实例来了解一下这个家伙吧
- (void)fxTest {
NSLog(@"%s", __func__);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self fxTest];
}
复制代码
cmd+B 编译生成MachO文件 ,使用Hopper打开,找到fxTest 方法
此时拿到了“内存地址”就可以打下内存断点来进行调试了,其实0x1340 只是个虚拟地址——只是相对于MachO文件的偏移地址(前面的1表示空段,对地址没有任何影响)
刚才说了物理地址 = ASLR + 虚拟地址 ,那么此时的ASLR又是多少呢?
通过image list 就可以打印出来——0x000000010301e000 就是这个进程的ASLR (看过dlyd源码就知道每次运行这个ASLR值都不相同)
实际地址 = 0x000000010ae85000 + 0x1340 = 0x10AE86340
写在后面
LLDB这些指令如同Xcode的快捷键一般,不掌握也无妨,掌握了就是锦上添花,多了一种调试的思路,便能让你拉开与同行之间的差距
作者:我是好宝宝 链接:https://juejin.cn/post/6847902223926722573
|