• 缓冲区溢出保护


    一、内存安全

    c/c++饱为诟病的一个重点就是内存的安全问题,一个典型的例子就是内存的越界。在c/c++中,是不对内存越界进行检查的,比如一个数组,程序员操作其越界的指针仍然可以使用。但一旦用完释放,程序就会crash掉。缓冲区溢出(buffer overflow),是针对程序设计瑕疵或者语言本身的应用缺陷,向程序的数据缓冲区写入可使缓冲区溢出的数据(即上面提到的内存越界),导致程序运行异常从而获取程序乃至系统的控制权。
    大家有没有注意到OpenSSL中提供的SM2算法中的缓冲区溢出漏洞,这个漏洞可以让非法用户得到更多的数据从而导致程序拒绝服务或者改变程序运行的行为。有兴趣的可以去网上搜索一下。不光这个软件的缓冲区漏洞,从历史上来看,基本上所有的有名气的软件或多或少都出现过这个安全问题,在一些白帽子论坛上,经常会有这些问题被提出。
    内存问题,导致的整个程序的安全问题层出不穷,大到如微软、谷歌等大型公司,小到一些个人开发者,对内存管理的失效,往往会产生或者触发这种内存的安全问题。一般来说,内存就是放数据的缓冲区,所以内存安全问题也就称为缓冲区溢出问题。

    二、缓冲区溢出保护

    那么,既然缓冲区溢出如此重要,这也便成为了计算机安全的一个重要问题。一般来说,计算机的内存分为堆和栈两大类(寄存器之类的暂时不考虑)。程序的缓冲区安全保护可以从软件编写层次、编译层次、运行层次进行保护;而在运行层面上又可分为OS系统的保护和应用程序自身的保护。
    1、语言层次的的安全控制
    a、在编写程序时,注意检查和控制数组长度、指针大小等边界的控制,也即完整性的检查。
    b、使用一些安全的库来替代原始的数组和指针。
    c、加入安全控制代码。
    d、使用内存静态检查工具

    2、编译层次的安全控制
    a、使用支持编译期内存检查的开发工具如visual studio,默认打了GS选项,GCC也提供了类似的机制。
    b、编译期内的边界检查。
    c、堆栈标记控制,用来管理内存中的位置。
    d、使用运行期内存检测工具。

    3、运行时的安全控制
    a、操作系统中的预防性内存安全控制,如访问非法地址。
    b、内存和数据的安全检查,包含越界管理。
    c、沙箱托管运行。

    这里说一下在内存中增加标记位来控制缓冲区溢出,一般有三种方式,一种是增加结尾字符,比如C/C++中的\0字符;一种是在内存中随机分布一些控制字符如canary ;最后是做异或动作。

    三、应用

    在实际的应用中,GNU Compiler Collection、LLVM、Microsoft Visual Studio还有Intel、IBM等的编译器都支持在编译期的安全检查。当然还有静态工具可以在编码阶段对内存问题进行检查如Lint等,在运行期一般操作系统都提供了内存安全控制,比如在栈空间上设置一些标志性检查,防止被覆盖来保护栈的安全。在Linux上,就有类似于这种行为称之为canary。
    而StackGhost则是从硬件的层次上进行保护,这个就比较少见了。下面看一下Linux中如何进行内存安全检查:
    1、CANNARY(栈保护)
    gcc在4.2版本中添加了-fstack-protector和-fstack-protector-all编译参数以支持栈保护功能,4.9新增了-fstack-protector-strong编译参数让保护的范围更广。

    2、FORTIFY
    使用参数-U_FORTIFY_SOURCE编译来保护缓冲区溢出。
    3、NX(DEP)
    默认开启,在遇到溢出时,自动抛出异常,而不是去执行。
    4、PIE(ASLR)
    内存地址随机化机制(address space layout randomization),它分为映像、堆栈和PEB与TEB随机化三种。
    5、RELRO
    设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。GOT可以去看看动态库的加载相关内容。

    四、总结

    这里说明一下,本文不会教怎么做缓冲区溢出更不会教怎么进行攻击,那个是非法的。如果有想学习安全知识的,可以去看雪等安全网站去学习。本文重点还是说明如何保护好内存的安全。现在计算机安全问题已经上升到了国家层面上,包括俄乌战争背后都隐隐约约的有黑客的影子。程序大了,参与编程的人多了,人们的编程水平和能力的参差不齐以及对安全问题的认知程度和重视程度,都决定着程序的整体安全。缓冲区溢出做为一个从计算机出现就伴随着的安全问题,几乎可以说非常至关重要,说到这里,就需要提醒一下,一个优秀的程序员,一定深入了解语言的内存模型,操作系统内存管理机制和程序编译运行的机制等等。可以说有一处想不到,就意味着问题可能会发生,就看代价如何了。
    千里之堤,溃于蚁穴!绝不是危言耸听。

  • 相关阅读:
    MySQL的UPDATE及SELECT...FOR UPDATE语句关于锁的一些简单验证
    Qt HTTP 摘要认证(海康球机摄像机ISAPI开发)
    windows (win11) vscode配置WSL(ubuntu)终端完美解读
    竞赛选题 深度学习疲劳驾驶检测 opencv python
    HTTPS自动延期证书申请
    uniapp使用@microsoft/signalr(报错“ReferenceError: require is not defined“)
    3、Pod资源管理
    [OpenCV-dlib]人脸识别功能拓展-通过随机要求头部动作实现活体检测
    QT 使用C++线程池运行Lambda自定义函数
    解决防火墙导致虚拟机不能ping通宿主机的问题
  • 原文地址:https://blog.csdn.net/fpcc/article/details/125632598