GCC安全编译参数

一、写在前面

A main pillar in security is that security is done in layers. During software build, compilation is done using a number of flags and switches that enhance the functionality and security of the output program.
Below is a list of a number of compilation flags that must be enabled during build of all software components, including both proprietary and 3rd party open-source components.

总的来说就是安全是分层的,编译安全也是一个重要的方面,我们要在所有软件以及开源库都使用如下标志来进行构建:-fstack-protector-all、-Wl,relro,Wl,now、-s、-fPIE –pie、-fPIC、-D_FORTIFY_SOURCE=2 -O1,这些编译参数适用于gcc和clang.

-fstack-protector-all:

用于在编译和链接过程中自动添加堆栈保护机制。它的作用是在函数调用时检查堆栈的完整性,以防止缓冲区溢出等常见的安全问题。

具体来说,-fstack-protector-all会在编译时为每个函数添加额外的代码,用于检查函数的返回地址是否被修改,以及在函数调用前后检查函数的堆栈帧是否完整。如果检测到堆栈被破坏,程序将会终止,并输出相关的错误信息。

使用-fstack-protector-all的好处是提高程序的安全性。通过检查堆栈的完整性,可以防止一些常见的攻击手段,如缓冲区溢出和栈溢出攻击。这些攻击手段常被用于执行恶意代码、绕过程序的安全检查以及获取系统权限。使用堆栈保护机制可以大大减少这些攻击的成功率。

需要注意的是,使用-fstack-protector-all会增加程序的运行时开销和内存占用,因为需要额外的代码来检查堆栈的完整性。但是这个开销通常是可以接受的,尤其对于对安全性要求较高的程序而言。

Wl,-z,relro,-z,now是用于向链接器传递选项的编译器参数。具体作用如下:

-Wl: 该选项用于将其后的参数传递给链接器。
-z,relro: 这个选项告诉链接器在运行时将只读段设为“重定位只读”(RelRO),即只读段的重定位信息将在程序加载时完成,并且只读段将被标记为只读,防止攻击者修改这些段的内容。
-z,now: 这个选项告诉链接器在程序加载时立即解析所有符号引用,而不是在第一次使用时再解析。这可以防止动态链接器在程序运行时进行符号解析,减少了攻击者利用延迟绑定漏洞的机会。
这些选项的好处是增强了程序的安全性。通过将只读段设为“重定位只读”,可以防止攻击者通过修改只读段来实施攻击,例如覆盖只读数据或插入恶意代码。而通过立即解析所有符号引用,可以减少动态链接器在运行时的工作量,提高程序的启动速度,并减少了攻击者利用延迟绑定漏洞的机会。

-s:从二进制文件中去掉符号表

-fPIE -pie:构建为与位置无关的代码,以在编译的软件二进制文件上启用 ASLR 随机化

-fPIC:构建与位置无关的代码以在编译的软件库上启用 ASLR 随机化。

-D_FORTIFY_SOURCE=2:启用FORTIFY_SOURCE特性的级别2,用于提供更强的安全性

-O1:启用优化级别1,该级别会进行一些基本的优化,但不会对代码进行过度优化

注意:部分编译器可能没有提供-fstack-protector-all编译选项,至少对我测试的编译器来说,这是可以使用-Wa,–noexecstack来尽可能地保证安全。

-Wa,–noexecstack: 禁止生成可执行文件时使用可执行栈

-Wa,–noexecstack是用于向汇编器传递选项的编译器参数。具体作用如下:

  • -Wa: 该选项用于将其后的参数传递给汇编器。
  • –noexecstack: 这个选项告诉汇编器生成的可执行文件不允许在堆栈上执行代码。它将禁用堆栈上的可执行代码段,防止攻击者利用堆栈溢出漏洞在堆栈上注入和执行恶意代码。

使用–noexecstack的好处是提高程序的安全性。通过禁止在堆栈上执行代码,可以防止攻击者利用堆栈溢出漏洞注入和执行恶意代码。堆栈溢出是一种常见的安全漏洞,攻击者可以利用它来执行任意代码,绕过程序的安全机制,并获取系统权限。

需要注意的是,使用–noexecstack可能会影响某些特定的程序功能,因为有些程序可能需要在堆栈上执行代码。在这种情况下,可以根据具体需求来决定是否使用–noexecstack选项。

二、位置无关代码编译验证
结论一:gcc编译器默认开启pie

$ g++ -c pic.cpp
$ ar rcs libPic.a pic.o
$ g++ -c -I./ pie.cpp
$ g++  pie.o libPic.a -o test.exe
$ ./checksec.sh  --file test.exe
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      No canary found   NX enabled    PIE enabled     No RPATH   No RUNPATH   test.exe

结论二:我们使用的交叉编译器默认关闭pie,编译时全部文件加上-fPIC最终才能使用-pie生成位置无关程序

$ arm-ca9-linux-uclibcgnueabihf-g++ -c -fPIC pic.cpp
$ arm-ca9-linux-uclibcgnueabihf-ar rcs libPic.a pic.o
$ arm-ca9-linux-uclibcgnueabihf-g++ -c -fPIC -fPIE -I./ pie.cpp
$ arm-ca9-linux-uclibcgnueabihf-g++  pie.o libPic.a -pie -o test.exe
$ ./checksec.sh  --file test.exe
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX disabled   PIE enabled     No RPATH   No RUNPATH   test.exe

结论三:如果存在未使用-PIC编译参数的库,最终无法使用-pie生成目标程序

$ arm-ca9-linux-uclibcgnueabihf-g++ -c pic.cpp
$ arm-ca9-linux-uclibcgnueabihf-ar rcs libPic.a pic.o
$ arm-ca9-linux-uclibcgnueabihf-g++ -c -fPIC -fPIE -I./ pie.cpp
$ arm-ca9-linux-uclibcgnueabihf-g++  pie.o libPic.a -pie -o test.exe
arm-ca9-linux-uclibcgnueabihf/bin/ld: libPic.a(pic.o): relocation R_ARM_MOVW_ABS_NC against `a local symbol' can not be used when making a shared object; recompile with -fPIC
arm-ca9-linux-uclibcgnueabihf/bin/ld: libPic.a(pic.o)(.text+0x10): unresolvable R_ARM_MOVW_ABS_NC relocation against symbol `_ZSt4cout@@GLIBCXX_3.4'
arm-ca9-linux-uclibcgnueabihf/bin/ld: final link failed: nonrepresentable section on output
collect2: error: ld returned 1 exit status
arm-ca9-linux-uclibcgnueabihf-g++ -c pic.cpp
arm-ca9-linux-uclibcgnueabihf-ar rcs libPic.a pic.o
arm-ca9-linux-uclibcgnueabihf-g++ -c -I./ pie.cpp
arm-ca9-linux-uclibcgnueabihf-g++  pie.o libPic.a -znoexecstack -o test.exe
readelf -l test.exe | grep GNU_STACK

你可能感兴趣的:(c++,c语言)