荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: worldhero (一生,求索.), 信区: Linux
标  题: 制造缓冲区溢出
发信站: BBS 荔园晨风站 (Tue Nov  7 05:06:45 2000), 转信

∫桓龀绦蛟谀诖嬷型ǔ7治绦蚨危荻撕投颜蝗糠帧3绦蚨卫?
放着程序的机器码和只读数据,这个段通常是只读,对它的写操作是非法
的。数据段放的是程序中的静态数据。动态数据则通过堆栈来存放。在内
存中,它们的位置如下:

  /――――――――\  内存低端
  |程序段|
  |―――――――――|
  |数据段|
  |―――――――――|
  |堆栈|
  \―――――――――/内存高端

  堆栈是内存中的一个连续的块。一个叫堆栈指针的寄存器(SP)指向
堆栈的栈顶。堆栈的底部是一个固定地址。
堆栈有一个特点就是,后进先出。也就是说,后放入的数据第一个取
出。它支持两个操作,PUSH和POP。PUSH是将数据放到栈的顶端,POP是将
栈顶的数据取出。
  在高级语言中,程序函数调用和函数中的临时变量都用到堆栈。参数
的传递和返回值是也用到了堆栈。通常对局部变量的引用是通过给出它们
对SP的偏移量来实现的。另外还有一个基址指针(FP,在Intel芯片中是BP),
许多编译器实际上是用它来引用本地变量和参数的。通常,参数的相对FP
的偏移是正的,局部变量是负的。
  当程序中发生函数调用时,计算机做如下操作:首先把参数压入堆栈;
然后保存指令寄存器(IP)中的内容,做为返回地址(RET);第三个放入堆栈
的是基址寄存器(FP);然后把当前的栈指针(SP)拷贝到FP,做为新的基地
址;最后为本地变量留出一定空间,把SP减去适当的数值。
  下面举个例子:
  example1.c:
  ------------------------------------------------------------
  void function(int a, int b, int c) {
  char buffer1[5];
  char buffer2[10];
  }

  void main() {
function(1,2,3);
  }
  -----------------------------------------------------------
  为了理解程序是怎样调用函数function()的,使用-S选项,在Linux下,
用gcc进行编译,产生汇编代码输出:
  $ gcc -S -o example1.s example1.c
  看看输出文件中调用函数的那部分:
  pushl $3
  pushl $2
  pushl $1
  call function
  这就将3个参数压到堆栈里了,并调用function()。指令call会将指令
指针IP压入堆栈。在返回时,RET要用到这个保存的IP。
  在函数中,第一要做的事是进行一些必要的处理。每个函数都必须有
这些过程:

  pushl %ebp
  movl %esp,%ebp
  subl $20,%esp
≌饧柑踔噶罱獷BP,基址指针放入堆栈。然后将当前SP拷贝到EBP。然
后,为本地变量分配空间,并将它们的大小从SP里减掉。由于内存分配是
以字为单位的,因此,这里的buffer1用了8字节(2个字,一个字4字节)
。Buffer2用了12字节(3个字)。所以这里将ESP减了20。这样,现在,堆
栈看起来应该是这样的。

  低端内存高端内存
  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);
  }
  ------------------------------------------------------------

  这个程序是一个经典的缓冲区溢出编码错误。函数将一个字符串不经
过边界检查,拷贝到另一内存区域。当调用函数function()时,堆栈如下


  低内存端buffer sfp ret *str高内存端
  < ------ [ ][ ][ ][ ]
  栈顶栈底
  很明显,程序执行的结果是"Segmentation fault (core dumped)"或
类似的出错信息。因为从buffer开始的256个字节都将被*str的内容'A'覆
盖,包括sfp, ret,甚至*str。'A'的十六进值为0x41,所以函数的返
回地址变成了0x41414141, 这超出了程序的地址空间,所以出现段错误

  可见,缓冲区溢出允许我们改变一个函数的返回地址。通过这种方式,
可以改变程序的执行顺序。


--
※ 来源:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.36.85]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店