也許我們都一樣
總是想要活成什么樣
但是生活
總是告訴我們
應(yīng)該怎么活

接下來上今天的干貨LLDB調(diào)試部分。日常的正向開發(fā),Xcode提供了多種快捷鍵以及快捷方式方便我們開發(fā)者進行LLDB調(diào)試,所以其中的很多操作我們都相當(dāng)熟悉了;但是這里需要說明的是,逆向開發(fā)所有的符號斷點都是無效的,你只能對著MachO文件摸索著調(diào)試,更不存在像正向開發(fā)那樣,把斷點斷到指定類的某一行的界面快捷操作,逆向開發(fā)主要使用的就是內(nèi)存斷點,所以逆向開發(fā)熟練掌握LLDB調(diào)試快捷指令是非常有必要的。下面是干貨部分內(nèi)容:
- 簡單了解LLDB及LLDB斷點設(shè)置
- LLDB執(zhí)行代碼
- 查看函數(shù)調(diào)用棧
- 內(nèi)存斷點
- 斷點添加command
- target stop-hook
一、簡單了解LLDB及LLDB斷點設(shè)置
1、了解LLDB
LLDB(Low Level Debug)是默認內(nèi)置于Xcode中的輕量級動態(tài)調(diào)試工具。標準的LLDB提供了一組廣泛的命令,旨在與老版本的GDB命令兼容。除了使用標準配置外,還可以很容易地自定義LLDB以滿足實際需要。
2、LLDB斷點設(shè)置命令
- 設(shè)置斷點
$breakpoint set -n XXX (set 是子命令, -n是選項,是--name的縮寫)
$b -n XXX 是上面的縮寫,breakpoint 可以縮寫成break,部分情況下直接縮寫成b也是可以的
$breakpoint set --selector XXX(此處XXX詳細解釋:如果設(shè)置函數(shù)斷點,這里就是函數(shù)或者方法名;如果方法或者函數(shù)有入?yún)ⅲ枰由厦疤?,比?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:
-
繼續(xù)執(zhí)行
- $continue 簡寫 c
-
單步運行,將子函數(shù)當(dāng)做整體一步執(zhí)行
- $next 簡寫 n
-
單步運行,遇到子函數(shù)會進去
- $s
-
stop-hook
- 讓你在每次stop的時候去執(zhí)行一些命令,針對breakpoint 和 watchpoint
-
其他命令
- image list
- p (expression的簡寫)
- b -[xxx xxx]
- x
- register read
- po (expression -O的簡寫, 其中 -O代表Object description,就是輸出對象的description方法,相當(dāng)于NSLog)
- help(help可以查看所有指令意思,也可以help+xxx查看指定LLDB指令的意思)
3、LLDB設(shè)置斷點后的信息解釋
(lldb) breakpoint set -n test1
Breakpoint 1: where = LogicDemo1`-[ViewController viewDidLoad] + 41 [inlined] test1 at ViewController.m:26, address = 0x00000001051227ee
詳細解釋就是
Breakpoint 1 -> 代表斷點第1組,斷點的組數(shù),一組里面可以有多個斷點
where = LogicDemo1`-[ViewController viewDidLoad] + 41 [inlined] ->代表斷點斷在ViewController的方法viewDidLoad中
test1 at ViewController.m:26 ->代表斷點斷在ViewController.m文件中的第26行
address = 0x00000001051227ee ->地址是0x00000001051227ee
如果我們想要設(shè)置方法斷點,比如斷在ViewDidLoad中,可以設(shè)置如下
$breakpoint set -n "-[ViewController viewDidLoad]"
在一組斷點中一次性設(shè)置多個斷點可以這樣,函數(shù)有入?yún)⒌挠浀眉由厦疤?:'哦
$breakpoint set -n "-[ViewController viewDidLoad]" -n "-[ViewController viewWillAppear:]"
下完斷點的結(jié)果解釋:
(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
比如你只想禁用第一組的第一個,可以這樣設(shè)置,結(jié)果同理
(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執(zhí)行代碼
LLDB的 p 指令是可以執(zhí)行代碼的。在斷點中執(zhí)行修改背景色的代碼,然后 c 指令執(zhí)行
(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
可以看出視圖的背景色就變成了紅色;這樣就可以實時調(diào)試執(zhí)行代碼了
比如往數(shù)據(jù)源中添加一個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)
三、查看函數(shù)調(diào)用棧
先來介紹一些指令
- bt 查看函數(shù)調(diào)用棧
- frame select X 查看棧中的指定組,比如frame select 1
- frame variable 查看變量
- $thread return 回滾到上面一步操作,然后直接return,不會再執(zhí)行后面的代碼了
實戰(zhàn)演練
(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)
四、內(nèi)存斷點
以上所有設(shè)置斷點方式只針對正向開發(fā),逆向的時候只有一個MachO文件是沒有任何符號的,所以上面所說的斷點方式在逆向中全部無效。
逆向中使用的是內(nèi)存斷點:
- $watchpoint
- $watchpoint set expression XXX(此處XXX是0x開頭的內(nèi)存地址,因為逆向一般是拿不到變量名的)
這個和breakpoint的使用方式類似
比如VC里面有這樣依據(jù)代碼
- (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";
}
使用內(nèi)存斷點能獲取到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命令查看完整的觸發(fā)調(diào)用堆棧信息,可以看出是touchBegin觸發(fā)的。
(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/我愛學(xué)習(xí)/iOS進階作業(yè)/3、安全攻防作業(yè)文件夾/6、Mach-O/20191016-應(yīng)用安全-第六講-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就可以結(jié)束命令
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文件,所以這些全局命令可以直接寫在這個文件里面,如果沒有這個文件,可以自己創(chuàng)建一個同名的,這樣以來,相當(dāng)于只要在這個電腦上都可以使用,一勞永逸哦~
進入隱藏文件夾,然后創(chuàng)建或者編輯這個文件內(nèi)容就可以全局配置了!
$ls -a
$vi .lldbinit
好了,今天的LLDB就到這里睡了睡了狗命要緊
-END-