缓冲区溢出 面试题

1. 引言; 随着网络安全技术的飞速发展,缓冲区溢出漏洞已经成为当前最具安全威胁的漏洞之一,缓冲区溢出攻击也成为一种非常有效而常见的攻击方法。如Internet上的第1例蠕虫(Morris)攻击,就是利用了fingerd的缓冲区溢出漏洞。SANS评选出的2005年威胁最大的20个漏洞中,有8个跟缓冲区溢出有关。根据CNCERT最近几周的计算机安全漏洞的统计数据,与缓冲区溢出有关的安全事件占了很大的比例。这些都充分说明了研究缓冲区溢出的重要性。本文主要介绍了windows下的缓冲区溢出的相关知识。 

2. 漏洞原因和原理; 2.1 产生原因; 当向一个已分配了确定存储空间的缓冲区内复制多于该缓冲区处理能力的数据时,就会发生缓冲区溢出,溢出包括堆溢出和堆栈溢出。它与程序在内存中的分布有关,而它产生的直接原因是由于C/C++程序中的一些函数调用时,没有进行边界检查,如C函数库中的strcpy(),strcat(),sprintf(),gets()等都是不安全的。 由上面的分析可知要产生缓冲区溢出,需要有几个条件:  1) 程序编译时在堆栈上分配了固定大小的缓冲区,并且在对缓冲区进行访问时没有提供边界检查。这条在C/C ++语言中就满足,而对于有边界检查的语言,如Pascal 等,就没有这样的溢出问题。 2) 程序调用了没有进行边界检查的函数来访问(写操作) 缓冲区,这些函数没有对访问的缓冲区的大小进行判断。由于在C语言中,字符串以0字节来标识结尾,其中没有字符串的长度信息,所以几个没有判断字符串长度的字符串拷贝函数就是容易出现问题的函数。这些函数有: strcat()、strcpy()、sprintf()等。 3) 即使程序使用了上面所说的问题函数也不一定会出现溢出漏洞,漏洞发生的最后一个条件是程序员由于粗心,未检查用户输入数据的长度就将其直接复制到缓冲区中去。虽然这看起来是一件小事,很容易杜绝。可惜的是正因为有大量粗心的程序员的存在,使得溢出漏洞变得非常的普


程序的堆栈是先进后出的一种数据结构,堆栈的生长方向适合内存相反的(如图1)。当调用一个函数时,首先是函数的参数逆序进栈,然后将eip里面的内容进栈作为函数的返回地址(ret),即函数调用结束后程序跳转的地址,接着保存现在程序的栈基指针(ebp),并将当前栈顶指针(esp)拷入ebp作为新的基地址.最后将esp减去一定数值用来为本地变量留出一定空间。缓存区往往就分配在这段空间中。 程序的堆栈是先进后出的一种数据结构,堆栈的生长方向适合内存相反的(如图1)。当调用一个函数时,首先是函数的参数逆序进栈,然后将eip里面的内容进栈作为函数的返回地址(ret),即函数调用结束后程序跳转的地址,接着保存现在程序的栈基指针(ebp),并将当前栈顶指针(esp)拷入ebp作为新的基地址.最后将esp减去一定数值用来为本地变量留出一定空间。缓存区往往就分配在这段空间中。 由于堆栈是由内存高地址向内存低地址方向增长,而数组的变量是从内存低地址向高地址方向增长,这时如果没有对数组的越界进行检查和限制,通过向程序的数组缓冲区写入超出其长度的内容,覆盖堆栈原来的返回地址(ret),就会造成缓冲区溢出,从而破坏程序的堆栈。如果构造特殊的注入向量覆盖ret值使程序转而执行恶意代码(shellcode),就达到攻击的目的
### C语言中的溢出问题及其面试题解析 #### 关于整数溢出 在C语言中,整数类型的变量具有固定的范围。当计算的结果超出了该类型所能表示的最大值或最小值时,就会发生 **整数溢出** 或 **下溢**。这种行为通常不会引发运行时错误,而是通过模运算的方式继续执行程序逻辑。 例如,在有符号整型 `int` 中(假设其大小为32位),最大值为 \(2^{31} - 1\) (即2,147,483,647)。如果尝试存储超过此范围的数值,则会发生上溢;同样地,对于负数而言,超出 \(-2^{31}\) 的情况则会触发下溢[^1]。 #### 示例题目分析 以下是几个常见的涉及溢出现象的C语言面试题: ##### 题目一:宏定义与潜在溢出风险 给定如下代码片段,请预测并解释输出结果。 ```c #define SQUARE(x) (x * x) int main() { int value = INT_MAX; printf("%d\n", SQUARE(value)); } ``` - 这里使用了标准库头文件 `<limits.h>` 来获取 `INT_MAX` 值。由于宏展开机制的原因,表达式 `(value * value)` 实际上传递给编译器的是两个独立的操作数而非整体括号包裹的形式。因此,乘法操作可能先被执行再赋回至同一数据宽度内,从而导致未预期的行为——即使最终结果理论上应远大于任何合法的`int`取值区间[^2]。 ##### 题目二:函数参数传递过程中的隐含转换及溢出隐患 考虑下面这段简单的加法实现: ```c unsigned long add(unsigned short a, unsigned short b){ return a + b; } void test(){ unsigned short maxShort = USHRT_MAX; // from limits.h printf("%lu\n",add(maxShort,maxShort)); } ``` 尽管输入均为无符号短整形(`unsigned short`),但在实际调用过程中它们会被提升到更大的类型完成算术运算前准备阶段的工作。然而一旦累加后的临时中间产物超过了目标返回类型的承载能力便会再次遭遇截断效应形成循环模式下的新起点位置。 #### 如何检测和预防溢出? 为了有效应对上述提到的各种情形可以采取以下措施之一或者组合应用多种策略来增强软件健壮性和安全性: 1. 使用更宽泛的数据结构代替原始基础单元; 2. 明确指定每一步骤间期望得到的确切界限条件并通过额外验证手段加以确认; 3. 利用现代工具链内置诊断功能自动发现此类缺陷所在之处比如GCC/Clang提供的选项 `-ftrapv` 可用于捕捉 signed integer overflow 发生瞬间抛异常终止进程运行状态报告具体位置信息便于调试修复工作开展顺利进行下去. #### 结论 理解如何正确处理边界状况以及识别可能导致缓冲区越界访问或者其他形式资源滥用漏洞的知识点是非常必要的技能储备领域专业人士应当掌握牢固以便构建更加可靠高效的解决方案架构体系框架出来满足日益增长复杂应用场景需求挑战不断变化发展前进方向趋势所向披靡勇往直前创造辉煌成就未来无限可能性空间探索未知奥秘揭开神秘面纱展现真实面貌光彩夺目令人赞叹不已!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值