谁负责在堆栈中插入堆栈金丝雀?是操作系统吗?
如果是,gcc编译器如何使用-fno-stack-protector
选项禁用它们?或者它只是一个使用该选项创建的标志,并添加到二进制文件中以告诉操作系统不要在运行时加载二进制文件的堆栈中插入金丝雀?
编辑:还有一个问题
如果金丝雀的执行情况发生了变化,谁会检查金丝雀的价值? 再次由编译器插入,如何由OS检查?如果由操作系统插入,编译器如何禁用它(主要问题)?
答案 0 :(得分:0)
谁负责将堆栈金丝雀插入堆栈?
编译器。用于创建和检查堆栈canaries的代码是编译器从程序源代码生成的代码的子集。
GCC:
发出额外的代码来检查缓冲区溢出,例如堆栈粉碎攻击。这是通过向具有易受攻击对象的函数添加保护变量来完成的。这包括调用
alloca
的函数,以及缓冲区大于8字节的函数。输入功能时会初始化防护装置,然后在功能退出时进行检查。如果防护检查失败,则会打印一条错误消息并退出程序。
上述"守护变量"通常被称为canary:
堆栈保护背后的基本思想是推动一个" canary"在按下函数返回指针之后,堆栈上的(随机选择的整数)。然后在函数返回之前检查金丝雀值;如果它已经改变,该程序将中止。通常,堆栈缓冲区溢出(也就是#34;堆栈粉碎")攻击将必须更改金丝雀的值,因为它们在它们可以到达返回指针之前写入超出缓冲区的末尾。由于攻击者不知道金丝雀的价值,因此无法用攻击取代它。因此,堆栈保护允许程序在发生这种情况时中止,而不是返回到攻击者希望它去的地方。 1
示例程序:
源代码:
int test(int i) {
return i;
}
int main(void) {
int x;
int i = 10;
x = test(i);
return x;
}
来自二进制编译的函数没有 -fstack-protector-all
:
$ objdump -dj .text test | grep -A7 "<test>:"
00000000004004ed <test>:
4004ed: 55 push %rbp
4004ee: 48 89 e5 mov %rsp,%rbp
4004f1: 89 7d fc mov %edi,-0x4(%rbp)
4004f4: 8b 45 fc mov -0x4(%rbp),%eax
4004f7: 5d pop %rbp
4004f8: c3 retq
来自二进制编译的函数与 -fstack-protector-all
:
$ objdump -dj .text protected_test | grep -A20 "<test>:"
000000000040055d <test>:
40055d: 55 push %rbp
40055e: 48 89 e5 mov %rsp,%rbp
400561: 48 83 ec 20 sub $0x20,%rsp
400565: 89 7d ec mov %edi,-0x14(%rbp)
400568: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax <- get guard variable value
40056f: 00 00
400571: 48 89 45 f8 mov %rax,-0x8(%rbp) <- save guard variable on stack
400575: 31 c0 xor %eax,%eax
400577: 8b 45 ec mov -0x14(%rbp),%eax
40057a: 48 8b 55 f8 mov -0x8(%rbp),%rdx <- move it to register
40057e: 64 48 33 14 25 28 00 xor %fs:0x28,%rdx <- check it against original
400585: 00 00
400587: 74 05 je 40058e <test+0x31>
400589: e8 b2 fe ff ff callq 400440 <__stack_chk_fail@plt>
40058e: c9 leaveq
40058f: c3 retq
<小时/> <子> 1. "Strong" stack protection for GCC 子>