名称:堆栈检查
描述:
- 在Debug模式下将所有栈帧变量初始化为非零值(0xcc)
- 对所有多字节变量(如数组)进行溢出检查 —— 通过在数组的前端和末尾添加八字节cc,并在函数末尾检查这些额外的字节是否还是cc。
测试:
首先开启\RTCs开关: 项目属性 —> C\C++ —> 代码生成 —> 基本运行时检查 —> 堆栈帧(\RTCs)
接下来写一个简单的函数调用,在x64debug中查看开启\RTCs的结果,代码如下:
#include <tchar.h>
int func()
{
char a[8] = "AAAAAAA";
// __int64 b = 13;
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
func();
return 0;
}
查看main函数反汇编:
int _tmain(int argc, _TCHAR* argv[])
{
00000001400010C0 mov qword ptr [rsp+10h],rdx ;将参数2(argv)保存到预留栈空间中
00000001400010C5 mov dword ptr [rsp+8],ecx ;将参数1(argc)保存到预留栈空间中
00000001400010C9 push rdi ;保持栈帧平衡
00000001400010CA sub rsp,20h ;分配栈空间,(20h--4个传参寄存器)
00000001400010CE mov rdi,rsp ;将预留栈空间初始化为0xcc
00000001400010D1 mov ecx,8
00000001400010D6 mov eax,0CCCCCCCCh
00000001400010DB rep stos dword ptr [rdi]
00000001400010DD mov ecx,dword ptr [rsp+30h]
func();
00000001400010E1 call func (14000100Ah)
return 0;
00000001400010E6 xor eax,eax
}
00000001400010E8 add rsp,20h
00000001400010EC pop rdi
00000001400010ED ret
在地址10D8处的 rep stos dword ptr [rdi] 操作结束后,栈帧中便都被初始化为0xcc。在x64debug中查看栈帧内容如下:
查看func函数反汇编:
int func()
{
0000000140001070 push rdi
0000000140001072 sub rsp,40h
0000000140001076 mov rdi,rsp ;将栈空间内容初始化为0xcc
0000000140001079 mov ecx,10h
000000014000107E mov eax,0CCCCCCCCh
0000000140001083 rep stos dword ptr [rdi]
char a[8] = "AAAAAAA";
0000000140001085 mov rax,qword ptr [__xi_z+130h (140006790h)]
000000014000108C mov qword ptr [a],rax
// __int64 b = 13;
return 0;
0000000140001091 xor eax,eax
}
0000000140001093 mov edi,eax
0000000140001095 mov rcx,rsp
0000000140001098 lea rdx,[__xi_z+180h (1400067E0h)]
000000014000109F call _RTC_CheckStackVars (140001190h) ;在此检查数组a[8]前后八字节是否还为0xcc
00000001400010A4 mov eax,edi
00000001400010A6 add rsp,40h
00000001400010AA pop rdi
00000001400010AB ret
在x64dbg中查看func函数栈帧内容:
如上图,绿色框内为4个传参寄存器;两条红线分别在char数组上下,为\RTCs的结果。若再添加一个局部变量,则应在12F4D0处分配。将func函数中的 __int64 b = 13 注释取消。在x64dbg中查看栈帧内容,12F4D0处内容为0D。
可以看出 \RTCs 选项可以在编写代码时,使得缓冲区与其他数据不在直接相邻,从而避免缓冲区溢出。