如何在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
1条答案
按热度按时间zed5wv101#
在@fuz的建议下,我在
-march=armv8.5-a
的基础上添加了-arch arm64e
,这将二进制代码标记为arch64e
而不是arm64
,例如file
:尝试使用
Killed: 9
运行它失败了。在谷歌上搜索一下,我发现您需要在系统范围内启用arm64e
体系结构,使用 Boot 标志-arm64e_preview_abi
:但是,只有在禁用系统完整性之后才能这样做。因此,我在恢复模式下重新启动,执行
csrutil disable
,在正常模式下重新启动,设置启动标志,在恢复模式下重新启动,执行csrutil enable
,在正常模式下重新启动。没有运气,仍然是Killed: 9
。使用nvram -p
检查启动标志,选项-arm64e_preview_abi
在那里。事实上,似乎你需要保持系统完整性 * 禁用 * 才能运行
arm64e
二进制文件。这是一个遗憾,因为PAC指令应该 * 增加 * 应用程序的安全性,但你需要 * 禁用 * 系统完整性(从而降低系统安全性)来运行更安全的应用程序。无论如何,经过几次重新启动和系统完整性永久禁用,应用程序运行。
然而,它最初产生奇怪的结果:
输入地址在MSB部分已经有了额外的信息。PACIA通常会将其 * 删除 *(两种情况下为
0000
,一种情况下为0080
),AUTIA不会恢复以前的地址。实际上,使用
-arch arm64e
也会影响代码生成,使用&&lab
中的真实的代码地址会生成隐式指针验证:所以,我的显式PACIA处理了一个已经有PAC的地址,这破坏了以前的PAC。
为了展示PACIA的确切行为,我简单地使用了一个整数值
data = 0x12345678
来控制显式PACIA的确切输入值。这一次,它按预期工作:
现在我们知道了如何在macOS上启用PAC。令人不安的一点是,你需要保持系统完整性永久禁用。对于一些开发者测试来说很好,但不适合生产使用。