简介
自第六代智能英特尔® 酷睿™ 处理器起,英特尔公司开始推出英特尔® 内存保护扩展(英特尔® MPX),它是一种针对指令集架构的全新扩展,旨在通过帮助抵御缓冲区溢出攻击,增强软件安全性。 在本文中,我们将介绍缓冲区溢出,并提供详细的步骤指导应用开发人员如何保护 Windows® 10 上的应用免受缓冲区溢出攻击。 英特尔 MPX 适用于传统桌面应用和 Universal Windows Platform* 应用。
前提条件
为运行本文提供的示例,您需要准备以下软硬件:
- 采用第六代智能英特尔® 酷睿™ 处理器和 Microsoft Windows 10 操作系统(2015 年 11 月更新版或更高版本,首选 Windows 10 版本 1607)的计算机(台式机、笔记本电脑或其他外形的计算机)
- 在 UEFI 中启用英特尔 MPX(如果该选项可用)
- 安装正确的英特尔 MPX 驱动程序
- Microsoft Visual Studio* 2015(更新 1 或更高版本 IDE;首选 Visual Studio 2015 更新 3)
缓冲区溢出
从本质上来说,C/C++ 代码更容易遭受缓冲区溢出。 例如,在以下代码中,main() 中的字符串操作函数 “strcpy” 将会导致程序遭受缓冲区溢出攻击的风险。
#include "stdafx.h" #include <iostream> #include <time.h> #include <stdlib.h> using namespace std; void GenRandomUname(char* uname_string, const int uname_len) { srand(time(NULL)); for (int i = 0; i < uname_len; i++) { uname_string[i] = (rand() % ('9' - '0' + 1)) + '0'; } uname_string[uname_len] = '\0'; } int main(int argnum, char** args) { char user_name[16]; GenRandomUname(user_name, 15); cout << "random gentd user name: "<< user_name << endl; char config[10] = { '\0' }; strcpy(config, args[1]); cout << "config mem addr: "<< &config << endl; cout << "user_name mem addr: "<< &user_name << endl; if (0 == strcmp("ROOT", user_name)) { cout << "Buffer Overflow Attacked!"<< endl; cout << "Uname changed to: "<< user_name << endl; } else { cout << "Uname OK: "<< user_name << endl; } return 0; }
为了提高准确性,如果我们以 C++ 控制台应用的形式编译并运行以上示例,将 CUSM_CFG 当作参数,那么该程序将正常运行,并且控制台将显示以下输出:
但如果我们重新运行该程序,将 CUSTOM_CONFIGUREROOT 当作参数,输出将“出乎意料”,且控制台显示以下消息:
这一简单示例说明了缓冲区溢出攻击的工作原理。 出现意外输出的原因是 strcpy 函数调用无法检查目标数组的联系。 尽管编译器通常为数组提供额外字节以达到内存对齐,但如果源数组足够长,还是会发生缓冲区溢出。 在这种情况下,程序的一部分运行时内存布局将如下所示(结果可能因编译器或编译选项的不同而有所差异):
英特尔内存保护扩展
借助英特尔 MPX,只需为 Visual Studio C++ 编译器添加编译选项 /d2MPX,就可避免缓冲区溢出这一安全问题。
借助英特尔 MPX 选项进行编译后,该程序将能够抵御缓冲区溢出攻击。 如果我们尝试运行重新进行编译,且包含 CUSTOM_CONFIGUREROOT 参数的程序,运行时例外情况将会增加,并导致程序退出。
我们来深入了解一下生成的汇编代码,看看英特尔 MPX 对该程序有何作用。 从结果来看,原始指令中插入了许多有关英特尔 MPX 的指令,以检测运行时的缓冲区溢出。
现在我们来详细了解一下有关英特尔 MXP 的指令:
bndmov: 从内存中获取(上下)界限信息并将其放在界限寄存器中。
bndcl: 根据上述代码快照中的参数 (%rax) 检查下界限。
bndcu: 根据上述代码快照中的参数 (%rax) 检查上界限。
故障排除
如果 MPX 无法正常工作。
- 复查 CPU、操作系统和 Visual Studio 2015 的版本。 将 PC 启动至 UEFI 设置,检查是否有英特尔 MPX 开关,如有必要打开开关。
- 确认英特尔 MPX 驱动程序安装正确并在 Windows* Device Manager 中正常运行。
- 检查编译的可执行文件是否包含有关英特尔 MPX 的指令。 插入一个断点,然后运行程序。 如果命中断点,右击鼠标,然后单击Go To Disassembly。 将显示一个新的窗口以供查看汇编代码。
结论
英特尔 MPX 是一款全新的硬件解决方案,有助于抵御缓冲区溢出攻击。 从应用开发人员的角度来看,相比于 AddressSanitizer (https://code.google.com/p/address-sanitizer/) 等软件解决方案,英特尔 MPX 拥有多项优势,包括:
- 检测指针点在对象之外,但仍然指向有效内存。
- 英特尔 MPX 更加灵活,可用于许多模块,但不影响其他模块。
- 与传统代码的兼容性更高,适用于用英特尔 MPX 控制的代码。
- 由于特殊的指令编码,因此仍然可以发布单一版本的二进制。 在不支持的硬件或操作系统上,与英特尔 MPX 相关的指令将以 NOP(无操作)的形式执行。
在第六代智能英特尔® 酷睿™ 处理器和 Windows 10 上,只需添加编译器选项,即可受益于面向应用的英特尔 MPX,从而帮助增强应用安全性,且丝毫不影响应用的后向兼容性。
相关文章
英特尔® 内存保护扩展启用指南:
https://software.intel.com/zh-cn/articles/intel-memory-protection-extensions-enabling-guide
参考资料
[1] AddressSanitizer: https://code.google.com/p/address-sanitizer/
关于作者
Fanjiang Pei 是软件和解决方案事业部 (SSG) 开发人员关系部门客户端计算移动支持团队的一名应用工程师, 负责为英特尔 MPX、英特尔® Software Guard 扩展等英特尔安全技术提供支持。