
在上篇文章中,我們講了Hook的原理分析,fishHook的簡(jiǎn)單使用,在使用的過程中,我們也詳細(xì)的了解了一下MachO文件的構(gòu)成,fishHook的執(zhí)行流程、使用方法等。
在我們玩逆向的時(shí)候在大多數(shù)時(shí)候其實(shí)是拿不到源碼的。所以了解一些LLDB來輔助我對(duì)別人APP的學(xué)(破)習(xí)(壞),是非常有必要的。
本篇文章,我們來了解下LLDB相關(guān)的內(nèi)容吧。。
今天的DEMO也比較簡(jiǎn)單,可以在點(diǎn)擊這里下載到:Demo7_LLDB
接下來本文會(huì)從以下幾點(diǎn)進(jìn)行闡述:
- LLDB簡(jiǎn)介
- LLDB斷點(diǎn)設(shè)置
- LLDB執(zhí)行代碼
- LLDB查看堆棧信息
- LLDB內(nèi)存斷點(diǎn)
- LLDB其它指令
- target-stop-hook
- 關(guān)于image和其它常用指令
LLDB 簡(jiǎn)介
LLDB(Low Lever Debug) 默認(rèn)內(nèi)置于Xcode中的動(dòng)態(tài)調(diào)試工具。標(biāo)準(zhǔn)的 LLDB 提供了一組廣泛的命令,旨在與老版本的 GDB 命令兼容。 除了使用標(biāo)準(zhǔn)配置外,還可以很容易地自定義 LLDB 以滿足實(shí)際需要。
默認(rèn)內(nèi)置于Xcode中的動(dòng)態(tài)調(diào)試工具。標(biāo)準(zhǔn)的 LLDB 提供了一組廣泛的命令,旨在與老版本的 GDB 命令兼容。 除了使用標(biāo)準(zhǔn)配置外,還可以很容易地自定義 LLDB 以滿足實(shí)際需要。
命令格式如下:
<command> [<subcommand> [<subcommand>...]] + <action> + [-options [option-value]] + [argument [argument...]]
<action>:我們想在前面的命令序列的上下文中執(zhí)行的一些操作。
<options>:行為修改器(action modifiers)。通常帶有一些值。
-
[]表示命令是可選的,可以有也可以沒有。 -
<command>(命令)和<subcommand>(子命令):LLDB調(diào)試命令的名稱。命令和子命令按層級(jí)結(jié)構(gòu)來排列:一個(gè)命令對(duì)象為跟隨其的子命令對(duì)象創(chuàng)建一個(gè)上下文,子命令又為其子命令創(chuàng)建一個(gè)上下文,依此類推。 -
<action>:我們想在前面的命令序列的上下文中執(zhí)行的一些操作。 -
<options>:行為修改器(action modifiers)。通常帶有一些值。 -
<argument>:根據(jù)使用的命令的上下文來表示各種不同的東西。
The full lldb command names are often long, but any unique short form can be used. Instead of "breakpoint set", "br se" is also acceptable.
一般lldb的命令會(huì)很長(zhǎng),但是只要能夠想出足夠斷,并且又能代表唯一性的縮寫,那么縮寫命令也是同一生效的如:breakpoint set==br se
LLDB的所有命令在LLVM官網(wǎng)或者Apple官網(wǎng) 都可以查詢到。筆者會(huì)在這篇文章中列舉一些比較常用的命令。
1. LLDB斷點(diǎn)設(shè)置
首先,我們先看一下官網(wǎng)關(guān)于LLDB的命令,下面我們提供關(guān)于部分添加斷點(diǎn)的命令,也是我們本段主要的內(nèi)容:

1.1 LLDB 命令設(shè)置斷點(diǎn)

breakpoint set -n test1(函數(shù)名稱)
注意: 我們項(xiàng)目運(yùn)行起來可以點(diǎn)擊下面按鈕進(jìn)入LLDB模式

1.2 LLDB 設(shè)置多個(gè)斷點(diǎn)

4.使用LLDB下載多個(gè)斷點(diǎn)
breakpoint set -n "[ViewController save:]" -n "[ViewController pauseGame:]" -n "[ViewController continueGame:]"
1.3 LLDB 設(shè)置斷點(diǎn)失效、啟用


// 使某個(gè)斷點(diǎn)失效
breakpoint disable 1
//啟用某個(gè)斷點(diǎn)
breakpoint enable 1
注意:
breakpoint disable 1 使當(dāng)前的這一組斷點(diǎn)全部失效。
breakpoint disable 1.1 使某一個(gè)斷點(diǎn)失效
使某一個(gè)斷點(diǎn)失效:

1.4 LLDB 刪除斷點(diǎn)


// delete 使用
breakpoint delete 1.1
breakpoint delete 1
注意:
breakpoint delete 1.1 是使某個(gè)斷點(diǎn)失效。
breakpoint delete 1 是刪除第一組的數(shù)據(jù)。刪除只能刪除指定的組。
breakpoint delete 刪除所有的斷點(diǎn)。
1.5 LLDB 給某一個(gè)函數(shù)設(shè)置斷點(diǎn)(批量設(shè)置斷點(diǎn))

// 給某一個(gè)函數(shù)設(shè)置斷點(diǎn)(批量設(shè)置斷點(diǎn))
breakpoint set --select touchesBegan:withEvent:
1.6 LLDB 根據(jù)某個(gè)字符串設(shè)置斷點(diǎn)

// 遍歷文件/系統(tǒng)中含有Game的函數(shù)設(shè)置斷點(diǎn)
breakpoint set -r Game:
1.7 LLDB 根據(jù)某個(gè)字符串設(shè)置斷點(diǎn)
// 給某一個(gè)文件中的某一方法設(shè)置斷點(diǎn)
breakpoint set --file ViewController.m -select touchesBegan:withEvent:
1.8 設(shè)置斷點(diǎn)命令總結(jié)
| 命令名稱 | 命令參樣例 | |||
|---|---|---|---|---|
| 使用名稱設(shè)置斷點(diǎn) | breakpoint set --name test1(函數(shù)名) | |||
| 使用內(nèi)存地址設(shè)置斷點(diǎn) | breakpoint -a 0xXXXXXXXX | 刪除斷點(diǎn) | breakpoint delete 1 | |
| 使斷點(diǎn)失效/生效 | breakpoint disable/enable 2 | |||
| 查看所有斷點(diǎn) | breakpoint list | |||
| OC中所有命名中包含為Test4的方法設(shè)置斷點(diǎn) | breakpoint set -r Test4 | |||
| 下載多個(gè)斷點(diǎn) | breakpoint set -n "[ViewController save:]" -n "[ViewController pauseGame:]" -n "[ViewController continueGame:]" | |||
| 使某個(gè)斷點(diǎn)啟用/失效 |
breakpoint enable 1 breakpoint disable 1注意:breakpoint disable 1 會(huì)使當(dāng)前的這一組斷點(diǎn)全部失效,使用breakpoint disable 1.1 使某一個(gè)斷點(diǎn)失效 |
|||
| 刪除斷點(diǎn) |
breakpoint delete 1.1 breakpoint delete 1注意:breakpoint delete 1.1 是使某個(gè)斷點(diǎn)失效,而breakpoint delete 1 是刪除第一組的數(shù)據(jù)。刪除只能刪除指定的組 |
|||
| 查看幫助命令 | help breakpoint | |||
| 給所有同名的函數(shù)設(shè)置斷點(diǎn) | breakpoint set --select touchesBegan:withEvent: | |||
| 清空所有斷點(diǎn) | breakpoint delete | |||
| 給某一個(gè)文件中的某一方法設(shè)置斷點(diǎn) | breakpoint set --file ViewController.m -select touchesBegan:withEvent: | |||
| 遍歷文件/系統(tǒng)中含有Game | breakpoint set -r Game: |
2. LLDB執(zhí)行代碼
上一節(jié)中我們知道了LLDB怎么去設(shè)置斷點(diǎn),那么這一小節(jié)我們來看一下LLDB怎么執(zhí)行代碼吧。
準(zhǔn)備工作:
- 1.創(chuàng)建Person類,添加name、age屬性
- 2.在viewDidLoad中將數(shù)據(jù)添加到數(shù)組中。
// 大致代碼如下:
#import "Person.h"
@interface ViewController ()
@property (nonatomic,strong) NSMutableArray <Person*>*models;
@end
@implementation ViewController
- (NSMutableArray<Person *> *)models{
if (!_models) {
_models = [NSMutableArray array];
}
return _models;
}
void test1(){
NSLog(@"test1: %d",3);
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
Person *p1 = [[Person alloc] init];
p1.name = @"p1";
p1.age = 11;
Person *p2 = [[Person alloc] init];
p2.name = @"p2";
p2.age = 12;
Person *p3 = [[Person alloc] init];
p3.name = @"p3";
p3.age = 13;
[self.models addObject:p1];
[self.models addObject:p2];
[self.models addObject:p3];
}
2.1 使用LLDB命令向models中添加一個(gè)數(shù)據(jù)
// 添加數(shù)據(jù)
p [self.models addObject:[[Person alloc] init]]
// 查看數(shù)據(jù)
po self.models

2.2 使用LLDB命令給數(shù)據(jù)設(shè)值
// 獲取數(shù)據(jù)(拿到最后一個(gè)數(shù)據(jù))
p (Person *)self.models.lastObject
// 設(shè)值(上一步會(huì)返回一個(gè)標(biāo)記,我們使用這個(gè)標(biāo)記就好了,類似于動(dòng)態(tài)對(duì)象,如下圖)

2.3 使用LLDB命令查看數(shù)據(jù)值

1.先獲取數(shù)據(jù)
2.讀取數(shù)據(jù)
2.4 使用LLDB執(zhí)行代碼

1.打開LLDB
2.mac 快捷鍵 option+回車,在面板中寫代碼
3.p Person *p4 = [[Person alloc] init];
p4.name = @"p4";
p4.age = 14;
[self.models addObject:p4]
2.1 LLDB動(dòng)態(tài)運(yùn)行代碼總結(jié)
| 命令名稱 | 命令例子 |
|---|---|
| 插入數(shù)據(jù) | 如上 2.1 |
| 給數(shù)據(jù)設(shè)值 | 如上 2.2 |
| 查看數(shù)據(jù) | 如上 2.3 |
| LLDB執(zhí)行代碼 | 如上 2.4 |
3. LLDB查看堆棧信息
| 命令名稱 | 命令例子 |
|---|---|
| 查看當(dāng)前所有堆棧 | bt |
| 返回上一步堆棧 | up |
| 執(zhí)行下一步堆棧 | down |
| 查看某一條堆棧 | frame select 1 |
| 查看當(dāng)前堆棧的參數(shù) | frame variable |
| 堆棧回滾到上一條 | thread return |
| 程序繼續(xù)執(zhí)行 | c |
| 單步下一步 | n |
| 進(jìn)入下一個(gè)函數(shù)(方法) | s |
| 匯編級(jí)別的單步下一步 | ni |
| 匯編級(jí)別的進(jìn)入下一個(gè)函數(shù)(方法) | si |
下面出示例圖:
LLDB_bt堆棧

LLDB 選擇堆棧,查看參數(shù)

LLDB 修改堆棧函數(shù)的參數(shù)

注意:
- 當(dāng)我們使用frame查看指定堆棧,修改函數(shù)的參數(shù)的時(shí)候
frame 修改參數(shù)
frame select 1 // 切換堆棧
frame variable // 修改查看參數(shù)
p str = @"123" // 設(shè)置新參數(shù)
frame variable // 查看新設(shè)置的參數(shù)
如果后面有輸出語句,參數(shù)依然是輸出之前的參數(shù),這是因?yàn)槲覀兇a已經(jīng)走到這里了,函數(shù)調(diào)用關(guān)系已經(jīng)確定。
- 回滾指令 thread return
1.回滾指令之后函數(shù)不會(huì)再往下執(zhí)行。
2.函數(shù)直接返回,不會(huì)再執(zhí)行斷點(diǎn)之后的代碼
3.up指令不會(huì)改變其執(zhí)行的流程
- si、ni 可以使用 control + 鼠標(biāo)點(diǎn)擊指令按鈕查看
4. LLDB內(nèi)存斷點(diǎn)
某個(gè)屬性地址只要有改變,就觸發(fā)斷點(diǎn)。相當(dāng)于對(duì)某個(gè)屬性設(shè)置了KVO。
| 命令名稱 | 命令例子 |
|---|---|
| 直接觀察一個(gè)變量 | watchpoint set variable global_var |
| 直接觀察一個(gè)變量的地址 | watchpoint set expression -- 0xxxxxx |
| 刪除斷點(diǎn) | watchpoint delete 1 |
| 使斷點(diǎn)失效/生效 | watchpoint disable/enable 2 |
| 查看所有內(nèi)存斷點(diǎn) | watchpoint list |
// 例子
1.touchesBegan 添加代碼
Person *p1 = self.models.firstObject;
p1.name = @"hello";
2.再將數(shù)據(jù)添加到模型的時(shí)候設(shè)置斷點(diǎn),當(dāng)p1賦值完成后LLDB執(zhí)行以下代碼。
watchpoint set variable p1->_name
3. 斷點(diǎn)執(zhí)行下一步,查看(查看所有內(nèi)存斷點(diǎn) watchpoint list)內(nèi)存斷點(diǎn)

5. LLDB command命令
LLDB 添加 command 指令
1.添加2個(gè)斷點(diǎn),當(dāng)程序走到斷點(diǎn)時(shí),我們執(zhí)行一些其它的命令,第一組屏幕點(diǎn)擊,第二組給某個(gè)方法設(shè)置指令
2.breakpoint 執(zhí)行command命令
breakpoint command add 2(表示第幾組)
Enter your debugger command(s). Type 'DONE' to end.
> po self
> p self.view.subViews
> DONE
// 也可以添加、執(zhí)行刪除命令
breakpoint command delete 2
如下圖:

| 命令名稱 | 命令例子 |
|---|---|
| breakpoint 添加指令 | breakpoint command add 2(表示第幾組) |
| breakpoint 刪除指令 | breakpoint command delete 2(表示第幾組) |
6. target stop-hook 和 .lldbinit的配置
target stop-hook含義:給所有的斷點(diǎn)添加參數(shù),執(zhí)行某些命令。
// 不管你斷住了哪一個(gè)方法,我都會(huì)把這個(gè)方法的參數(shù)打印出來 -o 表示添加一條指令
target stop-hook add -o "frame variable"
// 刪除
target stop-hook delete
// 查看列表
target stop-hook list

以下是部分命令
| 命令名稱 | 命令例子 |
|---|---|
| 增加一個(gè)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 |
由于每次使用的斷點(diǎn)比較多,并且都是執(zhí)行相同的代碼,這時(shí)我們就可以把這部分代碼封裝起來,寫成一個(gè)文本文件夾,每次執(zhí)行默認(rèn)加載
.lldbinit的配置
1. .lldbinit 是在當(dāng)前用戶的家目錄下,為隱藏文件。
2. .lldbinit 是隱藏文件,默認(rèn)看不見,需要使用 command+shift+. 組合鍵顯示文件。
3. 如果之前沒有使用過,則沒有.lldbinit文件,我們需要使用vim .lldbinit 創(chuàng)建文件。
4. 在 文件中 添加以下命令:target stop-hook add -o "frame variable",保存退出。
5. 使用 cat .lldbinit 查看 .lldbinit文件的內(nèi)容。
6. 執(zhí)行Xcode代碼,會(huì)發(fā)現(xiàn)有斷點(diǎn)的地方都執(zhí)行了一下 “target stop-hook add -o "frame variable""這個(gè)命令
7. 關(guān)于image和其它常用指令
| 命令名稱 | 命令例子 |
|---|---|
| 查看工程中使用的庫(kù)(包括MachO自己) | image list |
| 查找可執(zhí)行文件或共享庫(kù)的原始地址 | image lookup --address 0x0000000100000de0 |
| 輸出NSURL的成員變量及屬性信息。 | image lookup --type NSURL |
| 導(dǎo)出可執(zhí)行文件和共享庫(kù)的所有符號(hào)表 | image dump symtab |
8. 總結(jié)&Demo
本篇文章,我們講了LLDB的一些常用的語法,和一些命令,
這片文章的內(nèi)容其實(shí)非常簡(jiǎn)單,首先介紹了一下LLDB的一下基本用法,有.lldbinit文件可以幫我們自動(dòng)加載腳本,所有就有了一個(gè)簡(jiǎn)單的LLDB腳本案例,
參考代碼:LLDBDemo
參考:
作者:一縷清風(fēng)揚(yáng)萬里
原文地址:http://m.itdecent.cn/p/fd10bb8b89d6
LLDB調(diào)試器使用簡(jiǎn)介