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 实际上是做啥的? [关闭]