endbr64 指令实际上是做啥的?

Posted

技术标签:

【中文标题】endbr64 指令实际上是做啥的?【英文标题】:What does the endbr64 instruction actually do?endbr64 指令实际上是做什么的? 【发布时间】:2019-11-16 06:00:44 【问题描述】:

我一直在尝试理解 GCC 生成的汇编语言代码,并且经常在许多函数的开头遇到此指令,包括 _start(),但找不到任何解释其用途的指南:

31-0000000000001040 <_start>:
32:    1040:    f3 0f 1e fa             endbr64 
33-    1044:    31 ed                   xor    ebp,ebp

【问题讨论】:

见this pdf from intel 你通常只会在像 _start 这样的代码中找到它,它已经是机器代码形式,gcc 链接到你的可执行文件(来自 crt0.o 或其他) ,而不是从 C 源发出的 gcc。 (除非您的 GCC 配置了 -fcf-protection=branch 作为默认值,或者您手动使用它。请参阅 gcc.gnu.org/onlinedocs/gcc/x86-Options.html 中的 gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html 和 -mmanual-endbr 【参考方案1】:

它代表“End Branch 64 bit”——或者更准确地说,Terminate Indirect Branch in 64 bit。

这里是操作:

IF EndbranchEnabled(CPL) & EFER.LMA = 1 & CS.L = 1
  IF CPL = 3
  THEN
    IA32_U_CET.TRACKER = IDLE
    IA32_U_CET.SUPPRESS = 0
  ELSE
    IA32_S_CET.TRACKER = IDLE
    IA32_S_CET.SUPPRESS = 0
  FI
FI;

否则该指令被视为NOP

CET 功能用于确保您的间接分支实际上转到有效位置。这允许额外的安全性。以下是英特尔关于它的段落:

ENDBRANCH(详见第 73 节)是一条新指令,用于标记程序中间接调用和跳转的有效跳转目标地址。该指令操作码被选择为旧机器上的 NOP,以便使用 ENDBRANCH 新指令编译的程序继续运行 在没有 CET 强制执行的旧机器上。在支持 CET 的处理器上,ENDBRANCH 仍然是 NOP,主要用作处理器流水线的标记指令以检测控制流违规。 CPU 实现了一个跟踪间接 jmp 和调用指令的状态机。当其中之一 看到指令,状态机从 IDLE 移动到 WAIT_FOR_ENDBRANCH 状态。在 WAIT_FOR_ENDBRANCH 状态下,程序流中的下一条指令必须是 ENDBRANCH。如果未看到 ENDBRANCH,则处理器会导致控制保护异常 (#CP),否则状态机将返回 IDLE 状态。

【讨论】:

答案中提到的第 11 代英特尔® 酷睿™ 处理器数据表的地址,第 1 卷,2021 年 6 月 2 日修订版 006 不起作用。本月有效的链接是cdrdv2.intel.com/v1/dl/getContent/631121 @vitsoft 有趣的是,你引用的文档有我在这里的链接,这确实是死的。由于我有一份解释说明的段落的副本,我想我可以没有链接...【参考方案2】:

endbr64(和endbr32)是Intel's Control-Flow Enforcement Technology (CET)的一部分(另见Intel Software Developer Manual, Volume 1, Chapter 18)。

英特尔 CET 提供针对 Return-oriented Programming (ROP) 和 Jump/Call-oriented Programming (JOP/COP) 攻击的硬件保护,这些攻击操纵控制流以将现有代码重新用于恶意目的。

它的两大特点是

shadow stack 用于跟踪退货地址和 间接分支跟踪,endbr64 是其中的一部分。

虽然 CET 只是在当前的处理器中慢慢变得可用,但它是 already supported as of GCC 8,默认插入 endbrXX 指令。操作码被选择为旧处理器上的无操作,这样如果不支持 CET,则忽略该指令;在禁用间接分支跟踪的支持 CET 的处理器上也会发生同样的情况。


那么endbr64 做了什么?

前提条件:

必须通过将控制寄存器标志 CR4.CET 设置为 1 来启用 CET。 在IA32_U_CET(用户模式)或IA32_S_CET(主管模式)MSR 中设置了用于间接分支跟踪的适当标志。

CPU 设置了一个小型状态机,用于跟踪最后一个分支的类型。举个例子:

some_function:
    mov rax, qword [vtable+8]
    call rax
    ...

check_login:
    endbr64
    ...
authenticated:
    mov byte [is_admin], 1
    ...
    ret

现在让我们简要地看一下两种情况。

无攻击:

    some_function 从virtual method table vtable 中检索虚拟方法check_login 的地址并调用它。 由于这是一个间接调用,因此 CET 状态机被激活并设置为在下一条指令上触发 (TRACKER = WAIT_FOR_ENDBRANCH)。 下一条指令是endbr64,因此间接调用被认为是“安全的”并继续执行(endbr64 仍然表现为空操作)。状态机已重置 (TRACKER = IDLE)。

攻击: 攻击者以某种方式设法操纵vtable,使得vtable+8 现在指向authenticated

    some_function 从虚拟方法表vtable 中检索authenticated 的地址并调用它。 由于这是间接调用,因此 CET 状态机被激活并设置为在下一条指令上触发 (TRACKER = WAIT_FOR_ENDBRANCH)。 下一条指令是mov byte [is_admin], 1,而不是预期的endbr64 指令。 CET 状态机推断控制流被操纵并引发#CP 故障,终止程序。

如果没有 CET,控制流操作将被忽视,攻击者将获得管理员权限。


总之,英特尔 CET 的间接分支跟踪功能确保间接调用和跳转只能重定向到以 endbr64 指令开头的函数。

请注意,这确保调用 right 函数 - 如果攻击者更改控制流以跳转到另一个以 endbr64 开头的函数,状态机不会抱怨并继续执行程序。然而,这仍然大大减少了攻击面,因为大多数 JOP/COP 攻击针对的是中间功能指令(甚至直接“跳入”指令)。

【讨论】:

可以从 ring 3 设置 CR4.CET,还是需要内核支持? CR4 只能在 ring 0 中更改,因此需要内核支持。

以上是关于endbr64 指令实际上是做啥的?的主要内容,如果未能解决你的问题,请参考以下文章

打开文件实际上是做啥的?

“OperationContext.Current.GetCallbackChannel”实际上是做啥的?

AFNetworking 中的 registerHTTPOperationClass 实际上是做啥的? [关闭]

NSLog 实际上是做啥的?

Linux下怎么建立任务计划,shell文件是做啥的?以啥为后缀,前两者之间有啥关系

ImageView android:cropToPadding,它实际上是做啥的?