1.示例copy函数
汇编代码:
失去栈保护的汇编代码:
比对发现,在有栈保护的汇编代码中增加了一些指令,其中:
mov %gs:0x14,%eax
mov %eax,-0xc(ebp)
两句指令,是向栈中插入一个canary值。使用栈保护的copy函数栈是这样的:
canary的值是随机生成的,在内存中保存有两份:
①一个是保存在特殊的段内,这个段被标示为只读,不可修改。
②另一个在栈内,可修改。
③如果缓冲区溢出,canary在栈中的值被修改,系统比对两处的值发现不一样,就会调用一个错误处理例程。
另外,发现形式参数s在被调用者栈内多了一份拷贝
博主zq301在文章GCC编译器局部变量地址分配为什么总是从低地址向高地址增加?中提到:
SSP有意将局部变量中的数组放在函数栈的高地址,而将其他变量放在低地址。这样就使得通过溢出一个数组来修改其他变量(比如一个函数指针)变得更为困难。
2.canary实现
①gcc编译时选择canary的插入位置,生成含有canary的汇编代码。
②glibc产生实际的canary值,以及提供错误捕捉函数和报错函数。
因此canary是运行时动态产生的,不能通过查看静态的bianry得到。
也就是说gcc只是声明和使用了_ stack chk guard/_ stack_ chk_ fail,并没有定义定义是在glibc里.
stack_ chk_ guard和 _ stack_ chk_fail 的插入是在gcc将GIMPLE转换为RTL的pass里分别通过函数default__stack_ protect_guard() 和 ix86_ stack_ protect _fail() 构建手动的tree,然后调用 expand_ normal() 自动转换为RTL再插入待分析的用户代码来进行的.
canary的值的流向
①kernel初始化了跟TLS相关的寄存器gs,并且提供了canary这个随机值。
②glibc写入%gs:0x14这个保存随机值的位置并且提供变定义和打印函数定义。
③gcc插入对canary的值的引用和出错函数到用户代码里。
详细细节见参考文章:最初九月雪《canary分析》