作者: wzt
原文鏈接:https://mp.weixin.qq.com/s/-RyiuZIZWxUc38q7vGXY7A

1 用戶層

Freebsd默認的編譯器是clang,在libc的實現如下:

libc/secure/stack_protector.c:

long __stack_chk_guard[8] = {0, 0, 0, 0, 0, 0, 0, 0};[1]
static void __guard_setup(void) __attribute__((__constructor__, __used__));[2
static 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, NULL, 0) ==
            -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.c:
long __stack_chk_guard[8] = {};
static void
__stack_chk_init(void *dummy __unused)
{
        size_t i;
        long guard[nitems(__stack_chk_guard)];

        arc4rand(guard, sizeof(guard), 0);
        for (i = 0; i < nitems(guard); i++)
                __stack_chk_guard[i] = guard[i];
}

很簡單,使用arc4rand生成一個隨機值,僅此而已, 非常簡單。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1621/