起初遇到下面一个问题:
void fun()
{
char buffer[] = "2014/12/25 16:07:26";
char msg[10] = {0};
sscanf(buffer, "%s", msg);
printf("msg:-%s-\nbuffer:-%s-\n", msg, buffer);
}
屏幕输出结果:
msg:-2014/12/25-
buffer:-2014/12/25 16:07:26-
发现sscanf在遇到空格、字符'\0'时会停止截取,然把上述字符串
中的空格修改成'='后,出现了奇怪的问题。
void fun()
{
char buffer[] = "2014/12/25=16:07:26";
char msg[10] = {0};
sscanf(buffer, "%s", msg);
printf("msg:-%s-\nbuffer:-%s-\n", msg, buffer);
}
屏幕输出结果:
msg:-2014/12/25=16:07:26-
buffer:-6:07:26-
会发现buffer被修改了,原以为是在某些特定情况下sscanf会扰乱
源字符串,但仔细一想问题是处在了栈溢出上,msg的维数不够,
导致(高地址的)buffer的部分内容被篡改。
再仔细研究会发现:如果按照msg的维度10的话,buffer的内容会被
干扰到“=16:07:26”,事实上却是干扰到“6:07:26”。
推测是32位机上存在四个字节的对齐(为了提高查询速率)。
到VC6.0--DEBUG模式下调试
--- F:\C&C++\su\text.cpp ---------------------------------------------------------------------------------------------------------------------------------
58:
59:
60:
61: void fun()
62: {
00401050 push ebp
00401051 mov ebp,esp
00401053 sub esp,60h
00401056 push ebx
00401057 push esi
00401058 push edi
00401059 lea edi,[ebp-60h]
0040105C mov ecx,18h
00401061 mov eax,0CCCCCCCCh
00401066 rep stos dword ptr [edi]
63: char buffer[] = "2014/12/25=16:07:26";
00401068 mov ecx,5
0040106D mov esi,offset string "2014/12/25=16:07:26" (0041b120)
00401072 lea edi,[ebp-14h]
00401075 rep movs dword ptr [edi],dword ptr [esi]
64: char msg[10] = {0};
00401077 mov byte ptr [ebp-20h],0
0040107B xor eax,eax
0040107D mov dword ptr [ebp-1Fh],eax
00401080 mov dword ptr [ebp-1Bh],eax
00401083 mov byte ptr [ebp-17h],al
65:
66: sscanf(buffer, "%s", msg);
00401086 lea ecx,[ebp-20h]
00401089 push ecx
0040108A push offset string "%s" (0041b11c)
0040108F lea edx,[ebp-14h]
00401092 push edx
00401093 call sscanf (00408240)
00401098 add esp,0Ch
67:
68: printf("msg:-%s-\nbuffer:-%s-\n", msg, buffer);
0040109B lea eax,[ebp-14h]
0040109E push eax
0040109F lea ecx,[ebp-20h]
004010A2 push ecx
004010A3 push offset string "msg:-%s-\nbuffer:-%s-\n" (0041b104)
004010A8 call printf (00408180)
004010AD add esp,0Ch
69: }
004010B0 pop edi
004010B1 pop esi
004010B2 pop ebx
004010B3 add esp,60h
004010B6 cmp ebp,esp
004010B8 call __chkesp (00408200)
004010BD mov esp,ebp
004010BF pop ebp
004010C0 ret
--- No source file ---------------------------------------------------------------------------------------------------------------------------------------
004010C1 int 3
004010C2 int 3
004010C3 int 3
004010C4 int 3
004010C5 int 3
004010C6 int 3
004010C7 int 3
004010C8 int 3
004010C9 int 3
004010CA int 3
004010CB int 3
004010CC int 3
004010CD int 3
004010CE int 3
004010CF int 3
--- F:\C&C++\su\text.cpp ---------------------------------------------------------------------------------------------------------------------------------
70:
71:
72:
73: int main(int argc, char* argv[], char* argevn[])
74: {
004010D0 push ebp
004010D1 mov ebp,esp
004010D3 sub esp,40h
004010D6 push ebx
004010D7 push esi
004010D8 push edi
004010D9 lea edi,[ebp-40h]
004010DC mov ecx,10h
004010E1 mov eax,0CCCCCCCCh
004010E6 rep stos dword ptr [edi]
75:
76: fun();
004010E8 call fun (00401050)
77:
78:
79:
80: return 0;
004010ED xor eax,eax
81: }
004010EF pop edi
004010F0 pop esi
004010F1 pop ebx
004010F2 add esp,40h
004010F5 cmp ebp,esp
004010F7 call __chkesp (00408200)
004010FC mov esp,ebp
004010FE pop ebp
004010FF ret
根据在fun()函数被调用前sp(ESP = 0018FEFC)值可找到fun()的栈域起始,
&buffer = 0x0018fee0,&msg = 0x0018fed4则在sscanf()函数被调用前栈布局
大致如下:
0018FE84 48 FF 18 00 轧..H...
0018FE8C 00 00 00 00 00 E0 FD 7E .....帻~
0018FE94 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FE9C CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEA4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEAC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEB4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEBC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEC4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FECC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FED4 00 00 00 00 00 00 00 00 ........
0018FEDC 00 00 CC CC 32 30 31 34 ..烫2014
0018FEE4 2F 31 32 2F 32 35 3D 31 /12/25=1
0018FEEC 36 3A 30 37 3A 32 36 00 6:07:26.
0018FEF4 48 FF 18 00 ED 10 40 00 H.....@.
0018FEFC
fun()函数的调用大致发生如下事情:(1)sp = sp - 4;把ip压入栈中,
即0018FEF8处的004010ED(大端)为执行完fun()函数后,程序的跳转地址。
(2)sp = sp - 4;把bp的内容压入栈中,即0018FEF4处的0018FF48为bp的内容。
(3)sp = sp - 60H;为局部变量开辟了60H大小的空间
(4)依次把接下来要使用到的bx、si、di依次压入栈中,bx原值为7EFDE000,
si原值为00000000,di的原值为0018FF48
(5)将0018FE8C--0018FEEC的内容全部置为CC(int 3中断指令,防止内存中的
东东被意外执行)
(6)为buffer和msg分别赋值。其中会发现0018FEDE和0018FEDF两个字节仍为CC,
则此两个字节为编译期为4个字节边界对齐而留出来的。
(7)执行sscanf()函数,执行完sscanf()后内存大致布局
0018FE84 48 FF 18 00 轧..H...
0018FE8C 00 00 00 00 00 E0 FD 7E .....帻~
0018FE94 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FE9C CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEA4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEAC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEB4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEBC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEC4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FECC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FED4 32 30 31 34 2F 31 32 2F 2014/12/
0018FEDC 32 35 3D 31 36 3A 30 37 25=16:07
0018FEE4 3A 32 36 00 32 35 3D 31 :26.25=1
0018FEEC 36 3A 30 37 3A 32 36 00 6:07:26.
0018FEF4 48 FF 18 00 ED 10 40 00 H.....@.
0018FEFC
则0018FEDE-0018FEE8全部被扰乱。其中buffer的起始地址为0x0018fee0所以buffer也受到了
干扰。
然后依次将buffer的维度修改为9、10、11、12
void fun()
{
char buffer[] = "2014/12/25 16:07:26";
char msg[9] = {0};
sscanf(buffer, "%s", msg);
printf("msg:-%s-\nbuffer:-%s-\n", msg, buffer);
}
则屏幕输出结果一直为:
msg:-2014/12/25=16:07:26-
buffer:-6:07:26-
如果干扰的的程度有足够厉害,足可干扰到栈内bp、ip的值。将msg和buffer调整下位置
&buffer = 0x0018fedc,&msg = 0x0018fee8
void fun()
{
char msg[10] = {0};
char buffer[] = "2014/12/25=";
sscanf(buffer, "%s", msg);
printf("msg:-%s-\nbuffer:-%s-\n", msg, buffer);
}
在sizeof(buffer) = 12时
msg:-2014/12/25=-
buffer:-2014/12/25=-
0018FE8C E8 FE 18 00 48 FF 18 00 棹..H...
0018FE94 00 00 00 00 00 E0 FD 7E .....帻~
0018FE9C CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEA4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEAC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEB4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEBC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEC4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FECC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FED4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEDC 32 30 31 34 2F 31 32 2F 2014/12/
0018FEE4 32 35 3D 00 32 30 31 34 25=.2014
0018FEEC 2F 31 32 2F 32 35 3D 00 /12/25=.
0018FEF4 48 FF 18 00 ED 10 40 00 H.....@.
0018FEFC
继续调整,在sizeof(buffer) = 13时
void fun()
{
char msg[10] = {0};
char buffer[] = "2014/12/25=1";
sscanf(buffer, "%s", msg);
printf("msg:-%s-\nbuffer:-%s-\n", msg, buffer);
}
则会报栈失衡的错误:The value of ESP was not properly saved
across a function call.This is usually a result of calling
a function declared with one calling ...
sscanf()执行前
0018FE98 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEA0 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEA8 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEB0 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEB8 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEC0 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEC8 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FED0 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FED8 32 30 31 34 2F 31 32 2F 2014/12/
0018FEE0 32 35 3D 31 00 CC CC CC 25=1.烫.
0018FEE8 00 00 00 00 00 00 00 00 ........
0018FEF0 00 00 CC CC 48 FF 18 00 ..烫H...
0018FEF8 FD 10 40 00
sscanf()执行后
0018FE98 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEA0 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEA8 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEB0 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEB8 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEC0 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEC8 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FED0 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FED8 32 30 31 34 2F 31 32 2F 2014/12/
0018FEE0 32 35 3D 31 00 CC CC CC 25=1.烫.
0018FEE8 32 30 31 34 2F 31 32 2F 2014/12/
0018FEF0 32 35 3D 31 00 FF 18 00 25=1....
0018FEF8 FD 10 40 00
其中0018FEF5为dp内容由48变成了00
继续调整,在sizeof(buffer) = 17时
void fun()
{
char msg[10] = {0};
char buffer[] = "2014/12/25=16:07";
sscanf(buffer, "%s", msg);
printf("msg:-%s-\nbuffer:-%s-\n", msg, buffer);
}
出现了久违的程序崩溃
sscanf()执行前
0018FE94 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FE9C CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEA4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEAC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEB4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEBC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEC4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FECC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FED4 32 30 31 34 2F 31 32 2F 2014/12/
0018FEDC 32 35 3D 31 36 3A 30 37 25=16:07
0018FEE4 00 CC CC CC 00 00 00 00 .烫.....
0018FEEC 00 00 00 00 00 00 CC CC ......烫
0018FEF4 48 FF 18 00 FD 10 40 00 H.....@.
0018FEFC
sscanf()执行后
0018FE94 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FE9C CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEA4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEAC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEB4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEBC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FEC4 CC CC CC CC CC CC CC CC 烫烫烫烫
0018FECC CC CC CC CC CC CC CC CC 烫烫烫烫
0018FED4 32 30 31 34 2F 31 32 2F 2014/12/
0018FEDC 32 35 3D 31 36 3A 30 37 25=16:07
0018FEE4 00 CC CC CC 32 30 31 34 .烫.2014
0018FEEC 2F 31 32 2F 32 35 3D 31 /12/25=1
0018FEF4 36 3A 30 37 00 10 40 00 6:07..@.
0018FEFC
其中0018FEF8为dp内容由FD变成了00
总结:在VC6.0--DEBUG模式下栈溢出时可能会影响到(1)自己剩余的边界对齐字节
(2)其他更高地址的局部变量(3)bp值,栈失衡(4)ip值,程序的正确运行方向
(5)更高地址的内存......
由于VC6.0--RELEASE模式与DEBUG模式的不同之处是:release直接操作sp,而不会往
栈中压入bp。
阅读(1527) | 评论(0) | 转发(0) |