c++ cfi控制流完整性 c++ clang的cfi如何防止rop攻击

酷宇大大_7566

酷宇大大_7566

2026-03-20

760人浏览

原创

clang的cfi通过编译期剔除非法间接调用目标来阻断rop,运行时仅允许跳转到同类型签名的已知函数地址;它不防护ret指令、sigreturn/setcontext及数据流劫持,且要求全量编译以避免签名不匹配。

c++ cfi控制流完整性 c++ clang的cfi如何防止rop攻击

CFI 在 Clang 中如何实际阻断 ROP 链构造

Clang 的 CFI(Control Flow Integrity)不是靠“检测异常跳转”来防 ROP,而是编译期就**砍掉非法间接调用的合法目标**。它不阻止你跳,而是让绝大多数 gadget 失去被 call/jmp 到的资格——因为那些地址根本不在 CFI 允许的 target set 里。

  • 启用 -fsanitize=cfi 后,所有虚函数调用、函数指针调用、成员函数指针调用都会插入运行时检查,只允许跳转到同一类型签名的、编译期已知的函数地址
  • 关键点:CFI 不保护 ret 指令本身(那是 Shadow Stack 或 CET 的事),但它让 call *%rax 这类指令在跳向伪造的 gadget 地址时直接 abort,因为 %rax 指向的地址未通过类型签名验证
  • 典型失败现象:runtime error: control flow integrity check for type 'void (int)' failed (0x7f... != 0x55...) —— 这不是警告,是进程立即终止

为什么虚函数表劫持在 CFI 下大概率失效

传统 vtable 覆盖攻击依赖将虚函数指针替换成 gadget 地址,但 CFI 会强制校验每次虚调用的目标是否属于该虚函数声明的 exact 类型签名。哪怕你覆盖了 vtable 项,只要目标地址没被编译器标记为“可作为该虚函数的实现”,调用就会崩溃。

  • Clang 对每个虚函数生成唯一 type identifier(基于函数签名哈希),vtable 条目存储的是带 signature 标签的函数指针,而非裸地址
  • 即使你用堆喷控制了 vtable 内存,覆盖进去的 gadget 地址无法通过 runtime signature check,vptr->foo() 会在进入 foo 前就触发 cfi-check-failure
  • 注意:CFI 默认不保护纯函数指针(如 void (*fp)() = &gadget;),除非启用 -fsanitize=cfi-icall 显式开启间接调用检查

-fsanitize=cfi-icall 和 -fsanitize=cfi-vcall 的区别与坑

这两个开关控制不同粒度的检查,开错组合会导致漏防或误杀,且影响性能和链接行为。

C++ Code Review Master
C++ Code Review Master

组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。

下载
  • -fsanitize=cfi-vcall:只检查虚函数调用(vtable lookup + call),开销较小,兼容性好,是 CFI 最常用子集
  • -fsanitize=cfi-icall:检查所有间接调用(call *%rax 形式),包括函数指针、回调、dlsym 返回值等,但要求整个程序(含所有 .so)都用相同 CFI 签名编译,否则 dlopen 会失败或调用崩溃
  • 常见错误:只给主程序加 -fsanitize=cfi-icall,但链接的第三方库没重编译 → 运行时报 undefined symbol: __cfi_check 或直接 abort
  • 必须全量编译:静态库、动态库、主程序都要用相同 Clang 版本 + 相同 CFI flag 编译,否则类型签名不匹配

CFI 无法防御的 ROP 场景(别误信“开了 CFI 就安全了”)

CFI 是强约束,但不是万能盾。它对某些 ROP 利用路径完全无感,尤其当攻击不依赖间接跳转时。

  • 不检查 ret 指令:栈上布置好 gadget 链后单纯靠 ret 指令“滑动”,CFI 完全不干预(需 CET / Shadow Stack 补位)
  • 不防护信号上下文切换:若攻击者触发 sigreturn 或 setcontext,直接重写寄存器状态,绕过所有 CFI 插入的检查点
  • 不处理数据流劫持:比如篡改 std::string 的内部指针导致后续 memcpy 覆盖关键内存,这类非控制流篡改 CFI 不管
  • 最易忽略的一点:CFI 默认不验证 longjmp 目标,除非额外启用 -fsanitize=cfi-jump(Clang 15+),且仍不覆盖所有 setjmp/longjmp 变体

CFI 的真正价值在于大幅提高 ROP 开发门槛——它逼攻击者要么放弃通用 gadget,要么转向更难利用的内核态或 JIT 绕过路径。但只要存在一个未受保护的间接调用点,或一个未启用 CFI 的共享库,整条防线就可能松动。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

c++速学教程(入门到精通)
c++速学教程(入门到精通)

c++怎么学习?c++怎么入门?c++在哪学?c++怎么学才快?不用担心,这里为大家提供了c++速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.02

5959

3

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.18

949

5

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.25

2740

5

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.02

5959

3

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.08.29

2925

6

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

2025.08.29

3728

10

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

2025.08.29

2625

10

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.11.23

636

5

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

2025.11.27

351

13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习