【Linux】栈保护 栈溢出 stack smashing detected 定位方法

一、Linux开发环境

OS:Ubuntu 16.04.1
编译工具:gcc 5.4.0
调试工具:gdb

二、关键词

栈保护
栈溢出
*** stack smashing detected ***
-fstack-protector
-fno-stack-protector

三、背景

gcc提供栈溢出保护机制,即默认编译时-fstack-protector选项为开。
在该保护机制下,如果程序中有栈溢出,会有以下报错信息,程序异常终止:
*** stack smashing detected ***: ./test terminated
Aborted (core dumped)

若要关闭栈溢出保护,在gcc编译选项中增加-fno-stack-protector即可。
关闭后,如果程序中有栈溢出,仍可能会成功执行完,没有任何报错。
但实际上,栈溢出会导致程序中定义的变量被篡改。

如果你的程序出现上面提到的两种异常:变量被篡改、或者栈溢出导致的异常终止,希望本文会对你有帮助。

四、定位分析

1)第一种情况:变量的值被篡改。

a)源代码(可不看)

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"
#include <ctype.h>

int c2i(char ch)
{
	//如果是数字,则用ascii 减去48
	//isdight 判断数字函数
	if(isdigit(ch))
	{
		return ch-48;
	}
	//判断字母
	if(ch<'A'||(ch>'F'&&ch<'a')||ch>'z')
	{
		return -1;
	}
	//大写字母减去55,小写字母减去87
	if(isalpha(ch))
	{
		return isupper(ch)?ch-55:ch-87;
	}
}

int hex2dec(char *hex)
{
	int len, num,temp,bits,i;
	num = 0;
	len = strlen(hex);
	//printf("len = %d\n",len);
	for(i=0,temp=0;i<len;i++,temp=0)
	{
		//printf("hex = %c\n",hex[i]);
		//printf("num = %d\n",num);
		temp = c2i(*(hex+i));
		//printf("temp = %d\n",temp);
		bits = (len-i-1)*4;
		//printf("bits = %d\n",bits);
		temp = temp << bits;
		//printf("temp = %d\n",temp);
		num = num|temp;
	}
	return num;
}

char* itoa(long int num,char *str,int radix)
{
	char index[] = "0123456789ABCDEF";
	long unsigned unum;
	int i=0,j,k;
	if(radix==10&&num<0)
	{
		unum = (unsigned) -num;
		str[i++] = '-';
	}
	else
		unum = (unsigned)num;
	do
	{
		str[i++] = index[unum%(unsigned)radix];
		unum/=radix;
	}while(unum);
	//printf("str = %s\n",str);
	str[i] = '\0';
	if(str[0]=='-')
	{
		k = 1;
	}
	else
		k = 0;
	for(j = k;j<=(i-1)/2;j++)
	{
		char temp;
		temp = str[j];
		str[j] = str[i-1+k-j];
		str[i-1+k-j] = temp;
	}
	//printf("str = %s\n",str);
	return str;
}

/******************************************************
生成command 的chksum函数
*******************************************************/
char *creatChksum(char *Commandinfo,char* chkstr)
{
	int temp = 0;
	int chksum = 0;
	
	for(int i=1;i<strlen(Commandinfo);i++)
	{
		temp = Commandinfo[i]+temp;
	}
	//printf("creatChksum = %s\n",Commandinfo);
	//printf("creatChksum = %d\n",temp);
	sprintf(chkstr,"%04X",temp);
	return chkstr;
}

double getRandData(int min,int max)
{
      double m1=(double)(rand()%101)/101;                        							  // 计算 0,1之间的随机小数,得到的值域近似为(0,1)
      min++;                                                                            	  //将 区间变为(min+1,max),
      double m2=(double)((rand()%(max-min+1))+min);    										  //计算 min+1,max 之间的随机整数,得到的值域为[min+1,max]
      m2=m2-1;                                                                        		  //令值域为[min,max-1]
      return m1+m2;                                                                			  //返回值域为(min,max),为所求随机浮点数
}

int creatfloatstr(float num,char *str)
{
	
	int rtn = 0;
	long int tempdata;
	float mantissa = 0;
	int stepdata = 0;
	char tempstr[24];
	char mantissastr[24];
	char strstep[9];
	char strsteptemp[9];
	char floatstr[33];
	#if 1
	if(rtn == 0)
	{
		memset(str,'\0',33);
		//printf("%f\n",num);
		if(rtn == 0)
		{
			for(int i=1;i<20;i++)
			{
				num = num/2;
				if(num<2)
				{
					stepdata = i;
					break;
				}
			}
			mantissa = num -1;
			memset(mantissastr,'\0',24);
			long int mantissadata = mantissa*8388608;
			itoa(mantissadata,tempstr,2);
			if(strlen(tempstr)<23)
			{
				int length = 23 -strlen(tempstr);
				for(int i=0;i<length;i++)
				{
					mantissastr[i] = '0';
				}
				strncat(mantissastr,tempstr,23);
			}
			else
			{
				strncpy(mantissastr,tempstr,23);
			}
		}
		if(rtn == 0)
		{
			memset(strsteptemp,'\0',9);
			memset(strstep,'\0',9);
			itoa(stepdata,strsteptemp,2);
			if(strlen(strsteptemp)<8)
			{
				int length = 8 - strlen(strsteptemp);
				for(int i=0;i<length;i++)
				{
					strstep[i] = '0';
				}
				strcat(strstep,strsteptemp);
			}
			else
			{
				strncpy(strstep,strsteptemp,8);
			}
			//printf("strstep = %s\n",strstep);
		}
		if(rtn == 0)
		{
			if(num<0)
			{
				strcat(str,"1");
			}
			else
			{
				strcat(str,"0");
			}
			strcat(str,strstep);
			strcat(str,mantissastr);
			for(int i = 0;i<strlen(str);i++)
			{
				if(str[i] == '1')
				tempdata = tempdata + pow(2,31-i);
			}
			sprintf(str,"%08lX",tempdata);
		}
		//printf("str = %s\n",str);
	}
	#endif
	return rtn;
}

unsigned int virtual_data(char *receivebuf,char *sendbuf)
{
	int rtn = 0;
	int chkNumber = 0;
	char chucksum[5];
	char cid2[3];
	char length[5];
	int datalength=0;
	char *commandinfo;
	char buf[10];
	char data[8];
	int len = strlen(receivebuf);
	
	memset(buf, '\0', 10);
	memcpy(buf, "ABCDEFGH", 8);
	
	printf("------- buf = %s, line[%d]\n", buf, __LINE__);
	
	if(rtn == 0)
	{
		//首先chucksum
		if(rtn == 0)
		{
			//字符串转数字,计算chksum值
			memset(chucksum,'\0',4);
			for(int i = 0;i<4;i++)
			{
				chucksum[i] = receivebuf[len-6+i];
			}
			chucksum[4] = '\0';
			strncpy(chucksum,chucksum,4);
			for(int i = 1;i<len-6;i++)
			{
				chkNumber = chkNumber + receivebuf[i];
				//printf("receivebuf[i]=%c\n",receivebuf[i]);	
			}
			chkNumber = chkNumber%65536;
			chkNumber = ~chkNumber+1+65536;
			
			if(chkNumber != hex2dec(chucksum))
			{
				//printf("chkNumber=%d\n",chkNumber);			
				//printf("hex2dec(chucksum)=%d\n",hex2dec(chucksum));

				rtn = -1;
			}
		}
		//解析设备类型,填充设备数据,主要解析cid2 length commandinfo chucksum
		#if 1
		if(rtn == 0)
		{
			//填充sendbuf 
			strncpy(sendbuf,receivebuf,7);
			memset(cid2,'\0',3);
			*(cid2) = *(receivebuf+7);
			*(cid2+1) = *(receivebuf+8);
			//printf("%s\n",cid2);
			if(strcmp(cid2,"41") == 0)
			{
				//默认数据正常 rtn 赋值00
				strncat(sendbuf,"00",2);
				memset(length,'\0',5);
				//数据为浮点型数据,处理数据长度
				for(int i = 0;i<4;i++)
				{
					length[i] = receivebuf[10+i];
				}
				strncat(sendbuf,length,4);
				length[3] = '\0';
				//printf("length = %s\n",length);
				datalength = hex2dec(length)/2;
				//printf("datalength = %d\n",datalength);
				
				for(int i=0;i<datalength;i++)
				{
					
					memset(data,'\0',9);
					creatfloatstr(getRandData(0,100),data);
					strncat(sendbuf,data,8);
				}
				

				//memset(sendbuf,'\0',200);
				creatChksum(sendbuf,chucksum);
				strncat(sendbuf,chucksum,4);
				strncat(sendbuf,"CR",2);
			}
			if(strcmp(cid2,"42") == 0)
			{
				//默认数据正常 rtn 赋值00
				strncat(sendbuf,"00",2);
				//数据为整型数据,处理数据长度
				for(int i = 0;i<4;i++)
				{
					length[3-i] = receivebuf[13+i];
				}
				strncat(sendbuf,length,4);
				length[3] = '0';
				datalength = hex2dec(length);
				for(int i=0;i<datalength;i++)
				{
					memset(data,'\0',8);
					sprintf(data,"%04X",rand()%101);
					strncat(sendbuf,data,8);
				}
				memset(sendbuf,'\0',4);
				creatChksum(sendbuf,chucksum);
				strncat(sendbuf,chucksum,4);
				strncat(sendbuf,"CR",2);
			}
		}
		#endif
	}	
	
	printf("------- buf = %s, line[%d]\n", buf, __LINE__);
	
	return rtn;
}

int main()
{
	int rtn = 0;
	char *receivebuf = malloc(256*sizeof(char));
	strncpy(receivebuf,"~20012441E00201FD3ACR",32*sizeof(char));
	char sendbuf[200];

	rtn = virtual_data(receivebuf,sendbuf);
	//printf("send=%s\n",sendbuf);
	exit(0);
	return 0;
}

b)编译

gcc -g -o test main.c –lm

c)运行

在这里插入图片描述
发现buf中的数据被篡改!通过阅读程序,buf在virtual_data函数中定义,之后便再无赋值,那么应该 是在两次打印之间被篡改的。像这种情况,首先怀疑的就是发生了内存越界!

d)定位

在这里插入图片描述

e)总结

程序中能明确看到某个变量的值被篡改,这种情况一般定位思路:
使用watch命令观察该变量;
执行c继续运行,程序会停在观察点发生变化的地方;
执行where命令,查看当前的位置,即定位到哪行语句篡改了被观察的变量。

2)第二种情况:程序报”stack smashing detected”然后终止。

a)源代码(可不看)

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"
#include <ctype.h>

int c2i(char ch)
{
	//如果是数字,则用ascii 减去48
	//isdight 判断数字函数
	if(isdigit(ch))
	{
		return ch-48;
	}
	//判断字母
	if(ch<'A'||(ch>'F'&&ch<'a')||ch>'z')
	{
		return -1;
	}
	//大写字母减去55,小写字母减去87
	if(isalpha(ch))
	{
		return isupper(ch)?ch-55:ch-87;
	}
}

int hex2dec(char *hex)
{
	int len, num,temp,bits,i;
	num = 0;
	len = strlen(hex);
	//printf("len = %d\n",len);
	for(i=0,temp=0;i<len;i++,temp=0)
	{
		//printf("hex = %c\n",hex[i]);
		//printf("num = %d\n",num);
		temp = c2i(*(hex+i));
		//printf("temp = %d\n",temp);
		bits = (len-i-1)*4;
		//printf("bits = %d\n",bits);
		temp = temp << bits;
		//printf("temp = %d\n",temp);
		num = num|temp;
	}
	return num;
}

char* itoa(long int num,char *str,int radix)
{
	char index[] = "0123456789ABCDEF";
	long unsigned unum;
	int i=0,j,k;
	if(radix==10&&num<0)
	{
		unum = (unsigned) -num;
		str[i++] = '-';
	}
	else
		unum = (unsigned)num;
	do
	{
		str[i++] = index[unum%(unsigned)radix];
		unum/=radix;
	}while(unum);
	//printf("str = %s\n",str);
	str[i] = '\0';
	if(str[0]=='-')
	{
		k = 1;
	}
	else
		k = 0;
	for(j = k;j<=(i-1)/2;j++)
	{
		char temp;
		temp = str[j];
		str[j] = str[i-1+k-j];
		str[i-1+k-j] = temp;
	}
	//printf("str = %s\n",str);
	return str;
}

/******************************************************
生成command 的chksum函数
*******************************************************/
char *creatChksum(char *Commandinfo,char* chkstr)
{
	int temp = 0;
	int chksum = 0;
	
	for(int i=1;i<strlen(Commandinfo);i++)
	{
		temp = Commandinfo[i]+temp;
	}
	//printf("creatChksum = %s\n",Commandinfo);
	//printf("creatChksum = %d\n",temp);
	sprintf(chkstr,"%04X",temp);
	return chkstr;
}

double getRandData(int min,int max)
{
      double m1=(double)(rand()%101)/101;                        							  // 计算 0,1之间的随机小数,得到的值域近似为(0,1)
      min++;                                                                            	  //将 区间变为(min+1,max),
      double m2=(double)((rand()%(max-min+1))+min);    										  //计算 min+1,max 之间的随机整数,得到的值域为[min+1,max]
      m2=m2-1;                                                                        		  //令值域为[min,max-1]
      return m1+m2;                                                                			  //返回值域为(min,max),为所求随机浮点数
}

int creatfloatstr(float num,char *str)
{
	
	int rtn = 0;
	long int tempdata;
	float mantissa = 0;
	int stepdata = 0;
	char tempstr[24];
	char mantissastr[24];
	char strstep[9];
	char strsteptemp[9];
	char floatstr[33];
	#if 1
	if(rtn == 0)
	{
		memset(str,'\0',64);
		//printf("%f\n",num);
		if(rtn == 0)
		{
			for(int i=1;i<20;i++)
			{
				num = num/2;
				if(num<2)
				{
					stepdata = i;
					break;
				}
			}
			mantissa = num -1;
			memset(mantissastr,'\0',24);
			long int mantissadata = mantissa*8388608;
			itoa(mantissadata,tempstr,2);
			if(strlen(tempstr)<23)
			{
				int length = 23 -strlen(tempstr);
				for(int i=0;i<length;i++)
				{
					mantissastr[i] = '0';
				}
				strncat(mantissastr,tempstr,23);
			}
			else
			{
				strncpy(mantissastr,tempstr,23);
			}
		}
		if(rtn == 0)
		{
			memset(strsteptemp,'\0',9);
			memset(strstep,'\0',9);
			itoa(stepdata,strsteptemp,2);
			if(strlen(strsteptemp)<8)
			{
				int length = 8 - strlen(strsteptemp);
				for(int i=0;i<length;i++)
				{
					strstep[i] = '0';
				}
				strcat(strstep,strsteptemp);
			}
			else
			{
				strncpy(strstep,strsteptemp,8);
			}
			//printf("strstep = %s\n",strstep);
		}
		if(rtn == 0)
		{
			if(num<0)
			{
				strcat(str,"1");
			}
			else
			{
				strcat(str,"0");
			}
			strcat(str,strstep);
			strcat(str,mantissastr);
			for(int i = 0;i<strlen(str);i++)
			{
				if(str[i] == '1')
				tempdata = tempdata + pow(2,31-i);
			}
			sprintf(str,"%08lX",tempdata);
		}
		//printf("str = %s\n",str);
	}
	#endif
	return rtn;
}

unsigned int virtual_data(char *receivebuf,char *sendbuf)
{
	int rtn = 0;
	int chkNumber = 0;
	char chucksum[5];
	char cid2[3];
	char length[5];
	int datalength=0;
	char *commandinfo;
	char buf[10];
	char data[8];
	int len = strlen(receivebuf);
	
	memset(buf, '\0', 10);
	memcpy(buf, "ABCDEFGH", 8);
	
	//printf("------- buf = %s, line[%d]\n", buf, __LINE__);
	
	if(rtn == 0)
	{
		//首先chucksum
		if(rtn == 0)
		{
			//字符串转数字,计算chksum值
			memset(chucksum,'\0',4);
			for(int i = 0;i<4;i++)
			{
				chucksum[i] = receivebuf[len-6+i];
			}
			chucksum[4] = '\0';
			strncpy(chucksum,chucksum,4);
			for(int i = 1;i<len-6;i++)
			{
				chkNumber = chkNumber + receivebuf[i];
				//printf("receivebuf[i]=%c\n",receivebuf[i]);	
			}
			chkNumber = chkNumber%65536;
			chkNumber = ~chkNumber+1+65536;
			
			if(chkNumber != hex2dec(chucksum))
			{
				//printf("chkNumber=%d\n",chkNumber);			
				//printf("hex2dec(chucksum)=%d\n",hex2dec(chucksum));

				rtn = -1;
			}
		}
		//解析设备类型,填充设备数据,主要解析cid2 length commandinfo chucksum
		#if 1
		if(rtn == 0)
		{
			//填充sendbuf 
			strncpy(sendbuf,receivebuf,7);
			memset(cid2,'\0',3);
			*(cid2) = *(receivebuf+7);
			*(cid2+1) = *(receivebuf+8);
			//printf("%s\n",cid2);
			if(strcmp(cid2,"41") == 0)
			{
				//默认数据正常 rtn 赋值00
				strncat(sendbuf,"00",2);
				memset(length,'\0',5);
				//数据为浮点型数据,处理数据长度
				for(int i = 0;i<4;i++)
				{
					length[i] = receivebuf[10+i];
				}
				strncat(sendbuf,length,4);
				length[3] = '\0';
				//printf("length = %s\n",length);
				datalength = hex2dec(length)/2;
				//printf("datalength = %d\n",datalength);
				
				for(int i=0;i<datalength;i++)
				{
					
					memset(data,'\0',9);
					creatfloatstr(getRandData(0,100),data);
					strncat(sendbuf,data,8);
				}
				

				//memset(sendbuf,'\0',200);
				creatChksum(sendbuf,chucksum);
				strncat(sendbuf,chucksum,4);
				strncat(sendbuf,"CR",2);
			}
			if(strcmp(cid2,"42") == 0)
			{
				//默认数据正常 rtn 赋值00
				strncat(sendbuf,"00",2);
				//数据为整型数据,处理数据长度
				for(int i = 0;i<4;i++)
				{
					length[3-i] = receivebuf[13+i];
				}
				strncat(sendbuf,length,4);
				length[3] = '0';
				datalength = hex2dec(length);
				for(int i=0;i<datalength;i++)
				{
					memset(data,'\0',8);
					sprintf(data,"%04X",rand()%101);
					strncat(sendbuf,data,8);
				}
				memset(sendbuf,'\0',4);
				creatChksum(sendbuf,chucksum);
				strncat(sendbuf,chucksum,4);
				strncat(sendbuf,"CR",2);
			}
		}
		#endif
	}	
	
	//printf("------- buf = %s, line[%d]\n", buf, __LINE__);
	
	return rtn;
}

int main()
{
	int rtn = 0;
	char *receivebuf = malloc(256*sizeof(char));
	strncpy(receivebuf,"~20012441E00201FD3ACR",32*sizeof(char));
	char sendbuf[200];

	rtn = virtual_data(receivebuf,sendbuf);
	//printf("send=%s\n",sendbuf);
	exit(0);
	return 0;
}

b)编译

gcc -g -o test main.c –lm

c)运行

在这里插入图片描述
此种情况看不到程序内部变量是否发生篡改,会直接退出程序,所以不适合用watch。但是又确实发生了栈溢出,在栈保护机制的作用下,程序并不会在执行到导致栈溢出的那行语句时就报错然后终止,而是把子函数全部执行完,再回到main函数中去。但是由于栈已经被破坏,在往main函数跳的时候,就无法获取main函数的栈空间了,所以异常终止。

d)定位

在这里插入图片描述

e)总结

即先看堆栈,大致确定是哪个函数。然后进到该函数中,逐条语句执行、查看堆栈信息,直到看到栈被破坏。
遇到函数调用层次比较多时,效率会比较低,但也算有效。
  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值