1.什么是LLDB
LLDB(Low Lever Debug)是内置于XCode中的动态调试工具。标准的LLDB提供了一组广泛的命令,旨在与老版本的GDB命令兼容。除了使用标准配置外,还可以很容易地自定义LLDB以满足实际需要。
LLDB API官方文档
2.LLDB常用命令
2.1 设置断点
- 设置符号断点
breakpoint set -n 符号名 (缩写格式:break set -n 符号名 或 b set -n 符号名 或 b 符号名)
set:是子命令
-n: 是选项,是--name的缩写
使用:如下图所示,在执行到18行的断点的时候,我们添加一个test方法的符号断点,lldb会将所有类或文件中有符号test的方法或函数添加到断点,因此下面会有7个断点,当运行到test方法时,程序就会断到这个符号断点。
此外,我们也可以指定某个类,一次为这个类添加多个断点,这些断点就会成为一组,假设我们在ViewController.m中设置多个断点,其代码如下:
//ViewController.m中的代码如下
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self test];
}
- (void)test {
NSLog(@"这是一个test方法");
}
- (IBAction)startBtnClick:(UIButton *)sender {
NSLog(@"点击了开始按钮");
}
- (IBAction)pauseBtnClick:(UIButton *)sender {
NSLog(@"点击了暂停按钮");
}
- (IBAction)stopBtnClick:(UIButton *)sender {
NSLog(@"点击了停止按钮");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self test];
}
@end
设置多个断点,如下图所示:
设置成功后,我们点击各个按钮,看会不会来到断点,如下图所示:
- 查看断点列表
breakpoint list(缩写格式:break list)
使用:如下图所示,使用这个命令可以查看所有的断点及其信息。
- 删除断点
breakpoint delete 组号 (缩写格式:break delete 组号)
使用:如下图所示,使用这个命令可以删除一组断点。
注意:但是不能删除组中的某个断点,如果这么使用,会使这个断点禁用掉,如下图所示:
另外,还可以只用这个命令还可以删除所有的断点,只要使用的时候不写组号就可以了,如下图所示:
- 禁用断点
breakpoint disable 断点序号
(简写格式:break dis 断点序号)
使用:如下图所示,可以使用这个命令禁用组中的某个断点,禁用之后,程序就不会在断点的位置中止了。
也可以只用这个命令禁用一组断点,禁用之后,这些按钮的点击都不会进入断点了,如下图所示:
- 启用断点
breakpoint enable 断点序号
(简写格式:break en 断点序号)
使用:如下图所示,我们可以使用这个命令启用一组中的某个断点。
同样的,我们也可以使用这个命令启用一组断点,如下图所示:
-
删除多个断点之后又添加断点
根据上图我们发现,当删除多个断点,然后再添加断点的时候,这个新的断点的序号还是继续累加的。
-
为一个符号添加多个断点
虽然为一个符号打了多个断点,但执行的时候只会断住一次。
模糊设置断点
breakpoint set -r 符号的部分或全部名字(缩写格式:break set -r 符号名 或 b set -r 符号名)
使用:如下图所示,这个命令是将项目中包含-r后面的字符串的所有方法或者函数的符号都打上断点,所以会有很多。
- 完全按照符号名设置断点
breakpoint set --selector 符号完整名(缩写格式:break set --selctor 符号完整名 或 b set --selector 符号完整名)
使用:这个命令需要将符号的完整名字写上,然后遍历项目中的所有方法和函数,与上面符号一致的都会打上断点,如下图所示。
- 指定某个文件设置断点
指定文件的某个方法设置断点
breakpoint set --file 文件名 --selector 方法名
(简写格式:break set -f 文件名 --selector 方法名)
使用举例如下图所示:
指定某个文件的某一行
breakpoint set --file 文件名 --line 行号
(简写格式:break set -f 文件名 -l 行号)
使用举例如下图所示:
- breakpoint 帮助命令
如果想要了解更多关于breakpoint的使用可以使用如下命令,查看帮助
help breakpoint
2.2 代码执行命令expression
2.2.1 expression命令的作用
emsp;expression的简写格式就是p,它的意思就是执行跟在这个指令后面的代码语句,如下图所示:
2.2.2 po命令
po命令其实是expression加上-O选项,它的作用就是打印后面对象的description操作。
如下图所示:
2.2.3 使用expression调试程序
首先,创建一个有两个属性(name, age)的person的类,在ViewController.m中编写如下代码:
#import "ViewController.h"
#import "Person.h"
@interface ViewController ()
@property (nonatomic, strong) NSMutableArray<Person *> *persons;
@end
@implementation ViewController
- (NSMutableArray<Person *> *)persons {
if (!_persons) {
NSMutableArray *ps = [[NSMutableArray alloc] initWithCapacity:5];
_persons = ps;
}
return _persons;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self test];
Person *p1 = [[Person alloc] init];
p1.name = @"one";
p1.age = 18;
Person *p2 = [[Person alloc] init];
p2.name = @"two";
p2.age = 19;
Person *p3 = [[Person alloc] init];
p3.name = @"three";
p3.age = 20;
[self.persons addObject:p1];
[self.persons addObject:p2];
[self.persons addObject:p3];
}
- (void)test {
NSLog(@"这是一个test方法");
}
- (IBAction)startBtnClick:(UIButton *)sender {
NSLog(@"点击了开始按钮");
}
- (IBAction)pauseBtnClick:(UIButton *)sender {
NSLog(@"点击了暂停按钮");
}
- (IBAction)stopBtnClick:(UIButton *)sender {
NSLog(@"点击了停止按钮");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"%@", self.persons);
}
@end
然后运行程序,添加如下断点:
继续执行程序,点击屏幕
-
使用p命令在数组中添加新的Person对象,如下图所示:
-
使用p命令修改person中最后一个对象的数据,如下图所示:
或者如下设置:
按住option+回车可以换行执行多条命令,如下图所示:
2.3 堆栈信息
2.3.1 使用bt命令查看堆栈信息
运行程序,添加如下断点:
继续执行程序,点击屏幕
-
使用bt命令查看当前堆栈信息
-
使用up命令查看上一个堆栈信息,如下图所示:
-
使用down查看下一个堆栈信息,如下图所示:
- 使用frame selecte 标号查看特定标号的堆栈信息,如下图所示:
注意:这些回滚选择对当前的寄存器的环境是没有影响的
在ViewController.m文件中添加如下代码
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self test1:@"wuhuhu"];
}
- (void)test1:(NSString*)str {
[self test2:[str stringByAppendingString:@"1"]];
}
- (void)test2:(NSString *)str {
[self test3:[str stringByAppendingString:@"2"]];
}
- (void)test3:(NSString *)str {
[self test4:[str stringByAppendingString:@"3"]];
}
- (void)test4:(NSString *)str {
NSLog(@"test4 : %@", str);
}
运行程序,打上touchBegan方法的符号断点以及test4的符号断点,点击屏幕:
-
使用frame variable查看方法调用后传递的参数以及局部变量,如下图所示:
修改test1方法的参数str并查看执行结果,如下图所示:
我们可以发现使用堆栈回滚或选择命令后修改其参数的值,并不影响最终指向结果。 -
使用thread return命令直接执行方法或函数return语句,使用这个命令可以很方便我们在逆向开发的过程中测试别人写的代码是否是防护代码,我们可以通过这个命令直接跳到我们怀疑是方法代码的renturn语句,看程序执行的结果是否有影响而判断这个方法或函数是否是防护代码,然后使用fishhook或者inlinehook hook这个方法或函数以达到跳过防护代码的目的。使用如下图所示:
2.4 watchpoint命令
//修改touchBegan方法的代码,如下:
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
Person *p1 = self.persons.firstObject;
p1.name = @"xxxx";
}
-
使用watchpoint命令设置对象属性的符号断点
当我们点击屏幕改变这个对象的name属性值时就会触发这个断点,如下所示:
-
使用watchpoint命令设置对象属性的内存地址断点
当我们在逆向开发的时候,不知道这个符号的时候该怎么办呢?我们可以通过内存地址断点的方式,如下所示:
同样的,当我们点击屏幕的时候也会触发这个断点,如下所示:
-
使用watchpoint命令删除断点
如下所示:
其他操作与breakpoint类似。
2.4 其他命令
- 给一个断点添加执行命令(每当执行到断点的时候就会执行添加的命令)
break command add 断点序号 (可以是组中的也可以是组号)
命令内容
DONE
首先添加一个touchBegan断点
- 给所有断点都添加执行命令(每当执行到断点的时候就会执行添加的命令)
target stop-hook add -o "命令内容"
或者 display "命令内容"
或
或
点击屏幕
查看target stop-hook列表
target stop-hook list
删除target stop-hook命令
target stop-hook delete 序号
或者 undisplay 序号
2.5 在lldb开始启动的时候添加命令
-
打开根目录下的lldbinit文件(LLDB一旦启动就会去加载这个文件),添加命令,如下所示:
其中添加如下命令,保存并退出
执行程序,结果如下:
2.5 disassemble(反汇编)命令
2.5.1 查看当前断点所在方法的代码汇编
disassemble -f
简写格式:dis -f
2.5.2 查看某个方法的汇编代码
disassemble -n 方法名(或函数名)
简写格式:dis -n 方法名(或函数名)
2.5.3 查看指定内存地址的汇编代码
disassemble -a 内存地址
简写格式:dis -a 内存地址
2.5.4 查看断点当前行的汇编代码
disassemble -l
简写格式:dis -l
如果你想要查看更多有关disassemble命令的用法可以使用help disassemble命令查看。
2.6 其他常用命令
2.6.1 image list命令
这个命令是用来查看模块列表的,如下图所示:
2.6.2 register read/write
读取以及写入寄存器