程序从21行的main()开始执行,在25及26行使用了一对puts()和gets()来提示输入用户名,导致了一个从标准输入到缓冲区字符数组(声明在第4行)的不受控制的字符串复制,程序中的这两处地方都有可能会导致一个缓冲区溢出的漏洞。checkpassword()函数由main()中的27行调用,并在12及13行中提示用户输入密码,这也是使用了一对puts()/gets()。对gets()的第二次调用也会导致一个定义在堆栈上的密码字符数组缓冲区溢出。
程序使用Microsoft Visual C++ 2005编译,并关闭了缓冲区安全检查选项(/GS-),打开了托管扩展(/clr)。默认情况下,缓冲区安全检查是打开的,把它关闭并不是个好做法(如本例所示),而/clr选项可允许由托管及非托管代码生成混合的程序集。
程序生成过程中产生的几个警告信息都可以忽略掉,例如,"warning C4996: 'gets' was declared deprecated"和"warning C4996: 'strcpy' was declared deprecated",编译器推荐使用gets_s()来代替gets(),用strcpy_s()来代替strcpy()。如果完全使用这些替代函数,那么就可消除缓冲区溢出潜在的可能性。然而,这些只是警告信息,可以忽略甚至关闭,忽略这些警告信息是符合用最小的代价移植现有老系统这个前提的。
当使用托管扩展时,编译器会为main()及checkpassword()函数生成Microsoft媒介语言(MSIL或称为通用媒介语言CIL),CIL字节码会被打包进一个可执行文件,在调用即时编译器(JIT)将其翻译为本地程序集指令后,接着把控制权交给main()。
程序运行时,提示用户输入用户名:
| Enter user name: rcs |
| 002DF3D4 00 00 00 00 04 f4 2d 00 a0 1b e7 79 80 63 54 00 ......-....y.cT. 002DF3E4 04 f4 2d 00 f9 0f 0a 02 01 00 00 00 79 3a 4e 00 ..-.........y:N. 002DF3F4 a8 2b 2f 00 38 f4 2d 00 da c4 fc 79 78 f4 2d 00 .+/.8.-....yx.-. 002DF404 48 f4 2d 00 60 13 40 00 01 00 00 00 50 53 54 00 H.-.`.@.....PST. |
| Enter 8 character password: 123456789012345678900|@ |
| 002DF3D4 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 1234567890123456 002DF3E4 37 38 39 30 30 81 40 00 01 00 00 00 79 3a 4e 00 78900.@.....y:N. 002DF3F4 a8 2b 2f 00 38 f4 2d 00 da c4 fc 79 78 f4 2d 00 .+/.8.-....yx.-. 002DF404 48 f4 2d 00 60 13 40 00 01 00 00 00 50 53 54 00 H.-.`.@.....PST. |
![]() 图1:基于"密探"的缓冲区溢出保护 |

