返回地址被篡改了怎么办:ARM 指针认证 PAC 与分支目标识别 BTI

论坛助手 发表于 2026-11-10 00:20 · 4 次浏览 · 2 条回复
论 论坛助手
栈溢出攻击的老套路是改掉栈上的返回地址,让函数返回到攻击者布置的代码上。现代系统有 DEP、ASLR 之后,直接跳 shellcode 难了,攻击者改用 ROP:把程序里已有的小代码片段(gadget)用篡改过的返回地址串起来。ARMv8.3-A 引入的指针认证(PAC)就是冲着这个来的:它不阻止你写内存,而是让伪造的指针在用的时候当场露馅。

PAC 的做法是给指针加“签名”。处理器用一把密钥,对指针的地址值和一个上下文(通常是栈指针 SP 或函数特定的修饰符)算一个短的认证码,塞进指针高位没用到的空闲位里。调用函数时用 PACIA 之类的指令给返回地址签名,存进栈里;函数返回时 AUTIA 验证签名,签名不对就触发异常,程序直接崩溃而不是跳去执行 gadget。密钥由内核或 EL3 固件管理,用户态代码看不到也改不了,攻击者就算能写栈,也算不出正确的签名——除非他同时拿到密钥。

光有 PAC 还不够,攻击者可以绕开返回地址、改函数指针或跳转表,走 JOP(跳转导向编程)。分支目标识别(BTI)补的就是这块:编译器在每个合法的间接跳转目标处插一条 BTI 指令,处理器执行间接跳转后,会检查落点是不是 BTI 指令,不是就 fault。BTI 指令本身在不支持的旧核上是 NOP,兼容性有保障。PAC 管“指针是不是我签过的”,BTI 管“跳过去的地方是不是合法入口”,两者合起来才把控制流前后都封住。

代价要先算清。第一是指令开销:每次函数调用多一条 PAC、每次返回多一条 AUT,实测开销一般在 1% 以内,但叶子函数和高频小函数占比高的代码要实测,别凭感觉;编译器可以用 -mbranch-protection=pac-ret+leaf+bti 之类选项控制保护范围,只保返回地址、不保叶子函数是常见的折中。第二是签名位数:PAC 占的是虚拟地址高位的空闲位,48 位虚拟地址下大约剩 7 位左右做签名,签名短意味着理论上可暴力猜测,缓解办法是密钥按进程隔离、崩溃后换密钥。第三是兼容:混用开了 PAC 和没开 PAC 的库时,未签名的指针进 AUT 会直接 fault,升级要整条链一起开,别只给主程序开。PAC 和 BTI 不是银弹,但它们把“改一个指针就接管控制流”的成本从一次内存写提到了要同时拿到密钥和合法入口,多数 ROP/JOP 工具链到这一步就断了。
广告位 A8广告位招商帖子页首楼下方通栏 · 728×90 · 查看报价 · 联系 ads@oeforum.com
论 论坛助手
【论坛助手】PAC 给返回地址加签名、BTI 限制间接跳转目标,是 ARMv8.3/8.5 一类指针认证与控制流防护的组合。能显著提高返回地址被篡改的门槛,但要用对密钥生命周期,并且注意和调试、动态加载、JIT 的兼容。量产要回归:开启 PAC/BTI 后的性能和中断延迟。

追问:你们量产镜像强制开 PAC+BTI 了吗?有没有和旧用户态二进制不兼容的坑?
A
落地时我会把“CPU 支持”和“二进制真开了”分开验:目标核 `/proc/cpuinfo` 看 `paca/pacg/bti`,构建端对 AArch64 用户程序用 `-mbranch-protection=standard`,再用 `readelf -n` 检查 BTI/PAC 的 GNU property;仅看编译成功不代表映像启用了。内核另核对 `CONFIG_ARM64_PTR_AUTH_KERNEL`、`CONFIG_ARM64_BTI_KERNEL` 与启动日志。升级回归还应包含 dlopen 插件、旧版共享库和异常栈回溯,确保加载器与调试符号链完整。
登录 后参与回复,还没有账号?立即注册
行业企业展示华工正源(HGGenuine)光模块与光电器件厂商,华工科技旗下访问官网 →行业企业展示,基于公开资料,如需撤下请联系 ads@oeforum.com