最新的 Visual Studio 编译器使用运行时检查来检测溢出,它使用各种运行时检查来执行它们
您只能在未优化的构建中使用它们 /Od(这些在不使用 /O1 或 /O2 或 /Ox 的优化构建中不起作用)
这些可以是#pragmas或/RTC1 /RTCS | 你| C命令行开关
通过分配比所需更大的缓冲区并用已知模式填充它来检测堆栈损坏
由于编译器知道应该使用多少空间,它可以检查边界是否被未知模式践踏
(是的,一些聪明的模式匹配漏洞可能仍然会试图欺骗这一点,但它适用于您正在编写无意中溢出的代码的真正用法)
以这个代码为例
#define CRT_SECURE_NO_WARNING
#include <string.h>
#include <stdio.h>
void foo(void){
char flowoverme[0x10];
strcpy(flowoverme,"yaddaaayadddaaafoo");
}
int main(void){
foo();
printf("checking overflows by pattern pasting \n");
}
(如果你使用 /analyze 编译器开关,它会吐出这段代码会溢出
:\>cl /nologo /Zi /RTC1 /analyze /Od /EHsc rtcchk.cpp /link /nologo /debug
rtcchk.cpp
rtcchk.cpp(8) : warning C6386: Buffer overrun while writing to 'flowoverme': the wr
itable size is '16' bytes, but '19' bytes might be written.: Lines: 7, 8
但假设你刚刚做了 cl foo.cpp
:\>cl /nologo /Zi /RTC1 /Od /EHsc rtcchk.cpp /link /nologo /debug
rtcchk.cpp
如果您执行此编译后的代码,如果启用了运行时检查,则不会到达 printf
:\>rtcchk.exe
:\>
我们可以反汇编并查看函数 foo 内部发生了什么以及为什么不执行 printf()
让我们打开 windbg 中的二进制文件,转到 foo() 的开头并要求 windbg 上升(gu 即返回 main() 返回),如下所示,您会注意到 windbg 没有返回到 main 而是停止并显示错误消息
:\>cdb -c "g rtcchk!foo;gu" rtcchk.exe
Microsoft (R) Windows Debugger Version 10.0.16299.15 X86
0:000> cdb: Reading initial command 'g rtcchk!foo;gu'
rtcchk!failwithmessage+0x255:
013d75da cc int 3
并且调用堆栈会显示
0:000> kP
ChildEBP RetAddr
0028f544 013d72a9 rtcchk!failwithmessage(
void * retaddr = 0x013d698a,
int crttype = 0n1,
int errnum = 0n2,
char * msg = 0x0028f568 "Stack around the variable 'flowoverme' was corrupted.")+0x255
0028f96c 013d6c3d rtcchk!_RTC_StackFailure(
void * retaddr = 0x013d698a,
char * varname = 0x013d69b8 "flowoverme")+0x94
0028f98c 013d698a rtcchk!_RTC_CheckStackVars(
void * frame = 0x0028f9b8,
struct _RTC_framedesc * v = 0x013d69a4)+0x42
0028f9b8 013d69d8 rtcchk!foo(void)+0x4a
0028f9c0 013d6ecd rtcchk!main(void)+0x8
(Inline) -------- rtcchk!invoke_main+0x1c
0028fa08 76a9ed6c rtcchk!__scrt_common_main_seh(void)+0xf9
0028fa14 77cb37eb kernel32!BaseThreadInitThunk+0xe
0028fa54 77cb37be ntdll!__RtlUserThreadStart+0x70
0028fa6c 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000>
如果您仍然想知道这些函数是什么或如何运行,请在 vs 中打开 crt 源或反汇编这些函数
编译器知道所需的大小和边界在哪里
0:000> dx -r3 (_RTC_framedesc *) 0x013d69a4
(_RTC_framedesc *) 0x013d69a4 : 0x13d69a4 [Type: _RTC_framedesc *]
[+0x000] varCount : 1 [Type: int]
[+0x004] variables : 0x13d69ac [Type: _RTC_vardesc *]
[+0x000] addr : -24 [Type: int]
[+0x004] size : 16 [Type: int]
[+0x008] name : 0x13d69b8 : "flowoverme" [Type: char *]
0:000>