前言
对于的栈的检查,一般都是有编译器来做。也就是说,当某个数据的分配越过规定的范围,就会报异常,那么这个过程是怎么样的呢?本篇来看下
概括
1.代码示例
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,备注:加群。拉你进去),一起学习超级技术。