作者: wzt
原文链接:https://mp.weixin.qq.com/s/-RyiuZIZWxUc38q7vGXY7A
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字节。
大家要注意一个进程有两个栈, 一个栈用于运行在用户态, 一个栈用于进程使用系统调用时的内核栈。 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生成一个随机值,仅此而已, 非常简单。
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1621/