1988年1月,蠕虫程序入侵了数千台接入Internet的计算机。当清除蠕虫并完成调查后,人们发现蠕虫繁殖的途径之一就是通过脆弱的 finger进程中一句十分简单的代码。finger命令用来查找并显示用户信息,系统管理员通过使用该命令可以知道某个时候到底有多少用户在使用这台Linux主机。
当时,用于进行finger服务的远程计算机进程,使用了标准C库函数gets。gets函数正式的任务是从流中读入一个字符串。它的调用者会告诉它把读入的字符放在什么地方。C 库函数 char *gets(char *str) 从标准输入 stdin 读取一行,并把它存储在 str 所指向的字符串中。当读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
但是gets()函数并不检查缓冲区的空间,事实上它也无法检查缓冲区的空间。如果函数的调用者提供了一个指向堆栈的指针,并且gets函数读入的字符数量超过了缓冲区的空间,gets()函数将会愉快地将多出来的字符继续写入到堆栈中,这就覆盖了堆栈原先的内容。
finger防护进程包含下列代码
int main(int argc, char *argv[])
{
char line[512];
…
gets(line);
…
}
这里,line是个能容纳512个字符的数组,它是在堆栈上自动分配的。当用户的输入超过了finger防护进程规定的512个字符时,get()函数将会继续把多出来的字符压到堆栈中。想要学习编程写程序的话,关于C/C++编程学习,小编给大家提供一个学习交流裙,欢迎到访:784,143,133。
这时候,黑客可以通过属于多余的字符来改写堆栈中的内容。黑客可以在字符串实参中设置正确的二进制模式来修改堆栈中的过程活动记录,改变函数的返回地址。
关于堆栈的知识,可以去论坛找一下:程序的内存布局、堆、栈和函数调用等知识。
结果,程序的执行流就不会返回到函数调用点的位置,而是跳转到一个特殊的指令序列(也是精心布置在堆栈中的),它将调用 exec()函数用一个shell替换正在运行的映像程序。这样现在就是与远程机器上的shell对话,而不是 finger防扩进程。你可以发布命令,把一份病毒的拷贝传播到其他的机器上。
其实,gets()函数是个过时的函数。在C语言的官方手册中,强烈建议用fgets彻底取代 gets. fgets函数对读入的字符数设置了一个限制,这样就不会超出缓冲区范围。C 库函数 char *fgets(char *str, int n, FILE *stream) 从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
现在,函数只能接受有限数量的字符,不会超出缓冲区的范围。这样就不会由于其他人运行程序而覆盖堆栈中的重要区域。
网友评论