你说的“负溢出”是什么意思?
用计算器计算字符的东西,一个游戏?
缓冲区溢出
缓冲区是内存中存储数据的地方。当程序试图将数据放入计算机内存的某个位置,但没有足够的空间时,就会发生缓冲区溢出。
缓冲区是程序运行时计算机内存中的一个连续块,它保存给定类型的数据。变量的动态分配出现了问题。为了不使用太多的内存,一个具有动态分配变量的程序在运行时决定分配给它们多少内存。如果程序把太多的数据放入动态分配缓冲区会发生什么?它溢出并泄漏到其他地方。缓冲区溢出应用程序使用这些溢出数据将汇编语言代码放入计算机的内存中,通常在内存中生成根权限。简单的缓冲区溢出不会导致安全问题。仅将溢出发送到您可以使用root权限运行命令的区域。这样,一个缓冲区使用程序将可执行指令以root权限放入内存,这样一旦这些指令运行,计算机就被root权限控制。总结以上描述。缓冲区溢出是指一种系统攻击手段。通过将超出其长度的内容写入程序的缓冲区,使缓冲区溢出,从而破坏程序的堆栈,使程序执行其他指令,达到攻击的目的。据统计,通过缓冲区溢出的攻击占所有系统攻击的80%以上。缓冲区溢出的原因是程序中没有仔细检查用户输入的参数。例如以下程序:
示例0.c
-
void函数(char *str) {
char缓冲区[16];
strcpy(缓冲区,str);
}
-
上面的Strcpy()会将str的内容直接复制到缓冲区中。这样,只要str的长度大于16,就会造成缓冲区溢出,使程序运行不正确。像strcpy这样有问题的标准函数有strcat()、sprintf()、vsprintf()、gets()、scan()和getc()、fgetc()、getchar()等等。在C语言中,静态变量分布在数据段,动态变量分布在堆栈段。缓冲区溢出是堆栈段溢出。内存中的程序通常分为三部分:程序段、数据终端和堆栈。程序段包含程序的机器码和只读数据。这个段通常是只读的,写入它是非法的。数据段包含程序中的静态数据。动态数据通过堆栈存储。在内存中,它们的位置如下:
/ -低端内存
|节目片段|
|―――――――――|
数据段|
|―――――――――|
|堆栈|
\-/高端内存
堆栈是内存中的连续块。称为堆栈指针的寄存器(SP)指向堆栈的顶部。堆栈的底部是一个固定地址。堆栈的一个特点就是后进先出。也就是说,后面放进去的数据先取出来。它支持两种操作,PUSH和POP。PUSH是把数据放在栈顶,POP是取栈顶的数据。在高级语言中,程序函数调用和函数中的临时变量都使用堆栈。堆栈还用于传递和返回参数值。通常,对局部变量的引用是通过将它们的偏移量赋予SP来实现的。还有一个基址指针(Intel芯片中的FP,BP),实际上是很多编译器用来引用局部变量和参数的。通常,参数与FP的偏差为正,局部变量为负。当程序中发生函数调用时,计算机做如下操作:首先,将参数推送到堆栈上;然后将指令寄存器(IP)中的内容保存为返回地址(RET);第三个是基址寄存器(FP);然后将当前堆栈指针(SP)复制到FP作为新的基址;最后,给局部变量留一些空间,从SP中减去适当的值。
这里有一个例子:
示例1.c:
-
void函数(int a,int b,int c) {
char buffer 1[5];
字符缓冲器2[10];
}
void main() {
函数(1,2,3);
}
-
为了理解程序如何调用函数(),使用-S选项,在Linux下用gcc编译,产生汇编代码输出:
$ gcc -S -o示例1.s示例1.c
查看调用该函数的输出文件部分:
推3美元
推售2美元
推动65438美元+0
呼叫功能
这将三个参数放到堆栈上,并调用function()。指令调用将把指令指针IP推到堆栈上。RET返回时将使用这个保存的IP。在函数中,首先要做的是做一些必要的处理。每个功能都必须有这些程序:
pushl %ebp
运动百分比esp,%ebp
子$20,%esp
这些指令将EBP和基址指针放入堆栈。然后将当前SP拷贝到EBP。然后,为局部变量分配空间,并从SP中减去它们的大小。由于内存分配是以字为单位的,所以这里的buffer1使用了8个字节(2个字和4个字节代表一个字)。缓冲器2使用12字节(3个字)。所以这里的ESP减少了20。现在,堆栈应该是这样的。
低端内存和高端内存
缓冲器2缓冲器1 sfp ret a b c
& lt- [ ][ ][ ][ ][ ][ ][ ]
堆栈的顶部和底部
缓冲区溢出意味着在缓冲区中写入太多数据。那么怎么用呢?看啊。
检查以下程序:
示例2.c
-
void函数(char *str) {
char缓冲区[16];
strcpy(缓冲区,str);
}
void main() {
char large _ string[256];
int I;
for(I = 0;我& lt255;i++)
large _ string[I]= ' A ';
函数(large _ string);
}
-
这个程序是一个经典的缓冲区溢出编码错误。函数将一个字符串复制到另一个内存区域,而不进行边界检查。调用函数()时,堆栈如下:
低存储端缓冲器sfp ret *str高存储端
& lt- [ ][ ][ ][ ]
堆栈的顶部和底部
显然,程序执行的结果是“分段错误(内核转储)”或类似的错误消息。因为从buffer开始的256字节会被*str的内容‘a’覆盖,包括sfp,ret甚至*str。' a '的十六进制值是0x41,所以函数的返回地址变成了0x414141,超出了程序的地址空间,所以出现了段错误。可以看出,缓冲区溢出允许我们改变函数的返回地址。这样就可以改变程序的执行顺序。
返回