您现在的位置是:烟消火灭网 > 百科
系统堆栈缓冲区溢出怎么修复
烟消火灭网2026-10-17 08:15:06【百科】4人已围观
简介栈溢出能直接覆盖函数返回地址,把程序执行流劫持到攻击者指定的位置。这不是理论推演,1988年Morris蠕虫就用fingerd的gets()溢出打穿了六千多台机器。三十多年过去,同类漏洞仍在CVE列表
栈溢出能直接覆盖函数返回地址,系统把程序执行流劫持到攻击者指定的堆栈位置。这不是缓冲理论推演,1988年Morris蠕虫就用fingerd的区溢gets()溢出打穿了六千多台机器。三十多年过去,出修同类漏洞仍在CVE列表里反复出现。系统修复它不复杂,堆栈但需要理解几件事。缓冲
最直接的区溢修法:别用不检查边界的函数。gets()、出修strcpy()、系统sprintf()、堆栈strcat()——这些名字本身就意味着风险。缓冲换成fgets()、区溢strncpy()、出修snprintf()、strncat(),给目标缓冲区一个明确的长度上限。但注意,strncpy()不保证结尾有'\0',用完后手动补一个,否则后续strlen()会读越界。这个坑我踩过,调试了两个小时才发现字符串尾巴是脏的。
编译器层面也有手段。GCC的-fstack-protector-strong会在栈帧里插入canary值,函数返回前检查它是否被改写。Visual Studio的/GS同理。这玩意儿不是万能的——不覆盖canary的溢出照样能得手,但它拦住了大量粗糙的攻击。加上-D_FORTIFY_SOURCE=2,编译时能对已知的库函数调用做静态检查,运行时再做一次。代价是少量性能开销,通常可以接受。
操作系统提供的缓解机制值得打开。Linux的ASLR让栈地址随机化,攻击者猜不到shellcode的落点。配合NX位(AMD叫NX,Intel叫XD),栈上的代码直接不可执行,经典的“跳转到栈上执行shellcode”就废了。Windows的DEP和ASLR是同类东西。这些机制在编译链接时加-z execstack关掉可执行栈、-pie -fPIE开启位置无关可执行文件,就能生效。
但ASLR有弱点。32位系统熵太低,暴力破解可行;信息泄露漏洞能绕过它。所以别把ASLR当唯一防线。真正的做法是分层:安全函数替换 + 编译器canary + 系统ASLR/NX + 代码审计。少一层,攻击面就大一圈。
代码审计时重点看几类地方。循环里的数组索引有没有越界检查;从网络或文件读入的长度字段有没有验证;整数溢出导致malloc大小算错,后续写入撑爆堆——堆溢出和栈溢出修复思路不同,但根源往往一样:信任了外部输入。我见过一个案例,协议头里声明payload长度是uint16,程序直接拿它做memcpy的size参数,攻击者填0xFFFF,栈上256字节的缓冲区瞬间被淹没。
如果必须保留旧代码,又没法大改,可以用静态分析工具扫一遍。Coverity、Fortify、Clang Static Analyzer都能标出可疑的缓冲区操作。误报不少,但漏报的代价更大。手工审计配合工具,效率比纯人眼高得多。
最后说个实际修复流程。先复现溢出,用gdb看EIP被覆盖成什么值,确认偏移量。然后定位到具体哪一行代码写越界了。改代码,换成安全版本。重新编译,打开所有缓解选项。再跑一遍exploit,确认打不通了。最后用valgrind或AddressSanitizer过一遍,看有没有遗漏的内存错误。这套走完,基本就干净了。
栈溢出不是新问题,修复手段也成熟。问题在于很多项目还在用二十年前的编码习惯,编译选项也没动过。改起来不难,难的是意识到该改。
很赞哦!(8)
上一篇: 通过推行网上安全监管和监测预警
下一篇: 国际环保组织有哪些







