[譯]《iOS Crash Dump Analysis》- Pointer Authentication

點贊評論,感覺有用的朋友可以關注筆者公眾號 iOS 成長指北,持續(xù)更新
原書為 iOS Crash Dump Analysis Book,已得作者授權,歡迎 star

Pointer Authentication(指針驗證機制)

在本章中,我們將研究指針驗證機制以及相關的崩潰。

使用 Apple A12 芯片或更高版本的設備將稱為 Pointer Authentication 的安全功能作為 ARMv8.3-A 體系結構的一部分。例如, iPhone XS,iPhone XS Max, 和 iPhone XR 使用 A12芯片。其基本思想是在 64 位指針中存在未使用的位,因為它已經可以尋址相當廣泛的地址,因此只有 40 位被分配給這樣的目的。 @iospac 因此,剩余的位可用于存儲在將預期的指針地址與上下文值和密鑰組合后計算出的哈希值。 然后,如果由于錯誤或惡意操作而要更改指針地址,則該指針將被認為是無效的,并且如果將其用于更改程序的控制流,則最終將導致 SIGSEGV。

實際上,在許多情況下都使用了指針驗證機制,例如確保 C++ 虛擬調度表未被篡改。 但是,我們只看一個錯誤操作跳轉地址的簡單情況。因此,我們只看一個錯誤操作跳轉地址的簡單情況。

配置指針驗證機制

在使用 A12 或更高版本的設備,Apple 在設備內核中啟用了指針驗證機制。對于用戶空間代碼,指針身份驗證是一項可選功能??梢栽陧椖康?em>構建設置中來啟用它,如圖為 icdab_ptr 示例 Enable Pointer Authentication(啟用指針驗證機制)。我們將架構 arm64e 添加到架構設置中。

enable_ptr_auth.png

如果我們正在編寫對安全性敏感的軟件,那么值得盡早的采用此功能。

操縱指針

讓我們考慮下面的程序,該程序以人工方式操縱指針,以揭示指針驗證機制背后的一些思想。

#import "ViewController.h"

#include "ptrauth.h"

@interface ViewController ()

@end

@implementation ViewController

typedef void(*ptrFn)(void);

static void interestingJumpToFunc(void) {
    NSLog(@"Simple interestingJumpToFunc\n");
}

// this function's address is where we will be jumping to
static void nextInterestingJumpToFunc(void) {
    NSLog(@"Simple nextInterestingJumpToFunc\n");
}

- (void)viewDidLoad {
    [super viewDidLoad];
    ptrFn result = [self generatePtrToFn];
    NSLog(@"ptrFn result is %p\n", result);
    result(); // will crash; deferences a pointer with a bad PAC
}

- (ptrFn)generatePtrToFn {
    uintptr_t a1 = (uintptr_t)interestingJumpToFunc;
    uintptr_t a2 = (uintptr_t)nextInterestingJumpToFunc;
    NSLog(@"ptr addresses as uintptr_t are 0x%lx 0x%lx\n",
          a1, a2);
    ptrdiff_t delta = a2 - a1;
    ptrdiff_t clean_delta =
    ptrauth_strip(&nextInterestingJumpToFunc,
                  ptrauth_key_asia) -
    ptrauth_strip(&interestingJumpToFunc,
                  ptrauth_key_asia);
    
    NSLog(@"delta is 0x%lx clean_delta is 0x%tx\n", delta,
 clean_delta);
    
    ptrFn func = interestingJumpToFunc;
    func += clean_delta; // correct offset but neglects PAC
 component
    return func;
}

如果運行上述程序,則會崩潰。 該程序的日志輸出為:

ptr addresses as uintptr_t are 0x2946180102c55dd8
 0x22b810102c55df8
delta is 0xd8e5690000000020 clean_delta is 0x20
ptrFn result is 0x2946180102c55df8

我們看到,當獲得指向函數(shù)interestingJumpToFunc 的指針,然后將其存儲在足以容納指針地址的整數(shù)中時,我們將獲得一個較大的值0x2946180102c55dd8。 這是因為地址的前24位是指針驗證碼(PAC)。 在這種情況下,PAC 為 0x294618 ,有效指針為0x0102c55dd8。

在物理上相鄰的下一個指針 nextInterestingJumpToFunc0x22b810102c55df8; 顯然,它具有相似的有效地址0x0102c55df8,但具有完全不同的 PAC 0x22b81

當我們計算指針之間的增量時,由于指針值的 PAC 部分,我們顯然會得到一個無意義的地址。 為了正確計算指針的有效地址之間的增量,我們需要使用 ptrauth_strip實用程序函數(shù)。 這是作為內置宏匯編指令實現(xiàn)的。

經過宏預處理后,代碼為:

ptrdiff_t clean_delta =
    __builtin_ptrauth_strip(&nextInterestingJumpToFunc,
 ptrauth_key_asia) -

    __builtin_ptrauth_strip(&interestingJumpToFunc,
 ptrauth_key_asia);

生成的裝配說明的格式為:

    xpaci   x9

當使用 __builtin_ptrauth_strip 剝離功能時。 這將從寄存器(在本例中為x9)中刪除PAC。

使用帶狀函數(shù)的好處是我們能夠正確確定感興趣的兩個函數(shù)之間的距離。它是 0x20。 我們的函數(shù) generatePtrToFn 實際上只是將delta加到interestingJumpToFunc的地址上來計算 nextInterestingJumpToFunc 的地址,但是這樣做是錯誤的。 它將PAC在其計算出的地址中留給interestingJumpToFunc。

請注意,這類指針操作均不會導致崩潰。 檢查指針的時間是用于更改鏈接寄存器\index{register!link} 的時間。 也就,當我們根據(jù)指針值來更改程序的控制流時。

在函數(shù) viewDidLoad() 我們存在以下代碼

result(); // will crash; deferences a pointer with a bad PAC

使用的指針是我們的錯誤指針0x2946180102c55df8。 匯編指令檢測到錯誤的指針

blraaz  x8

這是具有指向注冊的鏈接的分支,具有使用指令密鑰 A 的指針身份驗證。

請注意,當我們剝離指針時,我們使用了ptrauth_key_asia來刪除指令密鑰A。但是,我們無法訪問執(zhí)行相反操作所需的特殊值,而是使用帶有適當?shù)?salt 的指令密鑰A來對指針進行簽名值以獲取上下文中正確的帶符號指針。

現(xiàn)在,我們已經檢查了錯誤的代碼,讓我們看看崩潰時會發(fā)生什么。

icdab_ptr PAC 崩潰

在具有 A13 仿生芯片的 iPhone 11上,運行icdab_ptr 程序時會得到以下崩潰信息。

Incident Identifier: DA9FE0C7-6127-4660-A214-5DF5D432DBD9
CrashReporter Key:   d3e622273dd1296e8599964c99f70e07d25c8ddc
Hardware Model:      iPhone12,1
Process:             icdab_ptr [2125]
Path:               
 /private/var/containers/Bundle/Application/32E9356D-AF19-4F30-BB
87-E4C056468063/icdab_ptr.app/icdab_ptr
Identifier:          perivalebluebell.com.icdab-ptr
Version:             1 (1.0)
Code Type:           ARM-64 (Native)
Role:                Foreground
Parent Process:      launchd [1]
Coalition:           perivalebluebell.com.icdab-ptr [1288]

Date/Time:           2020-10-14 23:09:20.9645 +0100
Launch Time:         2020-10-14 23:09:20.6958 +0100
OS Version:          iPhone OS 14.2 (18B5061e)
Release Type:        Beta
Baseband Version:    2.02.00
Report Version:      104

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x2000000100ae9df8 ->
 0x0000000100ae9df8 (possible pointer authentication failure)
VM Region Info: 0x100ae9df8 is in 0x100ae4000-0x100aec000;  bytes
 after start: 24056  bytes before end: 8711
      REGION TYPE                 START - END      [ VSIZE]
 PRT/MAX SHRMOD  REGION DETAIL
      UNUSED SPACE AT START
--->  __TEXT                   100ae4000-100aec000 [   32K]
 r-x/r-x SM=COW  ...app/icdab_ptr
      __DATA_CONST             100aec000-100af0000 [   16K]
 r--/rw- SM=COW  ...app/icdab_ptr

Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [2125]
Triggered by Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   icdab_ptr                           0x0000000100ae9df8
 nextInterestingJumpToFunc + 24056 (ViewController.m:26)
1   icdab_ptr                           0x0000000100ae9cf0
 -[ViewController viewDidLoad] + 23792 (ViewController.m:35)
2   UIKitCore                           0x00000001aca4cda0
 -[UIViewController
 _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 108
3   UIKitCore                           0x00000001aca515fc
 -[UIViewController loadViewIfRequired] + 956
4   UIKitCore                           0x00000001aca519c0
 -[UIViewController view] + 32
.
.

Thread 0 crashed with ARM Thread State (64-bit):
    x0: 0x0000000103809718   x1: 0x0000000103809718   x2:
 0x0000000000000000   x3: 0x00000000000036c8
    x4: 0x00000000000062dc   x5: 0x000000016f319b20   x6:
 0x0000000000000031   x7: 0x0000000000000700
    x8: 0x045d340100ae9df8   x9: 0x786bdebd0a110081  x10:
 0x0000000103809718  x11: 0x00000000000007fd
   x12: 0x0000000000000001  x13: 0x00000000d14208c1  x14:
 0x00000000d1621000  x15: 0x0000000000000042
   x16: 0xe16d9e01bf21b57c  x17: 0x00000002057e0758  x18:
 0x0000000000000000  x19: 0x0000000102109620
   x20: 0x0000000000000000  x21: 0x0000000209717000  x22:
 0x00000001f615ffcb  x23: 0x0000000000000001
   x24: 0x0000000000000001  x25: 0x00000001ffac7000  x26:
 0x0000000282c599a0  x27: 0x00000002057a44a8
   x28: 0x00000001f5cfa024   fp: 0x000000016f319dd0   lr:
 0x0000000100ae9cf0
    sp: 0x000000016f319da0   pc: 0x0000000100ae9df8 cpsr:
 0x60000000
   esr: 0x82000004 (Instruction Abort) Translation fault

Binary Images:
0x100ae4000 - 0x100aebfff icdab_ptr arm64e 
 <83e44566e30039258fd14db647344501>
 /var/containers/Bundle/Application/32E9356D-AF19-4F30-BB87-E4C05
6468063/icdab_ptr.app/icdab_ptr

首先,我們注意到的點是崩潰發(fā)生在 ViewController.m:26

0   icdab_ptr                           0x0000000100ae9df8
 nextInterestingJumpToFunc + 24056 (ViewController.m:26)

在我們的源代碼里有:

24 // this function's address is where we will be jumping to
25 static void nextInterestingJumpToFunc(void) {
26     NSLog(@"Simple nextInterestingJumpToFunc\n");
27 }

這個程序的目的是通過指針運算來計算nextInterestingJumpToFunc 函數(shù)的地址,然后跳轉到它。它成功地做到了,然后崩潰了。從上一節(jié)我們知道這是因為我們故意使用了從 interestingJumpToFunc函數(shù)借用的函數(shù)地址 PAC。

崩潰報告系統(tǒng)會對指針進行拆解,以推導出所給的錯誤指針的有效指針地址。 我們有:

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x2000000100ae9df8 ->
 0x0000000100ae9df8 (possible pointer authentication failure)
VM Region Info: 0x100ae9df8 is in 0x100ae4000-0x100aec000;  bytes
 after start: 24056  bytes before end: 8711
      REGION TYPE                 START - END      [ VSIZE]
 PRT/MAX SHRMOD  REGION DETAIL
      UNUSED SPACE AT START
--->  __TEXT                   100ae4000-100aec000 [   32K]
 r-x/r-x SM=COW  ...app/icdab_ptr
      __DATA_CONST             100aec000-100af0000 [   16K]
 r--/rw- SM=COW  ...app/icdab_ptr

我們的指針 0x2000000100ae9df8 指向程序的文本區(qū)域 0x0000000100ae9df8,但是指針的高 24 位不正確,因此顯示了消息 (可能的指針身份驗證失?。?/code>,并導致了SIGSEGV。 注意,PAC是一個特殊值 0x200000,大概是代表 無效PAC的值。

從上一節(jié)中,我們知道可以通過下面的代碼檢查程序中 PAC:

blraaz  x8

我們的 x8 寄存器是 0x045d340100ae9df8,所以估計出錯誤的 PAC 是 0x045d34。

指針驗證機制調試技巧

在本節(jié)中,我們將指出在架構構 armv8e 目標上運行調試器時的一些區(qū)別。我們還將展示如何將崩潰報告與調試會話匹配。

當我們打印出指針時,我們得到了去掉 PAC 值的指針。例如,對于指針0x36f93010201ddf8,我們的 result 變量,我們會得到:

(lldb) po result
(actual=0x000000010201ddf8 icdab_ptr`nextInterestingJumpToFunc at
 ViewController.m:25)

該值來自產生以下輸出的執(zhí)行

ptr addresses as uintptr_t are 0x36f93010201ddd8
 0xc7777b010201ddf8
delta is 0xc407e80000000020 clean_delta is 0x20
ptrFn result is 0x36f93010201ddf8

通過調試器進行連接時,看不到崩潰分析報告。 但是,如果我們分離調試器:

(lldb) detach

系統(tǒng)將繼續(xù)運行,并出發(fā)崩潰,然后生成報告。

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x200000010201ddf8 ->
 0x000000010201ddf8 (possible pointer authentication failure)
VM Region Info: 0x10201ddf8 is in 0x10201c000-0x102020000;  bytes
 after start: 7672  bytes before end: 8711
      REGION TYPE                 START - END      [ VSIZE]
 PRT/MAX SHRMOD  REGION DETAIL
      __TEXT                   102018000-10201c000 [   16K]
 r-x/r-x SM=COW  ...app/icdab_ptr
--->  __TEXT                   10201c000-102020000 [   16K]
 r-x/rwx SM=COW  ...app/icdab_ptr
      __DATA_CONST             102020000-102024000 [   16K]
 r--/rw- SM=COW  ...app/icdab_ptr

Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [2477]
Triggered by Thread:  0

Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   icdab_ptr                       0x000000010201ddf8
 nextInterestingJumpToFunc + 24056 (ViewController.m:25)
1   icdab_ptr                       0x000000010201dcf0
 -[ViewController viewDidLoad] + 23792 (ViewController.m:34)
2   UIKitCore                       0x00000001aca4cda0
 -[UIViewController
 _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 108
3   UIKitCore                       0x00000001aca515fc
 -[UIViewController loadViewIfRequired] + 956
4   UIKitCore                       0x00000001aca519c0
 -[UIViewController view] + 32
.
.

Thread 0 crashed with ARM Thread State (64-bit):
    x0: 0x000000010c80a738   x1: 0x000000010c80a738   x2:
 0x000000000000000d   x3: 0x0000000000000000
    x4: 0x000000016dde59b8   x5: 0x0000000000000040   x6:
 0x0000000000000033   x7: 0x0000000000000800
    x8: 0x036f93010201ddf8   x9: 0xb179df14ab2900d1  x10:
 0x000000010c80a738  x11: 0x00000000000007fd
   x12: 0x0000000000000001  x13: 0x00000000b3e33897  x14:
 0x00000000b4034000  x15: 0x0000000000000068
   x16: 0x582bcd01bf21b57c  x17: 0x00000002057e0758  x18:
 0x0000000000000000  x19: 0x000000010be0d760
   x20: 0x0000000000000000  x21: 0x0000000209717000  x22:
 0x00000001f615ffcb  x23: 0x0000000000000001
   x24: 0x0000000000000001  x25: 0x00000001ffac7000  x26:
 0x0000000280436140  x27: 0x00000002057a44a8
   x28: 0x00000001f5cfa024   fp: 0x000000016dde5b60   lr:
 0x000000010201dcf0
    sp: 0x000000016dde5b30   pc: 0x000000010201ddf8 cpsr:
 0x60000000
   esr: 0x82000004 (Instruction Abort) Translation fault

這種方法很方便,因為這樣我們就可以將故障轉儲報告與調試器中的分析直接關聯(lián)起來。 請注意,x8 寄存器恰好是我們先前探討的 result值。

感謝你閱讀本文! ??

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容