也许我们都一样
总是想要活成什么样
但是生活
总是告诉我们
应该怎么活
接下来上今天的干货LLDB调试部分。日常的正向开发,Xcode提供了多种快捷键以及快捷方式方便我们开发者进行LLDB调试,所以其中的很多操作我们都相当熟悉了;但是这里需要说明的是,逆向开发所有的符号断点都是无效的,你只能对着MachO文件摸索着调试,更不存在像正向开发那样,把断点断到指定类的某一行的界面快捷操作,逆向开发主要使用的就是内存断点,所以逆向开发熟练掌握LLDB调试快捷指令是非常有必要的。下面是干货部分内容:
- 简单了解LLDB及LLDB断点设置
- LLDB执行代码
- 查看函数调用栈
- 内存断点
- 断点添加command
- target stop-hook
一、简单了解LLDB及LLDB断点设置
1、了解LLDB
LLDB(Low Level Debug)是默认内置于Xcode中的轻量级动态调试工具。标准的LLDB提供了一组广泛的命令,旨在与老版本的GDB命令兼容。除了使用标准配置外,还可以很容易地自定义LLDB以满足实际需要。
2、LLDB断点设置命令
- 设置断点
$breakpoint set -n XXX (set 是子命令, -n是选项,是--name的缩写)
$b -n XXX 是上面的缩写,breakpoint 可以缩写成break,部分情况下直接缩写成b也是可以的
$breakpoint set --selector XXX(此处XXX详细解释:如果设置函数断点,这里就是函数或者方法名;如果方法或者函数有入参,需要加上冒号,比如 viewDidLoad)
$breakpoint set --file XXX (此处XXX为文件名,比如VC.m, 这几种命令可以组合使用,可以用来指定具体的某个文件中的某个方法,来指定断点的具体位置)
$b -f 是上面的缩写
-
查看断点列表
- $breakpoint list
- $break li ,breakpoint li , break list 都是上面的缩写
-
删除断点
- $breakpoint delete 组号(这个组号是从断点列表中查询得到的)
-
启用/禁用断点
- $breakpoint disable 禁用 (简写 break dis)
- $breakpoint enable 启用 (简写 break en)
-
遍历整个项目中满足Test:这个字符的所有方法,-r后面跟的就是需要满足的条件
- $breakpoint set -r Test:
-
继续执行
- $continue 简写 c
-
单步运行,将子函数当做整体一步执行
- $next 简写 n
-
单步运行,遇到子函数会进去
- $s
-
stop-hook
- 让你在每次stop的时候去执行一些命令,针对breakpoint 和 watchpoint
-
其他命令
- image list
- p (expression的简写)
- b -[xxx xxx]
- x
- register read
- po (expression -O的简写, 其中 -O代表Object description,就是输出对象的description方法,相当于NSLog)
- help(help可以查看所有指令意思,也可以help+xxx查看指定LLDB指令的意思)
3、LLDB设置断点后的信息解释
(lldb) breakpoint set -n test1
Breakpoint 1: where = LogicDemo1`-[ViewController viewDidLoad] + 41 [inlined] test1 at ViewController.m:26, address = 0x00000001051227ee
详细解释就是
Breakpoint 1 -> 代表断点第1组,断点的组数,一组里面可以有多个断点
where = LogicDemo1`-[ViewController viewDidLoad] + 41 [inlined] ->代表断点断在ViewController的方法viewDidLoad中
test1 at ViewController.m:26 ->代表断点断在ViewController.m文件中的第26行
address = 0x00000001051227ee ->地址是0x00000001051227ee
如果我们想要设置方法断点,比如断在ViewDidLoad中,可以设置如下
$breakpoint set -n "-[ViewController viewDidLoad]"
在一组断点中一次性设置多个断点可以这样,函数有入参的记得加上冒号':'哦
$breakpoint set -n "-[ViewController viewDidLoad]" -n "-[ViewController viewWillAppear:]"
下完断点的结果解释:
(lldb) breakpoint set -n "-[ViewController viewDidLoad]" -n "-[ViewController viewWillAppear:]"
Breakpoint 1: 2 locations.
- Breakpoint 1 -> 代表的是下了1组断点
- 2 locations -> 代表的是这1组包含2个断点
接下来我们查一下断点列表
(lldb) breakpoint list
Current breakpoints:
1: names = {'-[ViewController viewDidLoad]', '-[ViewController viewWillAppear:]'}, locations = 2, resolved = 2, hit count = 0
1.1: where = LogicDemo1`-[ViewController viewDidLoad] + 12 at ViewController.m:23:5, address = 0x000000010f075786, resolved, hit count = 0
1.2: where = LogicDemo1`-[ViewController viewWillAppear:] + 12 at ViewController.m:32:5, address = 0x000000010f0757df, resolved, hit count = 0
(lldb)
这里注意组头部分
1: names = {'-[ViewController viewDidLoad]', '-[ViewController viewWillAppear:]'}, locations = 2, resolved = 2, hit count = 0
在禁用断点后,不管是组还是组中的元素,再被禁用后再查看断点信息,后面会多出来一个Options: disabled,如下
(lldb) breakpoint disable 1
1 breakpoints disabled.
(lldb) breakpoint list
Current breakpoints:
1: names = {'-[ViewController viewDidLoad]', '-[ViewController viewWillAppear:]'}, locations = 2 Options: disabled
1.1: where = LogicDemo1`-[ViewController viewDidLoad] + 12 at ViewController.m:23:5, address = 0x000000010f075786, unresolved, hit count = 0
1.2: where = LogicDemo1`-[ViewController viewWillAppear:] + 12 at ViewController.m:32:5, address = 0x000000010f0757df, unresolved, hit count = 0
比如你只想禁用第一组的第一个,可以这样设置,结果同理
(lldb) breakpoint disable 1.1
1 breakpoints disabled.
(lldb) breakpoint list
Current breakpoints:
1: names = {'-[ViewController viewDidLoad]', '-[ViewController viewWillAppear:]'}, locations = 2, resolved = 1, hit count = 0
1.1: where = LogicDemo1`-[ViewController viewDidLoad] + 12 at ViewController.m:23:5, address = 0x000000010f075786, unresolved, hit count = 0 Options: disabled
1.2: where = LogicDemo1`-[ViewController viewWillAppear:] + 12 at ViewController.m:32:5, address = 0x000000010f0757df, resolved, hit count = 0
使用help查看po指令的意思
(lldb) help po
Evaluate an expression on the current thread. Displays any returned value
with formatting controlled by the type's author. Expects 'raw' input (see
'help raw-input'.)
Syntax: po <expr>
Command Options Usage:
po <expr>
'po' is an abbreviation for 'expression -O --'
可以很清楚的看出po的意思就是 expression -O 的简写;
二、LLDB执行代码
LLDB的 p 指令是可以执行代码的。在断点中执行修改背景色的代码,然后 c 指令执行
(lldb) p self.view.backgroundColor = UIColor.redColor;
(UICachedDeviceRGBColor *) $0 = 0x0000600000065400
(lldb) c
2019-10-25 23:57:39.174041+0800 LogicDemo1[30217:3304533] HELLO WORLD
Process 30217 resuming
可以看出视图的背景色就变成了红色;这样就可以实时调试执行代码了
比如往数据源中添加一个view
(lldb) po UIView * view = [[UIView alloc] init]; [self.dataArray addObject:view];
(lldb) c
2019-10-26 00:11:27.500407+0800 LogicDemo1[30390:3320236] HELLO WORLD
2019-10-26 00:11:27.500528+0800 LogicDemo1[30390:3320448] XPC connection interrupted
Process 30390 resuming
(lldb) po self.dataArray
<__NSArrayM 0x6000012f3090>(
<UIView: 0x7fe41d50bd50; frame = (0 0; 0 0); layer = <CALayer: 0x600001c94d40>>
)
(lldb)
三、查看函数调用栈
先来介绍一些指令
- bt 查看函数调用栈
- frame select X 查看栈中的指定组,比如frame select 1
- frame variable 查看变量
- $thread return 回滚到上面一步操作,然后直接return,不会再执行后面的代码了
实战演练
(lldb) frame select 0
frame #0: 0x000000010dddb6f7 LogicDemo1`-[ViewController setup1withStr:](self=0x00007fcd0af031b0, _cmd="setup1withStr:", str=@"1") at ViewController.m:43:5 [opt]
40 [self setup1withStr:@"1"];
41 }
42 - (void)setup1withStr:(NSString *)str{
-> 43 [self setup2withStr:str];
^
44 }
45 - (void)setup2withStr:(NSString *)str{
46 [self setup3withStr:str];
(lldb) frame variable
(ViewController *) self = 0x00007fcd0af031b0
(SEL) _cmd = "setup1withStr:"
(__NSCFConstantString *) str = 0x000000010dddd098 @"1"
(lldb) p str = @"666";
(NSTaggedPointerString *) $0 = 0xbcec7f4581cd17f6 @"666"
(lldb) frame variable
(ViewController *) self = 0x00007fcd0af031b0
(SEL) _cmd = "setup1withStr:"
(NSTaggedPointerString *) str = 0xbcec7f4581cd17f6 @"666"
(lldb)
四、内存断点
以上所有设置断点方式只针对正向开发,逆向的时候只有一个MachO文件是没有任何符号的,所以上面所说的断点方式在逆向中全部无效。
逆向中使用的是内存断点:
- $watchpoint
- $watchpoint set expression XXX(此处XXX是0x开头的内存地址,因为逆向一般是拿不到变量名的)
这个和breakpoint的使用方式类似
比如VC里面有这样依据代码
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.person = [[Person alloc] init];
self.person.name = @"111";
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
self.person.name = @"666";
}
使用内存断点能获取到Person对象的name属性的改变,并且能拿到新值和旧值
(lldb) watchpoint set variable self->_person->_name
Watchpoint created: Watchpoint 1: addr = 0x60000255c638 size = 8 state = enabled type = w
watchpoint spec = 'self->_person->_name'
new value: 0x000000010138c0b8
Watchpoint 1 hit:
old value: 0x000000010138c0b8
new value: 0x000000010138c0d8
(lldb) po (NSString *)0x000000010138c0b8
111
(lldb) po (NSString *)0x000000010138c0d8
666
(lldb)
使用bt命令查看完整的触发调用堆栈信息,可以看出是touchBegin触发的。
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = watchpoint 1
* frame #0: 0x00007fff503c6bae libobjc.A.dylib`objc_setProperty_nonatomic_copy + 44
frame #1: 0x000000010138a4c9 LogicDemo1`-[ViewController touchesBegan:withEvent:](self=<unavailable>, _cmd=<unavailable>, touches=<unavailable>, event=<unavailable>) at ViewController.m:44:17 [opt]
...
frame #17: 0x00007fff5123bcf5 libdyld.dylib`start + 1
frame #18: 0x00007fff5123bcf5 libdyld.dylib`start + 1
(lldb)
五、断点添加command
给断点添加command的命令是:
- $breakpoint list
- $breakpoint command add X (X是断点的组号)
例如:
(lldb) breakpoint list
Current breakpoints:
1: file = '/Users/fightmaster/Desktop/我爱学习/iOS进阶作业/3、安全攻防作业文件夹/6、Mach-O/20191016-应用安全-第六讲-MachO/006--MachO文件/备课代码/LogicDemo1/LogicDemo1/ViewController.m', line = 36, exact_match = 0, locations = 1, resolved = 1, hit count = 1
1.1: where = LogicDemo1`-[ViewController touchesBegan:withEvent:] + 7 at ViewController.m:36:10, address = 0x000000010c98f4b5, resolved, hit count = 1
(lldb) breakpoint command add 1
Enter your debugger command(s). Type 'DONE' to end.
> po self
> po self.view
> DONE
2019-10-26 01:14:42.697650+0800 LogicDemo1[31167:3387465] XPC connection interrupted
po self
<ViewController: 0x7ffec1e08200>
po self.view
<UIView: 0x7ffec1c081e0; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x60000063b700>>
(lldb)
其中需要注意的是输入DONE就可以结束命令
Enter your debugger command(s). Type 'DONE' to end.
> po self
> po self.view
> DONE
六、target stop-hook
除了使用command还可以使用
- target stop-hook
指令,这个和command差不多,并且这个是全局断点!
target stop-hook 命令用法和breakpoint一样,删除命令就是target stop-hook delete等等,查列表target stop-hook list等等~
(lldb) target stop-hook add -o "frame variable"
Stop hook #1 added.
(lldb) c
Process 31167 resuming
po self
<ViewController: 0x7ffec1e08200>
po self.view
<UIView: 0x7ffec1c081e0; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x60000063b700>>
由于每次Xcode启动,都会加载.lldbinit文件,所以这些全局命令可以直接写在这个文件里面,如果没有这个文件,可以自己创建一个同名的,这样以来,相当于只要在这个电脑上都可以使用,一劳永逸哦~
进入隐藏文件夹,然后创建或者编辑这个文件内容就可以全局配置了!
$ls -a
$vi .lldbinit
好了,今天的LLDB就到这里睡了睡了狗命要紧
-END-