C++ 怎么使用控制流防护(CFG)和影子栈(Shadow Stack)阻断 ROP 攻击

阿辰同学_7712

阿辰同学_7712

2026-10-23

836人浏览

原创

启用clang/llvm的cfi需组合-fsanitize=cfi-vcall、-fsanitize=cfi-icall、-fsanitize-cfi-cross-dso并强制配合-flto,否则虚函数和跨dso调用校验失效;影子栈还需cpu cet支持、内核≥5.19及-mshstk/-fcf-protection=full协同,缺一不可。

c++ 怎么使用控制流防护(cfg)和影子栈(shadow stack)阻断 rop 攻击

启用 Clang/LLVM 的 CFI 编译选项,必须匹配目标平台特性

Clang 的 -fsanitize=cfi 系列选项是目前最实用的前向边缘(Forward-Edge)CFI 实现,但默认不保护虚函数调用或函数指针——除非你显式启用更严格的子模式。只加 -fsanitize=cfi 会漏掉大量 C++ 多态场景。

实际编译时应组合使用:

  • -fsanitize=cfi -fsanitize-cfi-cross-dso:跨动态库的间接调用也校验(否则 libc.so 中的函数指针调用会被绕过)
  • -fsanitize=cfi-icall:强制校验所有间接调用((*fn_ptr)()、std::function::operator())
  • -fsanitize=cfi-vcall:针对 C++ 虚函数调用插入类型签名检查(依赖 -fvisibility=hidden 和完整的 LTO 链接)
  • 必须配合 -flto(Link Time Optimization),否则虚函数表校验无法跨编译单元生效

漏掉 -flto 是常见错误:编译器在单个 .cpp 文件里能生成 vcall 检查代码,但链接时若未合并符号定义,运行时校验会因类型信息缺失而静默失败。

Intel CET 影子栈需硬件+内核+编译器三者协同,缺一不可

影子栈(Shadow Stack)是防御 ROP 的关键,但它不是“开个编译开关就能用”的功能。x86-64 下必须同时满足:

  • CPU 支持 CET(Intel 11 代 Tiger Lake 及更新,或部分 Xeon Scalable)且 BIOS 中开启 Control-flow Enforcement Technology
  • Linux 内核 ≥ 5.19 并启用 CONFIG_X86_INTEL_CET=y,启动参数含 cet-report=1(否则用户态无法访问影子栈指令)
  • Clang ≥ 14 且编译时加 -mshstk(启用 setssbsy/rdsspd 等指令)和 -fcf-protection=full

典型错误现象:Illegal instruction (core dumped)——说明 CPU 或内核没准备好,但编译通过了。此时 cat /proc/cpuinfo | grep cet 应返回非空;dmesg | grep cet 应显示初始化成功。只靠编译器加 flag 不会自动降级,它会直接生成非法指令。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

CFI + 影子栈不是叠加关系,而是分工覆盖不同攻击面

CFI(如 -fsanitize=cfi-vcall)管的是“调用去哪”,影子栈(-fcf-protection=full)管的是“返回去哪”。两者不能互相替代,但可以共存。一个典型误判是:以为开了 CFI 就不用影子栈了。

  • CFI 对 ret 指令完全无感——它不拦截返回,只拦 call 和 jmp
  • 影子栈对虚函数调用、函数指针调用完全无感——它只保护每次 ret 弹出的地址是否来自影子栈副本
  • ROP 攻击链中,攻击者先用栈溢出覆盖返回地址(影子栈防这个),再用 gadget 里的 call rax 跳转到 libc 函数(CFI 防这个)

所以真实加固必须双开:-fsanitize=cfi-vcall -fsanitize=cfi-icall -flto 用于前向,-fcf-protection=full -mshstk 用于后向。中间若缺任何一环,ROP 链仍可能完成拼接。

Windows 上 CFG 的局限性与绕过风险要心里有数

MSVC 的 Control Flow Guard(CFG)本质是静态间接调用白名单,靠编译器扫描所有 call [rax] 类指令并生成跳转目标表。但它有硬伤:

  • 不校验虚函数调用——因为 vtable 地址在运行时才确定,MSVC 默认不把它纳入 CFG 表
  • 不防护 SetThreadContext、SEH 链指针等 Windows 特有后向劫持入口
  • 若程序加载了未启用 CFG 的第三方 DLL(如旧版 OpenSSL),其内部函数指针调用可被用作 gadget 跳板

也就是说,Windows CFG 单独启用时,对现代 ROP 利用链的阻断效果有限。它更适合配合 EMET 或 Windows Defender Exploit Guard 的“CFG 启用强制策略”一起用,而非当作独立防线。

真正难的不是加几个 flag,而是确认整个工具链、内核、CPU 微码都对齐了同一套控制流语义。一个环节版本错配,防护就变成幻觉。

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

相关专题

更多
c++和c语言的区别有哪些
c++和c语言的区别有哪些

c++和c语言的区别:1、面向对象编程(OOP)支持不同;2、新增特性不同;3、标准库不同;4、编译方式不同;5、命名空间不同等等。想了解更多c++的相关内容,可以阅读本专题下面的文章。

2024.03.14

2248

9

c++和python学习顺序推荐
c++和python学习顺序推荐

一般建议先学习C++,再学习Python,因为这样可以逐步从较为底层的编程语言向更高级的语言过渡。想了解更多python的相关内容,可以阅读本专题下面的文章。

2024.03.14

1019

6

python和c++学习性价比分析
python和c++学习性价比分析

Python易于学习,广泛应用于Web开发、数据科学和人工智能等领域,但性能较低。C语言性能高,适用于对性能要求较高的场景,如游戏开发和系统编程,但学习曲线陡峭,错误处理复杂。想了解更多python的相关内容,可以阅读本专题下面的文章。

2024.03.14

447

5

c语言和c++一样吗
c语言和c++一样吗

c语言和c++是两种不同的编程语言,虽然有相似之处,但存在显著差异。c语言专注于过程式编程和系统级开发,以简洁、高效著称。c++作为c语言的超集,引入了面向对象编程,增强了代码组织和管理能力,但学习曲线也更陡峭。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.03.14

327

5

c语言和c++先学哪个好
c语言和c++先学哪个好

初学者选择学习c语言还是c++语言,需要根据个人学习目标、背景以及编程兴趣和预期应用方向来决定。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.03.14

426

5

c语言和c++的区别和联系
c语言和c++的区别和联系

c语言和c++是计算机科学领域应用广泛的编程语言。虽然它们有着相似的基础,但它们在语言类型、语法功能和内存管理方面存在着显著差异。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.03.14

580

5

c++软件中文更改教程
c++软件中文更改教程

对于 ide,可通过打开设置,找到语言设置,选择中文,并保存更改。对于非 ide 应用程序,可查找设置或选项,选择语言设置,更改为中文,并保存更改。想了解更多c++的相关内容,可以阅读本专题下面的文章。

2024.03.21

1409

9

python和java和c++学习性价比分析
python和java和c++学习性价比分析

Python以其易学性、丰富的库和活跃的社区而著称,适合数据科学、人工智能和Web开发。Java以其跨平台性、企业级应用开发和Android应用开发而闻名。C++以其底层控制能力、高效性能和游戏开发而著称。选择哪种语言取决于个人兴趣、职业方向和特定需求。想了解更多python和java和c++的相关内容,可以阅读本专题下面的文章。

2024.03.22

1217

7

c++和c语言学习顺序推荐
c++和c语言学习顺序推荐

对于初学者,建议先学习C语言,掌握编程基础后再转入C++,便于理解面向对象编程概念。有编程经验者可直接学习C++,快速接触高级编程技术。想了解更多c++和c语言的相关内容,可以阅读本专题下面的文章。

2024.03.25

1325

9

热门下载

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

精品课程

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

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

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

共0课时 | 0人学习