Freebsd stack protector解读
2021-1-2 10:48:5 Author: mp.weixin.qq.com(查看原文) 阅读量:0 收藏

1 用户层

Freebsd默认的编译器是clang,在libc的实现如下:

libc/secure/stack_protector.c:
long __stack_chk_guard[8] = {00000000};[1]static void __guard_setup(void) __attribute__((__constructor__, __used__));[2static void__guard_setup(void){        error = _elf_aux_info(AT_CANARY, (void *)tmp_stack_chk_guard,[3]            sizeof(tmp_stack_chk_guard));        if (error == 0 && tmp_stack_chk_guard[0] != 0) {                for (idx = 0; idx < nitems(__stack_chk_guard); idx++) {                        __stack_chk_guard[idx] = tmp_stack_chk_guard[idx];                        tmp_stack_chk_guard[idx] = 0;                }                return;        } len = sizeof(__stack_chk_guard);
        if (__sysctl(mib, nitems(mib), __stack_chk_guard, &len, NULL0) ==            -1 || len != sizeof(__stack_chk_guard)) {                /* If sysctl was unsuccessful, use the "terminator canary". */                ((unsigned char *)(void *)__stack_chk_guard)[0] = 0;[4]                ((unsigned char *)(void *)__stack_chk_guard)[1] = 0;                ((unsigned char *)(void *)__stack_chk_guard)[2] = '\n';                ((unsigned char *)(void *)__stack_chk_guard)[3] = 255;        }}

[1] 处的__stack_chk_guard是个全局数组,保存的是每个进程的stack canary值,它是通过[3]处的_elf_aux_info获取, 而这个值是在内核执行execve加载二进制程序时就提前设置好的:

exec_copyout_strings:        /*         * Prepare the canary for SSP.         */        arc4rand(canary, sizeof(canary), 0);        destp -= sizeof(canary);        imgp->canary = destp;        copyout(canary, (void *)destp, sizeof(canary));        imgp->canarylen = sizeof(canary);

如果用户使用的是gcc编译器,那么stack canary的值则是在libc初始化时动态生成的:

contrib/gcclibs/libssp/ssp.c:void *__stack_chk_guard = 0;
static void __attribute__ ((constructor))__guard_setup (void){  fd = open ("/dev/urandom", O_RDONLY);  if (fd != -1)    {      ssize_t size = read (fd, &__stack_chk_guard,                           sizeof (__stack_chk_guard)); }
  p = (unsigned char *) &__stack_chk_guard;  p[sizeof(__stack_chk_guard)-1] = 255;  p[sizeof(__stack_chk_guard)-2] = '\n';  p[0] = 0;}

通过读取/dev/urandom来获取一个指针地址,在64位上就是8字节。

2 内核层

大家要注意一个进程有两个栈, 一个栈用于运行在用户态, 一个栈用于进程使用系统调用时的内核栈。 freebsd的所有进程的内核栈都使用的是同一个stack canary值, 而linux的每个进程内核栈stack canary值都是不一样的,这样做会带来更高的安全强度,但是对于设计来讲,则会加大了性能开销,linux为了实现这个功能,需要在进程切换的路径中增加对stack canary的切换。

kern/stack_protector.clong __stack_chk_guard[8] = {};static void__stack_chk_init(void *dummy __unused){        size_t i;        long guard[nitems(__stack_chk_guard)];
        arc4rand(guardsizeof(guard), 0);        for (i = 0; i < nitems(guard); i++)                __stack_chk_guard[i] = guard[i];}

很简单,使用arc4rand生成一个随机值,仅此而已, 非常简单。


文章来源: https://mp.weixin.qq.com/s?__biz=Mzg4NjU1NDU4MA==&mid=2247483689&idx=1&sn=b56ce7a8e7eaba443a14a2c766a5d591&chksm=cf96ab92f8e12284ff8c058e371f5ade6bf43203974ab70e2a7b9289195f93f8f096a3bd9fe2&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh