您的位置:首页 > 职场人生

strcpy,一道有趣的面试题

2017-01-20 20:54 190 查看
一道有趣的面试题,题目是这样的:

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

#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是在栈上分配的
4000
!而且栈的地址生长方向是由高地址到低地址!

不妨用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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: