strcpy,一道有趣的面试题

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

#include <stdio.h> 
#include <string.h>

int main(int argc, char *argv[]) 
{ 
    int flag = 0; 
    char passwd[10]; 

    memset(passwd,0,sizeof(passwd)); 
    strcpy(passwd, argv[1]); 

    if(0 == strcmp("LinuxGeek", passwd)) 
    { 
        flag = 1; 
    } 

    if(flag) 
    { 
        printf("\n Password cracked \n"); 
    } 
    else 
    { 
        printf("\n Incorrect passwd \n"); 
    } 
    return 0; 
}

coder的愿意是想验证用户输入的密码,正确的密码应该是”LinuxGeek”,但是这不是个鲁棒的设计,以至于给了他人以“攻破”密码的机会。
事实上只要用户输入的密码超过”一定位数”,那么“LinuxGeek”将形同虚设。

[root@localhost project]# g++ -g strcpy.cpp -o run
[root@localhost project]# ./run 0123456789
 Incorrect passwd 
[root@localhost project]# ./run 01234567890
 Password cracked 
[root@localhost project]# ./run 0123456789a
 Password cracked 
[root@localhost project]# ./run 0123456789ab
 Password cracked 

问题的根源在于:
1.strcpy并不是一个安全的拷贝方式
//把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间
char strcpy(char dest, const char *src);

char passwd[10];
strcpy(passwd, argv[1]);
将输入的密码拷贝到以passwd的地址空间,
如果argv[1]内容的长度超过10位了?argv[1]内容的内容将继续接着原地址写,直到遇到argv[1]的结束符!
如果flag的地址紧随passwd地址之后了?那么flag地址指向的内容将被argv[1]的内容“侵占”!
如果“被侵占”的的内容不为0,那么if(flag)将为true,即使argv[1]内容不是”LinuxGeek”

2.如果argv[1]内容长度足够长,flag一定会被“侵占”吗?或者说flag的地址一定在passwd地址之后吗?
是的,一定!因为 flag和passwd是在栈上分配的!而且栈的地址生长方向是由高地址到低地址!
不妨用gdb来看下吧!
在第10行设置断点,打印flag和passwd的地址

Breakpoint 1, main (argc=2, argv=0xbfffe9f4) at strcpy.cpp:10
10      strcpy(passwd, argv[1]); 
(gdb) print &flag
$1 = (int *) 0xbfffe948
(gdb) print &passwd
$2 = (char (*)[10]) 0xbfffe93e

3.这里还有另一个问题?在本题中,输入的passwd的长度到底要为多长时,才能cracked密码了?或者passwd的地址一定紧随flag吗?
cracked密码的长度当然取决于passwd和flag的地址;而passwd的地址并不一定紧随flag,因为还受到“内存对齐”的影响!

4.为什么这种“溢出”,还能正常编译并运行了?
取决于编译器,在我的gcc 4.1.2,编译和运行都不会有异常提示,但是在vs2010上运行时,则给出了提示。

5.为什么“./run 01234567890”将“0”写到flag地址上也可以了?
其实并不是把“0”写给flag,而是ASCII码 48

那么请使用strncpy吧!
//把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回dest。
char *strncpy(char *dest, const char *src, int n);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值