strcpy,一道有趣的面试题
2017-01-20 20:54
190 查看
一道有趣的面试题,题目是这样的:
问:下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?
coder的愿意是想验证用户输入的密码,正确的密码应该是”LinuxGeek”,但是这不是个鲁棒的设计,以至于给了他人以“攻破”密码的机会。
事实上只要用户输入的密码超过”一定位数”,那么“LinuxGeek”将形同虚设。
问题的根源在于:
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的地址
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);
问:下面是一个简单的密码保护功能,你能在不知道密码的情况下将其破解吗?
#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);
相关文章推荐
- 一道有趣的面试题 (转)
- 一道有趣的逻辑面试题(数独)
- 一道有趣的面试算法题
- 面试中必须会写的函数源码--------strcpy()与strlen()
- poj 1189 钉子和小球 一道有趣的题
- 【白话经典算法系列之十一】一道有趣的GOOGLE面试题 --【解法2】 .
- 程序员有趣的面试智力题
- 昨天面试遇到的一道C语言题
- 内存对齐的一道面试题目
- 关于Notify() wait()的一道面试题目
- 一道很有趣的面试题——————SQL
- 关于byte的一道有趣的题目
- 一道有趣的字符串部分转逆算法题
- 一道简单有趣的C语言面试题
- 一道概率题---网易面试
- 一道面试附加题的另类求解
- 面试时碰到的一道数据库题目
- 一道字符串复制的面试题目
- 一道猴子选大王的面试老题
- 微软面试100题系列:一道合并链表问题的解答[第42题]