ARMv8.3-A PA在GCC里的相关实现

栏目: 服务器 · 编程工具 · 发布时间: 7年前

内容简介:ARMv8.3-A PA在GCC里的相关实现

ARMv8.3-A PA在GCC里的相关实现

@(GCC)[GNU System-Security] – zet

00 导引

很多的安全问题通过攻击者人为制造的恶意指针,然后处理器解释恶意指针为代码地址,然 后执行恶意指针所指的代码,这里的代码恰恰就是攻击者预先准备的恶意代码。所以对于指 针的合法性问题一直是安全防御的重点,有很多的防御手段以保证指针没有被污染为重点: SSP(software statck protection),CFI(control flow integrity)。这些防御手段的核心 思想就是检查指针有没有被更改过,这些防御手段几乎都需要多条软件指令来实现。

在2016年的时候ARMv8架构里增加了ARMv8.3-A,这个版本里增加了Pointer Authentication 指令:强化指针安全的一种机制。硬件级别的安全防御可以预见将会是趋势,因为不论是从 代码执行效率代码的清晰度还是加固的彻底程度来说都明显优于软件级别的实现。所以这也 是一个非常让人兴奋的ARM处理器特性。

本文的描述基于GCC最新的一个release: GCC-7.1.0,由于PA是一个非常新的特性,即便是 最新的GCC版本(GCC-7.1.0)目前也只是做了一个大概雏型,计划的让GCC选择key(A/B key) 用作返回地址签名的选项:-msign-return-address-key=key_name(key_name可选的值为: a_key/b_key分别对应A/B key)并没有在GCC-7.1.0里完成。目前相关key的代码还在GNU binutils里进行。所以本文只是在GCC-7.1.0现有代码的基础上做一个分析。后面随着相关 代码在GCC里的merge再进行补充完善。

01 细节

ARMv8.3-A能够支持PA的根本原因是因为当前的64位的 linux 下,地址空间也就是指针的实际 长度并不是64位,比如三级页表的情况下实际有效值只有低40位,这样高位的未使用的bit 就可以用来存储特定于某个指针的签名:Pointer Authentication Code(PAC)。

总得来说很简单,就是处理器内部在硬件级别以指针为一个参数来计算其特别的PAC然后存 储在指针的未使用的高位,计算的算法采用了一种叫做QARMA的可调分组密码(tweakable block ciphers)。计算/验证PAC的时候有(数据/指令 x A-key/B-key)四种组合指令: PAC*/AUT*,还有另外一种使用普通key计算PAC的指令PACGA。ARMv8.3-A还提供了检测PAC 的跳转指令族BRA*。

key存放在处理器内部的不可见寄存器里。所以攻击者很难不知道key的情况下得到PAC。

02 示例

在SSP(software statck protection)下如果不使用PA指令和使用PA指令的对比示例如下:

不使用PA指令

+--------------------------------------------------------------------+
|           | No SSP                    |   SSP                      |
+-----------|---------------------------|----------------------------+
|           | SUB sp, sp, #0x40         | SUB sp, sp, #0x50          |
|           | STP x29, x30, [sp,#0x30]  | STP x29, x30, [sp, #0x40]  |
| Prologue  | ADD x29, sp, #0x30        | ADD x29, sp, #0x40         |
|           | ...                       | ` ADRP x3, {pc} `          |
|           |                           | ` LDR x4, [x3, #SSP] `     |
|           |                           | ` STR x4, [sp, #0x38] `    |
|           |                           | ...                        |
+-----------|---------------------------|----------------------------+
|           | ...                       | ...                        |
|           | LDP x29,x30,[sp,#0x30]    | ` LDR x1, [x3, #SSP] `     |
| Epilogue  | ADD sp,sp,#0x40           | ` LDR x2, [sp, #0x38] `    |
|           | RET                       | ` CMP x1, x2 `             |
|           |                           | ` B.NE __stack_chk_fail `  |
|           |                           | LDP x29, x30, [sp, #0x40]  |
|           |                           | ADD sp, sp, #0x50          |
|           |                           | RET                        |
+--------------------------------------------------------------------+

使用PA指令

+--------------------------------------------------------------------+
|           | No SSP                    |   SSP                      |
+-----------|---------------------------|----------------------------+
|           | SUB sp, sp, #0x40         | ` PACIASP `                |
|           | STP x29, x30, [sp,#0x30]  | SUB sp, sp, #0x40          |
| Prologue  | ADD x29, sp, #0x30        | STP x29, x30, [sp,#0x30]   |
|           | ...                       | ADD x29, sp, #0x30         |
|           |                           | ...                        |
+-----------|---------------------------|----------------------------+
|           | ...                       | ...                        |
|           | LDP x29,x30,[sp,#0x30]    | LDP x29,x30,[sp,#0x30]     |
| Epilogue  | ADD sp,sp,#0x40           | ADD sp,sp,#0x40            |
|           | RET                       | ` AUTIASP `                |
|           |                           | RET                        |
+--------------------------------------------------------------------+

注意反引号里指令的对应。可以看出PA的优势。

03 实现

下面来查看要输出上面示例代码在GCC里的实现。

因为GCC在后端和处理调用参数的时候使用了大量的文本处理程序来自动生成C++源代码,所 以在GCC后端特性的实现和参数的实现上基本就是一些配置文件加回调的特化C++代码。

void
aarch64_expand_prologue (void)
{
  ... // 删掉不重要代码

  /* Sign return address for functions.  */
  if (aarch64_return_address_signing_enabled ())
    {
      // 指导输出PACIASP,这里的insn再经过一个简单匹配就是输出汇编代码了。
      insn = emit_insn (gen_pacisp ());
      add_reg_note (insn, REG_CFA_TOGGLE_RA_MANGLE, const0_rtx);
      RTX_FRAME_RELATED_P (insn) = 1;
    }

  ... // 删掉不重要代码

}

void
aarch64_expand_epilogue (bool for_sibcall)
{
  ... // 删掉不重要代码

  if (aarch64_return_address_signing_enabled ()
      && (for_sibcall || !TARGET_ARMV8_3 || crtl->calls_eh_return))
    {
      // 指导输出AUTIASP
      insn = emit_insn (gen_autisp ());
      add_reg_note (insn, REG_CFA_TOGGLE_RA_MANGLE, const0_rtx);
      RTX_FRAME_RELATED_P (insn) = 1;
    }

  ... // 删掉不重要代码

}

上面输出的insn都是GCC rtl的一种表示。

04 总结

虽然硬件级别的加固特性目前很少,PA这种指针级别的加密验证更是属于开创阶段,不过由 于硬件加固的优势:代码清晰,效率高,易于实现。预计在安全问题越来越重要的今天将会 是未来的发展方向。

live long and prosper.


以上所述就是小编给大家介绍的《ARMv8.3-A PA在GCC里的相关实现》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

JavaScript入门经典

JavaScript入门经典

Paul Wilton、Jeremy McPeak / 施宏斌 / 清华大学出版社 / 2009-2 / 98.00元

《Java Script入门经典(第3版)》首先介绍了J avaScript的基本语法,并介绍了如何发挥JavaScript中对象的威力。《Java Script入门经典(第3版)》还介绍了如何操纵最新版本浏览器所提供的BOM对象。在《Java Script入门经典(第3版)》的高级主题中,将介绍如何使用cookie,以及如何应用DHTML技术使Web页面焕发动感和活力。另外,《Java Scri......一起来看看 《JavaScript入门经典》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码