GCC 中的编译器堆栈保护技术

本文深入探讨了GCC中的编译器堆栈保护技术,包括堆栈保护原理、GCC的具体实现,如Canaries探测和局部变量顺序调整。通过示例分析了GCC 4.1中的相关编译选项,并揭示了堆栈保护的局限性,如只保护控制信息而不保护局部变量,以及可利用函数参数进行攻击等。

编译器堆栈保护原理

我们知道攻击者利用堆栈溢出漏洞时,通常会破坏当前的函数栈。例如,攻击者利用清单1 中的函数的堆栈溢出漏洞时,典型的情况是攻击者会试图让程序往 name数组中写超过数组长度的数据,直到函数栈中的返回地址被覆盖,使该函数返回时跳转至攻击者注入的恶意代码或 shellcode 处执行(关于溢出攻击的原理参见《Linux 下缓冲区溢出攻击的原理及对策》)。

溢出攻击后,函数栈变成了图 2所示的情形,与溢出前(图 1)比较可以看出原本堆栈中的 EBP,返回地址已经被溢出字符串覆盖,即函数栈已经被破坏。

清单 1. 可能存在溢出漏洞的代码

int vulFunc() {
    char name[10];
    //…
    return 0;
}

在这里插入图片描述
在这里插入图片描述

GCC 中的堆栈保护实现

Stack Guard是第一个使用Canaries 探测的堆栈保护实现,它于 1997年作为GCC的一个扩展发布。最初版本的Stack Guard使用 0x00000000作为canary word。尽管很多人建议把 Stack Guard 纳入GCC,作为 GCC的一部分来提供堆栈保护。但实际上,GCC 3.x没有实现任何的堆栈保护。直到GCC 4.1 堆栈保护才被加入,并且 GCC4.1 所采用的堆栈保护实现并非Stack Guard,而是 Stack-smashing ProtectionSSP,又称 ProPolice)。

SSPStack Guard 的基础上进行了改进和提高。它是由IBM的工程师Hiroaki Rtoh开发并维护的。与 Stack Guard相比,SSP 保护函数返回地址的同时还保护了栈中的 EBP 等信息。此外,SSP还有意将局部变量中的数组放在函数栈的高地址,而将其他变量放在低地址。这样就使得通过溢出一个数组来修改其他变量(比如一个函数指针)变得更为困难。

GCC 4.1 中三个与堆栈保护有关的编译选项

-fstack-protector:

启用堆栈保护,不过只为局部变量中含有char数组的函数插入保护代码。

-fstack-protector-all:

启用堆栈保护,为所有函数插入保护代码。

-fno-stack-protector:

禁用堆栈保护。

GCC 中的 Canaries 探测

下面通过一个例子分析 GCC 堆栈保护所生成的代码。分别使用-fstack-protector选项和 -fno-stack-protector 编译清单2中的代码得到可执行文件 demo_sp (-fstack-protector)demo_nosp (-fno-stack-protector)

清单 2. demo.c

int main() {
    int i;
    char buffer[64];
    i = 1;
    buffer[0] = 'a';
    return 0;
}

然后用 gdb分别反汇编demo_spdeno_nosp

清单 3. demo_nosp 的汇编代码

(gdb) disas main
Dump of assembler code for function main:
0x08048344 <main+0>:    lea    0x4(%esp),%ecx
0x08048348 <main+4>:    and    $0xfffffff0,%esp
0x0804834b <main+7>:    pushl  0xfffffffc(%ecx)
0x0804834e <main+10>:   push   %ebp
0x0804834f <main+11>:   mov    %esp,%ebp
0x080483
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值