總結(jié)
- NX:-z execstack / -z noexecstack (關(guān)閉 / 開(kāi)啟)
- Canary:-fno-stack-protector /-fstack-protector / -fstack-protector-all (關(guān)閉 / 開(kāi)啟 / 全開(kāi)啟)
- PIE:-no-pie / -pie (關(guān)閉 / 開(kāi)啟)
- RELRO:-z norelro / -z lazy / -z now (關(guān)閉 / 部分開(kāi)啟 / 完全開(kāi)啟)
Canary
gcc在4.2版本中添加了-fstack-protector和-fstack-protector-all編譯參數(shù)以支持棧保護(hù)功能框仔,4.9新增了-fstack-protector-strong編譯參數(shù)讓保護(hù)的范圍更廣齿拂。
編譯控制選項(xiàng)
gcc -o test test.c // 默認(rèn)情況下,不開(kāi)啟Canary保護(hù)
gcc -fno-stack-protector -o test test.c //禁用棧保護(hù)
gcc -fstack-protector -o test test.c //啟用堆棧保護(hù)燃少,不過(guò)只為局部變量中含有 char 數(shù)組的函數(shù)插入保護(hù)代碼
gcc -fstack-protector-all -o test test.c //啟用堆棧保護(hù)仆潮,為所有函數(shù)插入保護(hù)代碼
Fortify
fority其實(shí)非常輕微的檢查检盼,用于檢查是否存在緩沖區(qū)溢出的錯(cuò)誤。適用情形是程序采用大量的字符串或者內(nèi)存操作函數(shù)颁独,如memcpy彩届,memset,stpcpy誓酒,strcpy樟蠕,strncpy,strcat靠柑,strncat寨辩,sprintf,snprintf病往,vsprintf捣染,vsnprintf,gets以及寬字符的變體停巷。
-
_FORTIFY_SOURCE
設(shè)為1耍攘,并且將編譯器設(shè)置為優(yōu)化1(gcc -O1),以及出現(xiàn)上述情形畔勤,那么程序編譯時(shí)就會(huì)進(jìn)行檢查但又不會(huì)改變程序功能 -
_FORTIFY_SOURCE
設(shè)為2蕾各,有些檢查功能會(huì)加入,但是這可能導(dǎo)致程序崩潰庆揪。
gcc -D_FORTIFY_SOURCE=1
僅僅只會(huì)在編譯時(shí)進(jìn)行檢查 (特別像某些頭文件 #include <string.h>)
gcc -D_FORTIFY_SOURCE=2
程序執(zhí)行時(shí)也會(huì)有檢查 (如果檢查到緩沖區(qū)溢出式曲,就終止程序)
gcc -o test test.c // 默認(rèn)情況下,不會(huì)開(kāi)這個(gè)檢查
gcc -D_FORTIFY_SOURCE=1 -o test test.c // 較弱的檢查
gcc -D_FORTIFY_SOURCE=2 -o test test.c // 較強(qiáng)的檢查
NX(DEP)
NX即No-eXecute(不可執(zhí)行)的意思缸榛,NX(DEP)的基本原理是將數(shù)據(jù)所在內(nèi)存頁(yè)標(biāo)識(shí)為不可執(zhí)行吝羞,當(dāng)程序溢出成功轉(zhuǎn)入shellcode時(shí),程序會(huì)嘗試在數(shù)據(jù)頁(yè)面上執(zhí)行指令内颗,此時(shí)CPU就會(huì)拋出異常钧排,而不是去執(zhí)行惡意指令。
gcc編譯器默認(rèn)開(kāi)啟了NX選項(xiàng)均澳,如果需要關(guān)閉NX選項(xiàng)恨溜,可以給gcc編譯器添加-z execstack
參數(shù)
gcc -o test test.c // 默認(rèn)情況下符衔,開(kāi)啟NX保護(hù)
gcc -z execstack -o test test.c // 禁用NX保護(hù)
gcc -z noexecstack -o test test.c // 開(kāi)啟NX保護(hù)
PIE(ASLR)
內(nèi)存地址隨機(jī)化機(jī)制(address space layout randomization),有以下三種情況
0 - 表示關(guān)閉進(jìn)程地址空間隨機(jī)化糟袁。
1 - 表示將mmap的基址判族,stack和vdso頁(yè)面隨機(jī)化。
2 - 表示在1的基礎(chǔ)上增加棧(heap)的隨機(jī)化项戴。
- Linux關(guān)閉PIE的方法如下
sudo -s echo 0 > /proc/sys/kernel/randomize_va_space
- gcc編譯選項(xiàng)
gcc -o test test.c // 默認(rèn)情況下形帮,不開(kāi)啟PIE
gcc -fpie -pie -o test test.c // 開(kāi)啟PIE,此時(shí)強(qiáng)度為1
gcc -fPIE -pie -o test test.c // 開(kāi)啟PIE肯尺,此時(shí)為最高強(qiáng)度2
gcc -fpic -o test test.c // 開(kāi)啟PIC沃缘,此時(shí)強(qiáng)度為1,不會(huì)開(kāi)啟PIE
gcc -fPIC -o test test.c // 開(kāi)啟PIC则吟,此時(shí)為最高強(qiáng)度2槐臀,不會(huì)開(kāi)啟PIE
說(shuō)明
PIE最早由RedHat的人實(shí)現(xiàn),他在連接起上增加了-pie選項(xiàng)氓仲,這樣使用-fPIE編譯的對(duì)象就能通過(guò)連接器得到位置無(wú)關(guān)可執(zhí)行程序水慨。fPIE和fPIC有些不同【纯福可以參考Gcc和Open64中的-fPIC選項(xiàng).
gcc中的-fpic選項(xiàng)晰洒,使用于在目標(biāo)機(jī)支持時(shí),編譯共享庫(kù)時(shí)使用啥箭。編譯出的代碼將通過(guò)全局偏移表(Global Offset Table)中的常數(shù)地址訪存谍珊,動(dòng)態(tài)裝載器將在程序開(kāi)始執(zhí)行時(shí)解析GOT表項(xiàng)(注意,動(dòng)態(tài)裝載器操作系統(tǒng)的一部分急侥,連接器是GCC的一部分)砌滞。而gcc中的-fPIC選項(xiàng)則是針對(duì)某些特殊機(jī)型做了特殊處理,比如適合動(dòng)態(tài)鏈接并能避免超出GOT大小限制之類的錯(cuò)誤坏怪。而Open64僅僅支持不會(huì)導(dǎo)致GOT表溢出的PIC編譯贝润。
gcc中的-fpie和-fPIE選項(xiàng)和fpic及fPIC很相似,但不同的是,除了生成為位置無(wú)關(guān)代碼外,還能假定代碼是屬于本程序垂睬。通常這些選項(xiàng)會(huì)和GCC鏈接時(shí)的-pie選項(xiàng)一起使用。fPIE選項(xiàng)僅能在編譯可執(zhí)行碼時(shí)用尊蚁,不能用于編譯庫(kù)。所以侣夷,如果想要PIE的程序横朋,需要你除了在gcc增加-fPIE選項(xiàng)外,還需要在ld時(shí)增加-pie選項(xiàng)才能產(chǎn)生這種代碼惜纸。即gcc -fpie -pie來(lái)編譯程序叶撒。單獨(dú)使用哪一個(gè)都無(wú)法達(dá)到效果。
RELRO
GCC, GNU linker以及Glibc-dynamic linker一起配合實(shí)現(xiàn)了一種叫做relro的技術(shù): read only relocation耐版。大概實(shí)現(xiàn)就是由linker指定binary的一塊經(jīng)過(guò)dynamic linker處理過(guò) relocation之后的區(qū)域?yàn)橹蛔x.
設(shè)置符號(hào)重定向表格為只讀或在程序啟動(dòng)時(shí)就解析并綁定所有動(dòng)態(tài)符號(hào)祠够,從而減少對(duì)GOT(Global Offset Table)攻擊。RELRO為” Partial RELRO”粪牲,說(shuō)明我們對(duì)GOT表具有寫(xiě)權(quán)限古瓤。
gcc編譯選項(xiàng)
gcc -o test test.c // 默認(rèn)情況下,是Partial RELRO
gcc -z norelro -o test test.c // 關(guān)閉腺阳,即No RELRO
gcc -z lazy -o test test.c // 部分開(kāi)啟落君,即Partial RELRO
gcc -z now -o test test.c // 全部開(kāi)啟,即Full RELRO