关于C/C++内存一点小知识

 


这里室友拷了一段代码过来调试,这个过程中发现了一点由使用了未分配的内存造成的一个问题,为此我花了一点时间来调试问题的根源;

代码如下:

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
 int n,i,j,k,a[1000];
 cout<<&n<<endl<<&i<<endl<<&j<<endl<<&k<<endl<<a<<endl<<a+1<<endl<<a+2<<endl<<a+998<<endl<<a+999<<endl<<a+1000<<endl<<a+1001<<endl;
  scanf("%d",&n);
  for(i=1;i<=n;i++)
  {
   a[i]=rand()%900+10;
   cout<<a[i]<<setw(6)<<i<<endl;
  

  }
  cout<<"a[1000] is :"<<a[1000]<<endl;
  cout<<"a[1001] is:"<<a[1001]<<endl;
  cout<<"a[1001] is:"<<&a[1001]<<endl;
  cout<<"the j' address is :"<<&j<<endl;

  for(i=2;i<=n;i++)
  { 
   cout<<"a[1001] is :"<<a[1001]<<endl;
    cout<<"a[1001] is :"<<&a[1001]<<endl;
  // if(i==1000)
  // {
    //cout<<"a[1001] is :"<<a[i+1]<<endl;
    //cout<<"a[1001] is :"<<&a[i+1]<<endl;
   //}
   
  //a[0]=a[i];
  
  //cout<<"a[i] is:"<<a[i]<<endl;
     j=i-1;
  if(j==998)
   cout<<"j is :"<<j<<endl;
  if(i==1000)
   cout<<"when the i is 1000 the j is :"<<j<<endl;

    //while(a[0]<a[j])
    //{
     //a[j+1]=a[j];
     
 
     //j--;
    //}
    
    //a[j+1]=a[0];
    
  }

  for(i=1;i<=n;i++)
   printf("%d ",a[i]);
   printf("\n");
   system("pause");
   return 0;
} 

对于a int型数组申请了1000个元素大小的内存空间,最开始先对a[1]起进行赋值,直到a[1000],在对a赋值结束之后,紧接着查看a[1001]的值,为未赋值的状态,一切正常,但当执行完后面for循环时,发现a[1001]为998,已经有值了,这时我感觉很奇怪,在后面的操作中并未对a[1001]执行任何赋值操作,为什么它的值就变了呢?后来开始挨着注释掉每条语句,发现是由中的j=i-1;这条语句造成a[1001]的改变;紧接着我打印了每个变量的内存地址,包括a[1001]的地址,发现,此处的a[1001]内存地址正是j的内存地址,这就释然了;修改了j的值自然修改了a[1001]内存中的值;
 
以上的整个调试过程,总结了两点:
第一:不要使用为分配的内存,这有可能导致修改到程序中其他变量的值,比如,如果这里我是对a[1001]进行赋值修改,那就等于把j得值给间接修改了,这会对程序造成意想不到的破坏;
第二:C++中变量内存的申请难道是从右到左依次申请的?这里至少是,如下:
cout<<&n<<endl<<&i<<endl<<&j<<endl<<&k<<endl<<a<<endl<<a+1<<endl<<a+2<<endl<<a+998<<endl<<a+999<<endl<<a+1000<<endl<<a+1001<<endl;
int n,i,j,k开始的地址依次是:
0012FF7C
0012FF78
0012FF74
0012FF70
a开始的地址:
0012EFD0
0012EFD4
0012EFD8
0012FF68
0012FF6C
0012FF70
0012FF74



过了几天,在csdn一片C++面试题里发现了这么一道题,和我这里遇到的问题相关,叫缓存溢出:

strcpy()函数

问:下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?


   
   
  1. #include<stdio.h> 
  2.  
  3. int main(int argc, char *argv[]) 
  4.     int flag = 0; 
  5.     char passwd[10]; 
  6.  
  7.     memset(passwd,0,sizeof(passwd)); 
  8.  
  9.     strcpy(passwd, argv[1]); 
  10.  
  11.     if(0 == strcmp("LinuxGeek", passwd)) 
  12.     { 
  13.         flag = 1; 
  14.     } 
  15.  
  16.     if(flag) 
  17.     { 
  18.         printf("\n Password cracked \n"); 
  19.     } 
  20.     else 
  21.     { 
  22.         printf("\n Incorrect passwd \n"); 
  23.  
  24.     } 
  25.     return 0; 

答:破解上述加密的关键在于利用攻破strcpy()函数的漏洞。所以用户在向“passwd”缓存输入随机密码的时候并没有提前检查“passwd”的容量是否足够。所以,如果用户输入一个足够造成缓存溢出并且重写“flag”变量默认值所存在位置的内存的长“密码”,即使这个密码无法通过验证,flag验证位也变成了非零,也就可以获得被保护的数据了。例如:

  
  
  1. $ ./psswd aaaaaaaaaaaaa 
  2.  
  3. Password cracked 

虽然上面的密码并不正确,但我们仍然可以通过缓存溢出绕开密码安全保护。

要避免这样的问题,建议使用 strncpy()函数。

作者注:最近的编译器会在内部检测栈溢出的可能,所以这样往栈里存储变量很难出现栈溢出。在我的gcc里默认就是这样,所以我不得不使用编译命令‘-fno-stack-protector’来实现上述方案。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值