CC_STACKPROTECTOR防止内核stack溢出补丁分析
来源:互联网 发布:手机qq网络硬盘登录 编辑:程序博客网 时间:2024/05/22 01:57
修改.config文件为CONFIG_CC_STACKPROTECTOR=y来启用。未来飞天内核可以将这个选项开启来防止利用内核stack溢出的0day攻击。
这个补丁的防溢出原理是: 在进程启动的时候, 在每个buffer的后面放置一个预先设置好的stack canary,你可以
把它理解成一个哨兵, 当buffer发生缓冲区溢出的时候, 肯定会破坏stack canary的值, 当stack canary的值被破坏的时候, 内核就会直接当机。那么是怎么判断stack canary
被覆盖了呢? 其实这个事情是gcc来做的,内核在编译的时候给gcc加了个-fstack-protector参数, 我们先来研究下这个参数是做什么用的。
先写个简单的有溢出的程序:
[wzt@localhost csaw]$ cat test.c
#include <stdio.h>
#include <stdlib.h>
void test(void)
{
char buff[64];
memset(buff, 0x41, 128); //向64大小的buffer拷贝128字节, 肯定会发生缓冲区溢出。
}
int main(void)
{
test();
return 0;
}
[wzt@localhost csaw]$ gcc -o test test.c
[wzt@localhost csaw]$ ./test
段错误
反汇编看看:
[wzt@localhost csaw]$ objdump -d test > hex
08048384 <test>:
8048384: 55 push %ebp
8048385: 89 e5 mov %esp,%ebp
8048387: 83 ec 58 sub $0x58,%esp
804838a: c7 44 24 08 80 00 00 movl $0x80,0x8(%esp)
8048391: 00
8048392: c7 44 24 04 41 00 00 movl $0x41,0x4(%esp)
8048399: 00
804839a: 8d 45 c0 lea 0xffffffc0(%ebp),%eax
804839d: 89 04 24 mov %eax,(%esp)
80483a0: e8 e3 fe ff ff call 8048288 <memset@plt>
80483a5: c9 leave
80483a6: c3 ret
没什么特别的,我们在加上-fstack-protector参数看看:
[wzt@localhost csaw]$ gcc -o test test.c -fstack-protector
[wzt@localhost csaw]$ ./test
*** stack smashing detected ***: ./test terminated
已放弃
这次程序打印了一条堆栈被溢出的信息,然后就自动退出了。
在反汇编看下:
[wzt@localhost csaw]$ objdump -d test > hex1
080483d4 <test>:
80483d4: 55 push %ebp
80483d5: 89 e5 mov %esp,%ebp
80483d7: 83 ec 68 sub $0x68,%esp
80483da: 65 a1 14 00 00 00 mov %gs:0x14,%eax
80483e0: 89 45 fc mov %eax,0xfffffffc(%ebp)
80483e3: 31 c0 xor %eax,%eax
80483e5: c7 44 24 08 80 00 00 movl $0x80,0x8(%esp)
80483ec: 00
80483ed: c7 44 24 04 41 00 00 movl $0x41,0x4(%esp)
80483f4: 00
80483f5: 8d 45 bc lea 0xffffffbc(%ebp),%eax
80483f8: 89 04 24 mov %eax,(%esp)
80483fb: e8 cc fe ff ff call 80482cc <memset@plt>
8048400: 8b 45 fc mov 0xfffffffc(%ebp),%eax
8048403: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
804840a: 74 05 je 8048411 <test+0x3d>
804840c: e8 db fe ff ff call 80482ec <__stack_chk_fail@plt>
8048411: c9 leave
8048412: c3 ret
使用-fstack-protector参数后, gcc在函数的开头放置了几条汇编代码:
80483d7: 83 ec 68 sub $0x68,%esp
80483da: 65 a1 14 00 00 00 mov %gs:0x14,%eax
80483e0: 89 45 fc mov %eax,0xfffffffc(%ebp)
将代码段gs偏移0x14内存处的值赋值给了ebp-4, 也就是第一个变量值的后面。
在call完memeset后,有如下汇编代码:
80483fb: e8 cc fe ff ff call 80482cc <memset@plt>
8048400: 8b 45 fc mov 0xfffffffc(%ebp),%eax
8048403: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
804840a: 74 05 je 8048411 <test+0x3d>
804840c: e8 db fe ff ff call 80482ec <__stack_chk_fail@plt>
在memset后,gcc要检查这个操作是否发生了堆栈溢出, 将保存在ebp-4的这个值与原来的值对比一下,
如果不相同, 说明堆栈发生了溢出,那么就会执行__stack_chk_fail这个函数, 这个函数是glibc实现的,
打印出上面看到的信息, 然后进程退出。
从这个例子中我们可以看出gcc使用了-fstack-protector参数后,会自动检查堆栈是否发生了溢出, 但是有一个前提就是
内核要给每个进程提前设置好一个检测值放置在%gs:0x14位置处, 这个值称之为stack canary。所以我们可以看到防止
堆栈溢出是由内核和gcc共同来完成的。
gcc的任务就是放置几条汇编代码, 然后和%gs:0x14位置处的值进行对比即可。 主要任务还是内核如何来设置stack canary, 也是
CC_STACKPROTECTOR补丁要实现的目的, 下面我们仔细来看下这个补丁是如何实现的。
既然gcc硬性规定了stack canary必须在%gs的某个偏移位置处, 那么内核也必须按着这个规定来设置。
对于32位和64位内核, gs寄存器有着不同的功能。
64位内核gcc要求stack canary是放置在gs段的40偏移处, 并且gs寄存器在每cpu变量中是共享的,每cpu变量irq_stack_union的结构如下:
arch/x86/include/asm/processor.h
union irq_stack_union {
char irq_stack[IRQ_STACK_SIZE];
/*
* GCC hardcodes the stack canary as %gs:40. Since the
* irq_stack is the object at %gs:0, we reserve the bottom
* 48 bytes of the irq stack for the canary.
*/
struct {
char gs_base[40];
unsigned long stack_canary;
};
};
DECLARE_PER_CPU_FIRST(union irq_stack_union, irq_stack_union);
gs_base只是一个40字节的站位空间, stack_canary就紧挨其后。
并且在应用程序进出内核的时候,内核会使用swapgs指令自动更换gs寄存器的内容。
32位下就稍微有点复杂了。由于某些处理器在加载不同的段寄存器时很慢, 所以内核使用fs段寄存器替换了
gs寄存器。 但是gcc在使用-fstack-protector的时候, 还要用到gs段寄存器, 所以内核还要管理gs寄存器,
我们要把CONFIG_X86_32_LAZY_GS选项关闭, gs也只在进程切换的时候才改变。 32位用每cpu变量stack_canary保存stack canary。
前面讲过当gcc检测到堆栈溢出的时候, 会调用glibc的__stack_chk_fail函数, 但是当内核堆栈发生溢出的时候,
不能调用glibc的函数,所以内核自己实现了一个__stack_chk_fail函数:
kernel/panic.c
#ifdef CONFIG_CC_STACKPROTECTOR
/*
* Called when gcc's -fstack-protector feature is used, and
* gcc detects corruption of the on-stack canary value
*/
void __stack_chk_fail(void)
{
panic("stack-protector: Kernel stack is corrupted in: %p\n",
__builtin_return_address(0));
}
EXPORT_SYMBOL(__stack_chk_fail);
#endif
当内核堆栈发生溢出的时候,就会执行__stack_chk_fail函数, 内核当机。 这就是这个补丁的原理,不懂的同学请参考:
http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=commitdiff;h=60a5317ff0f42dd313094b88f809f63041568b08
- CC_STACKPROTECTOR防止内核stack溢出补丁分析
- 【原理+分析】 缓冲区溢出笔记之---STACK溢出
- 为内核打上yaffs2补丁错误分析
- stack内存溢出
- 内核和lustre补丁
- linux内核实时补丁
- gentoo中文内核补丁
- 如何提交内核补丁
- linux 内核热补丁
- Linux 内核补丁测试
- Linux内核补丁升级
- Linux内核安装补丁
- 防止溢出类攻击
- 防止溢出的stringsprintf
- 防止缓冲区溢出
- 防止缓冲区溢出
- Android防止内容溢出
- 防止内存溢出浅析
- AOJ.综合训练.2016-12-1
- Google推荐的图片加载库Glide介绍
- Android逆向之旅---解析编译之后的AndroidManifest文件格式
- javax.servlet.jsp.PageContext cannot be resolved to a type问题如何解决???
- 从一个字符数组中读出相应的整数、实数
- CC_STACKPROTECTOR防止内核stack溢出补丁分析
- jmap命令,可以看jvm堆栈信息
- 第14周项目1 -(1)验证折半算法
- 校园招聘——双选会感悟(前段学习总结)
- Python如何Using Variable Explorer in Spyder to View Other Namespaces
- 请求转发与重定向
- SG函数初学整合
- Python学习笔记 --- pip生成依赖文件说明
- 接口的作业笔记