點贊評論,感覺有用的朋友可以關注筆者公眾號 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 添加到架構設置中。

如果我們正在編寫對安全性敏感的軟件,那么值得盡早的采用此功能。
操縱指針
讓我們考慮下面的程序,該程序以人工方式操縱指針,以揭示指針驗證機制背后的一些思想。
#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。
在物理上相鄰的下一個指針 nextInterestingJumpToFunc 是 0x22b810102c55df8; 顯然,它具有相似的有效地址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值。
感謝你閱讀本文! ??