0xCC

在VC++编写的程序编程成Debug版,反汇编代码,函数入口处经常看到如下一段代码:

00EA14E0 55                       push        ebp  
00EA14E1 8B EC                    mov         ebp,esp
00EA14E3 81 EC C0 00 00 00        sub         esp,0C0h
00EA14E9 53                       push        ebx  
00EA14EA 56                       push        esi  
00EA14EB 57                       push        edi  
00EA14EC 8D BD 40 FF FF FF        lea         edi,[ebp-0C0h]
00EA14F2 B9 30 00 00 00           mov         ecx,30h
00EA14F7 B8 CC CC CC CC           mov         eax,0CCCCCCCCh
00EA14FC F3 AB                    rep stos    dword ptr es:[edi]

其中主要分为3部分,最上面3行完成本函数局部变量栈空间的开辟,中间部分保存需要保护的寄存器(这部分并非必须),最后4行完成栈空间所有字节初始化为0xCC。
0xCC对应汇编代码 int 3,即软断点。

一方面,0xCC可以为某些检查提供标记(_RTC_CheckStackVars);另一方面,在栈操作错误或调试汇编代码(如使用OD调试shellcode)时可以对感兴趣的逻辑进行细致的分析,这通常可以通过加入

__asm {
    int 3;   // 或 _emit 0xcc
};

来加入断点。

_RTC_CheckEsp

注意,这个是在Debug下build时才有的,用于快速定位debug版程序栈操作错误。跟/GS是没有关系的。

esp_check: 堆栈平衡检查,在调用printf前保存esp到esi,在调用printf后,比较esi与esp达到检查esp的目的。

010A14FE 8B F4             mov         esi,esp                                           
010A1500 68 78 57 0A 01    push        offset string "call printf.\n" (10A5778h)    
010A1505 FF 15 BC 82 0A 01 call        dword ptr [__imp__printf (10A82BCh)]             
010A150B 83 C4 04          add         esp,4                                            
010A150E 3B F4             cmp         esi,esp                                          
010A1510 E8 35 FC FF FF    call        @ILT+325(__RTC_CheckEsp) (10A114Ah)     

_RTC_CheckEsp:
010A15D0 75 01             jne         esperror (10A15D3h)
010A15D2 C3                ret  

esperror:
010A15D3 55                push        ebp  
010A15D4 8B EC             mov         ebp,esp
010A15D6 83 EC 00          sub         esp,0
010A15D9 50                push        eax  
010A15DA 52                push        edx  
010A15DB 53                push        ebx  
010A15DC 56                push        esi  
010A15DD 57                push        edi  
010A15DE 8B 45 04          mov         eax,dword ptr [ebp+4]
010A15E1 6A 00             push        0    
010A15E3 50                push        eax  
010A15E4 E8 C5 FB FF FF    call        _RTC_Failure (10A11AEh)
010A15E9 83 C4 08          add         esp,8
010A15EC 5F                pop         edi  
010A15ED 5E                pop         esi  
010A15EE 5B                pop         ebx  
010A15EF 5A                pop         edx  
010A15F0 58                pop         eax  
010A15F1 8B E5             mov         esp,ebp
010A15F3 5D                pop         ebp  
010A15F4 C3                ret  

这里没有继续深入_RTC_Failure,这个是进行错误提示终止程序的,或许与下面的_RTC_CheckStackVars提示类似。

_RTC_CheckStackVars

_RTC_CheckStackVars 是新版VS(没有深究从哪个版本开始,但vs2008是有这个特性的)为Debug下build的程序提供栈操作错误的检查,方便在快速发现错误。下面是相关的处理流程,这个和/GS保护机制不是一回事。

struct SVarsInfo
{
    DWORD dwVarNum;   //需要验证的参数个数
    struct SVarBlock
    {
        DWORD dwOffset;   //相对当前ESP的偏移,由于栈向下增长,所以这里是个 负数
        DWORD dwSize;     //当前参数占用的字节总数
        PCHAR strVarName; //当前变量的名称
    }* pVarBlockArr;    //这个是指向存储变量详细信息数据的数组的指针
}

_RTC_CheckStackVars(PVOID pVarInfo, _RTC_framedesc* pEBP)
{
    DWORD dwEBP = pEBP;
    SVarsInfo* varsInfo = (SVarInfo*)pVarInfo;
    for(int i=0; idwVarNum; i++)
    {
        SVarBlock* varN = varsInfo->pVarBlockArr;
        if(*(PDWORD)(dwEBP+varN->dwOffset-4)!=0xCCCCCCCC)
            _RTC_StackFailure(参数首部, 参数尾部); 
        if(*(PDWORD)(dwEBP+varN->dwOffset+varN->dwSize)!=0xCCCCCCCC)
            _RTC_StackFailure(参数首部, 参数尾部);
    }
}

上面是基本的_RTC_CheckStackVars的伪代码逻辑。

报错内容如下:

---------------------------
Microsoft Visual C++ Debug Library
---------------------------
Debug Error!

Program: e:\path\to\my.exe
Module: e:\path\to\my.exe
File: e:\path\to\my.cpp
Line: 19

Run-Time Check Failure #2 - Stack around the variable 'strTemp' was corrupted.

(Press Retry to debug the application)
---------------------------
中止(A) 重试(R) 忽略(I)
---------------------------