`
yesjavame
  • 浏览: 688419 次
  • 性别: Icon_minigender_2
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

缓冲区溢出原理学习

阅读更多


缓冲区溢出原理学习

什么是缓冲区溢出?
缓冲区,简单说来是一块连续的计算机内存区域, 可以保存相同数据类型的多个实例。动态变量在程序运行时定位于堆栈之中。 我们这里只关心动态缓冲区的溢出问题, 即基于堆栈的缓冲区溢出。
进程的内存组织形式
一个进程在内存中被分成三个区域: 文本, 数据和堆栈。
文本区域是由程序确定的, 包括代码(指令)和只读数据。 该区域相当于可执行文件的文本段。 这个区域通常被标记为只读, 任何对其写入的操作都会导致段错误(segmentation violation)。
数据区域包含了已初始化和未初始化的数据。 静态变量储存在这个区域中。

/------------------\内存低地址
||
|文本|
||
|------------------|
|(已初始化)|
|数据|
|(未初始化)|
|------------------|
||
|堆栈|
||
\------------------/内存高地址

堆栈是一个后进先处(LIFO)队列。

为什么要使用堆栈?
一个过程调用可以象跳转(jump)命令那样改变程序的控制流程, 但是与跳转不同的是, 当工作完成时,函数把控制权返回给调用之后的语句或指令。 这种高级抽象实现起来要靠堆栈的帮助。
堆栈也用于给函数中使用的局部变量动态分配空间, 同样给函数传递参数和函数返回值也要用到堆栈。

堆栈区详解
堆栈是一块保存数据的连续内存。 一个名为堆栈指针(SP)的寄存器指向堆栈的顶部。堆栈的底部在一个固定的地址。 堆栈的大小在运行时由内核动态地调整。

堆栈由逻辑堆栈帧组成。当调用函数时逻辑堆栈帧被压入栈中, 当函数返回时逻辑堆栈帧被从栈中弹出。 堆栈帧包括函数的参数, 函数地局部变量, 以及恢复前一个堆栈帧所需要的数据, 其中包括在函数调用时指令指针(IP)的值。

堆栈既可以向下增长(向内存低地址)也可以向上增长, 这依赖于具体的实现。在我们的例子中, 堆栈是向下增长的。堆栈指针(SP)也是依赖于具体实现的。它可以指向堆栈的最后地址,或者指向堆栈之后的下一个空闲可用地址。 在我们的讨论当中, SP指向堆栈的最后地址。

除了堆栈指针(SP指向堆栈顶部的的低地址)之外, 为了使用方便还有指向帧内固定地址的指针叫做帧指针(FP)。有些文章把它叫做局部基指针(LB-local base pointer)。从理论上来说, 局部变量可以用SP加偏移量来引用。 然而, 当有字被压栈和出栈后, 这些偏移量就变了。 尽管在某些情况下编译器能够跟踪栈中的字操作, 由此可以修正偏移量, 但是在某些情况下不能。而且在所有情况下, 要引入可观的管理开销。 而且在有些机器上, 比如Intel处理器, 由SP加偏移量访问一个变量需要多条指令才能实现。

因此, 许多编译器使用第二个寄存器, FP, 对于局部变量和函数参数都可以引用, 因为它们到FP的距离不会受到PUSH和POP操作的影响。 在Intel CPU中, BP(EBP)用于这个目的。 在Motorola CPU中, 除了A7(堆栈指针SP)之外的任何地址寄存器都可以做FP。考虑到我们堆栈的增长方向, 从FP的位置开始计算, 函数参数的偏移量是正值, 而局部变量的偏移量是负值。

当一个例程被调用时所必须做的第一件事是保存前一个FP(这样当例程退出时就可以恢复)。 然后它把SP复制到FP, 创建新的FP, 把SP向前移动为局部变量保留空间。 这称为例程的序幕(prolog)工作。当例程退出时, 堆栈必须被清除干净, 这称为例程的收尾(epilog)工作。 Intel的ENTER和LEAVE指令, Motorola的LINK和UNLINK指令, 都可以用于有效地序幕和收尾工作。
这里利用了一个简单的例子来做堆栈溢出示例。首先描述了该例子编
译后的内存分配情况,然后修改这个例子,使它成为一个典型的溢出程
序。分析溢出时的堆栈情况。

------------------------------------------------------------------

一个简单的堆栈例子
example1.c:
------------------------------------------------------------------
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
}

void main() {
function(1,2,3);
}
------------------------------------------------------------------
使用gcc的-S选项编译, 以产生汇编代码输出:
$ gcc -S -o example1.s example1.c

通过查看汇编语言输出, 我们看到对function()的调用被翻译成:
pushl $3
pushl $2
pushl $1
call function

以从后往前的顺序将function的三个参数压入栈中, 然后调用function()。 指令call会把指令指针(IP)也压入栈中。 我们把这被保存的IP称为返回地址(RET)。 在函数中所做的第一件事情是例程的序幕工作:
pushl ëp
movl %esp,ëp
subl $20,%esp

将帧指针EBP压入栈中。 然后把当前的SP复制到EBP, 使其成为新的帧指针。 我们把这个被保存的FP叫做SFP。 接下来将SP的值减小, 为局部变量保留空间。

内存只能以字为单位寻址。 一个字是4个字节, 32位。 因此5字节的缓冲区会占用8个字节(2个字)的内存空间, 而10个字节的缓冲区会占用12个字节(3个字)的内存空间。 这就是为什么SP要减掉20的原因。 这样我们就可以想象function()被调用时堆栈的模样(每个空格代表一个字节):
内存低地址内存高地址
buffer2buffer1sfpretabc
<------[][][][][][][]
堆栈顶部堆栈底部

制造缓冲区溢出
现在试着修改我们第一个例子, 让它可以覆盖返回地址, 而且使它可以执行任意代码。堆栈中在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);
}
-------------------------------------------------------------------
我们把buffer1[]的地址加上12, 所得的新地址是返回地址储存的地方。 我们想跳过赋值语句而直接执行printf调用。

如何知道应该给返回地址加8个字节呢? 我们先前使用过一个试验值(比如1), 编译该程序, 祭出工具gdb:

-----------------------------------------------------------------
[aleph1]$ gdb example3
GDB is free software and you are welcome to distribute copies of it
under certain conditions; type "show copying" to see the conditions。
There is absolutely no warranty for GDB; type "show warranty" for details。
GDB 4。15 (i586-unknown-linux), Copyright 1995 Free Software Foundation, Inc...
(no debugging symbols found)...
(gdb) disassemble main
Dump of assembler code for function main:
0x8000490 :pushlëp
0x8000491 :movl%esp,ëp
0x8000493 :subl$0x4,%esp
0x8000496 :movl$0x0,0xfffffffc(ëp)
0x800049d :pushl$0x3
0x800049f :pushl$0x2
0x80004a1 :pushl$0x1
0x80004a3 :call0x8000470
0x80004a8 :addl$0xc,%esp
0x80004ab :movl$0x1,0xfffffffc(ëp)
0x80004b2 :movl0xfffffffc(ëp),êx
0x80004b5 :pushlêx
0x80004b6 :pushl$0x80004f8
0x80004bb :call0x8000378
0x80004c0 :addl$0x8,%esp
0x80004c3 :movlëp,%esp
0x80004c5 :poplëp
0x80004c6 :ret
0x80004c7 :nop
------------------------------------------------------------------

我们看到当调用function()时, RET会是0x8004a8, 我们希望跳过在0x80004ab的赋值指令。 下一个想要执行的指令在0x8004b2。 简单的计算告诉我们两个指令的距离为8字节。

分享到:
评论

相关推荐

    Q版缓冲区溢出教程 学习缓冲区溢出的好教材

    在学习过程中,你不仅会了解缓冲区溢出的原理,还能提升安全编程意识,对于提升个人的系统安全技能大有裨益。 总的来说,这是一份全面且深入的教程,旨在通过趣味性和易懂的方式,让学习者掌握这个关键的安全主题。...

    缓冲区溢出教程

    一、缓冲区溢出原理 缓冲区溢出攻击的原理是基于计算机内部的缓冲区机制。缓冲区是计算机内部的一个临时存放空间,用来存储输入数据。缓冲区的长度事先已经被程序或者操作系统定义好了。缓冲区就像一个啤酒杯,用来...

    缓冲区溢出实验

    - **了解缓冲区溢出常见的攻击手法**:学习攻击者利用缓冲区溢出进行攻击的具体方法和步骤。 #### 实验环境 - **操作系统**:Windows 2000或XP。 - **开发工具**:Visual C++ 6.0及以上版本。 #### 实验原理 - ...

    windows 缓冲区溢出

    一、缓冲区溢出原理 缓冲区溢出的发生源于编程错误,特别是不恰当的字符串处理和内存分配。例如,C/C++中的数组操作不当,没有正确检查输入数据长度,就可能导致溢出。当溢出发生时,原本应该保存在特定内存位置的...

    缓冲区溢出示例代码课件ppt

    缓冲区溢出原理 缓冲区是程序在内存中预先分配的一段空间,用于临时存储数据。当程序向缓冲区写入的数据超过了其实际大小时,超出部分的数据会覆盖到相邻内存区域,这便是缓冲区溢出。溢出的数据可能会破坏程序的...

    堆栈缓冲溢出原理

    通过对缓冲区溢出原理的深入理解,我们可以更好地识别潜在的安全风险,并采取有效的措施来防止此类漏洞的发生。同时,随着软件开发技术的进步,开发者也应该不断学习新的安全技术和最佳实践,以提高应用程序的整体...

    Q版缓冲区溢出教程(电子书版)

    总的来说,《Q版缓冲区溢出教程》是一本适合初学者和有一定基础的安全爱好者的学习资料,它通过Q版的形式,使复杂的概念更加易懂,同时也强调了安全编程的重要性。通过阅读这本书,读者不仅可以了解缓冲区溢出的原理...

    安全编程之缓冲区溢出.7z

    在深入学习缓冲区溢出后,我们需要思考以下问题: 1. 如何理解和识别栈溢出的迹象? 2. 为什么边界检查是防止溢出的基本方法? 3. ASLR如何帮助提高系统的安全性? 4. 非x86平台上防止溢出的方法有何独特之处? 5. ...

    Q版缓冲区溢出教程 王炜

    《Q版缓冲区溢出教程》是网络安全领域中王炜老师编写的一本针对初学者的漏洞分析入门书籍。...通过阅读本书,读者不仅可以学习到缓冲区溢出的基本原理,还能了解到相关防御技术,为深入研究网络安全打下坚实的基础。

    Q版缓冲区溢出教程源代码

    1. **缓冲区溢出原理**:首先,教程会解释什么是缓冲区,以及为什么在处理缓冲区时需要特别注意边界。它会详细阐述溢出是如何发生的,包括栈溢出和堆溢出的不同类型。 2. **经典示例**:源代码中可能包含了一些经典...

    C++缓冲区溢出实验

    **C++缓冲区溢出实验** 在编程领域,特别是系统安全和逆向工程中,缓冲区溢出是一...通过这次实验,参与者不仅可以理解缓冲区溢出的基本原理,还能掌握利用和防范溢出漏洞的技巧,这对于提升C++编程的安全性至关重要。

    Q版缓冲区溢出教程word格式

    《Q版缓冲区溢出教程》是一份详细阐述缓冲区溢出原理和技术的文档,针对初学者和进阶者提供了一种生动形象的学习方式。缓冲区溢出是计算机编程中的一个重要安全问题,它源于程序在处理内存时对缓冲区边界控制的疏忽...

    Q版缓冲区溢出教程

    在学习了缓冲区溢出的原理和防御措施后,你可以通过阅读《Q版缓冲区溢出教程.chm》文件进一步深入理解。这个教程可能会包括实例分析、代码演示以及一些实用的技巧,帮助你更好地掌握这一关键的安全知识。记住,理解...

    计算机系统基础之缓冲区溢出攻击实验

    通过这一系列的实验,学生不仅能够深入了解缓冲区溢出的工作原理和技术细节,还能掌握如何在实际环境中防范此类攻击。此外,这些实验还能够帮助学生培养解决复杂安全问题的能力,为未来的职业生涯打下坚实的基础。

    Q版缓冲区溢出教程.doc

    学习缓冲区溢出不仅有助于理解软件安全,还能提升逆向工程和程序调试技能。对于有志于信息安全的人来说,这是一项必备的技能。通过阅读和实践《Q版缓冲区溢出教程》,读者可以系统地掌握缓冲区溢出的相关知识,并...

Global site tag (gtag.js) - Google Analytics