我一直在尝试理解GCC生成的汇编语言代码,并且经常在包括_start()在内的许多函数的开头遇到此指令,但找不到任何解释其用途的指南:
_start()
31-0000000000001040 <_start>: 32: 1040: f3 0f 1e fa endbr64 33- 1044: 31 ed xor ebp,ebp
mqxuamgl1#
它代表“结束分支64位”(也有一个32位计数器部分)-或者更准确地说,终止间接分支64位。操作如下:
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;
如果指令不能清除TRACKER标志,则CPU生成#CP异常。换句话说,如果黑客能够改变间接跳转的目的地址,即使目的是法律的的汇编代码,程序也很可能终止。否则,该指令被认为是NOP。在其他情况下,CET特性用于确保间接分支实际上转到有效位置。这允许额外的安全性。以下是Intel的一段话:ENDBRANCH(详见第73节)是一条新指令,用于标记程序中间接调用和跳转的有效跳转目标地址。该指令操作码被选择为是遗留机器上的NOP的指令操作码,使得用ENDBRANCH新指令编译的程序在没有CET实施的情况下继续在旧机器上起作用。在支持CET的处理器上,ENDBRANCH仍然是NOP,并且主要被处理器流水线用作标记指令以检测控制流违规。CPU实现跟踪间接jmp和call指令的状态机。当看到其中一条指令时,状态机将从IDLE转到WAIT_FOR_ENDBRANCH状态。在WAIT_FOR_ENDBRANCH状态下,程序流中的下一条指令必须是ENDBRANCH。如果未看到ENDBRANCH,则处理器导致控制保护异常(#CP),否则状态机移回IDLE状态。作为旁注,可以告诉处理器不允许ENDBR 64。这是用前缀(3Eh)完成的。这对于地址位于只读存储器中的表中的交换机等情况非常有用。但是,CPU在许多情况下会忽略该前缀。
NOP
CET
xtupzzrd2#
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)攻击的硬件保护,这些攻击操纵控制流,以便将现有代码重新用于恶意目的。它的两大特点是
endbr64
endbr32
虽然CET在当前的处理器代中才慢慢可用,但它是already supported as of GCC 8,默认情况下插入endbrXX指令。操作码在较旧的处理器上被选择为无操作,使得如果不支持CET,则忽略该指令;同样的情况发生在具有CET能力的处理器上,其中间接分支跟踪被禁用。endbr64是做什么的?
endbrXX
CR4.CET
IA32_U_CET
IA32_S_CET
CPU建立一个小的状态机来跟踪最后一个分支的类型。举个例子:
some_function: mov rax, qword [vtable+8] call rax ... check_login: endbr64 ... authenticated: mov byte [is_admin], 1 ... ret
现在让我们简要地看两个场景。
some_function
vtable
check_login
TRACKER = WAIT_FOR_ENDBRANCH
TRACKER = IDLE
攻击者以某种方式操纵了vtable,使得vtable+8现在指向authenticated。
vtable+8
authenticated
mov byte [is_admin], 1
#CP
2条答案
按热度按时间mqxuamgl1#
它代表“结束分支64位”(也有一个32位计数器部分)-或者更准确地说,终止间接分支64位。
操作如下:
如果指令不能清除TRACKER标志,则CPU生成#CP异常。换句话说,如果黑客能够改变间接跳转的目的地址,即使目的是法律的的汇编代码,程序也很可能终止。
否则,该指令被认为是
NOP
。在其他情况下,
CET
特性用于确保间接分支实际上转到有效位置。这允许额外的安全性。以下是Intel的一段话:ENDBRANCH(详见第73节)是一条新指令,用于标记程序中间接调用和跳转的有效跳转目标地址。该指令操作码被选择为是遗留机器上的NOP的指令操作码,使得用ENDBRANCH新指令编译的程序在没有CET实施的情况下继续在旧机器上起作用。在支持CET的处理器上,ENDBRANCH仍然是NOP,并且主要被处理器流水线用作标记指令以检测控制流违规。CPU实现跟踪间接jmp和call指令的状态机。当看到其中一条指令时,状态机将从IDLE转到WAIT_FOR_ENDBRANCH状态。在WAIT_FOR_ENDBRANCH状态下,程序流中的下一条指令必须是ENDBRANCH。如果未看到ENDBRANCH,则处理器导致控制保护异常(#CP),否则状态机移回IDLE状态。
作为旁注,可以告诉处理器不允许ENDBR 64。这是用前缀(3Eh)完成的。这对于地址位于只读存储器中的表中的交换机等情况非常有用。但是,CPU在许多情况下会忽略该前缀。
xtupzzrd2#
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)攻击的硬件保护,这些攻击操纵控制流,以便将现有代码重新用于恶意目的。
它的两大特点是
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
从virtual method tablevtable
中检索虚拟方法check_login
的地址并调用它。1.由于这是间接调用,CET状态机被激活并设置为在下一条指令(
TRACKER = WAIT_FOR_ENDBRANCH
)上触发。1.下一个指令是
endbr64
,因此间接调用被认为是“安全的”,并且继续执行(endbr64
仍然表现为no-op)。状态机复位(TRACKER = IDLE
)。攻击者以某种方式操纵了
vtable
,使得vtable+8
现在指向authenticated
。some_function
从虚拟方法表vtable
中检索authenticated
的地址并调用它。1.由于这是一个间接调用,因此CET状态机被激活并设置为在下一条指令(
TRACKER = WAIT_FOR_ENDBRANCH
)时触发。1.下一条指令是
mov byte [is_admin], 1
,而不是预期的endbr64
指令。CET状态机推断控制流被操纵,并引发#CP
故障,终止程序。如果没有CET,控制流操作将不会被注意到,攻击者将获得管理员权限。
总之,英特尔CET的间接分支跟踪功能可确保间接调用和跳转只能重定向到以
endbr64
指令开头的函数。请注意,这并不能确保调用 * right * 函数--如果攻击者改变控制流,跳转到另一个同样以
endbr64
开头的函数,状态机将不会抱怨并继续执行程序。然而,这仍然大大减少了攻击面,因为大多数JOP/COP攻击的目标是功能中间的指令(甚至直接“跳转到”指令中)。