有 1 小段程序如下:
上面程序中声明了 1 个全局数组变量,且有 50 个元素。但在 main() 中,故意使它访问越界,而且越出不少,但在程序运行时并没有出错。然而当我们将 for 里的 i 循环次数增大到 700 时,会产生段错误。
这是因为每个进程都有自己的页表,一般情况下每个页为 4K,操作系统分配页表时,每次至少分配一个页。像上面程序中,为什么在小范围内对数组越界访问并不会造成错误,这正是由于没有越过页边界。但是当增大越界的范围时,终会导致缺页,从而发生段错误,因为再往下的线性地址没有进行映射。
再看一下局部数组变量越界的情况:
上面程序运行起来也没有出错,但是如果将 for 里的 i 的范围改为 53 时,就会看到段错误。这是因为,数组 a 和整数 i 都是局部变量,它们在运行时所进行的读写操作均在栈中。观察反汇编代码:
x86 的栈指针类型为满递减,上面的 0xd0 分配了 52x4 个字节栈空间。当我们写超出数组 1 个元素空间大小(4 个字节)时,那么将覆盖的是 i 变量,这种情况可以修改上面程序证明:
上面程序输出 i 的值为 56,可见 i 确实被覆盖了。
当我们写超出数组 2 个元素空间大小(8 个字节)时,保存在栈中的 EBP 寄存器也被覆盖了;
当我们写超出数组 3 个元素空间大小(3 个字节)时,main() 函数的返回地址被冲掉了,此时造成了缓冲区溢出。
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
int
a[50];
int
main(
void
)
{
int
i;
for
(i = 0; i < 300; i++)
a[i]=i;
return
0;
}
|
上面程序中声明了 1 个全局数组变量,且有 50 个元素。但在 main() 中,故意使它访问越界,而且越出不少,但在程序运行时并没有出错。然而当我们将 for 里的 i 循环次数增大到 700 时,会产生段错误。
这是因为每个进程都有自己的页表,一般情况下每个页为 4K,操作系统分配页表时,每次至少分配一个页。像上面程序中,为什么在小范围内对数组越界访问并不会造成错误,这正是由于没有越过页边界。但是当增大越界的范围时,终会导致缺页,从而发生段错误,因为再往下的线性地址没有进行映射。
再看一下局部数组变量越界的情况:
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
int
main(
void
)
{
int
a[50];
int
i;
for
(i = 0; i < 52; i++)
a[i]=i;
return
0;
}
|
上面程序运行起来也没有出错,但是如果将 for 里的 i 的范围改为 53 时,就会看到段错误。这是因为,数组 a 和整数 i 都是局部变量,它们在运行时所进行的读写操作均在栈中。观察反汇编代码:
08048394 <main>:
8048394: 55 push %ebp
8048395: 89 e5 mov %esp,%ebp
8048397: 81 ec d0 00 00 00 sub $0xd0,%esp
804839d: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
80483a4: eb 11 jmp 80483b7 <main+0x23>
80483a6: 8b 45 fc mov -0x4(%ebp),%eax
80483a9: 8b 55 fc mov -0x4(%ebp),%edx
80483ac: 89 94 85 34 ff ff ff mov %edx,-0xcc(%ebp,%eax,4)
80483b3: 83 45 fc 01 addl $0x1,-0x4(%ebp)
80483b7: 83 7d fc 33 cmpl $0x33,-0x4(%ebp)
80483bb: 7e e9 jle 80483a6 <main+0x12>
x86 的栈指针类型为满递减,上面的 0xd0 分配了 52x4 个字节栈空间。当我们写超出数组 1 个元素空间大小(4 个字节)时,那么将覆盖的是 i 变量,这种情况可以修改上面程序证明:
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
int
main(
void
)
{
int
a[50];
int
i;
for
( i = 0; i < 51; i++)
a[i] = (i+5);
printf
(
"%d\n"
, i);
return
0;
}
|
上面程序输出 i 的值为 56,可见 i 确实被覆盖了。
当我们写超出数组 2 个元素空间大小(8 个字节)时,保存在栈中的 EBP 寄存器也被覆盖了;
当我们写超出数组 3 个元素空间大小(3 个字节)时,main() 函数的返回地址被冲掉了,此时造成了缓冲区溢出。