gcc 若干安全相关选项

1. FORTIFY_SOURCE(buffer over-flow 防御)

参考:http://fedoraproject.org/wiki/Security/Features#Compile_Time_Buffer_Checks_.28FORTIFY_SOURCE.29

http://gcc.gnu.org/ml/gcc-patches/2004-09/msg02055.html

此选项用于 gcc 在编译阶段检查是否存在缓冲区溢出问题。相关的原理及说明见上面的两个网址。此处仅举几个简单的例子说明其作用。

示例程序如下:

  1 #include
  2 #include
  3 
  4 int main()
  5 {
  6         char str[3];
  7 
  8         strcpy(str, "abcde");   //这里有问题
  9 
 10         return 0;
 11 }

首先看看不带 FORTIFY_SOURCE 选项的结果:

[tom@localhost code]$ gcc -O2 -o test test.c
[tom@localhost code]$ ll

可见,不带 FORTIFY_SOURCE 选项的情况下,编译器无法检测到这类问题;

在看看带 FORTIFY_SOURCE 选项的编译结果(注意该选项只能和 -O 选项一起使用,且优化级别大于 0):

[tom@localhost code]$ gcc -O2 -D_FORTIFY_SOURCE=2(1 或者 2) -o test test.c
In file included from /usr/include/string.h:642:0,
                 from test.c:2:
In function 'strcpy',
    inlined from 'main' at test.c:8:8:
/usr/include/bits/string3.h:105:3: warning: call to __builtin___memcpy_chk will always overflow destination buffer [enabled by default]
[tom@localhost code]$ 

可以看到,已经报出来在源码的第 8 行存在溢出问题。

需要注意的是这个选项只能辅助检测出一部分缓冲区溢出的问题,而不是所有的。

2. gcc的pie和fpie选项

(转自:http://blog.sina.com.cn/s/blog_ae4e800401014pcg.html)

Position-Independent-Executable是Binutils,glibc和gcc的一个功能,能用来创建介于共享库和通常可执行代码之间的代码�能像共享库一样可分配地址的程序,这种程序必须连接到Scrt1.o。标准的可执行程序需要固定的地址,并且只有被载到这个地址时,程序才能正确执行。PIE能使程序像共享库一样在主存任何位置装载,这需要程序成位置无关,并链接为ELF共享对象
引入PIE的原因是让程序能装载在随机的地址,通常情况下,内核都在固定的地址运行,如果能改用位置无关,那攻击者就很难借助系统中的可执行码实施攻击了。类似缓冲区溢出之类的攻击将无法实施。而且这种安全提升的代价很小

谈到PIE就不得不说说Pax和Grsec内核。这两个东西都是为了构建坚不可摧到安全系统而准备的。PaX是Linux内核安全增强补丁,它能在两方面保证安全性,一是ASLR(Address Space Layout Randomization,地址空间分布随机化),这是一种将所有数据装载到内存时都随机化地址的方式,当使用PIE选项编译应用时,PaX能将应用的地址做随机加法;二是能提供不可执行的内存空间,这样就能使得攻击者放入内存中的恶意代码不可执行。不过PaX官网上能支持的最新内核是2.6.27,已经是一年前的更新了。Grsec也时类似的内核补丁,更新较为频繁能支持最新的2.6.32内核。这两种方式都能将内核完全位置无关,除了Grub和Glibc中无法位置无关的汇编码。
PIE最早由RedHat的人实现,他在连接起上增加了-pie选项,这样使用-fPIE编译的对象就能通过连接器得到位置无关可执行程序。fPIE和fPIC有些不同。可以参考Gcc和Open64中的-fPIC选项.
gcc中的-fpic选项,使用于在目标机支持时,编译共享库时使用。编译出的代码将通过全局偏移表(Global Offset Table)中的常数地址访存,动态装载器将在程序开始执行时解析GOT表项(注意,动态装载器操作系统的一部分,连接器是GCC的一部分).而gcc中的-fPIC选项则是针对某些特殊机型做了特殊处理,比如适合动态链接并能避免超出GOT大小限制之类的错误。而Open64仅仅支持不会导致GOT表溢出的PIC编译。
gcc中的-fpie和-fPIE选项和fpic及fPIC很相似,但不同的是,除了生成为位置无关代码外,还能假定代码是属于本程序。通常这些选项会和GCC链接时的-pie选项一起使用。fPIE选项仅能在编译可执行码时用,不能用于编译库。所以,如果想要PIE的程序,需要你除了在gcc增加-fPIE选项外,还需要在ld时增加-pie选项才能产生这种代码。即gcc -fpie -pie来编译程序。单独使用哪一个都无法达到效果。
你可以使用file命令来查看当前的可执行文件是不是PIE的。
下面是本博编译helloword的显示。可以看出,可执行文件属性从executable变成了shared object.
$ gcc  helloworld.c
$ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped
$ gcc -fpie -pie helloworld.c
$ file a.out
a.out: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped
接下来,我们就能实验了,使用strace命令来查看这两个a.out执行情况了。关于strace命令,可以参考strace命令介绍
在博主电脑上,有PIE时,执行第一个brk(0)系统调用时,返回的地址一直是变化的。而无PIE时,brk(O)系统调用返回地址一直不变(红色部分)。

[tom@localhost code]$ strace helloworld
strace: Can't stat 'helloworld': No such file or directory
[tom@localhost code]$ strace ./helloworld 
execve("./helloworld", ["./helloworld"], [/* 25 vars */]) = 0
brk(0)                                  = 0x7f98a5278000

[tom@localhost code]$ strace ./helloworld 
execve("./helloworld", ["./helloworld"], [/* 25 vars */]) = 0
brk(0)                                  = 0x7f9b4cebb000
注:
linux系统调用brk():
linux系统内部分配内存的系统调用,malloc()其实也是调用的brk().直接修改堆的大小,返回新内存区域的结束地址。

你可能感兴趣的:(Linux,gcc,Linux,编程)