0x00 前言
解决一下历史遗留问题,顺便晒一波式姐,其实这篇文章在好久之前就说要写的,之后就拖到了现在。。。。。
关于canary的作用以及各种编译命令下的形式在栈溢出中关于canary的总结中已经说过了,这里就不在赘述了,本篇文章主要讨论Canary如何实现。
0x01 canary概述
编译开启canary的样例代码(64位)
1 |
|
在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空境复刻式姐和藤乃都出了,有点小激动。