iOS 逆向 - LLDB

前言

在進入逆向正式實戰以前 , 動態調試和靜態分析都是咱們必不可少的能力 .面試

LLDB 是無論在正向開發仍是逆向開發中 , 都是幫助咱們調試必不可少的手段 . 而在逆向開發中不能像正向開發同樣頁面斷點 , 可視化數據展現 , 源代碼調試等方式的狀況下 , LLDB 的做用就會尤爲重要 .express

  • 考慮到並非全部同窗都會親自建一個工程來實戰演練一下 LLDB , 本篇文章我會結合實戰來介紹 LLDB 經常使用指令的實際效果 , 以此來更好地理解和記憶 LLDB ( 畢竟面試仍是常常會問到的 ) , 熟悉的同窗能夠自行跳過 .vim

  • 另外筆者寫指令時會盡可能寫全 , 全稱指令有助於理解 , 而且之後再換爲簡寫也很簡單 , 反過來就不行了 . 所以大佬勿噴 . bash

LLDB

概述

默認內置於 Xcode 中的動態調試工具。標準的 LLDB 提供了一組普遍的命令,旨在與老版本的 GDB 命令兼容。 除了使用標準配置外,還能夠很容易地自定義 LLDB 以知足實際須要。函數

查看指令和指令幫助

  • 任意工程進入斷點模式 或者暫停後 , 直接輸入 help
  • 再輸入具體指令加 help 能夠查看某一條指令的說明 : breakpoint help.

代碼準備

#import "ViewController.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"touch screen");
    testFunc();
}

void testFunc(){
    printf("testFunc");
}

- (IBAction)btnAction1:(id)sender {
    
}
- (IBAction)btnAction2:(id)sender {
    
}
- (IBAction)btnAction3:(id)sender {
    
}
@end
複製代碼

經常使用斷點指令

1、過掉這個斷點

  • 指令 : continue , 簡寫爲 c .
  • 單步走爲 step , 簡寫爲 s , 遇到嵌套子函數會進去.
  • 單步運行 next , 簡寫爲 n , 遇到嵌套子函數會當作總體一步執行 .

2、查看當前斷點列表

  • 指令 : breakpoint list , 簡寫爲 b l工具

    ( 咱們在 Xcode 可視化加的斷點也能夠被查看到 , 由於自己都在寄存器中 ) .ui

3、方法名加斷點

  • 指令 : breakpoint set --name testFuncatom

  • 結果 : spa

  • 說明 :線程

    該指令直接經過方法名稱下斷點 . --name 可簡寫爲 -n

  • 查看斷點 :

  • 結果驗證 : continue 過掉斷點 , 點擊屏幕 , 來到咱們下的斷點 .

4、一次經過多個方法名加斷點

  • 指令 :

    breakpoint set --name "-[ViewController btnAction1:]" --name "-[ViewController btnAction2:]" --name "-[ViewController btnAction3:]"
    複製代碼
  • 結果 :

  • 說明 :

    該指令直接經過一次添加多個方法名稱下斷點 . --name 可簡寫爲 -n , 添加結果是一組斷點 , 可是爲多個位置 .

  • 查看斷點 :

5、禁用 / 啓用斷點

  • 指令 : breakpoint disable 5 / breakpoint enable 5

  • 結果 :

  • 說明 :

    經過 breakpoint list 先查看斷點 ID , 而後禁用或者啓用某一個或者某一組斷點.

  • 查看斷點 :

6、經過 sel 加斷點

  • 指令 : breakpoint set --selector touchesBegan:withEvent:

  • 結果 :

  • 說明 :

    • 經過 selector 添加斷點 , 不侷限於某個類 , 當再添加 --file 指令時 , 會具體到該類中尋找這個 sel 下斷點 .

    • 例: breakpoint set --file ViewController.m --selector touchesBegan:withEvent:

  • 查看斷點 :

7、不完整 sel 加斷點

  • 指令 : breakpoint set --func-regex btnActi

  • 結果 :

  • 說明 :

    • 經過 並無寫完整的selector 添加斷點 , 可是並不是是模糊搜索的模式 , 用的正則來處理的 , 所以輸入的名稱不能錯 , 只能是未寫完 .
    • 也可結合 --file 指令來針對某個類處理
  • 查看斷點 :

8、刪除斷點

  • 指令 : breakpoint delete 10

  • 結果 :

  • 說明 :

    不傳斷點 ID 則爲刪除全部斷點 , 會提示確認操做.

  • 查看斷點 :

提示

  • LLDB 指令對斷點作的操做與 Xcode 可視化頁面中對斷點所作的添加 / 刪除 / 禁用 / 啓用 操做都是同步的.

  • 當刪除斷點時 , 不能刪除某一組中的一個 , 刪除會變成 disable , 只能刪除組

  • breakpoint set 可直接簡寫爲 b

  • breakpoint list 可簡寫爲 break l , breakpoint disable 可簡寫爲 break dis 等等以此類推 , 能夠簡寫到只要系統能夠區分你到底敲的是哪一條命令均可以

其餘經常使用命令

執行代碼 expression / p

  • 指令 : expression self.view.subviews , 簡寫 p

  • 結果 :

  • 說明 :

    pexpression 的簡寫 , 並不是不少同窗理解的 print 哦 , poexpression -O ( --object-description NSObjectdescription 方法 ) 的簡寫.

  • 使用舉例 :

    • 執行代碼 : p self.view.backgroundColor = [UIColor orangeColor];
    • 過掉斷點 : c
    • 頁面效果 :

一樣的 , p 能夠寫多句代碼 , 使用 ; 便可 . 該方法經常使用與逆向中 Cycript 檢查是不是咱們要找的 view .

函數調用棧

  • 指令 : thread backtrace , 簡寫 bt ,
  • 結果 :
  • 說明 :
    • bt查看函數調用流程 , 可加上要查看的數量 , 例如 bt 3
    • 經過 up / down 方法能夠跳轉到 前一個 / 後一個 方法中
    • 經過 frame select 5 也能夠直接經過編號跳轉對應方法
    • 經過 frame variable 能夠查看方法參數
    • 經過 thread return 能夠線程回滾 , 並且能夠修改參數 , 可是執行完該函數會直接 return .

還有一些其餘的指令這裏就不一一列舉了 , 經過 help 指令能夠查看自行練習.

image 相關

image list
  • 指令 : image list
  • 說明 : 查看當前進程加載了哪些庫 .
  • 結果 :
image lookup
  • 指令 : image lookup -t LBPerson
  • 說明 : 查看某個類信息 .
  • 結果 :

逆向經常使用命令

剛剛咱們看了不少斷點調試指令 , 那麼逆向過程當中呢 ? 咱們並無源碼 , 結果就是 : 不少經過方法名稱下斷點等方式都不能用 😶 .

  • 爲何 ?

    • 咱們就一個 Mach-O , 沒有符號 , release 模式的狀況下 , 符號是會被編譯器自動去掉的 .
  • 怎麼辦 ?

    • 內存斷點

代碼準備

#import "ViewController.h"

@interface LBPerson : NSObject
@property(nonatomic , assign) int age;
@property(nonatomic , copy) NSString * name;
@end

@implementation LBPerson

@end

@interface ViewController ()
@property(nonatomic, strong) LBPerson * person;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.person = [[LBPerson alloc] init];
    self.person.age = 20;
    self.person.name = @"lb";
    NSLog(@"haha");
    
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.person.name = @"test";
}
複製代碼

NSLog 處加個斷點 , 運行 .

添加內存斷點

來到斷點後輸入指令 : watchpoint set variable self->_person->_name

過掉斷點 . 點擊屏幕 , 修改了 personname , 監控到內存斷點 , 並打印以下 :

Watchpoint 1 hit:
old value: 0x0000000107f72078
new value: 0x0000000107f720b8
複製代碼

查看內容 .

經過函數調用棧能夠看到實際上是在屬性的 set 方法時調用的 .

經過內存地址下斷點

逆向中咱們每每只有內存地址 , 甚至不必定拿獲得變量或者屬性 . 那麼就須要經過內存地址下斷點 .

一樣剛剛的代碼 , 從新運行來到 NSLog 斷點.

  • p 查看 name 內存地址 , p &self->_person->_name
  • watchpoint set expression 0x0000600000db2f70
  • 過掉斷點 , 點擊屏幕
  • 檢測到改變

內存斷點的刪除 禁用 查看列表

同上述 breakpoint , 很少贅述.

LLDB高級用法

特定斷點添加指令

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"touch screen");
    [self test];
}

- (void)test{
    NSLog(@"Add Command test");
}
複製代碼
1. 給 testFunc 添加斷點

指令 : b testFunc

2. 給這個斷點添加指令

指令 : breakpoint command add 1

3. 輸入要添加的指令

輸入 :

>po self
>p self.view.backgroundColor = [UIColor orangeColor];
>DONE
複製代碼

過掉斷點 , 點擊屏幕.

結果 :

再過掉斷點 , 查看頁面 .

這種方法適用場景較多 , 例如某個斷點一斷住就查看形參 等等 , 自由發揮 .

一樣的 查看斷點指令列表

breakpoint command list 1

刪除斷點指令

breakpoint command list 1

stop-hook

  • 指令 : target stop-hook add --one "frame variable"

  • 說明 :

    • --one 表明添加一條指令 , 可簡寫爲 -o
    • 可視化頁面中 Pause paogram execution 以及 Debug view 不屬於 stop 範疇.
  • 結果: 在 TouchBegan 添加一個斷點 , 點擊屏幕 , 打印以下 :

  • 相較於上一個 特定斷點添加指令 , 顯然是更通用 .

那麼一樣 , 查看斷點指令列表 : target stop-hook list

刪除斷點指令 : target stop-hook delete , 使用 undisplay 1 , 也是同樣的 .

禁用 / 啓用 同理 .

自動啓用加載指令

以上咱們說的這些 , 創建在工程運行起來以後 , 進入 lldb 模式 , 添加指令等操做的前提下 . 那麼咱們思考一個問題 , 每次運行工程都要從新添加 , 能否自動處理呢 ?

答案是確定的 .

打開 終端 / iTerm2 . 來到家目錄下 , ls -a 查看文件.

  • 若是你跟我同樣有這個 .lldbinit 文件 , 直接 vim 添加 target stop-hook add -o "frame variable" .
  • 若是你沒有這個文件 . 不要緊 直接 vim 建立 而後寫上 target stop-hook add -o "frame variable".
  • 保存並退出 .

來到工程中 , 從新 run 一下 , 點擊屏幕方法添加一個斷點 , 點擊屏幕 :

其緣由是 lldb 在啓動時會加載這個文件 , 對每一個工程都有效 .

最後

最後簡單說一下逆向過程當中如何下一個內存斷點

  • 使用 MachOView / Hopper , 找到方法地址 , 減去 PageZero 的虛擬內存 (也就是最前面的那個 1 , / 64 位下是 4 個 G, ) 獲得方法基於 Mach-O首地址的真實偏移地址.
  • lldb 查看 Mach-O 地址 .
  • 獲得的地址和方法偏移地址相加 , 獲得方法的真實地址 .
  • 根據地址添加斷點 , 便可添加成功.

這個是因爲 ASLR 的緣由 . 說白了就是地址空間配置隨機加載 , 所以咱們須要在加載完後再獲取 Mach-O 的地址便可.

物理地址 = ALSR + 虛擬地址

相關文章
相關標籤/搜索