assembly 如何在macOS上启用Arm指针验证码(PAC)?

f8rj6qna  于 2023-01-21  发布在  Mac
关注(0)|答案(1)|浏览(248)

如何在macOS上启用Arm指针验证码(PAC)?
我有一台配备苹果M1芯片的MacBook Air。CPU采用Arm架构版本v8.5-A,其中包括指针验证码(PAC)指令。该功能通常用于防止恶意代码通过ROP链注入,通常利用堆栈上的缓冲区溢出。
我试图通过简单的代码演示一些PAC指令的行为。
在原生macOS上,验证指令(PACIA、PACDA等)似乎没有任何作用,就好像该功能没有在CPU中实现一样。这是可能的,因为大多数Arm功能是可选的。然而,在同一台MacBook上安装Linux虚拟机后,相同的PAC指令在Linux VM中工作。在同一台物理CPU上,因此支持PAC功能。
因此,必须有某种方法在进程的基础上禁用PAC的行为:在原生macOS应用程序中不活动,在Linux VM中运行的应用程序中活动。
如何在macOS上启用PAC?
下面的示例代码pacia.c
1.显示指令地址,
1.使用PACIA指令添加PAC并显示它
1.使用AUTIA指令对其进行“验证”(如果PAC正确,则恢复其原始值)并显示。
我们希望第二个地址的MSB部分被PAC更改。我们希望第三个地址与第一个地址相同。

#include <stdio.h>
#include <inttypes.h>

// noinline for easier inspection of generated code in main
__attribute__((noinline)) void report(uint64_t value)
{
    printf("%016" PRIX64 "\n", value);
}

int main(int argc, char* argv[])
{
    uint64_t data = (uint64_t)(&&lab);
    uint64_t modifier = 2;
 lab:
    report(data);
    asm("pacia %[reg], %[mod]" : [reg] "+r" (data) : [mod] "r" (modifier) : );
    report(data);
    asm("autia %[reg], %[mod]" : [reg] "+r" (data) : [mod] "r" (modifier) : );
    report( data);
}

编制:

cc -O2 -march=armv8.5-a pacia.c -o pacia

在主机系统macOS 13.1上,PACIA指令不会修改要验证的地址。

$ ./pacia 
00000001028B3F50
00000001028B3F50  <-- not modified, no PAC
00000001028B3F50
$ ./pacia 
000000010080FF50
000000010080FF50
000000010080FF50
$ ./pacia 
0000000102A7FF50
0000000102A7FF50
0000000102A7FF50
$

在Ubuntu 22.10虚拟机上,地址的MSB部分由PACIA使用PAC更新,并由AUTIA正确删除。

$ ./pacia
0000AAAACF3D0680
0043AAAACF3D0680  <-- 0043 PAC added
0000AAAACF3D0680  <-- PAC removed, address restored
$ ./pacia
0000AAAAD7CF0680
0023AAAAD7CF0680
0000AAAAD7CF0680
$ ./pacia
0000AAAAAAE00680
0036AAAAAAE00680
0000AAAAAAE00680

为了确保安全,我检查了macOS上生成的代码。实际上使用了PACIA指令。

cc -O2 -march=armv8.5-a pacia.c -S -o pacia.s

在macOS上使用clang 14.0.0生成的main()代码:

_main:                                  ; @main
        .cfi_startproc
; %bb.0:
Ltmp0:                                  ; Block address taken
; %bb.1:
        stp     x20, x19, [sp, #-32]!           ; 16-byte Folded Spill
        stp     x29, x30, [sp, #16]             ; 16-byte Folded Spill
        add     x29, sp, #16
        .cfi_def_cfa w29, 16
        .cfi_offset w30, -8
        .cfi_offset w29, -16
        .cfi_offset w19, -24
        .cfi_offset w20, -32
Lloh2:
        adrp    x19, lCPI1_0@PAGE
Lloh3:
        ldr     x19, [x19, lCPI1_0@PAGEOFF]  <--- data = (uint64_t)(&&lab) in x19
        mov     x0, x19       <--- x19 is printed (first time)
        bl      _report
        mov     w20, #2       <--- modifier = 2 in x20
        ; InlineAsm Start
        pacia   x19, x20      <--- x19 should receive a PAC code
        ; InlineAsm End
        mov     x0, x19       <--- x19 is printed (second time)
        bl      _report
        ; InlineAsm Start
        autia   x19, x20
        ; InlineAsm End
        mov     x0, x19
        bl      _report
        mov     w0, #0
        ldp     x29, x30, [sp, #16]             ; 16-byte Folded Reload
        ldp     x20, x19, [sp], #32             ; 16-byte Folded Reload
        ret
zed5wv10

zed5wv101#

在@fuz的建议下,我在-march=armv8.5-a的基础上添加了-arch arm64e,这将二进制代码标记为arch64e而不是arm64,例如file

$ file pacia
pacia: Mach-O 64-bit executable arm64e

尝试使用Killed: 9运行它失败了。在谷歌上搜索一下,我发现您需要在系统范围内启用arm64e体系结构,使用 Boot 标志-arm64e_preview_abi

sudo nvram boot-args=-arm64e_preview_abi

但是,只有在禁用系统完整性之后才能这样做。因此,我在恢复模式下重新启动,执行csrutil disable,在正常模式下重新启动,设置启动标志,在恢复模式下重新启动,执行csrutil enable,在正常模式下重新启动。没有运气,仍然是Killed: 9。使用nvram -p检查启动标志,选项-arm64e_preview_abi在那里。
事实上,似乎你需要保持系统完整性 * 禁用 * 才能运行arm64e二进制文件。这是一个遗憾,因为PAC指令应该 * 增加 * 应用程序的安全性,但你需要 * 禁用 * 系统完整性(从而降低系统安全性)来运行更安全的应用程序。
无论如何,经过几次重新启动和系统完整性永久禁用,应用程序运行。
然而,它最初产生奇怪的结果:

$ ./pacia
5F3A800100787F3C
0000000100787F3C
2000000100787F3C
$ ./pacia
B273800102563F3C
0080000102563F3C
BFFF800102563F3C
$ ./pacia
555C80010065FF3C
000000010065FF3C
200000010065FF3C
$

输入地址在MSB部分已经有了额外的信息。PACIA通常会将其 * 删除 *(两种情况下为0000,一种情况下为0080),AUTIA不会恢复以前的地址。
实际上,使用-arch arm64e也会影响代码生成,使用&&lab中的真实的代码地址会生成隐式指针验证:

add x16, x16, Ltmp0@PAGEOFF
    mov x17, #13503
    pacia   x16, x17. <-- added PACIA due to -arch arm64e
    mov x19, x16
    mov x0, x16
    bl  _report
    mov w20, #2
    ; InlineAsm Start
    pacia   x19, x20.  <-- my explicit PACIA
    ; InlineAsm End
    mov x0, x19
    bl  _report

所以,我的显式PACIA处理了一个已经有PAC的地址,这破坏了以前的PAC。
为了展示PACIA的确切行为,我简单地使用了一个整数值data = 0x12345678来控制显式PACIA的确切输入值。
这一次,它按预期工作:

0000000012345678
2503000012345678
0000000012345678

现在我们知道了如何在macOS上启用PAC。令人不安的一点是,你需要保持系统完整性永久禁用。对于一些开发者测试来说很好,但不适合生产使用。

相关问题