GCC中的堆栈保护机制

以堆栈溢出为代表的缓冲区溢出已成为最为普遍的安全漏洞,由此引发的安全问题比比皆是。我们知道攻击者利用堆栈溢出漏洞时,通常会破坏当前的函数栈。在gcc中,通过编译选项可以添加 函数栈的保护机制,通过重新对局部变量进行布局来实现,达到监测函数栈是否非破坏的目的。

gcc中有3个与堆栈保护相关的编译选项

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

-fstack-protector-all:启用堆栈保护,为所有函数插入保护代码。

-fno-stack-protector:禁用堆栈保护。

1.1 编译选项为 gcc -fstack-protector-all main.c

#include <stdio.h>

int main(void)
{
	int i; /* 此时i的声明在数组a之前 */
	int a[4];
	//int i;

	printf("&a=%p,&i=%p\n",&a,&i);

	//a[4] = 0;
	printf("%d\n", a[4]);
	//a[5] = 0;
	//a[6] = 0;
	//a[7] = 0;
	//a[8] = 0;
	for(i=0;i<=3;i++) /* 当把3改为4的话,程序运行就会出错,因为开启了堆栈保护,在a[4]的地方编译器放置了一个保护变量,当该变量被修改时,程序
                           * 认为该函数栈被破坏,于是抛出错误 */
		a[i]=0;

	printf("hello\n");
	return 0;
}

输出结果为:

&a=0xbf9b7ebc,&i=0xbf9b7eb8
1425164544
hello

1.2 编译选项为 gcc -fstack-protector-all main.c

#include <stdio.h>

int main(void)
{
	//int i;
	int a[4];
	int i; /* 此时i的声明在数组a之后 */

	printf("&a=%p,&i=%p\n",&a,&i);

	//a[4] = 0;
	printf("%d\n", a[4]);
	//a[5] = 0;
	//a[6] = 0;
	//a[7] = 0;
	//a[8] = 0;
	for(i=0;i<=3;i++)/* 当把3改为4的话,程序运行就会出错,因为开启了堆栈保护,在a[4]的地方编译器放置了一个保护变量,当该变量被修改时,程序
                           * 认为该函数栈被破坏,于是抛出错误 */
		a[i]=0;

	printf("hello\n");
	return 0;
}

输出结果为:

&a=0xbfe0cd3c,&i=0xbfe0cd38
1166871552
hello

1.3 结论

在gcc -fstack-protector-all的编译选项下,编译器会对局部变量的组织方式进行重新布局,在本例中,无论i和a变量的声明前后顺序怎么样,它们的布局都是如上图中右边的布局方式,a数组始终在高地址,变量i始终在低地址。并且在a[4]的地址位置,编译器会放置一个保护变量,当该变量被修改时会抛出错误。

2.1 编译选项为 gcc -fno-stack-protector main.c

#include <stdio.h>

int main(void)
{
	int i; /* 此时i的声明在数组a之前 */
	int a[4];
	//int i;

	printf("&a=%p,&i=%p\n",&a,&i);

	//a[4] = 0;
	printf("%d\n", a[4]);
	//a[5] = 0;
	//a[6] = 0;
	//a[7] = 0;
	//a[8] = 0;
	for(i=0;i<=4;i++)
		a[i]=0;

	printf("hello\n");
	return 0;
}

输出结果:(程序陷入无限循环之中)

2.2 编译选项为 gcc -fno-stack-protector main.c

#include <stdio.h>

int main(void)
{
	//int i;
	int a[4];
	int i; /* 此时i的声明在数组a之后 */

	printf("&a=%p,&i=%p\n",&a,&i);

	//a[4] = 0;
	printf("%d\n", a[4]);
	//a[5] = 0;
	//a[6] = 0;
	//a[7] = 0;
	//a[8] = 0;
	for(i=0;i<=4;i++)
		a[i]=0;

	printf("hello\n");
	return 0;
}

输出结果:

&a=0xbfda2350,&i=0xbfda234c
134513824
hello

2.3 结论

在gcc -fno-stack-protector的编译选项下,编译器不会对局部变量的组织方式进行重新布局,在本例中,当i的声明在a之前时,内存布局如上图中的左边的那个一样;当i的声明在a之后时,内存布局如上图中的右边的那个一样。

3.1 编译选项为 gcc  main.c (默认情况下)

#include <stdio.h>

int main(void)
{
	int i; /* 此时i的声明在数组a之前 */
	int a[4];
	//int i;

	printf("&a=%p,&i=%p\n",&a,&i);

	//a[4] = 0;
	printf("%d\n", a[4]);
	//a[5] = 0;
	//a[6] = 0;
	//a[7] = 0;
	//a[8] = 0; /* 当把这行代码取消注释后,运行程序会出现段错误,把改行以上的3行取消注释没有问题 */
	for(i=0;i<=4;i++)
		a[i]=0;

	printf("hello\n");
	return 0;
}

输出结果:(程序陷入无限循环之中)

3.2 编译选项为 gcc  main.c (默认情况下)

#include <stdio.h>

int main(void)
{
	//int i;
	int a[4];
	int i;

	printf("&a=%p,&i=%p\n",&a,&i);

	//a[4] = 0;
	printf("%d\n", a[4]);
	//a[5] = 0;
	//a[6] = 0;
	//a[7] = 0;
	//a[8] = 0;
	for(i=0;i<=4;i++)
		a[i]=0;

	printf("hello\n");
	return 0;
}

输出结果:(程序陷入无限循环之中)

3.3 结论

默认情况下,编译器会对局部变量的组织方式进行重新布局,在本例中,无论i和a变量的声明前后顺序怎么样,它们的布局都是如上图中左边的布局方式,变量i始终在低地址,a数组始终在高地址。

参考资料:

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

2、C程序的存储空间

时间: 2024-09-30 07:03:21

GCC中的堆栈保护机制的相关文章

Linux中的保护机制

Linux中的保护机制 在编写漏洞利用代码的时候,需要特别注意目标进程是否开启了NX.PIE等机制,例如存在NX的话就不能直接执行栈上的数据,存在PIE 的话各个系统调用的地址就是随机化的. 一:canary(栈保护) 栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行.当启用栈保护后,函数开始执行的时候会先往栈里插入cookie信息,当函数真正返回的时候会验证cookie信息是否合法,如果不合法就停止程序运行.

Android中的软件安全和逆向分析[二]—apk反破解技术与安全保护机制

在Android应用开发中,当我们开发完软件之后,我们不希望别人能够反编译破解我们的应用程序,不能修改我们的代码逻辑.实际上,在应用程序的安全机制考虑中,我们希望自己的应用程序安全性高,通过各种加密操作等来增大竞争对手的反编译破解成本.设想,竞争对手开发一个同样的应用程序需要10天,而破解我们的软件程序需要100天,那么势必会打消黑客程序员破解我们应用程序的念头.如何增加对手的破解成本,就需要考验我们应用程序的安全性有多高,加密技术有多强.一个优秀的应用程序,不仅能为用户带来利益,同时也能保护自

二进制的保护机制

0x00 checksec 这里主要讲的是CTF中linux下的ELF二进制文件的保护机制.在linux中有一个脚本checksec命令可以查看当前二进制文件的保护机制.任意安装一款gdb插件都会把checksec脚本包含进来. 在gdb中执行: gdb> checksec test Canary : No NX : Yes PIE : No Fortify : No RelRO : Partial 直接在shell中执行: $ checksec test Arch: i386-32-littl

linux程序的常用保护机制

linux程序的常用保护机制 来源 https://www.cnblogs.com/Spider-spiders/p/8798628.html 操作系统提供了许多安全机制来尝试降低或阻止缓冲区溢出攻击带来的安全风险,包括DEP.ASLR等.在编写漏洞利用代码的时候,需要特别注意目标进程是否开启了DEP(Linux下对应NX).ASLR(Linux下对应PIE)等机制,例如存在DEP(NX)的话就不能直接执行栈上的数据,存在ASLR的话各个系统调用的地址就是随机化的. 一.checksec che

各种保护机制绕过手法

一.绕过GS编译选项 ●原理:通过VC++编译器在函数前后添加额外的处理代码,前部分用于由伪随机数生成的cookie并放入.data节段,当本地变量初始化,就会向栈中插入cookie,它位于局部变量和返回地址之间 ●绕过方法: 1.猜测/计算cookie Reducing the Effective Entropy of GS Cookies:http://www.uninformed.org/?v=7&a=2&t=html 至从覆盖SEH的方法出现后,这种方法目前已基本不用了,它没有后面

C/C++中的函数参数传递机制

对函数的形参感兴趣的可以看一下 一. 函数参数传递机制的基本理论 函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题.基本的参数传递机制有两种:值传递和引用传递.以下讨论称调用其他函数的函数为主调函数,被调用的函数为被调函数. 值传递(passl-by-value)过程中,被调函数的形式参数作为被调函数的局部变量处理,即在堆栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本.值传递的特点是被调函数对形式参数的任何操作都是作为

异常的保护机制

以栈作为基础的SEH本身具有很大的危险性,我们可以利用各种手段对栈上SEH节点进行覆盖重写,再次执行异常处理操作时就会将执行权给到了我们用来覆盖的函数上,这实际上在以前是很常见的windows栈溢出手段,当然,除了这种方法外还有许许多多的利用手段,可见这样的异常处理机制还是不够完善的.为了解决这些问题,微软逐步加入了Safe SEH.SEHOP.VCH等来弥补. Safe SEH SafeSEH又叫做软件DEP,是一种在软件层面实现的对SEH的保护机制,它需要操作系统和编译器的双重支持,在vs2

Java中的内存处理机制和final、static、final static总结

Java中的内存处理机制和final.static.final static总结 装载自:http://blog.csdn.net/wqthaha/article/details/20923579 Java程序运行在JVM上,可以把JVM理解成Java程序和操作系统之间的桥梁,JVM实现了Java的平台无关性,由此可见JVM的重要性.所以在学习Java内存分配原理的时候一定要牢记这一切都是在JVM中进行的,JVM是内存分配原理的基础与前提.         一个完整的Java程序运行过程会涉及以

【安全健行】(6):Windows漏洞保护机制

2015/5/21 11:07:55 之前我们一直在Linux平台上分析漏洞,那是因为对于绝大多数Hacker获得一个Linux平台更加容易,而且主流的服务器系统基本也都是Linux/Unix的:另外一个好处就是Linux提供了用户自定义的强大功能,我们可以根据需要编译汇编程序代码,关闭相应的安全保护机制,便于我们的研究学习. 然而现实中有影响力的漏洞大多是基于Windows系统,因此这节我们来介绍下Winodws系统上是安全保护机制,至于Windows上的漏洞分析,我们会在之后的恶意代码分析章