Glibc中canary的实现

0x00 前言

解决一下历史遗留问题,顺便晒一波式姐,其实这篇文章在好久之前就说要写的,之后就拖到了现在。。。。。

关于canary的作用以及各种编译命令下的形式在上一篇文章中已经说过了,这里就不在赘述了,本篇文章主要讨论Canary如何实现。

0x01 canary概述

编译开启canary的样例代码(64位)

1
2
3
4
5
6
7
#include
int main(void)
{
    printf("hello");
    return 0;
}
//gcc -fstack-protector-all h.c -o h

在64位下程序从一个神奇的地方(fs:28h)取出8(rax)字节的值,插入到栈中。放到栈上以后,rax中的副本也会被清空(xor eax,eax)。在函数基本执行完时,程序会再次从那个神奇的地方把canary的值取出来,和之前放在栈上的canary进行比较,如果发生栈溢出覆盖到了canary而导致canary发生了改变则跳到函数 ___stack_chk_fail 直接终止程序。

而32位基本相同只不过取出canary的地方变成(gs:14h),而且取出的canary字节为4字节,具体汇编代码可以看我上一篇文章。

在这里要多说的就是以64位为例,fs寄存器是被glibc定义为存放TLS信息的。

上面这个被定义在glibc/sysdeps/x86_64/nptl/tls.h中结构体tcbhead_t就是用来描述TLS的,同时也是%fs寄存器所指向的位置,其中0x28偏移位置的成员变量uintptr_t  stack_guard就是canary的值。至于canary值的产生以及如何存放入fs:28h将在下面进行说明。

0x02 canary细节

总的来说canary的实现分为两部分,在gcc编译时选择canary的插入位置, 以及引入与canary有关的汇编代码, 而glibc则产生实际的canary值, 以及提供报错函数。也就是gcc使用glibc提供的函数来生成canary值和报错,而gcc本身只是插入相关引用的汇编代码自身并不定义canary的值。因此canary的值只有运行时才知道, 而不能通过查看静态的binary得到。

Gcc

在gcc里canary的实现靠的是引用了结构体tcbhead_t的成员变量uintptr_t  stack_guard 和函数__stack_chk_faillai而变量uintptr_t  stack_guard存就是canary的值,至于函数__stack_chk_fail的作用就是当比较canary不一样时输出错误信息。

这两东西具体实现都在Glibc里,__stack_chk_fail在上一篇文章的0x03 ssp leak里已经讨论过了,这里不再赘述,而变量uintptr_t  stack_guard则会再下面Glibc里说明。也就是说gcc只是声明和使用了变量uintptr_t  stack_guard__stack_chk_fail, 并没有定义。 定义是在glibc里。

Glibc

首先来说一下canary值的生成,也就是变量__stack_chk_guard,其实它的生成还和内核有点关系。

这个过程在__libc_start_main中完成

而那个_dl_random是内核提供的随机数生成器

至于函数_dl_setup_stack_chk_guard只是读取内核生成的随机数,之后根据系统是32位还是64位来产生相应长度的canary。

当这一切执行完便将处理好的canary放入变量__stack_chk_guard中,至于再将它放入变量uintptr_t  stack_guard也就是fs:28h所指地址便是交由下面那个THREAD_SET_STACK_GUARD宏来处理了。

THREAD_SELF的作用便是返回当前线程的线程描述符,而header.stack_guard便是指向结构体tcbhead_t的成员stack_guard。

THREAD_SETMEM便会根据不同的地址大小将合适的cannary填入stack_guard。这样在GCC中便可以通过fs:28h对其读取,与栈上的canary进行比较,以确定程序是否发生了栈溢出。

0x03 总结

如果按发生顺序便是:

Ⅰ. Glibc中

1.内核提供的随机数生成器_dl_random产生随机数

2.函数_dl_setup_stack_chk_guard根据系统是32位还是64位将随机数处理成4byte或8byte的canary,并赋给变量__stack_chk_guard

3.THREAD_SET_STACK_GUARD通过处理将变量__stack_chk_guard放入结构体tcbhead_t的成员stack_guard中(即fs:28h)

Ⅱ. GCC中

4.选择canary的插入位置, 以及引入与canary有关的汇编代码

Ⅲ. 程序运行中

5.程序函数开头从 fs:28h 中取出8字节的值插入到栈中,同时清空rax中的副本

6.函数结束时,程序会再次从 fs:28h 中将canary的值取出与栈上的canary进行比较

7.如果canary不同则跳到函数 ___stack_chk_fail 直接终止程序,否则继续执行程序

0x04 Extra

 

 

式姐美如画(破声

今天fgo空境复刻式姐和藤乃都出了,有点小激动。

发表评论

电子邮件地址不会被公开。 必填项已用*标注