0x00 检测
可以使用 pwntools 中的 checksec 工具进行检测,红色表示未开启该保护。
1 | Arch: amd64-64-little |
0x01 保护
一、ASLR
1.介绍:
ASLR 的是操作系统的功能选项,作用于 executable(ELF)装入内存运行时,因而只能随机化 stack、heap、libraries 的基址。
2.表现形式
开启后每次加载程序的 stack、libarys、heap 等地址都会随机化,可用 cat /proc/pidof xxx/maps 查看。
3.作用
未开启:无作用
半开启:随机化 stack 和 libarys
全开启:随机化 stack、libarys 和 heap
4.保护开启和关闭方式
1 | ## 未开启:地址随机化关闭 |
二、NX
1.介绍
No-Execute(不可执行),Nx 的原理是将数据所在内存页标识为不可执行,当程序执行流被劫持到栈上时,程序会尝试在数据页面上执行指令,因为数据页被标记为不可知性,此时CPU 就会抛出异常,而不是去执行栈上数据。
2.表现形式
在程序的某个位置有控制程序是否可以执行的标志位(若为 6 不可执行 、7 不可执行),也可以用 execstack 工具查询和设置该标志位,该标志位在 51e5 7464 后边。详细见下图
xxd 1 :无保护
xxd 2 :有保护
3.作用
NX disabled: 栈可以执行,栈上的数据也可以被当作代码执行 。
NX enabled: 栈不可执行,栈上的数据程序只认为是数据,如果去执行的话会发生错误。即栈上的数据不可以被当作代码执行。
4.gcc 编译选项(为程序添加保护的方式)
1 | ## 栈可执行:NX disabled |
三、PIE
1.介绍
PIE(Position Independent Executables)是编译器(gcc,…)功能选项(-fPIE / -fpie),作用于编译过程,可将其理解为特殊的 PIC(so专用,Position Independent Code),加了 PIE 选项编译出来的 ELF 用 file 命令查看会显示其为 so,其随机化了 ELF 装载内存的基址(代码段、plt、got、data 等共同的基址)。其效果为用 objdump、IDA 反汇编之后的地址是用偏移表示的而不是绝对地址。
2.表现形式

3.作用
No PIE: 无作用
PIE enabled: 代码段、plt、got、data 等共同的基址会随机化。在编译后的程序中,只保留指令、数据等的偏移,而不是绝对地址的形式。
4.gcc 编译选项(为程序添加保护的方式)
1 | ## 关闭:No PIE(默认选项) |
四、Canary
1.介绍
金丝雀保护,是一种用来防护栈溢出的保护机制。其原理是在函数入口处,先从 fs/gs 寄存器中取出一个 4(eax)/8(rax) 字节的 cookie 信息存到栈上,当函数结束返回的时候会验证 cookie 信息是否合法(与开始存的是否一致),如果不合法就停止程序运行。真正的 cookie 信息也会保存在程序的某个位置。插入栈中的 cookie 一般在 ebp / rbp 之上的一个内存单元保存。
2.表现形式

3.作用
无 Canary 保护: 无任何作用
部分函数 Canary 保护: 在一些容易受到攻击的函数返回地址之前添加 cookie 。在函数返回时,检查该 cookie 与原本程序插入该位置的 cookie 是否一致,若一致则程序认为没有受到栈溢出攻击。
全部函数 Canary 保护: 所有的自定义函数在返回地址之前都会添加 cookie 。在函数返回时,检查该 cookie 与原本程序插入该位置的 cookie 是否一致,若一致则程序认为没有受到栈溢出攻击。
4.gcc 编译选项(为程序添加保护的方式)
1 | ## 无 canary 保护:No canary found |
五、RELRO RELocation Read-Only
1.介绍
设置符号重定位表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对 GOT 攻击。
2.作用
No RELRO: 在这种模式下关于重定位并不进行任何保护。
Partial RELRO: 在这种模式下,一些段 (包括.dynamic) 在初始化后将会被标识为只读。
Full RELRO: 在这种模式下,除了会开启部分保护外。惰性解析会被禁用(所有的导入符号将在开始时被解析,.got.plt 段会被完全初始化为目标函数的终地址,并被标记为只读)。此外,既然惰性解析被禁用,GOT[1] 与 GOT[2] 条目将不会被初始化为提到的值。
3.gcc 编译选项(为程序添加保护的方式)
1 | ## 关闭: No RELRO |
把和“重定位(relocation)”相关的一些关键数据结构(常见是 GOT/相关段)在程序启动完成重定位后设为 只读,防止被篡改。
可以把“重定位(relocation)”理解成:程序编译出来时,很多地址先写成“以后再填/再算”,等程序真正被加载到内存里运行时,系统再把这些位置补成当次运行的真实地址。
只读:启用RELRO后,重定位完成后,这些重要的内存区域会被设置为只读,攻击者无法通过改变这些地址来控制程序流。
常见的有Partial RELRO/No RELRO/Full RELRO