gets引发的血案
来源:互联网 发布:淘宝千牛怎么同意退款 编辑:程序博客网 时间:2024/04/30 08:42
转自:http://hi.baidu.com/david_jlu/blog/item/3f742b1b74284a1a8618bf80.html
/* DO NOT USE THIS FUNCTION!! There is no limit on how much it will read. */
下面让我们浏览一下gets的源码:
1 char * 2 gets(char *str){ 3 char *cp; 4 int c; 5 if ((stdin->flags & __SRD) == 0) 6 return NULL; 7 for (c = 0, cp = str; c != '\n'; cp++) { 8 if ((c = getchar()) == EOF) { 9 stdin->flags |= __SERR;10 return NULL;11 }12 *cp = (char)c;13 }14 *--cp = '\0';15 return str;16 }一直读到‘\n’结束,看上去似乎没问题,但忽略了str的长度,如果输入的串超过str边界咋办?所以要避免使用这个函数,取而代之用fget就可以有效的检查有没有越界: 1 char * 2 fgets(char *as, int n, FILE *f){ 3 int c; 4 char *s=as; 5 c = EOF; 6 while(n>1 && (c=getc(f))!=EOF){ 7 *s++=c; 8 --n; 9 if(c=='\n') break;10 }11 if(c==EOF && s==as12 || ferror(f)) return NULL;13 if(n) *s='\0';14 return as;15 } 这是一个很典型的问题──缓冲区溢出(Buffer Overflow)。1988年11月,许多组织不得不因为“Morris 蠕虫”而切断 Internet 连接,“Morris 蠕虫”使得整个Internet的10%崩溃。2001年7月,一个名为“Code Red”的蠕虫病毒最终导致了全球运行微软的IIS Web Server的300000多台计算机受到攻击。2003年1月,“Slammer”蠕虫利用Microsoft SQL Server 2000中的一个缺陷,使得南韩和日本的部分Internet 崩溃,中断了芬兰的电话服务,并且使得美国航空订票系统、信用卡网络和自动出纳机运行缓慢。所有这些攻击都利用了缓冲区溢出的程序缺陷。为什么缓冲区溢出危害这么大呢?下面举一个例子:1 void print_input(int a,int b) {2 char str[3];3 gets(str);4 puts(str);5 }6 int main(int argc, char * argv[]) {7 print_input(1,2);8 return 0;9 }编译以后(gcc加-g)用gdb调试,设断点在gets后,直接continue到断点,看看当前状态
(gdb) bt
#0 print_input (a=1, b=2) at study.c:5
#1 0x080483e7 in main () at study.c:9
(gdb) p &a
$3 = (int *) 0xbfed95c0
(gdb) p &b
$4 = (int *) 0xbfed95c4
(gdb) p &str
$5 = (char (*)[3]) 0xbfed95b5
(gdb) x/3b 0xbfed95b5
0xbfed95b5: 0x41 0x42 0x00 #这里的‘1’和‘2’是我执行时输入的str
这里能看出栈区是从高地址向低地址延伸的,即高地址为栈底低地址为栈顶,当前内存状态:
0xbfed95b5 0xbfed95b6 0xbfed95b7 ………… 0xbfed95c0 0xbfed95c4
str[0] str[1] str[3] a b
中间还有8个字节,是什么呢?如果学过编译编译应该知道,肯定会有返回地址的,要不然执行
完print_input怎么返回main呢?!不信看看:
(gdb) x/4b 0xbfed95bc
0xbfed95bc: 0xe7 0x83 0x04 0x08 #这个就是返回地址0x080483e7
(gdb) x/4b 0xbfed95b8
0xbfed95b8: 0xd8 0x95 0xed 0xbf
(gdb) disassemble main #对main函数反汇编
Dump of assembler code for function main:
0x080483c2 <main+0>: lea 0x4(%esp),%ecx
0x080483c6 <main+4>: and $0xfffffff0,%esp
0x080483c9 <main+7>: pushl 0xfffffffc(%ecx)
0x080483cc <main+10>: push %ebp
0x080483cd <main+11>: mov %esp,%ebp
0x080483cf <main+13>: push %ecx
0x080483d0 <main+14>: sub $0x14,%esp
0x080483d3 <main+17>: movl $0x2,0x4(%esp)
0x080483db <main+25>: movl $0x1,(%esp)
0x080483e2 <main+32>: call 0x80483a4 <print_input>
0x080483e7 <main+37>: mov $0x0,%eax #函数执行完应该返回到这
0x080483ec <main+42>: add $0x14,%esp
0x080483ef <main+45>: pop %ecx
0x080483f0 <main+46>: pop %ebp
0x080483f1 <main+47>: lea 0xfffffffc(%ecx),%esp
0x080483f4 <main+50>: ret
End of assembler dump.
从上面可以看出,a变量左边紧挨着那个就是返回地址,即call 0x80483a4 <print_input>
的下一条指令,那还有一个空缺是什么呢?0xbfed95b8对应是堆栈指针sp,这个可有可无。
当前内存状态图:
0xbfed95b5 0xbfed95b6 0xbfed95b7 0xbfed95b8 0xbfed95bc 0xbfed95c0 0xbfed95c4
str[0] str[1] str[3] %sp ret地址 a b
现在的问题出在,gets根本不检查边界,倘若输入一个很长的串就会覆盖%sp和返回地址,
这就意味着cracker能够改写返回地址,当print_input完成时,它将返回──不过不是返回到
main函数
,而是返回到cracker想要执行的恶意代码。附:C语言中的危险函数
函数严重性解决方案gets最危险使用 fgets(buf, size, stdin)。这几乎总是一个大问题!strcpy很危险改为使用 strncpy。strcat很危险改为使用 strncat。sprintf很危险改为使用 snprintf,或者使用精度说明符。scanf很危险使用精度说明符,或自己进行解析。sscanf很危险使用精度说明符,或自己进行解析。fscanf很危险使用精度说明符,或自己进行解析。vfscanf很危险使用精度说明符,或自己进行解析。vsprintf很危险改为使用 vsnprintf,或者使用精度说明符。vscanf很危险使用精度说明符,或自己进行解析。vsscanf很危险使用精度说明符,或自己进行解析。streadd很危险确保分配的目的地参数大小是源参数大小的四倍。strecpy很危险确保分配的目的地参数大小是源参数大小的四倍。strtrns危险手工检查来查看目的地大小是否至少与源字符串相等。realpath很危险(或稍小,取决于实现)分配缓冲区大小为 MAXPATHLEN。同样,手工检查参数以确保输入参数不超过 MAXPATHLEN。syslog很危险(或稍小,取决于实现)在将字符串输入传递给该函数之前,将所有字符串输入截成合理的大小。getopt很危险(或稍小,取决于实现)在将字符串输入传递给该函数之前,将所有字符串输入截成合理的大小。getopt_long很危险(或稍小,取决于实现)在将字符串输入传递给该函数之前,将所有字符串输入截成合理的大小。getpass很危险(或稍小,取决于实现)在将字符串输入传递给该函数之前,将所有字符串输入截成合理的大小。getchar中等危险如果在循环中使用该函数,确保检查缓冲区边界。fgetc中等危险如果在循环中使用该函数,确保检查缓冲区边界。getc中等危险如果在循环中使用该函数,确保检查缓冲区边界。read中等危险如果在循环中使用该函数,确保检查缓冲区边界。bcopy低危险确保缓冲区大小与它所说的一样大。fgets低危险确保缓冲区大小与它所说的一样大。memcpy低危险确保缓冲区大小与它所说的一样大。snprintf低危险确保缓冲区大小与它所说的一样大。strccpy低危险确保缓冲区大小与它所说的一样大。strcadd低危险确保缓冲区大小与它所说的一样大。strncpy低危险确保缓冲区大小与它所说的一样大。vsnprintf低危险确保缓冲区大小与它所说的一样大。
- gets引发的血案
- ActiveX引发的“血案”
- size_t引发的血案
- 一个 * 引发的血案
- Print 引发的“血案”
- lease引发的血案
- 一个“-”引发的血案
- MD5引发的血案
- 一个"/"引发的血案
- wrap_content引发的血案
- merge_all引发的血案
- PersistableBundle引发的血案
- 看球引发的血案
- 一个松果引发的血案
- 一个memset引发的血案
- 一条语句引发的血案
- 一条短信引发的血案
- Javascript 逗号“,”引发的血案
- Visual C++ Project Model
- Unity 3D 脚本参考
- struts2.1乱码问题
- 在linux下的虚拟机安装系统完全教程
- 时间——休息,娱乐,编程
- gets引发的血案
- 关于Unity内部脚本如何工作的简单概览
- JQuery validate双重验证
- android 牛人必修 ant 编译android工程
- Gmail容量增加到9.125G啦
- ico转换网址
- 2012年春节后忙碌的工作
- BFS~~~迷宫
- PHP 爬虫记录