溺的文档
KernelPatch · 第 15 篇 / 共 22 篇

ARM64 安全特性:PAC 与 BTI 详解

2026-06-29 · 阅读 1

概述

PAC (Pointer Authentication Code)BTI (Branch Target Identification) 是 ARM64 架构引入的两大硬件安全特性,用于防御控制流劫持攻击。

引入版本

  • PAC:ARMv8.3-A(2016)
  • BTI:ARMv8.5-A(2019)

内核支持

  • Linux 5.7+ 开始支持 BTI
  • Linux 5.8+ 开始支持 PAC
  • Android 11+ 默认启用

在 KernelPatch 中的影响

  • setup1.S 中的 BTI 检测代码
  • paging_init 函数可能使用 PAC/BTI
  • Hook 机制需要处理这些指令

🔐 PAC (Pointer Authentication Code)

什么是 PAC?

PAC 通过在指针的未使用高位添加签名,防止指针被篡改。

核心思想

64 位指针在用户空间和内核空间都有大量未使用的高位:

用户空间(48 位 VA):
  63        48 47                    0
  ┌──────────┬──────────────────────┐
  │  unused  │   actual address     │
  └──────────┴──────────────────────┘
  
内核空间(48 位 VA):
  63        48 47                    0
  ┌──────────┬──────────────────────┐
  │  unused  │   actual address     │
  └──────────┴──────────────────────┘
  
PAC 使用高 16 位存储签名:
  63        48 47                    0
  ┌──────────┬──────────────────────┐
  │   PAC    │   actual address     │
  └──────────┴──────────────────────┘

PAC 指令集

签名指令(PACIA/PACIB 系列)

PACIASP         ; 使用 SP 签名 X30 (返回地址)
PACIASPPC       ; 使用 SP 和 PC 签名 X30
PACIBSP         ; 使用 SP 签名 X30(使用 key B)

PACIA X0, X1    ; 使用 X1 签名 X0
PACIB X0, X1    ; 使用 X1 签名 X0(使用 key B)

PACDA X0, X1    ; 签名数据指针
PACDB X0, X1

编码

PACIASP: 0xD503233F
  31 30 29 28 ... 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
  1  1  0  1  ... 0  0  0  0  1  1  0  0  1  1  1  1  1  1

验证指令(AUTIA/AUTIB 系列)

AUTIASP         ; 验证并移除 X30 的签名
AUTIASPPC
AUTIBSP

AUTIA X0, X1    ; 验证 X0 的签名
AUTIB X0, X1

AUTDA X0, X1    ; 验证数据指针
AUTDB X0, X1

编码

AUTIASP: 0xD503231F
  与 PACIASP 只差一位(bit 5)

PAC 工作原理

签名过程

// 伪代码
uint64_t pac_sign(uint64_t pointer, uint64_t context, uint64_t key)
{
    // 1. 提取地址的有效部分
    uint64_t address = pointer & 0x0000FFFFFFFFFFFF;  // 48 位
    
    // 2. 计算签名(使用硬件加密算法 QARMA)
    uint16_t pac = qarma_encrypt(address, context, key);
    
    // 3. 将签名填充到高位
    uint64_t signed_pointer = address | ((uint64_t)pac << 48);
    
    return signed_pointer;
}

验证过程

// 伪代码
uint64_t pac_verify(uint64_t signed_pointer, uint64_t context, uint64_t key)
{
    // 1. 分离地址和签名
    uint64_t address = signed_pointer & 0x0000FFFFFFFFFFFF;
    uint16_t pac = (signed_pointer >> 48) & 0xFFFF;
    
    // 2. 重新计算签名
    uint16_t expected_pac = qarma_encrypt(address, context, key);
    
    // 3. 验证
    if (pac != expected_pac) {
        // ❌ 签名不匹配!
        // 修改高位为错误标记(全 1 或全 0)
        return address | 0xFFFF000000000000;  // 非法地址
    }
    
    // ✅ 验证成功,返回原始地址
    return address;
}

PAC 密钥

ARM64 提供 5 个 128 位密钥:

APIAKey (A key for instruction addresses)
APIBKey (B key for instruction addresses)
APDAKey (A key for data addresses)
APDBKey (B key for data addresses)
APGAKey (Generic key)

每个密钥在系统寄存器中:
  APIAKey_EL1: bits[127:64] (high), bits[63:0] (low)

密钥特点

  • 每个进程/上下文可以有不同的密钥
  • 密钥随机生成(启动时或进程创建时)
  • 无法直接读取(安全)

典型使用场景

场景 1:保护返回地址

函数入口:
  PACIASP               ; 签名 X30 (返回地址)
                        ; 使用 SP 作为上下文
                        ; X30[63:48] = PAC
  STP X29, X30, [SP, #-16]!
  
  ; ... 函数体 ...
  ; 如果攻击者修改栈上的返回地址:
  ; [SP] = 0xDEADBEEF  ← 尝试劫持
  
函数返回:
  LDP X29, X30, [SP], #16
                        ; X30 = 0xDEADBEEF (被篡改)
  AUTIASP               ; 验证签名
                        ; PAC 不匹配!
                        ; X30 = 0xFFFFDEADBEEF (非法地址)
  RET                   ; 跳转到 0xFFFFDEADBEEF
                        ; → MMU 异常 → 崩溃(阻止了攻击)

场景 2:保护函数指针

// C 代码
void (*callback)(void) = my_function;

// 攻击者尝试修改
callback = evil_function;  // ❌ 希望劫持

// 使用 PAC 保护
void (**signed_callback)(void) = &callback;

// 编译器生成
PACIA X0, X1      ; 签名函数指针
STR X0, [X1]      ; 存储签名后的指针

// 调用时
LDR X0, [X1]      ; 加载(可能被篡改)
AUTIA X0, X1      ; 验证签名
BLR X0            ; 如果验证失败,跳转到非法地址

PAC 失败的表现

正常指针:
  0x0000FFFFFFE12345  (未签名)
  
签名后:
  0x1234FFFFFFE12345  (签名 = 0x1234)
  
篡改后:
  0x1234FFFFFFE99999  (地址被改)
  
验证失败后:
  0xFFFFFFFFE99999    (高位被填充为 0xFFFF)
  或
  0x0002FFFFFE99999   (高位被填充为错误值)
  
访问此地址:
  → Level 1/2 translation fault
  → 内核 panic

在 panic.txt 中看到的情况

  • 不是 PAC 失败(崩溃地址是 0xfffffffffffffff8)
  • 而是 NULL 指针解引用

🎯 BTI (Branch Target Identification)

什么是 BTI?

BTI 标记合法的跳转目标,防止跳转到函数中间。

攻击场景(无 BTI)

func:
  0x000: STP X29, X30, [SP, #-16]!
  0x004: MOV X29, SP
  0x008: BL other_func
  0x00C: ...
  0x010: LDP X29, X30, [SP], #16  ← 攻击者跳转到这里
  0x014: RET                       ; 跳过函数入口检查

攻击者构造:
  X17 = 0x010  (跳过入口,直接到返回)
  BR X17       ; 绕过函数序言

有 BTI 保护

func:
  0x000: BTI C              ← 标记:只能从 CALL 跳转到
  0x004: STP X29, X30, [SP, #-16]!
  0x008: MOV X29, SP
  ...
  0x010: LDP X29, X30, [SP], #16
  0x014: RET

攻击者构造:
  X17 = 0x010
  BR X17       ; ❌ 跳转到非 BTI 位置
               ; → BTI 异常 → 崩溃

BTI 指令

BTI C           ; 只能从 CALL (BL/BLR) 跳转到
BTI J           ; 只能从 JUMP (B/BR) 跳转到
BTI JC          ; 两者都可以
BTI             ; 等同于 BTI JC

编码

BTI C:  0xD503245F
  31 ... 11 10 9 8 7 6 5 4 ... 0
  1  ... 1  0  1  0 0 1 1 1 ... 1
         │     └─┴─ op2 = 0b011 (BTI)
         └─ CRm[0] = 1 (C)

BTI J:  0xD503249F
         CRm[1] = 1 (J)

BTI JC: 0xD50324DF
         CRm[1:0] = 0b11 (JC)

本质:BTI 是 HINT 指令的一种

HINT #0x14  (BTI C)
HINT #0x16  (BTI J)
HINT #0x17  (BTI JC)

BTI 工作原理

CPU 状态机

CPU 维护一个"分支状态":

初始状态:GUARDED
  ↓ 执行 BL/BLR (CALL)
GUARDED_CALL
  ↓ 下一条指令
  ├─ 如果是 BTI C 或 BTI JC → OK,状态恢复
  └─ 如果不是 → BTI 异常!
  
初始状态:GUARDED
  ↓ 执行 B/BR (JUMP)
GUARDED_JUMP
  ↓ 下一条指令
  ├─ 如果是 BTI J 或 BTI JC → OK
  └─ 如果不是 → BTI 异常!

示例

正常调用流程:
  BL func         ; CPU 进入 GUARDED_CALL 状态
  
func:
  BTI C           ; ✅ 匹配,状态恢复为 GUARDED
  STP X29, X30, [SP, #-16]!
  ...

攻击流程:
  X17 = func + 0x10
  BR X17          ; CPU 进入 GUARDED_JUMP 状态
  
func+0x10:
  LDP X29, X30, [SP], #16  ; ❌ 不是 BTI J
                            ; → BTI 异常!

BTI 与 HINT 的关系

HINT 指令

HINT #imm       ; 提示指令,可以被 NOP

NOP     = HINT #0    = 0xD503201F
YIELD   = HINT #1    = 0xD503203F
WFE     = HINT #2    = 0xD503205F
WFI     = HINT #3    = 0xD503207F
SEV     = HINT #4    = 0xD503209F
SEVL    = HINT #5    = 0xD50320BF
...
BTI C   = HINT #0x14 = 0xD503245F
BTI J   = HINT #0x16 = 0xD503249F
BTI JC  = HINT #0x17 = 0xD50324DF

向后兼容

  • 在不支持 BTI 的 CPU 上,BTI 被当作 NOP
  • 代码可以在新旧 CPU 上运行
  • 但只有新 CPU 提供安全保护

🔗 PAC 与 BTI 的配合使用

完整的函数保护

secure_function:
  BTI C                      ; 1️⃣ 只能从 CALL 跳转到
  PACIASP                    ; 2️⃣ 签名返回地址 X30
  STP X29, X30, [SP, #-16]!
  MOV X29, SP
  
  ; ... 函数体 ...
  ; 即使攻击者修改了栈上的返回地址
  ; 也无法绕过 AUTIASP 的验证
  
  LDP X29, X30, [SP], #16
  AUTIASP                    ; 3️⃣ 验证签名,移除签名值
  RET                        ; 4️⃣ 安全返回

攻击者的困境:
  1. 无法跳转到函数中间(BTI 阻止)
  2. 无法劫持返回地址(PAC 阻止)

编译器生成

GCC/Clang 选项

# 启用 PAC
-mbranch-protection=pac-ret

# 启用 BTI
-mbranch-protection=bti

# 同时启用
-mbranch-protection=standard  # pac-ret+bti

# 完整示例
aarch64-linux-gnu-gcc -mbranch-protection=standard \
                      -march=armv8.5-a \
                      -o secure_program program.c

生成的代码

my_function:
  BTI C
  PACIASP
  STP X29, X30, [SP, #-16]!
  ; ...
  LDP X29, X30, [SP], #16
  AUTIASP
  RET

🐛 在 KernelPatch 中的问题

问题 1:BTI 指令被错误处理

setup1.S 中的 bug

// kernel/base/setup1.S:227-246
ldr w12, [x13]              ; w12 = paging_init[0]

mov w3, #0x201F
movk w3, #0xD503, lsl#16    ; w3 = NOP (0xD503201F)
orr w1, w3, #0x100          ; w1 = 0xD503211F
mov w2, #0xFFFFFD1F         ; w2 = 掩码
and w0, w12, w2
cmp w0, w1
b.ne .backup

; 如果检测到 BTI C:
mov w12, w3                 ; ❌ 备份改为 NOP (0xD503201F)
                            ; 应该备份原始的 BTI C (0xD503245F)

问题

paging_init 原始:
  0x000: D503245F    BTI C

备份:
  backup = D503201F  NOP  ❌ 错误

恢复后:
  0x000: D503201F    NOP  ❌ 缺少 BTI

后果:
  如果有代码通过 BL 跳转到 paging_init
  期望看到 BTI C
  但实际是 NOP
  → BTI 异常?(取决于 CPU 实现)

问题 2:掩码错误匹配其他指令

掩码 0xFFFFFD1F 的问题:

BTI C (0xD503245F):
  D503245F & FFFFFE1F = D503201F ❌ 结果不是 D503211F

实际上应该检查的掩码:
  D503245F & FFFFFFDF = D503245F ✅
  
但当前掩码会匹配:
  PACIASP (0xD503233F) & FFFFFE1F = D503231F
  AUTIASP (0xD503231F) & FFFFFE1F = D503231F
  BTI C   (0xD503245F) & FFFFFE1F = D503245F

都不等于 D503211F,所以逻辑有问题!

让我重新分析 setup1.S 的代码

orr w1, w3, #0x100          ; w1 = 0xD503201F | 0x100 = 0xD503211F
mov w2, #0xFFFFFD1F         ; w2 = 0xFFFFFD1F
and w0, w12, w2             ; w0 = insn & 0xFFFFFD1F
cmp w0, w1                  ; 比较 w0 == 0xD503211F

如果 insn = BTI C (0xD503245F):
  w0 = 0xD503245F & 0xFFFFFD1F = 0xD503245F ≠ 0xD503211F

如果 insn = AUTIASP (0xD503231F):
  w0 = 0xD503231F & 0xFFFFFD1F = 0xD503211F ✅ 匹配!

结论:这段代码实际上在查找 AUTIASP,不是 BTI C!

问题 3:AUTIASP 被 NOP 化

setup1.S 的 .cmp_auti 循环

add x11, x13, #4            ; 从第二条指令开始
.cmp_auti:
    ldr w0, [x11], #4
    and w0, w0, w2
    cmp w0, w1              ; 查找 0xD503211F
    b.ne .cmp_auti
    stur w3, [x11, #-4]     ; ❌ 找到后 NOP 化

这个循环在找 AUTIASP 并将其改为 NOP!

后果

paging_init 原始:
  0x000: BTI C (或其他)
  0x004: PACIASP
  0x008: STP X29, X30, [SP, #-16]!
  ...
  0x0XX: LDP X29, X30, [SP], #16
  0x0YY: AUTIASP          ← 被找到
  0x0ZZ: RET

被修改后:
  0x000: B _paging_init   (临时)
  0x004: PACIASP
  ...
  0x0YY: NOP              ❌ 原本是 AUTIASP
  0x0ZZ: RET

恢复后:
  0x000: BTI C (或 NOP,如果被错误备份)
  0x004: PACIASP
  ...
  0x0YY: NOP              ❌ 仍然是 NOP
  0x0ZZ: RET

执行:
  PACIASP              ; 签名 X30
  ...
  (没有 AUTIASP)       ; ❌ 签名未移除
  RET                  ; X30 仍带签名
                       ; → PAC 验证失败
                       ; → X30 被修改为非法值
                       ; → 崩溃!

🔧 正确的处理方式

方案 1:完全不处理(推荐)

; kernel/base/setup1.S(修复版)

map_prepare:
    ; 简单备份第一条指令
    add x13, x15, x19
    ldr w12, [x13]
    str w12, [x9, #map_paging_init_backup_offset]
    
    ; ❌ 删除所有 BTI 检测代码
    ; ❌ 删除 .cmp_auti 循环
    
    ; 写入 B 指令
    ; ... (保持原有代码)

为什么可以不处理?

  1. ✅ B 指令直接跳转,不经过 BTI 检查
  2. ✅ 恢复原始指令后,BTI 自然生效
  3. ✅ 不修改函数内部的 PAC 指令,不会破坏验证

方案 2:跳过 BTI(如果必要)

; 如果确实需要 hook 第二条指令

map_prepare:
    add x13, x15, x19
    ldr w12, [x13]
    
    ; 检测 BTI C
    mov w1, #0x245F
    movk w1, #0xD503, lsl#16    ; w1 = 0xD503245F (精确匹配)
    cmp w12, w1
    b.ne .not_bti_c
    
    ; 是 BTI C,hook 第二条指令
    add x13, x13, #4
    ldr w12, [x13]
    
.not_bti_c:
    str w12, [x9, ...]          ; 备份
    
    ; 写入 B 指令到 x13 位置

问题

  • 恢复时需要特殊处理
  • 增加复杂度
  • 不如方案 1 简单

📚 PAC 与 BTI 的检测

检测 CPU 是否支持

// kernel code
#include <asm/cpufeature.h>

bool cpu_has_pac()
{
    // 读取 ID_AA64ISAR1_EL1 寄存器
    uint64_t isar1;
    asm volatile("mrs %0, ID_AA64ISAR1_EL1" : "=r"(isar1));
    
    // APA (Address PAC, using QARMA5)
    int apa = (isar1 >> 4) & 0xF;
    
    // API (Address PAC, using QARMA3)
    int api = (isar1 >> 8) & 0xF;
    
    return (apa != 0) || (api != 0);
}

bool cpu_has_bti()
{
    uint64_t pfr1;
    asm volatile("mrs %0, ID_AA64PFR1_EL1" : "=r"(pfr1));
    
    // BT field (bits [3:0])
    int bt = pfr1 & 0xF;
    
    return (bt != 0);
}

检测指令是否使用 PAC/BTI

bool is_pac_instruction(uint32_t insn)
{
    // PACIASP family
    if ((insn & 0xFFFFFFE0) == 0xD5033220) return true;
    
    // AUTIASP family
    if ((insn & 0xFFFFFFE0) == 0xD5033200) return true;
    
    // PACIA/AUTIA
    if ((insn & 0xFFE0FC00) == 0xDAC10000) return true;
    
    return false;
}

bool is_bti_instruction(uint32_t insn)
{
    // BTI C/J/JC
    if ((insn & 0xFFFFFF3F) == 0xD503245F) return true;
    
    return false;
}

🎓 实际案例分析

案例 1:paging_init 的正确形式

; 可能的 paging_init 函数(内核 6.1)

paging_init:
  0x000: D503245F    BTI C        ; ARMv8.5+
  0x004: D503233F    PACIASP      ; ARMv8.3+
  0x008: A9BF7BFD    STP X29, X30, [SP, #-16]!
  0x00C: 910003FD    MOV X29, SP
  ; ... 函数体 ...
  0x0XX: A8C17BFD    LDP X29, X30, [SP], #16
  0x0YY: D503231F    AUTIASP      ; ❌ setup1.S 会 NOP 化这条
  0x0ZZ: D65F03C0    RET

; 或者(不同编译选项)

paging_init:
  0x000: A9BF7BFD    STP X29, X30, [SP, #-16]!  ; 无 PAC/BTI
  0x004: 910003FD    MOV X29, SP
  ; ...

案例 2:setup1.S 的错误逻辑分析

当前代码试图做什么

目的(推测):
  1. 检测 paging_init 是否使用 PAC
  2. 如果使用,移除 PAC 保护(通过 NOP 化 AUTIASP)
  3. 避免 PAC 验证失败

实际效果:
  1. 掩码 0xFFFFFD1F 匹配 AUTIASP 而非 BTI
  2. 找到并 NOP 化 AUTIASP
  3. 导致 PAC 验证失败 → 崩溃

为什么要移除 PAC?(错误的想法)

担心:
  paging_init 被劫持(B _paging_init)
  ↓
  _paging_init 执行
  ↓
  恢复 paging_init 并调用
  ↓
  paging_init 返回时 PAC 验证
  ↓
  担心验证失败?

实际上:
  调用原始 paging_init 时,LR 是正确的
  PACIASP 会重新签名 LR
  AUTIASP 会正确验证
  完全不需要移除!

🔍 识别 PAC/BTI 的技巧

通过特征码识别

# 反汇编工具
def identify_security_feature(instructions):
    has_pac = False
    has_bti = False
    
    # 检查前几条指令
    for i, insn in enumerate(instructions[:10]):
        if insn == 0xD503245F:  # BTI C
            has_bti = True
        elif insn == 0xD503249F:  # BTI J
            has_bti = True
        elif insn == 0xD50324DF:  # BTI JC
            has_bti = True
        elif insn == 0xD503233F:  # PACIASP
            has_pac = True
        elif insn == 0xD503237F:  # PACIBSP
            has_pac = True
    
    # 检查函数末尾
    for insn in instructions[-10:]:
        if insn == 0xD503231F:  # AUTIASP
            has_pac = True
        elif insn == 0xD503237F:  # AUTIBSP
            has_pac = True
    
    return has_pac, has_bti

# 使用
instructions = [0xD503245F, 0xD503233F, 0xA9BF7BFD, ...]
pac, bti = identify_security_feature(instructions)
print(f"PAC: {pac}, BTI: {bti}")

🛡️ PAC/BTI 绕过技术(安全研究)

PAC 绕过方法

方法 1:泄漏 PAC 密钥

如果能读取 APIAKey_EL1 等寄存器
→ 可以自己计算正确的 PAC
→ 伪造签名指针

防御:密钥存储在安全寄存器,无法直接读取

方法 2:重用已签名的指针

// 如果有多个相同上下文的签名指针
void *ptr1 = sign_pointer(0x12345, SP);  // 签名 A
void *ptr2 = sign_pointer(0x12345, SP);  // 签名 A(相同)

// 可以互相替换
// 但无法跨上下文使用

方法 3:NOP 化验证指令(KernelPatch 的方法)

// 直接修改代码,移除 AUTIASP
// ❌ 危险:会导致崩溃
// ✅ 更好的方法:不要劫持使用 PAC 的函数

BTI 绕过方法

方法 1:禁用 BTI(内核级)

// 清除 SCTLR_EL1.BT 位
uint64_t sctlr_el1;
asm volatile("mrs %0, sctlr_el1" : "=r"(sctlr_el1));
sctlr_el1 &= ~(1UL << 50);  // 清除 BT 位
asm volatile("msr sctlr_el1, %0" : : "r"(sctlr_el1));

// 现在 BTI 不再生效

方法 2:在 BTI 之前插入 Hook

func:
  BTI C           ← 不 hook 这里
  STP X29, X30, [SP, #-16]!  ← hook 这里

KernelPatch 的做法

; 直接用 B 指令覆盖第一条指令
; B 指令跳转不触发 BTI 检查
; 所以不需要特殊处理

🎯 在 Hook 中正确处理 PAC/BTI

Hook 函数入口的正确方法

// kernel/base/hook.c

hook_err_t hook_prepare(hook_t *hook)
{
    // 1. 读取前几条指令
    for (int i = 0; i < TRAMPOLINE_NUM; i++) {
        hook->origin_insts[i] = *((uint32_t *)hook->origin_addr + i);
    }
    
    // 2. 检测 BTI
    if (hook->origin_insts[0] == ARM64_BTI_C || 
        hook->origin_insts[0] == ARM64_BTI_J ||
        hook->origin_insts[0] == ARM64_BTI_JC) {
        // ✅ 没问题,B 指令会跳过 BTI
    }
    
    // 3. 在重定位代码开头插入 BTI JC
    uint32_t *bti = hook->relo_insts + hook->relo_insts_num;
    bti[0] = ARM64_BTI_JC;  // 0xD50324DF
    bti[1] = ARM64_NOP;
    hook->relo_insts_num += 2;
    
    // 4. 重定位指令(包括 PACIASP/AUTIASP)
    for (int i = 0; i < hook->tramp_insts_num; i++) {
        // ✅ PACIASP/AUTIASP 会被当作普通指令直接复制
        // 因为它们不是 PC 相对寻址
        relocate_inst(hook, ...);
    }
    
    return HOOK_NO_ERR;
}

📖 总结

PAC (Pointer Authentication Code)

目的:防止指针篡改
机制:在指针高位添加签名
关键指令:PACIA*/AUTIA*, PACIB*/AUTIB*
保护对象:返回地址、函数指针、数据指针
失败表现:指针被修改为非法地址 → 访问异常

特点

  • ✅ 硬件加速,性能开销小
  • ✅ 透明保护,应用无需修改
  • ❌ 只保护指针,不保护数据
  • ❌ 密钥泄漏会失效

BTI (Branch Target Identification)

目的:防止跳转到函数中间
机制:标记合法的跳转目标
关键指令:BTI C, BTI J, BTI JC
保护对象:函数入口点
失败表现:BTI 异常 → 崩溃

特点

  • ✅ 防御 ROP/JOP 攻击
  • ✅ 编译器自动插入
  • ❌ 只保护跳转,不保护顺序执行
  • ❌ 可以通过禁用绕过

在 KernelPatch 中的正确处理

  1. 不要 NOP 化 PAC 指令(PACIASP/AUTIASP)
  2. 不要修改 BTI 的备份(直接备份原始值)
  3. 删除 setup1.S 中的 BTI 检测代码
  4. 在 relo_insts 开头添加 BTI JC
  5. 使用 B 指令劫持(跳过 BTI 检查)

🔗 相关文档


📚 参考资料

ARM 官方文档

  • ARM Architecture Reference Manual ARMv8
  • ARMv8.3-A Pointer Authentication
  • ARMv8.5-A Branch Target Identification

Linux 内核文档

  • Documentation/arm64/pointer-authentication.rst
  • Documentation/arm64/branch-protection.rst

研究论文

  • "PAC it up: Towards Pointer Integrity using ARM Pointer Authentication"
  • "Examining Pointer Authentication on the iPhone XS"

文档版本:2.0
最后更新:2026-06-26
相关 KernelPatch 版本:0.13.2+

评论

  • 还没有评论,来说点什么吧。

无需注册或登录,填个昵称即可评论。