缓冲区溢出的原理和实践(Phrack)
热 ★★★★
缓冲区溢出的原理和实践(Phrack)
作者:闵涛 文章来源:闵涛的学习笔记 点击数:2165 更新时间:2009/4/22 23:33:33
> pushl $1 call function 以从后往前的顺序将function的三个参数压入栈中, 然后调用function(). 指令call 会把指令指针(IP)也压入栈中. 我们把这被保存的IP称为返回地址(RET). 在函数中所做 的第一件事情是例程的序幕工作: pushl %ebp movl %esp,%ebp subl $20,%esp 将帧指针EBP压入栈中. 然后把当前的SP复制到EBP, 使其成为新的帧指针. 我们把这 个被保存的FP叫做SFP. 接下来将SP的值减小, 为局部变量保留空间. 我们必须牢记:内存只能以字为单位寻址. 在这里一个字是4个字节, 32位. 因此5字节 的缓冲区会占用8个字节(2个字)的内存空间, 而10个字节的缓冲区会占用12个字节(3个字) 的内存空间. 这就是为什么SP要减掉20的原因. 这样我们就可以想象function()被调用时 堆栈的模样(每个空格代表一个字节): 内存低地址 内存高地址 buffer2 buffer1 sfp ret a b c <------ [ ][ ][ ][ ][ ][ ][ ] 堆栈顶部 堆栈底部 缓冲区溢出 ~~~~~~~~~~~~ 缓冲区溢出是向一个缓冲区填充超过它处理能力的数据所造成的结果. 如何利用这个 经常出现的编程 错误来执行任意代码呢? 让我们来看看另一个例子: example2.c ------------------------------------------------------------------------------ void function(char *str) { char buffer[16]; strcpy(buffer,str); } void main() { char large_string[256]; int i; for( i = 0; i < 255; i++) large_string[i] = 'A'; function(large_string); } ------------------------------------------------------------------------------ 这个程序的函数含有一个典型的内存缓冲区编码错误. 该函数没有进行边界检查就复 制提供的字符串, 错误地使用了strcpy()而没有使用strncpy(). 如果你运行这个程序就 会产生段错误. 让我们看看在调用函数时堆栈的模样: 内存低地址 内存高地址 buffer sfp ret *str <------ [ ][ ][ ][ ] 堆栈顶部 堆栈底部 这里发生了什么事? 为什么我们得到一个段错误? 答案很简单: strcpy()将*str的 内容(larger_string[])复制到buffer[]里, 直到在字符串中碰到一个空字符. 显然, buffer[]比*str小很多. buffer[]只有16个字节长, 而我们却试图向里面填入256个字节 的内容. 这意味着在buffer之后, 堆栈中250个字节全被覆盖. 包括SFP, RET, 甚至*str! 我们已经把large_string全都填成了A. A的十六进制值为0x41. 这意味着现在的返回地 址是0x41414141. 这已经在进程的地址空间之外了. 当函数返回时, 程序试图读取返回 地址的下一个指令, 此时我们就得到一个段错误. 因此缓冲区溢出允许我们更改函数的返回地址. 这样我们就可以改变程序的执行流程. 现在回到第一个例子, 回忆当时堆栈的模样: 内存低地址 内存高地址 buffer2 buffer1 sfp ret a b c <------ [ ][ ][ ][ ][ ][ ][ ] 堆栈顶部 堆栈底部 现在试着修改我们第一个例子, 让它可以覆盖返回地址, 而且使它可以执行任意代码. 堆栈中在buffer1[]之前的是SFP, SFP之前是返回地址. ret从buffer1[]的结尾算起是4个 字节.应该记住的是buffer1[]实际上是2个字即8个字节长. 因此返回地址从buffer1[]的开 头算起是12个字节. 我们会使用这种方法修改返回地址, 跳过函数调用后面的赋值语句 'x=1;', 为了做到这一点我们把返回地址加上8个字节. 代码看起来是这样的: example3.c: ------------------------------------------------------------------------------ void function(int a, int b, int c) { char buffer1[5]; char buffer2[10]; int *ret; ret = buffer1 + 12; (*ret) += 8; } void main() { int x; x = 0; function(1,2,3); x = 1; printf("%d\n",x); }
上一页 [1] [2]
[C语言系列 ]C#自动投票器的原理及设计思路 [网络安全 ]病毒在局域网中的传播原理及防范方法 [网络技术 ]三层交换技术之原理、种类、产品选购 [网络技术 ]宽带共享用户需要了解的防火墙工作原理及设置技巧 [网络技术 ]详细介绍计算机远程控制的原理、实现方法、应用以… [电脑技术 ]详细介绍扫描仪的工作原理、性能及应用 [电脑技术 ]电子邮件工作原理及工作过程介绍
教程录入:mintao 责任编辑:mintao
上一篇教程: Java的多进程运行模式分析 下一篇教程: JavaSE6入门之脚本引擎让程序如虎添翼
【字体:小 大 】【发表评论 】【加入收藏 】【告诉好友 】【打印此文 】【关闭窗口 】
注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
网友评论: (只显示最新10条。评论内容只代表网友观点,与本站立场无关!)
同类栏目
赞助链接
500 - 内部服务器错误。
500 - 内部服务器错误。
您查找的资源存在问题,因而无法显示。