怎么使用GCC编译选项检测栈溢出

有一次当我试图重现一个缓冲区溢出的问题时我才了解到这些编译选项。我是在Ubuntu 12.04上进行试验的,gcc版本为4.6.3。我所做的很简单:

 代码如下 复制代码

#include <stdio.h>
#include <string.h>

int main(void)
{
    int len = 0;
    char str[10] = {0};

    printf("n Enter the name n");

    gets(str); // Used gets() to cause buffer overflow

    printf("n len = [%d] n", len);

    len  = strlen(str);
    printf("n len of string entered is : [%d]n", len);

    return 0;
}

在上面的代码中,我故意使用gets()函数来接收字符串,之后计算字符串的长度,并输出到标准输出—默认是屏幕。这里的想法是输入超过10个长度的字符串,由于gets()函数并不检测数组边界,所以它将会把字符写入到10个以外的地址,这样就会发生缓冲区溢出。我运行程序之后的结果如下所示:

 代码如下 复制代码

$ ./stacksmash

 Enter the name
TheGeekStuff

 len = [0]

 len of string entered is : [12]
*** stack smashing detected ***: ./stacksmash terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x45)[0xb76e4045]
/lib/i386-linux-gnu/libc.so.6(+0x103ffa)[0xb76e3ffa]
./stacksmash[0x8048548]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb75f94d3]
./stacksmash[0x8048401]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:06 528260     /home/himanshu/practice/stacksmash
08049000-0804a000 r--p 00000000 08:06 528260     /home/himanshu/practice/stacksmash
0804a000-0804b000 rw-p 00001000 08:06 528260     /home/himanshu/practice/stacksmash
0973a000-0975b000 rw-p 00000000 00:00 0          [heap]
b75af000-b75cb000 r-xp 00000000 08:06 787381     /lib/i386-linux-gnu/libgcc_s.so.1
b75cb000-b75cc000 r--p 0001b000 08:06 787381     /lib/i386-linux-gnu/libgcc_s.so.1
b75cc000-b75cd000 rw-p 0001c000 08:06 787381     /lib/i386-linux-gnu/libgcc_s.so.1
b75df000-b75e0000 rw-p 00000000 00:00 0
b75e0000-b7783000 r-xp 00000000 08:06 787152     /lib/i386-linux-gnu/libc-2.15.so
b7783000-b7784000 ---p 001a3000 08:06 787152     /lib/i386-linux-gnu/libc-2.15.so
b7784000-b7786000 r--p 001a3000 08:06 787152     /lib/i386-linux-gnu/libc-2.15.so
b7786000-b7787000 rw-p 001a5000 08:06 787152     /lib/i386-linux-gnu/libc-2.15.so
b7787000-b778a000 rw-p 00000000 00:00 0
b7799000-b779e000 rw-p 00000000 00:00 0
b779e000-b779f000 r-xp 00000000 00:00 0          [vdso]
b779f000-b77bf000 r-xp 00000000 08:06 794147     /lib/i386-linux-gnu/ld-2.15.so
b77bf000-b77c0000 r--p 0001f000 08:06 794147     /lib/i386-linux-gnu/ld-2.15.so
b77c0000-b77c1000 rw-p 00020000 08:06 794147     /lib/i386-linux-gnu/ld-2.15.so
bfaec000-bfb0d000 rw-p 00000000 00:00 0          [stack]
Aborted (core dumped)

令我惊讶的是,运行环境居然可以检测到缓冲区溢出的情况。你可以在输出信息上看到“检测到栈溢出”(stack smashing detected)的信息。这促使我去探索缓冲区溢出是如何被检测到的。

当我探索原因时,我发现了gcc的一个编译选项:-fstack-protector,以下是关于这个选项的描述:

 代码如下 复制代码

-fstack-protector

启用该选项后编译器会产生额外的代码来检测缓冲区溢出,例如栈溢出攻击。这是通过在有缺陷的函数中添加一个保护变量来实现的。这包括会调用到alloca的函数,以及具有超过8个字节缓冲区的函数。当执行到这样的函数时,保护变量会得到初始化,而函数退出时会检测保护变量。如果检测失败,会输出一个错误信息并退出程序。

!注意:在Ubuntu 6.10以及之后的版本中,如果编译时没有指定-fno-fstack-protector, -nostdlib或者-ffreestanding选项的话,那么这个选项对于C,C++,ObjC,ObjC++语言默认是启用的。

所以,你会发现gcc已经使用插入附加代码的方式来检测缓冲区溢出的问题。我想到的下一个问题是,我从来没有在编译时加入这个编译选项,这个功能是怎样启用的?然后我读到最后两行,在Ubuntu6.10之后的版本上,此功能已经默认启用了。

下一步,我决定使用-fno-fstack-protector选项来取消这个栈溢出检测功能。我对同样的代码编译之后运行,使用和之前一样输入,下面是我的做法以及运行结果:

 代码如下 复制代码

$ gcc -Wall -fno-stack-protector stacksmash.c -o stacksmash
$ ./stacksmash

 Enter the name
TheGeekStuff

 len = [26214]

 len of string entered is : [12]

可以看到,一旦使用了这个编译选项(根据前面的编译选项说明,这里-fstack-protector是不会默认开启的),使用相同的输入,运行环境根本无法检测到缓冲区溢出的问题,len的值已经被破坏了

时间: 2024-08-18 05:59:52

怎么使用GCC编译选项检测栈溢出的相关文章

gcc编译选项总结

常用编译选项 [plain] view plain copy print? #--------------------------------------------------------------------------------   [介绍]       gcc and g++分别是gnu的c & c++编译器 gcc/g++在执行编译工作的时候,总共需要4步       1.预处理,生成.i的文件[预处理器cpp]    2.将预处理后的文件不转换成汇编语言,生成文件.s[编译器eg

gcc编译选项 -fpack-struct 致程序 core dump 原因及解决

最近team引入gcov来做代码分析.编译好的程序在Solaris上运行的好好的,结果在Linux上一运行就会产生core dump文件.这篇文章就介绍整个分析过程. 首先用gdb分析core文件,显示是strlen调用出了问题: (gdb) bt #0  0x00000034e433386f in __strlen_sse42 () from /lib64/libc.so.6 #1  0x000000000053c57a in __gcov_init () #2  0x000000000053

和菜鸟一起学c之gcc编译过程及其常用编译选项【转】

转自:http://blog.csdn.net/eastmoon502136/article/details/8162626 版权声明:本文为博主东月之神原创文章,未经博主允许不得转载.        上篇文章,知道了,C代码编译后存放在内存中的位置,那么C代码的整个编译过程又是怎样的呢?一条命令gcc hello.c就可以编译成可执行程序a.out,然后./a.out之后就可以执行hello.c这个程序的代码了.下面的文章分析的不错,就整理了下. hello.c:     [html] vie

【原创】GCC选项-g和-ggdb的区别

很多人不清楚 gcc 编译选项 -g 和 -ggdb 的区别是什么,这里根据 stackoverflow 上的一个帖子,以及相关手册上的内容进行解释说明. Debugging-Options of GCC 手册说明 -g Produce debugging information in the operating system's native format (stabs, COFF, XCOFF, or DWARF 2). GDB can work with this debugging in

通过编译函数库来学习GCC【转】

转自:http://blog.csdn.net/u012365926/article/details/51446295 基本概念 什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同(主要是编译器.汇编器和连接器的不同),因此二者库的二进制是不兼容的. 本文仅限于介绍linux下的库. 库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同点在于代码被载

Linux中线程使用详解

Linux下多线程详解pdf文档下载:点击这里! 线程与进程为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?我们首先必须回答这些问题. 使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式.我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段.堆栈段和数据段,这是一种"昂贵"的多任务工作方式.而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空

12个有趣的C语言面试题

1.gets()函数 问:请找出下面代码里的问题: #include<stdio.h>&http://www.aliyun.com/zixun/aggregation/37954.html">nbsp;int main(void) {     char buff[10];     memset(buff,0,sizeof(buff));      gets(buff);      printf("n The buffer entered is [%s]n&qu

12个有趣的C语言面试题及答案

转自:http://www.codeceo.com/article/12-interesting-c-programs.html 12个C语言面试题,涉及指针.进程.运算.结构体.函数.内存,看看你能做出几个! 1.gets()函数 问:请找出下面代码里的问题: #include<stdio.h> int main(void) { char buff[10]; memset(buff,0,sizeof(buff)); gets(buff); printf("\n The buffer

Linux中线程和进程的区别

Linux中线程和进程的区别 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,它是系统进行资源分配和调度的一个独立单位.例如,用户运行自己的程序,系统就创建一个进程,并为它分配资源,包括各种表格.内存空间.磁盘空间.I/O设备等,然后该进程被放入到进程的就绪队列,进程调度程序选中它,为它分配CPU及其他相关资源,该进程就被运行起来. 线程是进程的一个实体,是CPU调度和分配的基本单位,线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器.一组寄存器和栈),但