微软官方编译器对于栈的检查

前言

对于的栈的检查,一般都是有编译器来做。也就是说,当某个数据的分配越过规定的范围,就会报异常,那么这个过程是怎么样的呢?本篇来看下.

概括

1.代码示例

#include<stdio.h>#include<Windows.h>void testFunc(char* Buf){    char testBuf[8];    memcpy(testBuf, Buf, 30);    return;}int main(int argc, char** argv){   char Buf[64] = { 0 };   memset(Buf, 0x41, 64);   testFunc(Buf);   getchar();   return 0;}

这里面函数testFunc的testBuf只分配了8个字符串长度,但是memcpy往里面赋值了30个长度字符,所以导致了数组溢出,进而导致栈溢出。

memcpy处下断点x64下的汇编

00007FF6370117D9 41 B8 1E 00 00 00    mov   r8d,1Eh  //1Ehmemcpy的第三个参数00007FF6370117DF 48 8B 95 00 01 00 00 mov   rdx,qword ptr [Buf] //第二个参数 00007FF6370117E6 48 8D 4D 08          lea   rcx,[testBuf] //第一个参数 00007FF6370117EA E8 BF F9 FF FF       call  memcpy (07FF6370111AEh)  //调用00007FF6370117EF 48 8D 4D E0          lea   rcx,[rbp-20h]  //rcx等于进入testFun函数的rsp值00007FF6370117F3 48 8D 15 06 84 00 00 lea   rdx,[__xt_z+160h (07FF637019C00h)] //rdx包含了需要被检查数组testBuf信息00007FF6370117FA E8 03 FB FF FF       call  _RTC_CheckStackVars (07FF637011302h)//调用栈检查函数

这里其实很清晰,主要是rdx包含的被检查数组testBuf的信息。设若rdx地址如下,则内存信息:

0x00007FF637019C00  0000000000000001 00007ff637019bc0

表示1个数组,00007ff637019bc0进一步表示数组的信息

0x00007FF637019BC0  0000000800000028 00007ff637019bb0

这里的00000008表示testBuf数组的字节数,因为是char类型,所以占用8个字节。00000028则表示testBuf起始地址距离rbp的偏移量。00007ff637019bb0表示数组名称,如下所示:

0x00007FF637019BB0  0066754274736574   testBuf.

了解了以上,来看下核心代码:

2.核心

提示:向左边拖动有注释

00007FF637011997 39 1A                cmp         dword ptr [rdx],ebx   //第一个判断,它这个地方判断rdx也即是数组是否为空,00007FF637011999 7E 56                jle         _RTC_CheckStackVars+71h (07FF6370119F1h) //若是为空,直接返回00007FF63701199B 48 89 7C 24 30       mov         qword ptr [rsp+30h],rdi  00007FF6370119A0 8B FB                mov         edi,ebx  00007FF6370119A2 0F 1F 40 00          nop         dword ptr [rax]  00007FF6370119A6 66 66 0F 1F 84 00 00 00 00 00 nop         word ptr [rax+rax]  00007FF6370119B0 48 8B 56 08          mov         rdx,qword ptr [rsi+8]  00007FF6370119B4 48 63 0C 3A          movsxd      rcx,dword ptr [rdx+rdi]  00007FF6370119B8 81 7C 29 FC CC CC CC CC cmp dword ptr [rcx+rbp-4],0CCCCCCCCh  //这里第二个判断,它判断的是数组testBuf的地址前面四个字节是否是0CCCCCCCCh,这里主要判断数组是否正常rbp是rsp的地址,rcx是数组字节长度加上距离rbp偏移量。减4,即时testBuf数组地址前四个字节。00007FF6370119C0 75 11                jne         _RTC_CheckStackVars+53h (07FF6370119D3h)  00007FF6370119C2 48 63 44 3A 04       movsxd rax,dword ptr [rdx+rdi+4]  00007FF6370119C7 48 03 C1             add    rax,rcx  00007FF6370119CA 81 3C 28 CC CC CC CC cmp    dword ptr [rax+rbp],0CCCCCCCCh  //第三个判断,这里相对于第二个判断rax里面多了数组字节长度,加上数组字节长度到了数组的结尾,然后判断数组结尾是否0CCCCCCCCh。这里明显不是,因为超出了,所以下面调用_RTC_StackFailure函数,报异常,表示当前函数出了异常。00007FF6370119D1 74 0F                je     _RTC_CheckStackVars+62h (07FF6370119E2h)  00007FF6370119D3 48 8B 4C 24 28       mov    rcx,qword ptr [rsp+28h]  00007FF6370119D8 48 8B 54 3A 08       mov    rdx,qword ptr [rdx+rdi+8]  00007FF6370119DD E8 71 F8 FF FF       call   _RTC_StackFailure (07FF637011253h)

以上三个判断,第一个判断是数组是否存在,第二个判断数组地址前四个字节是否是0CCCCCCCCh,第三个判断判断数组末尾的四个字节是否是0CCCCCCCCh。这里前两个判断都是正确的,第三个判断错误。因为30超出了数组分配的8个长度,调用_RTC_StackFailure 所以报了异常

以上就是微软官方检测栈的核心代码。

3.结构

参考下图

微软官方编译器对于栈的检查

结尾

点击下方卡片,关注后,后台回复:dotnet7。免费领取一套CLR/JIT/MSIL视频技术教程。你也可以加入我们(可加微信tyz_jhpt,备注:加群。拉你进去),一起学习超级技术。