在X86中 - linux段寄存器用于缓冲区溢出检查[参见下面的代码片段,它已在堆栈中定义了一些char数组]:
static void
printint(int xx, int base, int sgn)
{
char digits[] = "0123456789ABCDEF";
char buf[16];
int i, neg;
uint x;
neg = 0;
if(sgn && xx < 0){
neg = 1;
x = -xx;
} else {
x = xx;
}
i = 0;
do{
buf[i++] = digits[x % base];
}while((x /= base) != 0);
if(neg)
buf[i++] = '-';
while(--i >= 0)
my_putc(buf[i]);
}
现在,如果我们看到代码gcc生成代码的解组。
函数printint的汇编代码转储:
0x00000000004005a6 : push %rbp
0x00000000004005a7 : mov %rsp,%rbp
0x00000000004005aa : sub $0x50,%rsp
0x00000000004005ae : mov %edi,-0x44(%rbp)
0x00000000004005b1 : mov %esi,-0x48(%rbp)
0x00000000004005b4 : mov %edx,-0x4c(%rbp)
0x00000000004005b7 : mov %fs:0x28,%rax ------> obtaining an 8 byte guard from based on a fixed offset from fs segment register [from the descriptor base in the corresponding gdt entry]
0x00000000004005c0 : mov %rax,-0x8(%rbp) -----> pushing it as the first local variable on to stack
0x00000000004005c4 : xor %eax,%eax
0x00000000004005c6 : movl $0x33323130,-0x20(%rbp)
0x00000000004005cd : movl $0x37363534,-0x1c(%rbp)
0x00000000004005d4 : movl $0x42413938,-0x18(%rbp)
0x00000000004005db : movl $0x46454443,-0x14(%rbp)
...
...
// function end
0x0000000000400686 : jns 0x40066a
0x0000000000400688 : mov -0x8(%rbp),%rax -------> verifying if the stack was smashed
0x000000000040068c : xor %fs:0x28,%rax --> checking the value on stack is matching the original one based on fs
0x0000000000400695 : je 0x40069c
0x0000000000400697 : callq 0x400460 <__stack_chk_fail>
0x000000000040069c : leaveq
0x000000000040069d : retq
现在,如果我们从此函数中删除基于堆栈的char数组,gcc将不会生成此警卫检查。
我已经看到了gcc甚至为内核模块生成的内容。基本上我在看到一些内核代码崩溃时发生了崩溃,并且虚拟地址为0x28。后来我认为我已经正确初始化了堆栈指针并正确加载了程序,我没有在gdt中使用正确的条目,这会将基于fs的偏移转换为有效的虚拟地址。
但是在内核代码的情况下,它只是忽略了错误而不是像__stack_chk_fail @ plt>那样跳转。
在gcc中添加此保护的相关编译器选项是-fstack-protector。我认为这是默认启用的,它编译用户应用程序。
对于内核,我们可以通过config CC_STACKPROTECTOR选项启用此gcc标志。
配置CC_STACKPROTECTOR
699 bool“启用-fstack-protector buffer overflow detection(EXPERIMENTAL)”
700取决于SUPERH32
701帮助
702此选项打开-fstack-protector GCC功能。这个
703功能在函数的开头放置一个金丝雀值
704返回地址之前的堆栈,并验证
705实际返回前的值。基于堆栈的缓冲区
706现在也溢出(需要覆盖此返回地址)
707覆盖了金丝雀,后者被检测到,然后是攻击
708通过内核恐慌中和。
709
710此功能需要gcc 4.2或更高版本。
相关的内核文件,其中gs / fs是linux / arch / x86 / include / asm / stackprotector.h