看下面代码:
#include <stdio.h>
int cp (char *p)
{
int a;
char b[8];
a=strcmp(p,"1234567");
strcpy(b,p);
return a;
}
int main()
{
int v=0;
char p[1024];
while(1)
{
printf("please input password: ");
scanf("%s",p);
v = cp(p);
if(v)
{
printf("incorrect password!\n\n");
}
else
{
printf("Congralations \n\n");
break;
}
}
return 0;
}
在vs2008下面建个工程,我输入“1234567”的时候输出是“Congralations ”,程序是正确的,但是当我输入是“12345678”的时候,崩溃了,于是我把GS关了,结果输出竟然也是“Congralations ”。我开始的时候很奇怪,于是我再试了下还是输出“Congralations ”。于是用OD调试了下····
首先输入是“1234567”的时候:
这个是子函数cp ()里面的反汇编,程序停在了strcpy之后,这时候EAX(函数执行完的返回值)是0,下面的图是堆栈的情况,字符1对应的16进制是31,由于我是intel的cpu,所以是小端模式(高字节存在地位,低字节存在高位),[EBP-12]到[EBP-4]是数组b[8]的内存地址,从低到高依次存放了“1234567”和’\0′,而[EBP-4]存的是a的值,作为返回值,如果是0那么就代表密码输入正确,否则就失败。输入是“1234567”的情况下,结果子函数返回的是0,输出结果也是正确的。
然后我输入“12345678”的情况:
这下就奇怪了,我们看寄存器EAX的值,竟然也是0!真是奇怪了,“12345678”明明比“1234567”大啊,strcmp返回的结果肯定是要比0大的额,再看看[EBP-4]的值也是0额(是它赋值给EAX的),这就意味着返回值a是0啊,按照正常结果返回值应该是大于0的数啊,奇怪了!我们再看看堆栈情况(下图),[EBP-12]到[EBP-4]是数组b[8]的内存地址,从低到高依次存放了“12345678”,那么‘\0’呢?这里已经是栈溢出了,b[8]只有八个字节,只能存取7个字符+’\0′,现在存取了八个字节+’\0′,肯定溢出了。cpu存取的时候是从低地址到高地址的,也就是说[EBP-4]这个字节里面存的应该是’\0′,但是[EBP-4]本来存的是strcmp的返回值啊,原本是大于0 的数,这下被strcpy执行的时候的’\0′覆盖了,所以[EBP-4]这个字节就变成了0。自然程序输出就有问题了。
最后是输入”123456789″的时候:
按照上面的推论,输入”123456789″的时候,在子函数里,[EBP-12]到[EBP-4]是数组b[8]的内存地址,从低到高依次存放了“12345678”,而[EBP-4]字节里面存的应该是’9′的十六进制39,[EBP-3]存取的是’\0′的十六进制0,再后面两个字节也是0。那么CPU读取的时候从高到底低读取,读出来的结果就是00000039,也就是39,那么返回值也就是39。下面实际的结果:
呵呵,果然如此!这下就搞明白了~~~
上面的程序也只是一个例子来演示栈溢出的结果,实际上,在溢出的时候甚至可以覆盖你的EIP,然后马上HOOK到自己的恶意程序,所以一定要小心栈溢出!
博主新地址www.firefoxbug.net