VC 2005编译器的运行时堆栈检查汇编码解读Author: 徐艺波(xuyibo) Views: 262 UpdateTime:2007-8-9 |
留言 |
微软的VC小组一直很忙,忙着增强VC编译器的功能。不断的有看起来很智能的功能加进来,做为其用户,了解一点Internal还是很有好处的。
这篇文章主要针对VC 2003/2005对堆栈上局部变量的检查的自动检查,相关编译器开关为:/RTCs、/RTCu。VC 2005中这些代码是以.lib或.obj的形式提供的,我从libcmtd.lib中抽出汇编码分析了一部分,不断补充中。
微软的编译器是这么检查:在一个存在局部变量的函数里头,编译器统计函数里头所有可能会引起周围局部变量变化的局部变量的个数(有点绕口)n,在堆栈上分配【192 + 2 * n * sizeof(DWORD) + totalsizeof(n)】个字节,都填充为0xCCCCCCCC,接着push三个寄存器:ebx、esi、edi。你可能会问前面为什么要分配【2 * n * sizeof(DWORD)】个字节,这是重点。编译器为每个需要的检查的局部变量多分配了2个DWORD,一个在前,一个在后,就像栅栏一样,其值都是0xCCCCCCCC,这样在函数结束的时候,检查每个栅栏是否为0xCCCCCCCC,如果不是,显然程序中存在错误。比如,缓冲区溢出这样的错误,这种机制可以很容易的检查出来。
; ; ; Copyright (c) 2007 xuyibo.org ; ; Module Name: ; ; stack.asm ; ; Abstract: ; ; 以下的函数提取在Visual Studio 2005安装目录下libcmtd.lib中的stack.obj. ; ; Author: ; ; xuyibo (xuyibo) 2021-08-09 ; ; Revision History: ; ; .686p .mmx .model flat ; ; Description: ; ; 这个函数用来检验堆栈是否平衡。在执行到这里之前,一般执行下面的动作: ; pop edi ; pop esi ; pop ebx ; add esp, num ; ; 相当于一个空函数执行: ; push ebp ; mov ebp, esp ; … ; cmp ebp, esp ; jnz short $esperror ; retn ; $esperror ; … ; ; __RTC_CheckEsp proc near arg_0 = dword ptr 4 jnz short $esperror$22413 retn $esperror$22413: push ebp mov ebp, esp sub esp, 0 push eax push edx push ebx push esi push edi mov eax, [ebp+arg_0] push 0 push eax call ?_RTC_Failure@@YAXPAXH@Z ; _RTC_Failure(void *,int) add esp, 8 pop edi pop esi pop ebx pop edx pop eax mov esp, ebp pop ebp retn __RTC_CheckEsp endp ; sp = 4 ; ; Description: ; ; 对于一个函数局部变量的检查,VC是这样做的,为没一个局部变量多分配2个DWORD, ; 变量前1个,变量后一个。检查一个函数中所有可能会修改其值的局部变量,比如: ; int* p = &xxx; ; foo(&p); ; foo(p); // 传引用调用 ; 都将检查这个变量的前和后DWORD是否为初始化值0CCCCCCCCh。 ; ; Argument: ; ecx – ebp ebp指向见图2。 ; edx – 指向函数ret后的第一个lable,编译器生成的名字不确定,我把它命名为$Variables。 ; ; $Variables: ; 这后面的两个DWORD保存的是局部变量的个数,和偏移表格$VariableTable ; DD 1 ; 总变量个数 ; DD $VariableTable ; 局部变量偏移表格 ; $VariableTable: ; 这里面保存的是局部变量偏移信息 ; DD -8 ; 局部变量保存位置(ebp – 8) ; DD 4 ; 局部变量大小 ; DD $LN3@main ; $LN3@main: ; DB 105 ; 00000069H ; DB 0 ; ; Stack Layout: ; ; |———-| <- proc start ; | ebp | ; |———-| ; | ecx | ; |———-| ; | ebx | ; |———-| ; | esi | ; |———-| ; | edi | ; |———-| ; 图1 ; ; |—————-| <- enter main ; | ebp | <- ebp ; |—————-| —– ; | | ^ ; | | ; | | ; | | ; | | ; | | ; | | ; | | ; | | ; | cccccccc | 51h = 204h ; | | ; | | ; | | ; | | ; | | ; | | ; | | ; | | ; | | ; | | ; | | | ; |—————-| —– ; | ebx | ; |—————-| ; | esi | ; |—————-| ; | edi | ; |—————-| ; 图2 ; ; ; ; ; __fastcall _RTC_CheckStackVars(x, x) public @_RTC_CheckStackVars@8 @_RTC_CheckStackVars@8 proc near var_4 = dword ptr –4 ; 用来保存现在检查的是第几个变量 push ebp mov ebp, esp push ecx push ebx push esi push edi xor edi, edi ; edi 保存的是现在检查的是第几个变量 mov esi, edx cmp [esi], edi ; esi等于edx=$Variables,edi=0,所以就是将$Variables后面的DD值和0做比较 mov ebx, ecx mov [ebp+var_4], edi jle short loc_7E loc_3A: mov eax, [esi+4] ; eax = $Variables mov ecx, [eax+edi] ; ecx 指向要检查的局部变量在ebp中的偏移,edi保存的是偏移,后面的ecx+ebx得到的就是变量保存位置 add eax, edi ; eax = $Variables[i] cmp dword ptr [ecx+ebx–4], 0CCCCCCCCh ; 就是检查局部变量前的那个dword是否为0CCCCCCCCh,不等的话跳出 jnz short loc_5A mov edx, [eax+4] ; edx = $VariableTable[i]的大小 add edx, ecx ; ecx为变量偏离 + 变量的大小,结果edx就为变量后面那个DWORD的偏移 cmp dword ptr [edx+ebx], 0CCCCCCCCh ; 就是检查局部变量后的那个dword是否为0CCCCCCCCh,不等的话跳出 jz short loc_6E loc_5A: mov eax, [esi+4] ; eax = $Variables mov ecx, [eax+edi+8] ; ecx 指向出错记录 mov edx, [ebp+4] ; edx = [ebp+4] push ecx push edx call ?_RTC_StackFailure@@YAXPAXPBD@Z ; _RTC_StackFailure(void *,char const *) add esp, 8 loc_6E: mov eax, [ebp+var_4] ; eax = ebp add eax, 1 ; eax++ add edi, 0Ch ; edi = 0Ch cmp eax, [esi] ; eax保存的是第几个变量, [esi]保存的是变量数组最高值(有4个变量,其值为3)。 mov [ebp+var_4], eax jl short loc_3A loc_7E: pop edi pop esi pop ebx mov esp, ebp pop ebp retn @_RTC_CheckStackVars@8 endp ; __fastcall _RTC_AllocaHelper(x, x, x) public @_RTC_AllocaHelper@12 @_RTC_AllocaHelper@12 proc near arg_0 = dword ptr 8 push ebp mov ebp, esp push esi mov esi, ecx test esi, esi jz short loc_B1 test edx, edx jz short loc_B1 push ebx mov ebx, [ebp+arg_0] test ebx, ebx jz short loc_B0 push edi mov al, 0CCh ; ‘? mov edi, esi mov ecx, edx rep stosb mov eax, [ebx] mov [esi+4], eax mov [esi+0Ch], edx mov [ebx], esi pop edi loc_B0: pop ebx loc_B1: pop esi pop ebp retn 4 @_RTC_AllocaHelper@12 endp ; __fastcall _RTC_CheckStackVars2(x, x, x) public @_RTC_CheckStackVars2@12 @_RTC_CheckStackVars2@12 proc near var_4 = dword ptr –4 arg_0 = dword ptr 8 push ebp mov ebp, esp push ecx push ebx push esi push edi mov esi, edx xor edi, edi cmp esi, edi mov ebx, ecx jz short loc_116 cmp [esi], edi mov [ebp+var_4], edi jle short loc_116 loc_CE: mov eax, [esi+4] mov ecx, [eax+edi] add eax, edi mov edx, 0CCCCCCCCh cmp [ecx+ebx–4], edx jnz short loc_EB mov eax, [eax+4] add eax, ecx cmp [eax+ebx], edx jz short loc_104 loc_EB: mov ecx, [esi+4] mov edx, [edi+ecx+8] mov eax, [ebp+4] push edx push eax call ?_RTC_StackFailure@@YAXPAXPBD@Z ; _RTC_StackFailure(void *,char const *) add esp, 8 mov edx, 0CCCCCCCCh loc_104: mov eax, [ebp+var_4] add eax, 1 add edi, 0Ch cmp eax, [esi] mov [ebp+var_4], eax jl short loc_CE jmp short loc_11B loc_116: mov edx, 0CCCCCCCCh loc_11B: mov esi, [ebp+arg_0] xor edi, edi test esi, esi mov eax, esi jz short loc_182 loc_126: mov eax, [eax+4] add edi, 1 test eax, eax jnz short loc_126 test esi, esi jz short loc_182 mov edi, edi loc_136: cmp [esi], edx jnz short loc_149 cmp [esi+14h], edx jnz short loc_149 cmp [esi+18h], edx jnz short loc_149 cmp [esi+1Ch], edx jz short loc_15C loc_149: mov ecx, [ebp+4] push edi push esi push ecx call ?_RTC_AllocaFailure@@YAXPAXPAU_RTC_ALLOCA_NODE@@H@Z ; _RTC_AllocaFailure(void *,_RTC_ALLOCA_NODE *,int) add esp, 0Ch mov edx, 0CCCCCCCCh loc_15C: mov eax, [esi+0Ch] cmp [eax+esi–4], edx jz short loc_178 mov ecx, [ebp+4] push edi push esi push ecx call ?_RTC_AllocaFailure@@YAXPAXPAU_RTC_ALLOCA_NODE@@H@Z ; _RTC_AllocaFailure(void *,_RTC_ALLOCA_NODE *,int) add esp, 0Ch mov edx, 0CCCCCCCCh loc_178: mov esi, [esi+4] sub edi, 1 test esi, esi jnz short loc_136 loc_182: pop edi pop esi pop ebx mov esp, ebp pop ebp retn 4 @_RTC_CheckStackVars2@12 endp end |
Email:[email protected] QQ:85521028 Copyright ©2002-2007 XuYibo All rights reserved. License | Contributor |